1792 lines
46 KiB
C++
1792 lines
46 KiB
C++
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: notify.cxx
|
|
//
|
|
// Contents: KDC password change notification code
|
|
//
|
|
//
|
|
// History: 19-Aug-1996 MikeSw Created
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include "kdcsvr.hxx"
|
|
extern "C"
|
|
{
|
|
#include <dns.h> // DNS_MAX_NAME_LENGTH
|
|
#include <ntdsa.h> // CrackSingleName
|
|
}
|
|
|
|
SAMPR_HANDLE KdcNotifyAccountDomainHandle = NULL;
|
|
UNICODE_STRING KdcNotifyDnsDomainName;
|
|
UNICODE_STRING KdcNotifyDomainName;
|
|
RTL_CRITICAL_SECTION KdcNotifyCritSect;
|
|
BOOLEAN KdcNotificationInitialized;
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcNotifyOpenAccountDomain
|
|
//
|
|
// Synopsis: Opens the account domain and stores a handle to it.
|
|
//
|
|
// Effects: Sets KdcNotifyAccountDomainHandle on success.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KdcNotifyOpenAccountDomain(
|
|
OUT SAMPR_HANDLE * AccountDomainHandle
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAPR_POLICY_INFORMATION PolicyInformation = NULL;
|
|
SAMPR_HANDLE ServerHandle = NULL;
|
|
|
|
|
|
Status = LsaIQueryInformationPolicyTrusted(
|
|
PolicyDnsDomainInformation,
|
|
&PolicyInformation
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to query information policy: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbDuplicateString(
|
|
&KdcNotifyDomainName,
|
|
(PUNICODE_STRING) &PolicyInformation->PolicyDnsDomainInfo.Name
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbDuplicateString(
|
|
&KdcNotifyDnsDomainName,
|
|
(PUNICODE_STRING) &PolicyInformation->PolicyDnsDomainInfo.DnsDomainName
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
Status = RtlUpcaseUnicodeString(
|
|
&KdcNotifyDnsDomainName,
|
|
&KdcNotifyDnsDomainName,
|
|
FALSE // don't allocate
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Connect to SAM and open the account domain
|
|
//
|
|
|
|
Status = SamIConnect(
|
|
NULL, // no server name
|
|
&ServerHandle,
|
|
0, // ignore desired access,
|
|
TRUE // trusted caller
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to connect to SAM: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Finally open the account domain.
|
|
//
|
|
|
|
Status = SamrOpenDomain(
|
|
ServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
(PRPC_SID) PolicyInformation->PolicyDnsDomainInfo.Sid,
|
|
AccountDomainHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to open account domain: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if (PolicyInformation != NULL)
|
|
{
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyDnsDomainInformation,
|
|
PolicyInformation
|
|
);
|
|
}
|
|
|
|
if (ServerHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&ServerHandle);
|
|
}
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcBuildPasswordList
|
|
//
|
|
// Synopsis: Builds a list of passwords for a user that just changed
|
|
// their password.
|
|
//
|
|
// Effects: allocates memory
|
|
//
|
|
// Arguments: Password - clear or OWF password
|
|
// PrincipalName - Name of principal
|
|
// MarshallKeys - if TRUE, the keys will be marshalled
|
|
// IncludeBuiltinTypes - if TRUE, include MD4 & LM hashes
|
|
// PasswordList - Receives new password list
|
|
// PasswordListSize - Size of list in bytes.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KdcBuildPasswordList(
|
|
IN PUNICODE_STRING Password,
|
|
IN PUNICODE_STRING PrincipalName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN KERB_ACCOUNT_TYPE AccountType,
|
|
IN PKERB_STORED_CREDENTIAL StoredCreds,
|
|
IN ULONG StoredCredSize,
|
|
IN BOOLEAN MarshallKeys,
|
|
IN BOOLEAN IncludeBuiltinTypes,
|
|
IN ULONG Flags,
|
|
IN KDC_DOMAIN_INFO_DIRECTION Direction,
|
|
OUT PKERB_STORED_CREDENTIAL * PasswordList,
|
|
OUT PULONG PasswordListSize
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
|
|
ULONG CryptCount = 0;
|
|
PKERB_STORED_CREDENTIAL Credentials = NULL;
|
|
ULONG CredentialSize = 0;
|
|
ULONG KerbEncryptionKeyCount = 0;
|
|
ULONG KerbKeyDataCount = 0;
|
|
PCRYPTO_SYSTEM CryptoSystem;
|
|
PCHECKSUM_FUNCTION CheckSum;
|
|
ULONG Index, CredentialIndex = 0;
|
|
PUCHAR Base, KeyBase;
|
|
ULONG Offset;
|
|
ULONG OldCredCount = 0;
|
|
KERB_ENCRYPTION_KEY TempKey;
|
|
UNICODE_STRING KeySalt = {0};
|
|
UNICODE_STRING EmptySalt = {0};
|
|
USHORT OldFlags = 0;
|
|
|
|
|
|
*PasswordList = NULL;
|
|
*PasswordListSize = 0;
|
|
|
|
//
|
|
// If we had passed in an OWF, then there is just one password.
|
|
//
|
|
|
|
if ((Flags & KERB_PRIMARY_CRED_OWF_ONLY) != 0)
|
|
{
|
|
CredentialSize += Password->Length + sizeof(KERB_ENCRYPTION_KEY);
|
|
KerbEncryptionKeyCount++;
|
|
#ifndef DONT_SUPPORT_OLD_TYPES
|
|
CredentialSize += Password->Length + sizeof(KERB_ENCRYPTION_KEY);
|
|
KerbEncryptionKeyCount++;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The salt is the realm name concatenated with the principal name
|
|
//
|
|
|
|
if (AccountType != UnknownAccount)
|
|
{
|
|
//
|
|
// For inbound trust, swap the domain names
|
|
//
|
|
|
|
if ((AccountType == DomainTrustAccount) &&
|
|
(Direction == Inbound))
|
|
{
|
|
if (!KERB_SUCCESS(KerbBuildKeySalt(
|
|
PrincipalName,
|
|
DomainName,
|
|
AccountType,
|
|
&KeySalt
|
|
)))
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!KERB_SUCCESS(KerbBuildKeySalt(
|
|
DomainName,
|
|
PrincipalName,
|
|
AccountType,
|
|
&KeySalt
|
|
)))
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KeySalt = *PrincipalName;
|
|
}
|
|
D_DebugLog((DEB_TRACE,"Building key list with salt %wZ\n",&KeySalt));
|
|
|
|
//
|
|
// For a cleartext password, build a list of encryption types and
|
|
// create a key for each one
|
|
//
|
|
|
|
Status = CDBuildIntegrityVect(
|
|
&CryptCount,
|
|
CryptTypes
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
|
|
|
|
//
|
|
// Now find the size of the key for each crypto system
|
|
//
|
|
|
|
for (Index = 0; Index < CryptCount; Index++ )
|
|
{
|
|
//
|
|
// Skip etypes stored as normal passwords
|
|
//
|
|
|
|
if (!IncludeBuiltinTypes &&
|
|
((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_NULL)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Status = CDLocateCSystem(
|
|
CryptTypes[Index],
|
|
&CryptoSystem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) || NULL == CryptoSystem)
|
|
{
|
|
D_DebugLog((DEB_ERROR, "CDLocateCSystem failed for etype: %d\n", CryptTypes[Index]));
|
|
continue;
|
|
}
|
|
|
|
CredentialSize += sizeof(KERB_ENCRYPTION_KEY) + CryptoSystem->KeySize;
|
|
KerbEncryptionKeyCount++;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// For a cleartext password, build a list of encryption types and
|
|
// create a key for each one
|
|
//
|
|
|
|
Status = CDBuildIntegrityVect(
|
|
&CryptCount,
|
|
CryptTypes
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
|
|
|
|
//
|
|
// Add the space for the salt
|
|
//
|
|
|
|
CredentialSize += KeySalt.Length;
|
|
|
|
//
|
|
// Now find the size of the key for each crypto system
|
|
//
|
|
|
|
for (Index = 0; Index < CryptCount; Index++ )
|
|
{
|
|
//
|
|
// Skip etypes stored as normal passwords
|
|
//
|
|
|
|
if (!IncludeBuiltinTypes &&
|
|
((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_NULL)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Status = CDLocateCSystem(
|
|
CryptTypes[Index],
|
|
&CryptoSystem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) || NULL == CryptoSystem)
|
|
{
|
|
D_DebugLog((DEB_ERROR, "CDLocateCSystem failed for etype: %d\n", CryptTypes[Index]));
|
|
continue;
|
|
}
|
|
CredentialSize += sizeof(KERB_KEY_DATA) + CryptoSystem->KeySize;
|
|
KerbKeyDataCount++;
|
|
}
|
|
|
|
//
|
|
// Add in space for oldcreds
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(StoredCreds))
|
|
{
|
|
if ((StoredCreds->Revision == KERB_PRIMARY_CRED_REVISION) &&
|
|
(StoredCreds->CredentialCount != 0))
|
|
{
|
|
OldFlags = StoredCreds->Flags;
|
|
for (Index = 0; Index < StoredCreds->CredentialCount ; Index++ )
|
|
{
|
|
CredentialSize += sizeof(KERB_KEY_DATA) + StoredCreds->Credentials[Index].Key.keyvalue.length +
|
|
StoredCreds->Credentials[Index].Salt.Length;
|
|
KerbKeyDataCount++;
|
|
}
|
|
OldCredCount = StoredCreds->CredentialCount;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add in the size of the base structure
|
|
//
|
|
|
|
CredentialSize += sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA));
|
|
Credentials = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(CredentialSize);
|
|
if (Credentials == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Fill in the base structure
|
|
//
|
|
|
|
Credentials->Revision = KERB_PRIMARY_CRED_REVISION;
|
|
Credentials->Flags = OldFlags | (USHORT) Flags ;
|
|
|
|
//
|
|
// Now fill in the individual keys
|
|
//
|
|
|
|
Base = (PUCHAR) Credentials;
|
|
if (MarshallKeys)
|
|
{
|
|
KeyBase = 0;
|
|
}
|
|
else
|
|
{
|
|
KeyBase = Base;
|
|
}
|
|
Offset = sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) +
|
|
(KerbEncryptionKeyCount * sizeof(KERB_ENCRYPTION_KEY)) +
|
|
(KerbKeyDataCount * sizeof(KERB_KEY_DATA));
|
|
|
|
|
|
//
|
|
// Add the default salt
|
|
//
|
|
|
|
Credentials->DefaultSalt.Length =
|
|
Credentials->DefaultSalt.MaximumLength = KeySalt.Length;
|
|
Credentials->DefaultSalt.Buffer = (LPWSTR) (KeyBase+Offset);
|
|
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
KeySalt.Buffer,
|
|
KeySalt.Length
|
|
);
|
|
Offset += Credentials->DefaultSalt.Length;
|
|
|
|
|
|
|
|
if ((Flags & KERB_PRIMARY_CRED_OWF_ONLY) != 0)
|
|
{
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
Password->Buffer,
|
|
Password->Length
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
|
|
&Credentials->Credentials[CredentialIndex].Key,
|
|
Base + Offset,
|
|
Password->Length,
|
|
KERB_ETYPE_RC4_HMAC_NT
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value =
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value - Base + KeyBase;
|
|
|
|
Offset += Password->Length;
|
|
CredentialIndex++;
|
|
|
|
#ifndef DONT_SUPPORT_OLD_TYPES
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
Password->Buffer,
|
|
Password->Length
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
|
|
&Credentials->Credentials[CredentialIndex].Key,
|
|
Base + Offset,
|
|
Password->Length,
|
|
KERB_ETYPE_RC4_HMAC_OLD
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value =
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value - Base + KeyBase;
|
|
|
|
Offset += Password->Length;
|
|
CredentialIndex++;
|
|
#endif
|
|
}
|
|
else // assume it's clear
|
|
{
|
|
//
|
|
// Now find the size of the key for each crypto system
|
|
//
|
|
|
|
for (Index = 0; Index < CryptCount; Index++ )
|
|
{
|
|
if (!IncludeBuiltinTypes &&
|
|
((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
|
|
(CryptTypes[Index] == KERB_ETYPE_NULL)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!KERB_SUCCESS(KerbHashPasswordEx(
|
|
Password,
|
|
&KeySalt,
|
|
CryptTypes[Index],
|
|
&TempKey)))
|
|
{
|
|
//
|
|
// It is possible that the password can't be used for every
|
|
// encryption scheme, so skip failures
|
|
//
|
|
|
|
D_DebugLog((DEB_WARN, "Failed to hash pasword %wZ with type 0x%x\n",
|
|
Password,CryptTypes[Index] ));
|
|
continue;
|
|
}
|
|
|
|
#if DBG
|
|
CDLocateCSystem(
|
|
CryptTypes[Index],
|
|
&CryptoSystem
|
|
);
|
|
DsysAssert(CryptoSystem->KeySize >= TempKey.keyvalue.length);
|
|
|
|
#endif
|
|
|
|
Credentials->Credentials[CredentialIndex].Key = TempKey;
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value = KeyBase + Offset;
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
TempKey.keyvalue.value,
|
|
TempKey.keyvalue.length
|
|
);
|
|
Offset += TempKey.keyvalue.length;
|
|
KerbFreeKey(
|
|
&TempKey
|
|
);
|
|
Credentials->Credentials[CredentialIndex].Salt = EmptySalt;
|
|
CredentialIndex++;
|
|
|
|
}
|
|
}
|
|
Credentials->CredentialCount = (USHORT) CredentialIndex;
|
|
|
|
//
|
|
// Now add in the old creds, if there were any
|
|
//
|
|
|
|
if (OldCredCount != 0)
|
|
{
|
|
for (Index = 0; Index < OldCredCount ; Index++ )
|
|
{
|
|
|
|
Credentials->Credentials[CredentialIndex] = StoredCreds->Credentials[Index];
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value = KeyBase + Offset;
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
StoredCreds->Credentials[Index].Key.keyvalue.value + (ULONG_PTR) StoredCreds,
|
|
StoredCreds->Credentials[Index].Key.keyvalue.length
|
|
);
|
|
Offset += StoredCreds->Credentials[Index].Key.keyvalue.length;
|
|
|
|
//
|
|
// Copy the salt
|
|
//
|
|
|
|
if (Credentials->Credentials[CredentialIndex].Salt.Buffer != NULL)
|
|
{
|
|
Credentials->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) Base+Offset;
|
|
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
(PBYTE) StoredCreds->Credentials[Index].Salt.Buffer + (ULONG_PTR) StoredCreds,
|
|
StoredCreds->Credentials[Index].Salt.Length
|
|
);
|
|
Offset += StoredCreds->Credentials[Index].Salt.Length;
|
|
}
|
|
else
|
|
{
|
|
Credentials->Credentials[CredentialIndex].Salt = EmptySalt;
|
|
}
|
|
|
|
CredentialIndex++;
|
|
}
|
|
Credentials->OldCredentialCount = (USHORT) OldCredCount;
|
|
}
|
|
else
|
|
{
|
|
Credentials->OldCredentialCount = 0;
|
|
}
|
|
*PasswordList = Credentials;
|
|
*PasswordListSize = CredentialSize;
|
|
Credentials = NULL;
|
|
|
|
Cleanup:
|
|
if (Credentials != NULL)
|
|
{
|
|
MIDL_user_free(Credentials);
|
|
}
|
|
if (AccountType != UnknownAccount)
|
|
{
|
|
KerbFreeString(&KeySalt);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcBuildKeySaltFromUpn
|
|
//
|
|
// Synopsis: Builds the salt by parsing the UPN, stripping out "@" & "/"
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KdcBuildKeySaltFromUpn(
|
|
IN PUNICODE_STRING Upn,
|
|
IN PUNICODE_STRING DomainName,
|
|
OUT PUNICODE_STRING Salt
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING RealUpn;
|
|
UNICODE_STRING LocalSalt = {0};
|
|
ULONG Index;
|
|
|
|
//
|
|
// If there is an "@" in UPN, strip it out & use the dns domain name
|
|
//
|
|
|
|
RealUpn = *Upn;
|
|
for ( Index = ((RealUpn.Length / sizeof(WCHAR)) - 1); Index-- > 0; )
|
|
{
|
|
if (RealUpn.Buffer[Index] == L'@')
|
|
{
|
|
RealUpn.Length = (USHORT) (Index * sizeof(WCHAR));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the salt. It starts off with the domain name & then has the
|
|
// UPN without any of the / pieces
|
|
//
|
|
|
|
LocalSalt.MaximumLength = DomainName->Length + RealUpn.Length;
|
|
LocalSalt.Length = 0;
|
|
LocalSalt.Buffer = (LPWSTR) MIDL_user_allocate(LocalSalt.MaximumLength);
|
|
if (LocalSalt.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
LocalSalt.Buffer,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
LocalSalt.Length += DomainName->Length;
|
|
|
|
//
|
|
// We have to uppercase the realmname for users
|
|
//
|
|
|
|
(VOID) RtlUpcaseUnicodeString( &LocalSalt,
|
|
&LocalSalt,
|
|
FALSE);
|
|
|
|
//
|
|
// Add in the real upn but leave out any "/" marks
|
|
//
|
|
|
|
for (Index = 0; Index < RealUpn.Length/sizeof(WCHAR) ; Index++ )
|
|
{
|
|
if (RealUpn.Buffer[Index] != L'/')
|
|
{
|
|
LocalSalt.Buffer[LocalSalt.Length / sizeof(WCHAR)] = RealUpn.Buffer[Index];
|
|
LocalSalt.Length += sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
*Salt = LocalSalt;
|
|
Cleanup:
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: PasswordChangeNotify
|
|
//
|
|
// Synopsis: Notifies KDC of a password change, allowing it to update
|
|
// its credentials
|
|
//
|
|
// Effects: Stores Kerberos credentials on user object
|
|
//
|
|
// Arguments: UserName - Name of user whose password changed
|
|
// RelativeId - RID of changed user
|
|
// Passsword - New password of user
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
PasswordChangeNotify(
|
|
IN PUNICODE_STRING UserName,
|
|
IN ULONG RelativeId,
|
|
IN PUNICODE_STRING Password
|
|
)
|
|
{
|
|
|
|
//
|
|
// Password change notify routine in kdcsvc was used to compute the
|
|
// "DES" keys for the user upon a password change.
|
|
// Subsequently this logic was inlined in samsrv.dll and but the original
|
|
// code has been preserved in the #if 0 block for reference below
|
|
//
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
#if 0
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SAMPR_HANDLE UserHandle = NULL;
|
|
SECPKG_SUPPLEMENTAL_CRED Credentials;
|
|
PSAMPR_USER_INFO_BUFFER UserInfo = NULL;
|
|
KERB_ACCOUNT_TYPE AccountType = UserAccount;
|
|
WCHAR Nt4AccountName[UNLEN+DNLEN+2];
|
|
WCHAR CrackedDnsDomain[DNS_MAX_NAME_LENGTH+1];
|
|
ULONG CrackedDomainLength = sizeof(CrackedDnsDomain) / sizeof(WCHAR);
|
|
WCHAR CrackedName[UNLEN+DNS_MAX_NAME_LENGTH+2];
|
|
ULONG CrackedNameLength = sizeof(CrackedName);
|
|
ULONG CrackError = 0;
|
|
UNICODE_STRING EmailName = {0};
|
|
UNICODE_STRING KeySalt = {0};
|
|
PKERB_STORED_CREDENTIAL StoredCreds = NULL;
|
|
ULONG CredentialSize;
|
|
BOOLEAN FreeSalt = FALSE;
|
|
|
|
Credentials.Credentials = NULL;
|
|
|
|
//
|
|
// Get a SAM handle
|
|
//
|
|
|
|
RtlEnterCriticalSection(&KdcNotifyCritSect);
|
|
|
|
if (KdcNotifyAccountDomainHandle == NULL)
|
|
{
|
|
Status = KdcNotifyOpenAccountDomain(&KdcNotifyAccountDomainHandle);
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&KdcNotifyCritSect);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SamrOpenUser(
|
|
KdcNotifyAccountDomainHandle,
|
|
USER_WRITE_ACCOUNT | USER_READ_ACCOUNT,
|
|
RelativeId,
|
|
&UserHandle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"BAD ERR: Can't open account of user whose password just changed (name =%wZ, rid = 0x%x) 0x%x\n",
|
|
UserName, RelativeId, Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&Credentials.PackageName,
|
|
MICROSOFT_KERBEROS_NAME_W
|
|
);
|
|
|
|
//
|
|
// Find out if the user is a machine account - if so, the principal name
|
|
// takes on a different format.
|
|
//
|
|
|
|
Status = SamrQueryInformationUser(
|
|
UserHandle,
|
|
UserControlInformation,
|
|
&UserInfo
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SamIRetrievePrimaryCredentials(
|
|
UserHandle,
|
|
&GlobalKerberosName,
|
|
(PVOID *) &StoredCreds,
|
|
&CredentialSize
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to retrieve primary credentials: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((UserInfo->Control.UserAccountControl &
|
|
(USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) != 0)
|
|
{
|
|
AccountType = MachineAccount;
|
|
}
|
|
else if ((UserInfo->Control.UserAccountControl &
|
|
(USER_INTERDOMAIN_TRUST_ACCOUNT)) != 0)
|
|
{
|
|
AccountType = DomainTrustAccount;
|
|
}
|
|
|
|
//
|
|
// Get the UPN from CrackSingleName
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
Nt4AccountName,
|
|
KdcNotifyDomainName.Buffer,
|
|
KdcNotifyDomainName.Length
|
|
);
|
|
Nt4AccountName[KdcNotifyDomainName.Length / sizeof(WCHAR)] = L'\\';
|
|
|
|
RtlCopyMemory(
|
|
Nt4AccountName + 1 + (KdcNotifyDomainName.Length) / sizeof(WCHAR),
|
|
UserName->Buffer,
|
|
UserName->Length
|
|
);
|
|
Nt4AccountName[1 + (KdcNotifyDomainName.Length + UserName->Length) / sizeof(WCHAR)] = L'\0';
|
|
|
|
|
|
Status = CrackSingleName(
|
|
DS_NT4_ACCOUNT_NAME,
|
|
0, // don't check against GC
|
|
Nt4AccountName,
|
|
DS_USER_PRINCIPAL_NAME,
|
|
&CrackedDomainLength,
|
|
CrackedDnsDomain,
|
|
&CrackedNameLength,
|
|
CrackedName,
|
|
&CrackError
|
|
);
|
|
if ((Status != STATUS_SUCCESS) || (CrackError != DS_NAME_NO_ERROR))
|
|
{
|
|
KeySalt = *UserName;
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(
|
|
&EmailName,
|
|
CrackedName
|
|
);
|
|
AccountType = UnknownAccount;
|
|
|
|
Status = KdcBuildKeySaltFromUpn(
|
|
&EmailName,
|
|
&KdcNotifyDnsDomainName,
|
|
&KeySalt
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
FreeSalt = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Build a the credentials
|
|
//
|
|
|
|
|
|
|
|
//
|
|
// Set account type to unknown so it uses the UPN supplied salt
|
|
//
|
|
|
|
|
|
if ((Password != NULL) && (Password->Buffer != NULL))
|
|
{
|
|
Status = KdcBuildPasswordList(
|
|
Password,
|
|
&KeySalt,
|
|
&KdcNotifyDnsDomainName,
|
|
AccountType,
|
|
StoredCreds,
|
|
CredentialSize,
|
|
TRUE, // marshall
|
|
FALSE, // don't include builtins
|
|
0, // no flags
|
|
Unknown,
|
|
(PKERB_STORED_CREDENTIAL *) &Credentials.Credentials,
|
|
&Credentials.CredentialSize
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Credentials.CredentialSize = 0;
|
|
Credentials.Credentials = NULL;
|
|
}
|
|
|
|
Status = SamIStorePrimaryCredentials(
|
|
UserHandle,
|
|
&Credentials
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to store primary credentials: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if (UserHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&UserHandle);
|
|
}
|
|
if (Credentials.Credentials != NULL)
|
|
{
|
|
MIDL_user_free(Credentials.Credentials);
|
|
}
|
|
|
|
if (UserInfo != NULL)
|
|
{
|
|
SamIFree_SAMPR_USER_INFO_BUFFER( UserInfo, UserControlInformation );
|
|
}
|
|
if (FreeSalt)
|
|
{
|
|
KerbFreeString(&KeySalt);
|
|
}
|
|
|
|
if (StoredCreds != NULL)
|
|
{
|
|
LocalFree(StoredCreds);
|
|
}
|
|
return(Status);
|
|
|
|
#endif
|
|
}
|
|
|
|
extern "C"
|
|
|
|
|
|
NTSTATUS
|
|
KdcBuildKerbCredentialsFromPassword(
|
|
IN PUNICODE_STRING ClearPassword,
|
|
IN PVOID KerbCredentials,
|
|
IN ULONG KerbCredentialLength,
|
|
IN ULONG UserAccountControl,
|
|
IN PUNICODE_STRING UPN,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DnsDomainName,
|
|
OUT PVOID * NewKerbCredentials,
|
|
OUT PULONG NewKerbCredentialLength
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KERB_ACCOUNT_TYPE AccountType = UnknownAccount;
|
|
UNICODE_STRING KeySalt = {0};
|
|
BOOLEAN FreeSalt = FALSE;
|
|
|
|
PKERB_STORED_CREDENTIAL32 Cred32 = NULL;
|
|
PKERB_STORED_CREDENTIAL Cred64 = NULL;
|
|
ULONG CredLength = KerbCredentialLength;
|
|
|
|
|
|
//
|
|
// Compute the correct account type
|
|
//
|
|
|
|
|
|
if (ARGUMENT_PRESENT(UPN))
|
|
{
|
|
Status = KdcBuildKeySaltFromUpn(
|
|
UPN,
|
|
DnsDomainName,
|
|
&KeySalt
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
FreeSalt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
|
|
if ((UserAccountControl &
|
|
(USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) != 0)
|
|
{
|
|
AccountType = MachineAccount;
|
|
}
|
|
else if ((UserAccountControl &
|
|
(USER_INTERDOMAIN_TRUST_ACCOUNT)) != 0)
|
|
{
|
|
AccountType = DomainTrustAccount;
|
|
}
|
|
else
|
|
{
|
|
AccountType = UserAccount;
|
|
}
|
|
KeySalt = *UserName;
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN64
|
|
|
|
Status = KdcUnpack32BitStoredCredential(
|
|
(PKERB_STORED_CREDENTIAL32) KerbCredentials,
|
|
&Cred64,
|
|
&CredLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
KerbCredentials = (PVOID) Cred64;
|
|
KerbCredentialLength = CredLength;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Compute the kerb credentials
|
|
//
|
|
|
|
if ((ClearPassword != NULL))
|
|
{
|
|
UNICODE_STRING UpcaseDomainName = {0};
|
|
|
|
Status = RtlUpcaseUnicodeString(
|
|
&UpcaseDomainName,
|
|
DnsDomainName,
|
|
TRUE
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = KdcBuildPasswordList(
|
|
ClearPassword,
|
|
&KeySalt,
|
|
&UpcaseDomainName,
|
|
AccountType,
|
|
(PKERB_STORED_CREDENTIAL )KerbCredentials,
|
|
KerbCredentialLength,
|
|
TRUE, // marshall
|
|
FALSE, // don't include builtins
|
|
0, // no flags
|
|
Unknown,
|
|
(PKERB_STORED_CREDENTIAL *) NewKerbCredentials,
|
|
NewKerbCredentialLength
|
|
);
|
|
RtlFreeUnicodeString(&UpcaseDomainName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
|
|
// for 64 - 32 bit compat, we pack the struct in 32bit compliant form
|
|
|
|
|
|
Status = KdcPack32BitStoredCredential(
|
|
(PKERB_STORED_CREDENTIAL)(*NewKerbCredentials),
|
|
&Cred32,
|
|
NewKerbCredentialLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ((*NewKerbCredentials) != NULL)
|
|
{
|
|
MIDL_user_free(*NewKerbCredentials);
|
|
*NewKerbCredentials = Cred32;
|
|
}
|
|
|
|
#endif
|
|
|
|
Cleanup:
|
|
|
|
if (FreeSalt)
|
|
{
|
|
KerbFreeString(&KeySalt);
|
|
}
|
|
|
|
if (Cred64 != NULL)
|
|
{
|
|
MIDL_user_free(Cred64);
|
|
}
|
|
|
|
|
|
return(Status);
|
|
}
|
|
|
|
extern "C"
|
|
VOID
|
|
KdcFreeCredentials(
|
|
IN PVOID Credentials
|
|
)
|
|
{
|
|
MIDL_user_free(Credentials);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: InitializeChangeNotify
|
|
//
|
|
// Synopsis: KDC code for initializing password change notification
|
|
// code.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
extern "C"
|
|
BOOLEAN
|
|
InitializeChangeNotify(
|
|
)
|
|
{
|
|
if (KdcNotificationInitialized)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
D_DebugLog((DEB_TRACE, "Initialize Change Notify called!\n"));
|
|
if (!NT_SUCCESS(RtlInitializeCriticalSection(&KdcNotifyCritSect)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
KdcNotificationInitialized = TRUE;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcTimeHasElapsed
|
|
//
|
|
// Synopsis: Returns TRUE if the specified amount of time has
|
|
// elapsed since the specified start time
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
KdcTimeHasElapsed(
|
|
IN LARGE_INTEGER StartTime,
|
|
IN PLARGE_INTEGER Delta
|
|
)
|
|
{
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER ElapsedTime;
|
|
WCHAR PasswordBuffer[LM20_PWLEN];
|
|
|
|
//
|
|
// Check the password expiration time.
|
|
//
|
|
|
|
NtQuerySystemTime(&CurrentTime);
|
|
ElapsedTime.QuadPart = CurrentTime.QuadPart - StartTime.QuadPart;
|
|
|
|
//
|
|
// If the window hasn't elapsed, we are done.
|
|
//
|
|
|
|
if ((ElapsedTime.QuadPart > 0) && (ElapsedTime.QuadPart < Delta->QuadPart))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcUpdateKrbtgtPassword
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
extern "C"
|
|
BOOLEAN
|
|
KdcUpdateKrbtgtPassword(
|
|
IN PUNICODE_STRING DnsDomainName,
|
|
IN PLARGE_INTEGER MaxPasswordAge
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WCHAR PasswordBuffer[LM20_PWLEN];
|
|
UNICODE_STRING PasswordString;
|
|
ULONG Index;
|
|
|
|
BOOLEAN Result = FALSE;
|
|
|
|
|
|
if (KdcState != Running)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Check the password expiration time.
|
|
//
|
|
|
|
if (!KdcTimeHasElapsed(
|
|
SecData.KrbtgtPasswordLastSet(),
|
|
MaxPasswordAge
|
|
))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Build a random password
|
|
//
|
|
|
|
if (!CDGenerateRandomBits(
|
|
(PBYTE) PasswordBuffer,
|
|
sizeof(PasswordBuffer)
|
|
))
|
|
{
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Make sure there are no zero characters
|
|
//
|
|
|
|
for (Index = 0; Index < LM20_PWLEN ; Index++ )
|
|
{
|
|
if (PasswordBuffer[Index] == 0)
|
|
{
|
|
PasswordBuffer[Index] = (WCHAR) Index;
|
|
}
|
|
}
|
|
PasswordString.Length = sizeof(PasswordBuffer);
|
|
PasswordString.MaximumLength = PasswordString.Length;
|
|
PasswordString.Buffer = PasswordBuffer;
|
|
|
|
Status = SamIChangePasswordForeignUser(
|
|
SecData.KdcServiceName(),
|
|
&PasswordString,
|
|
NULL,
|
|
0 // no desired access
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to set KRBTGT password: 0x%x\n", Status));
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ReportServiceEvent(
|
|
EVENTLOG_SUCCESS,
|
|
KDCEVENT_KRBTGT_PASSWORD_CHANGED,
|
|
0, // no data
|
|
NULL, // no data
|
|
0, // no strings
|
|
NULL // no strings
|
|
);
|
|
|
|
Result = TRUE;
|
|
|
|
Cleanup:
|
|
|
|
if (!Result && !NT_SUCCESS(Status))
|
|
{
|
|
|
|
ReportServiceEvent(
|
|
EVENTLOG_ERROR_TYPE,
|
|
KDCEVENT_KRBTGT_PASSWORD_CHANGE_FAILED,
|
|
sizeof(NTSTATUS),
|
|
&Status,
|
|
0, // no strings
|
|
NULL // no strings
|
|
);
|
|
|
|
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CredentialUpdateNotify
|
|
//
|
|
// Synopsis: This routine is called from SAMSRV in order to obtain
|
|
// new kerberos credentials to be stored as supplemental
|
|
// credentials when ever a user's password is set/changed.
|
|
//
|
|
// Effects: no global effect.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// IN ClearPassword -- the clear text password
|
|
// IN OldCredentials -- the previous kerberos credentials
|
|
// IN OldCredentialsSize -- size of OldCredentials
|
|
// IN UserAccountControl -- info about the user
|
|
// IN UPN -- user principal name of the account
|
|
// IN UserName -- the SAM account name of the account
|
|
// IN DnsDomainName -- DNS domain name of the account
|
|
// OUT NewCredentials -- space allocated for SAM containing
|
|
// the credentials based on the input parameters
|
|
// to be freed by CredentialUpdateFree
|
|
// OUT NewCredentialSize -- size of NewCredentials
|
|
//
|
|
//
|
|
// Requires: no global requirements
|
|
//
|
|
// Returns: STATUS_SUCCESS, or resource error
|
|
//
|
|
// Notes: KDCSVC.DLL needs to be registered (in the registry) as a
|
|
// package that SAM calls out to in order for this routine
|
|
// to be involked.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
CredentialUpdateNotify (
|
|
IN PUNICODE_STRING ClearPassword,
|
|
IN PVOID OldCredentials,
|
|
IN ULONG OldCredentialsSize,
|
|
IN ULONG UserAccountControl,
|
|
IN PUNICODE_STRING UPN,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING NetbiosDomainName,
|
|
IN PUNICODE_STRING DnsDomainName,
|
|
OUT PVOID *NewCredentials,
|
|
OUT ULONG *NewCredentialsSize
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER( NetbiosDomainName );
|
|
|
|
return KdcBuildKerbCredentialsFromPassword(ClearPassword,
|
|
OldCredentials,
|
|
OldCredentialsSize,
|
|
UserAccountControl,
|
|
UPN,
|
|
UserName,
|
|
DnsDomainName,
|
|
NewCredentials,
|
|
NewCredentialsSize);
|
|
|
|
}
|
|
|
|
VOID
|
|
CredentialUpdateFree(
|
|
PVOID p
|
|
)
|
|
//
|
|
// Free's the memory allocated by CredentialUpdateNotify
|
|
//
|
|
{
|
|
if (p) {
|
|
KdcFreeCredentials(p);
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CredentialUpdateRegister
|
|
//
|
|
// Synopsis: This routine is called from SAMSRV in order to obtain
|
|
// the name of the supplemental credentials pass into this package
|
|
// when a password is changed or set.
|
|
//
|
|
// Effects: no global effect.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// OUT CredentialName -- the name of credential tag in the supplemental
|
|
// credentials. Note this memory is never freed
|
|
// by SAM, but must remain valid for the lifetime
|
|
// of the process.
|
|
//
|
|
// Requires: no global requirements
|
|
//
|
|
// Returns: TRUE
|
|
//
|
|
// Notes: KDCSVC.DLL needs to be registered (in the registry) as a
|
|
// package that SAM calls out to in order for this routine
|
|
// to be involked.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOLEAN
|
|
CredentialUpdateRegister(
|
|
OUT UNICODE_STRING *CredentialName
|
|
)
|
|
{
|
|
ASSERT(CredentialName);
|
|
|
|
RtlInitUnicodeString(CredentialName, MICROSOFT_KERBEROS_NAME_W);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// This compile-time test verifies that CredentialUpdateNotify and
|
|
// CredentialUpdateRegister have the correct signature
|
|
//
|
|
#if DBG
|
|
PSAM_CREDENTIAL_UPDATE_NOTIFY_ROUTINE _TestCredentialUpdateNotify
|
|
= CredentialUpdateNotify;
|
|
PSAM_CREDENTIAL_UPDATE_FREE_ROUTINE _TestCredentialFreeRegister
|
|
= CredentialUpdateFree;
|
|
PSAM_CREDENTIAL_UPDATE_REGISTER_ROUTINE _TestCredentialUpdateRegister
|
|
= CredentialUpdateRegister;
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef _WIN64
|
|
//
|
|
// Routines for packing and unpacking KERB_STORED_CREDENTIAL from DS
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcUnpack32BitStoredCredential
|
|
//
|
|
// Synopsis: This function converts a 32 bit KERB_STORED_CREDENTIAL (read
|
|
// from DS, likely) to a 64 bit KERB_STORED_CREDENTIAL
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KdcUnpack32BitStoredCredential(
|
|
IN PKERB_STORED_CREDENTIAL32 Cred32,
|
|
IN OUT PKERB_STORED_CREDENTIAL * ppCred64,
|
|
IN OUT PULONG pCredLength
|
|
)
|
|
{
|
|
|
|
PKERB_STORED_CREDENTIAL Cred64 = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG CredSize = sizeof(KERB_STORED_CREDENTIAL);
|
|
ULONG CredCount = 0, Cred = 0, Offset = 0;
|
|
PCHAR Where, Base;
|
|
CHAR UNALIGNED * From;
|
|
|
|
*pCredLength = 0;
|
|
*ppCred64 = NULL;
|
|
|
|
if (NULL == Cred32)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Calculate Allocation size
|
|
CredSize += ROUND_UP_COUNT(Cred32->DefaultSalt.MaximumLength, ALIGN_LPTSTR);
|
|
|
|
CredSize += ((Cred32->CredentialCount + Cred32->OldCredentialCount) * sizeof(KERB_KEY_DATA));
|
|
|
|
for (CredCount = 0; CredCount < (ULONG)(Cred32->CredentialCount + Cred32->OldCredentialCount); CredCount++)
|
|
{
|
|
CredSize += ROUND_UP_COUNT(Cred32->Credentials[CredCount].Key.keyvaluelength, ALIGN_LPTSTR);
|
|
CredSize += ROUND_UP_COUNT(Cred32->Credentials[CredCount].Salt.MaximumLength, ALIGN_LPTSTR);
|
|
}
|
|
|
|
Cred64 = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(CredSize);
|
|
|
|
if (NULL == Cred64)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// copy over data, remember, buffers packed in self-relative format
|
|
Cred64->CredentialCount = Cred32->CredentialCount;
|
|
Cred64->OldCredentialCount = Cred32->OldCredentialCount;
|
|
Cred64->DefaultSalt.Length = Cred32->DefaultSalt.Length;
|
|
Cred64->DefaultSalt.MaximumLength = Cred32->DefaultSalt.MaximumLength;
|
|
Cred64->Flags = Cred32->Flags;
|
|
Cred64->Revision = Cred32->Revision;
|
|
|
|
Base = (PCHAR)Cred64;
|
|
|
|
From = (CHAR UNALIGNED *) RtlOffsetToPointer(Cred32,Cred32->DefaultSalt.Buffer);
|
|
|
|
// Note: 1 KERB_KEY_DATA struct is already calculated in
|
|
// the sizeof(KERB_STORED_CREDENTIAL)
|
|
Offset = sizeof(KERB_STORED_CREDENTIAL) + ((CredCount) * sizeof(KERB_KEY_DATA));
|
|
|
|
Where = RtlOffsetToPointer(Cred64, Offset);
|
|
|
|
Cred64->DefaultSalt.Buffer = (PWSTR) (ULONG_PTR) Offset;
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred32->DefaultSalt.Length
|
|
);
|
|
|
|
Where += ROUND_UP_COUNT(Cred64->DefaultSalt.Length, ALIGN_LPTSTR);
|
|
|
|
// copy credentials
|
|
for (Cred = 0; Cred < CredCount; Cred++)
|
|
{
|
|
Cred64->Credentials[Cred].Salt.Length = Cred32->Credentials[Cred].Salt.Length;
|
|
Cred64->Credentials[Cred].Salt.MaximumLength = Cred32->Credentials[Cred].Salt.MaximumLength;
|
|
|
|
From = (CHAR UNALIGNED *) RtlOffsetToPointer(Cred32, Cred32->Credentials[Cred].Salt.Buffer);
|
|
if (Cred32->Credentials[Cred].Salt.Length != 0)
|
|
{
|
|
Cred64->Credentials[Cred].Salt.Buffer = (PWSTR) (ULONG_PTR) RtlPointerToOffset(Base,Where);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred32->Credentials[Cred].Salt.Length
|
|
);
|
|
|
|
Where += ROUND_UP_COUNT(Cred64->Credentials[Cred].Salt.Length, ALIGN_LPTSTR);
|
|
|
|
Cred64->Credentials[Cred].Key.keytype = Cred32->Credentials[Cred].Key.keytype;
|
|
Cred64->Credentials[Cred].Key.keyvalue.length = Cred32->Credentials[Cred].Key.keyvaluelength;
|
|
|
|
From = RtlOffsetToPointer(Cred32,Cred32->Credentials[Cred].Key.keyvaluevalue);
|
|
Cred64->Credentials[Cred].Key.keyvalue.value = (PUCHAR) (ULONG_PTR) RtlPointerToOffset(Base,Where);
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred32->Credentials[Cred].Key.keyvaluelength
|
|
);
|
|
|
|
Where += ROUND_UP_COUNT(Cred32->Credentials[Cred].Key.keyvaluelength, ALIGN_LPTSTR);
|
|
}
|
|
|
|
|
|
// TBD: Validation code ?
|
|
|
|
*ppCred64 = Cred64;
|
|
*pCredLength = CredSize;
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcPack32BitStoredCredential
|
|
//
|
|
// Synopsis: This function converts a 64 bit KERB_STORED_CREDENTIAL
|
|
// to a 32 bit KERB_STORED_CREDENTIAL
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: Free the return value using MIDL_user_free()
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KdcPack32BitStoredCredential(
|
|
IN PKERB_STORED_CREDENTIAL Cred64,
|
|
OUT PKERB_STORED_CREDENTIAL32 * ppCred32,
|
|
OUT PULONG pCredSize
|
|
)
|
|
{
|
|
|
|
ULONG Offset, CredSize = sizeof(KERB_STORED_CREDENTIAL32);
|
|
ULONG CredCount, Cred;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_STORED_CREDENTIAL32 Cred32 = NULL;
|
|
PCHAR Where, From, Base;
|
|
|
|
*ppCred32 = NULL;
|
|
*pCredSize = 0;
|
|
|
|
if (Cred64 == NULL)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Get the expected size of the resultant blob
|
|
CredSize += ((Cred64->CredentialCount + Cred64->OldCredentialCount) *
|
|
KERB_KEY_DATA32_SIZE);
|
|
|
|
|
|
CredSize += Cred64->DefaultSalt.MaximumLength;
|
|
for (CredCount = 0;
|
|
CredCount < (ULONG) (Cred64->CredentialCount+Cred64->OldCredentialCount);
|
|
CredCount++)
|
|
{
|
|
CredSize += Cred64->Credentials[CredCount].Salt.MaximumLength;
|
|
CredSize += Cred64->Credentials[CredCount].Key.keyvalue.length;
|
|
}
|
|
|
|
Cred32 = (PKERB_STORED_CREDENTIAL32) MIDL_user_allocate(CredSize);
|
|
|
|
if (NULL == Cred32)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Base = (PCHAR) Cred32;
|
|
|
|
// Copy over USHORTS
|
|
Cred32->Revision = Cred64->Revision;
|
|
Cred32->Flags = Cred64->Flags;
|
|
Cred32->CredentialCount = Cred64->CredentialCount;
|
|
Cred32->OldCredentialCount = Cred64->OldCredentialCount;
|
|
|
|
// Copy over salt
|
|
Cred32->DefaultSalt.Length = Cred64->DefaultSalt.Length;
|
|
Cred32->DefaultSalt.MaximumLength = Cred64->DefaultSalt.MaximumLength;
|
|
|
|
Offset = KERB_STORED_CREDENTIAL32_SIZE + ((CredCount+1) * KERB_KEY_DATA32_SIZE);
|
|
|
|
Where = RtlOffsetToPointer(Base,Offset);
|
|
From = RtlOffsetToPointer(Cred64, Cred64->DefaultSalt.Buffer);
|
|
Cred32->DefaultSalt.Buffer = RtlPointerToOffset(Base,Where);
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred64->DefaultSalt.Length
|
|
);
|
|
|
|
Where += Cred64->DefaultSalt.Length;
|
|
|
|
// Copy over creds (KERB_KEY_DATA)
|
|
for (Cred = 0; Cred < CredCount;Cred++)
|
|
{
|
|
Cred32->Credentials[Cred].Salt.Length = Cred64->Credentials[Cred].Salt.Length;
|
|
Cred32->Credentials[Cred].Salt.MaximumLength = Cred64->Credentials[Cred].Salt.MaximumLength;
|
|
From = RtlOffsetToPointer(Cred64, Cred64->Credentials[Cred].Salt.Buffer);
|
|
|
|
// Only add in buffer pointer if there's data to copy.
|
|
if (Cred32->Credentials[Cred].Salt.Length != 0)
|
|
{
|
|
Cred32->Credentials[Cred].Salt.Buffer = RtlPointerToOffset(Base,Where);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred64->Credentials[Cred].Salt.Length
|
|
);
|
|
|
|
Where += Cred64->Credentials[Cred].Salt.Length;
|
|
|
|
// Keys
|
|
Cred32->Credentials[Cred].Key.keytype = Cred64->Credentials[Cred].Key.keytype ;
|
|
Cred32->Credentials[Cred].Key.keyvaluelength = Cred64->Credentials[Cred].Key.keyvalue.length;
|
|
From = RtlOffsetToPointer(Cred64, Cred64->Credentials[Cred].Key.keyvalue.value);
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
From,
|
|
Cred64->Credentials[Cred].Key.keyvalue.length
|
|
);
|
|
|
|
Cred32->Credentials[Cred].Key.keyvaluevalue = RtlPointerToOffset(Base,Where);
|
|
Where += Cred64->Credentials[Cred].Key.keyvalue.length;
|
|
}
|
|
|
|
*ppCred32 = Cred32;
|
|
*pCredSize = CredSize;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|