Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2109 lines
51 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corporation
//
// SYNOPSIS
//
// Implements the IAS API into the NT LSA.
//
///////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <windows.h>
#include <align.h>
#include <ntsam.h>
#include <ntsamp.h>
#include <dsgetdc.h>
#include <lm.h>
#include <crypt.h>
#include <sha.h>
#include <rasfmsub.h>
#include <oaidl.h>
#include <iaspolcy.h>
#include <iastrace.h>
#define IASSAMAPI
#include <statinfo.h>
#include <ezsam.h>
#include <dyninfo.h>
#include <ezlogon.h>
#include <iaslsa.h>
#include <iasntds.h>
#include <iasparms.h>
#define STRSAFE_NO_DEPRECATE
#include <strsafe.h>
#include <raserror.h>
extern CRITICAL_SECTION critSec;
#define DEFAULT_PARAMETER_CONTROL \
(MSV1_0_DONT_TRY_GUEST_ACCOUNT | MSV1_0_TRY_SPECIFIED_DOMAIN_ONLY | MSV1_0_DISABLE_PERSONAL_FALLBACK)
//////////
// Make sure that the defines in iaslsa.h match the actual NT defines.
//////////
#if _MSV1_0_CHALLENGE_LENGTH != MSV1_0_CHALLENGE_LENGTH
#error _MSV1_0_CHALLENGE_LENGTH != MSV1_0_CHALLENGE_LENGTH
#endif
#if _NT_RESPONSE_LENGTH != NT_RESPONSE_LENGTH
#error _NT_RESPONSE_LENGTH != NT_RESPONSE_LENGTH
#endif
#if _LM_RESPONSE_LENGTH != LM_RESPONSE_LENGTH
#error _LM_RESPONSE_LENGTH != LM_RESPONSE_LENGTH
#endif
#if _MSV1_0_USER_SESSION_KEY_LENGTH != MSV1_0_USER_SESSION_KEY_LENGTH
#error _MSV1_0_USER_SESSION_KEY_LENGTH != MSV1_0_USER_SESSION_KEY_LENGTH
#endif
#if _MSV1_0_LANMAN_SESSION_KEY_LENGTH != MSV1_0_LANMAN_SESSION_KEY_LENGTH
#error _MSV1_0_LANMAN_SESSION_KEY_LENGTH != MSV1_0_LANMAN_SESSION_KEY_LENGTH
#endif
#if _ENCRYPTED_LM_OWF_PASSWORD_LENGTH != ENCRYPTED_LM_OWF_PASSWORD_LENGTH
#error _ENCRYPTED_LM_OWF_PASSWORD_LENGTH != ENCRYPTED_LM_OWF_PASSWORD_LENGTH
#endif
#if _ENCRYPTED_NT_OWF_PASSWORD_LENGTH != ENCRYPTED_NT_OWF_PASSWORD_LENGTH
#error _ENCRYPTED_NT_OWF_PASSWORD_LENGTH != ENCRYPTED_NT_OWF_PASSWORD_LENGTH
#endif
#if _MAX_ARAP_USER_NAMELEN != MAX_ARAP_USER_NAMELEN
#error _MAX_ARAP_USER_NAMELEN != MAX_ARAP_USER_NAMELEN
#endif
#if _AUTHENTICATOR_RESPONSE_LENGTH > A_SHA_DIGEST_LEN
#error _AUTHENTICATOR_RESPONSE_LENGTH > A_SHA_DIGEST_LEN
#endif
#if _CHAP_RESPONSE_SIZE != CHAP_RESPONSE_SIZE
#error _CHAP_RESPONSE_SIZE != CHAP_RESPONSE_SIZE
#endif
//////////
// Reference count for API initialization.
//////////
LONG theRefCount;
//////////
// Lock variable -- non-zero if API is locked.
//////////
LONG theLsaLock;
//////////
// SID lengths for the local domains.
//////////
ULONG theAccountSidLen, theBuiltinSidLen;
//////////
// Macros to lock/unlock the LSA API during intialization and shutdown.
//////////
#define LSA_LOCK() \
while (InterlockedExchange(&theLsaLock, 1)) Sleep(5)
#define LSA_UNLOCK() \
InterlockedExchange(&theLsaLock, 0)
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLsaInitialize
//
// DESCRIPTION
//
// Initializes the LSA API.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASLsaInitialize( VOID )
{
DWORD status;
LSA_LOCK();
if (theRefCount == 0)
{
IASTraceString("Initializing LSA/SAM sub-system.");
status = IASStaticInfoInitialize();
if (status != NO_ERROR) { goto exit; }
status = IASSamInitialize();
if (status != NO_ERROR) { goto shutdown_static; }
status = IASDynamicInfoInitialize();
if (status != NO_ERROR) { goto shutdown_sam; }
status = IASLogonInitialize();
if (status != NO_ERROR) { goto shutdown_dynamic; }
theAccountSidLen = IASLengthRequiredChildSid(theAccountDomainSid);
theBuiltinSidLen = IASLengthRequiredChildSid(theBuiltinDomainSid);
IASTraceString("LSA/SAM sub-system initialized successfully.");
}
else
{
// We're already initialized.
status = NO_ERROR;
}
++theRefCount;
goto exit;
shutdown_dynamic:
IASDynamicInfoShutdown();
shutdown_sam:
IASSamShutdown();
shutdown_static:
IASStaticInfoShutdown();
IASTraceFailure("LSA/SAM initialization", status);
exit:
LSA_UNLOCK();
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLsaUninitialize
//
// DESCRIPTION
//
// Uninitializes the LSA API.
//
///////////////////////////////////////////////////////////////////////////////
VOID
WINAPI
IASLsaUninitialize( VOID )
{
LSA_LOCK();
--theRefCount;
if (theRefCount == 0)
{
IASLogonShutdown();
IASDynamicInfoShutdown();
IASSamShutdown();
IASStaticInfoShutdown();
}
LSA_UNLOCK();
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLogonPAP
//
// DESCRIPTION
//
// Performs PAP authentication against the NT SAM database.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASLogonPAP(
PCWSTR UserName,
PCWSTR Domain,
PCSTR Password,
PHANDLE Token,
PIAS_PAP_PROFILE Profile
)
{
DWORD status;
ULONG authLength;
PMSV1_0_LM20_LOGON authInfo;
PBYTE data;
PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
ProfileBuffer = NULL;
// Calculate the length of the authentication info.
authLength = (ULONG)(sizeof(MSV1_0_LM20_LOGON) +
(ALIGN_WCHAR - 1) +
(wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
strlen(Password) * (sizeof(WCHAR) + sizeof(CHAR)));
// Allocate a buffer on the stack.
// Need extra room for the RtlCopyAnsiStringToUnicode conversion.
authInfo = (PMSV1_0_LM20_LOGON)_alloca(authLength + 2 * sizeof(WCHAR));
// Initialize the struct.
IASInitAuthInfo(
authInfo,
sizeof(MSV1_0_LM20_LOGON),
UserName,
Domain,
&data
);
// Copy in the ANSI password.
IASInitAnsiString(
authInfo->CaseInsensitiveChallengeResponse,
data,
Password
);
// Make sure that the Unicode string is properly aligned.
data = ROUND_UP_POINTER(data, ALIGN_WCHAR);
// Copy in the Unicode password. We have to force a UNICODE_STRING into
// an ANSI_STRING struct.
IASInitUnicodeStringFromAnsi(
*(PUNICODE_STRING)&authInfo->CaseSensitiveChallengeResponse,
data,
authInfo->CaseInsensitiveChallengeResponse
);
// Set the parameters.
authInfo->ParameterControl = DEFAULT_PARAMETER_CONTROL |
MSV1_0_CLEARTEXT_PASSWORD_ALLOWED |
MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED;
status = IASLogonUser(
authInfo,
authLength,
&ProfileBuffer,
Token
);
if (status == NO_ERROR)
{
Profile->KickOffTime.QuadPart = ProfileBuffer->KickOffTime.QuadPart;
// free it.
LsaFreeReturnBuffer(ProfileBuffer);
}
else
{
Profile->KickOffTime.QuadPart = MAXLONGLONG;
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLogonCHAP
//
// DESCRIPTION
//
// Performs MD5-CHAP authentication against the NT SAM database.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASLogonCHAP(
PCWSTR UserName,
PCWSTR Domain,
BYTE ChallengeID,
PBYTE Challenge,
DWORD ChallengeLength,
PBYTE Response,
PHANDLE Token,
PIAS_CHAP_PROFILE Profile
)
{
DWORD status;
ULONG authLength, rasAuthLength, md5AuthLength;
PMSV1_0_SUBAUTH_LOGON authInfo;
PBYTE data;
RAS_SUBAUTH_INFO* ras;
MD5CHAP_SUBAUTH_INFO* md5;
MD5CHAP_EX_SUBAUTH_INFO* md5ex;
PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
ProfileBuffer = NULL;
// Calculate the length of the MD5 subauth info.
if (ChallengeLength == 16)
{
md5AuthLength = sizeof(MD5CHAP_SUBAUTH_INFO);
}
else
{
md5AuthLength = sizeof(MD5CHAP_EX_SUBAUTH_INFO) + ChallengeLength - 1;
}
// Calculate the length of the RAS subauth info.
rasAuthLength = sizeof(RAS_SUBAUTH_INFO) + md5AuthLength;
// Calculate the length of all the subauth info.
authLength = (ULONG)(sizeof(MSV1_0_LM20_LOGON) +
(ALIGN_WORST - 1) +
(wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
rasAuthLength);
// Allocate a buffer on the stack.
authInfo = (PMSV1_0_SUBAUTH_LOGON)_alloca(authLength);
// Initialize the struct.
IASInitAuthInfo(
authInfo,
sizeof(MSV1_0_LM20_LOGON),
UserName,
Domain,
&data
);
//////////
// Set the RAS_SUBAUTH_INFO.
//////////
// Make sure the struct is properly aligned.
data = ROUND_UP_POINTER(data, ALIGN_WORST);
authInfo->AuthenticationInfo1.Length = (USHORT)rasAuthLength;
authInfo->AuthenticationInfo1.MaximumLength = (USHORT)rasAuthLength;
authInfo->AuthenticationInfo1.Buffer = (PCHAR)data;
ras = (RAS_SUBAUTH_INFO*)data;
ras->DataSize = md5AuthLength;
//////////
// Set the MD5CHAP_SUBAUTH_INFO or MD5CHAP_EX_SUBAUTH_INFO.
//////////
if (ChallengeLength == 16)
{
ras->ProtocolType = RAS_SUBAUTH_PROTO_MD5CHAP;
md5 = (MD5CHAP_SUBAUTH_INFO*)ras->Data;
md5->uchChallengeId = ChallengeID;
IASInitFixedArray(md5->uchChallenge, Challenge);
IASInitFixedArray(md5->uchResponse, Response);
}
else
{
ras->ProtocolType = RAS_SUBAUTH_PROTO_MD5CHAP_EX;
md5ex = (MD5CHAP_EX_SUBAUTH_INFO*)ras->Data;
md5ex->uchChallengeId = ChallengeID;
IASInitFixedArray(md5ex->uchResponse, Response);
memcpy(md5ex->uchChallenge, Challenge, ChallengeLength);
}
// Set the parameters and package ID.
authInfo->ParameterControl =
DEFAULT_PARAMETER_CONTROL |
(MSV1_0_SUBAUTHENTICATION_DLL_RAS << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT) |
MSV1_0_SUBAUTHENTICATION_DLL_EX;
status = IASLogonUser(
authInfo,
authLength,
&ProfileBuffer,
Token
);
if (status == NO_ERROR)
{
Profile->KickOffTime.QuadPart = ProfileBuffer->KickOffTime.QuadPart;
// free it.
LsaFreeReturnBuffer(ProfileBuffer);
}
else
{
Profile->KickOffTime.QuadPart = MAXLONGLONG;
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLogonMSCHAP
//
// DESCRIPTION
//
// Performs MS-CHAP authentication against the NT SAM database.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASLogonMSCHAP(
PCWSTR UserName,
PCWSTR Domain,
PBYTE Challenge,
PBYTE NtResponse,
PBYTE LmResponse,
PIAS_MSCHAP_PROFILE Profile,
PHANDLE Token
)
{
DWORD status;
ULONG authLength;
PMSV1_0_LM20_LOGON authInfo;
PBYTE data;
PMSV1_0_LM20_LOGON_PROFILE logonProfile;
DWORD len;
// Calculate the length of the authentication info.
authLength = sizeof(MSV1_0_LM20_LOGON) +
(wcslen(Domain) + wcslen(UserName)) * sizeof(WCHAR) +
(LmResponse ? LM_RESPONSE_LENGTH : 0) +
(NtResponse ? NT_RESPONSE_LENGTH : 0);
// Allocate a buffer on the stack.
authInfo = (PMSV1_0_LM20_LOGON)_alloca(authLength);
// Initialize the struct.
IASInitAuthInfo(
authInfo,
sizeof(MSV1_0_LM20_LOGON),
UserName,
Domain,
&data
);
/////////
// Fill in the challenges and responses.
/////////
IASInitFixedArray(
authInfo->ChallengeToClient,
Challenge
);
if (NtResponse)
{
IASInitOctetString(
authInfo->CaseSensitiveChallengeResponse,
data,
NtResponse,
NT_RESPONSE_LENGTH
);
}
else
{
memset(
&authInfo->CaseSensitiveChallengeResponse,
0,
sizeof(authInfo->CaseSensitiveChallengeResponse)
);
}
if (LmResponse)
{
IASInitOctetString(
authInfo->CaseInsensitiveChallengeResponse,
data,
LmResponse,
LM_RESPONSE_LENGTH
);
}
else
{
memset(
&authInfo->CaseInsensitiveChallengeResponse,
0,
sizeof(authInfo->CaseInsensitiveChallengeResponse)
);
}
// Set the parameters.
authInfo->ParameterControl = DEFAULT_PARAMETER_CONTROL;
status = IASLogonUser(
authInfo,
authLength,
&logonProfile,
Token
);
if (status == NO_ERROR)
{
Profile->KickOffTime.QuadPart = logonProfile->KickOffTime.QuadPart;
if (logonProfile->LogonDomainName.Buffer)
{
wcsncpy(Profile->LogonDomainName,
logonProfile->LogonDomainName.Buffer,
DNLEN);
}
else
{
memset(Profile->LogonDomainName, 0, sizeof(Profile->LogonDomainName));
}
IASInitFixedArray(
Profile->LanmanSessionKey,
logonProfile->LanmanSessionKey
);
IASInitFixedArray(
Profile->UserSessionKey,
logonProfile->UserSessionKey
);
LsaFreeReturnBuffer(logonProfile);
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASChangePassword1
//
// DESCRIPTION
//
// Performs V1 password change.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASChangePassword1(
IN PCWSTR UserName,
IN PCWSTR Domain,
IN PBYTE Challenge,
IN PBYTE LmOldPassword,
IN PBYTE LmNewPassword,
IN PBYTE NtOldPassword,
IN PBYTE NtNewPassword,
IN DWORD NewLmPasswordLength,
IN BOOL NtPresent,
OUT PBYTE NewNtResponse,
OUT PBYTE NewLmResponse
)
{
DWORD status;
SAM_HANDLE hUser;
LM_OWF_PASSWORD LmOwfOldPassword;
LM_OWF_PASSWORD LmOwfNewPassword;
NT_OWF_PASSWORD NtOwfOldPassword;
NT_OWF_PASSWORD NtOwfNewPassword;
BOOLEAN fLmOldPresent;
//////////
// Open the user object.
//////////
status = IASSamOpenUser(
Domain,
UserName,
USER_CHANGE_PASSWORD,
DS_WRITABLE_REQUIRED,
NULL,
NULL,
&hUser
);
if (status != NO_ERROR) { return status; }
//////////
// Decrypt the LM passwords.
//////////
RtlDecryptLmOwfPwdWithLmSesKey(
(PENCRYPTED_LM_OWF_PASSWORD)LmOldPassword,
(PLM_SESSION_KEY)Challenge,
&LmOwfOldPassword
);
RtlDecryptLmOwfPwdWithLmSesKey(
(PENCRYPTED_LM_OWF_PASSWORD)LmNewPassword,
(PLM_SESSION_KEY)Challenge,
&LmOwfNewPassword
);
//////////
// Decrypt the NT passwords if present.
//////////
if (NtPresent)
{
RtlDecryptNtOwfPwdWithNtSesKey(
(PENCRYPTED_NT_OWF_PASSWORD)NtOldPassword,
(PNT_SESSION_KEY)Challenge,
&NtOwfOldPassword
);
RtlDecryptNtOwfPwdWithNtSesKey(
(PENCRYPTED_NT_OWF_PASSWORD)NtNewPassword,
(PNT_SESSION_KEY)Challenge,
&NtOwfNewPassword
);
}
//////////
// Change the password for this user
//////////
fLmOldPresent = (NewLmPasswordLength > LM20_PWLEN) ? FALSE : TRUE;
status = SamiChangePasswordUser(
hUser,
fLmOldPresent,
&LmOwfOldPassword,
&LmOwfNewPassword,
(BOOLEAN)NtPresent,
&NtOwfOldPassword,
&NtOwfNewPassword
);
if (NT_SUCCESS(status))
{
//////////
// Calculate the user's reponse with the new password.
//////////
RtlCalculateLmResponse(
(PLM_CHALLENGE)Challenge,
&LmOwfNewPassword,
(PLM_RESPONSE)NewLmResponse
);
RtlCalculateNtResponse(
(PNT_CHALLENGE)Challenge,
&NtOwfNewPassword,
(PNT_RESPONSE)NewNtResponse
);
}
SamCloseHandle(hUser);
return RtlNtStatusToDosError(status);
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASChangePassword2
//
// DESCRIPTION
//
// Performs V2 password change.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASChangePassword2(
IN PCWSTR UserName,
IN PCWSTR Domain,
IN PBYTE OldNtHash,
IN PBYTE OldLmHash,
IN PBYTE NtEncPassword,
IN PBYTE LmEncPassword,
IN BOOL LmPresent
)
{
DWORD status;
PDOMAIN_CONTROLLER_INFOW dci;
UNICODE_STRING uniServerName, uniUserName;
//////////
// Get the name of the DC to connect to.
//////////
if (_wcsicmp(Domain, theAccountDomain) == 0)
{
//////////
// Local domain, so use theLocalServer.
//////////
dci = NULL;
RtlInitUnicodeString(
&uniServerName,
theLocalServer
);
}
else
{
//////////
// Remote domain, so use IASGetDcName.
//////////
status = IASGetDcName(
Domain,
DS_WRITABLE_REQUIRED,
&dci
);
if (status != NO_ERROR) { goto exit; }
RtlInitUnicodeString(
&uniServerName,
dci->DomainControllerName
);
}
RtlInitUnicodeString(
&uniUserName,
UserName
);
status = SamiChangePasswordUser2(
&uniServerName,
&uniUserName,
(PSAMPR_ENCRYPTED_USER_PASSWORD)NtEncPassword,
(PENCRYPTED_NT_OWF_PASSWORD)OldNtHash,
(BOOLEAN)LmPresent,
(PSAMPR_ENCRYPTED_USER_PASSWORD)LmEncPassword,
(PENCRYPTED_LM_OWF_PASSWORD)OldLmHash
);
status = RtlNtStatusToDosError(status);
if (dci)
{
NetApiBufferFree(dci);
}
exit:
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// Various constants used for MS-CHAP v2
//
///////////////////////////////////////////////////////////////////////////////
UCHAR AuthMagic1[39] =
{
0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
};
UCHAR AuthMagic2[41] =
{
0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
0x6E
};
UCHAR SHSpad1[40] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
UCHAR SHSpad2[40] =
{
0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2
};
UCHAR KeyMagic1[27] =
{
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x4D, 0x50, 0x50, 0x45, 0x20, 0x4D,
0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4B, 0x65, 0x79
};
UCHAR KeyMagic2[84] =
{
0x4F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69,
0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2C, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x6E, 0x64, 0x20, 0x6B, 0x65, 0x79,
0x3B, 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
0x2C, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6B, 0x65, 0x79, 0x2E
};
UCHAR KeyMagic3[84] =
{
0x4F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69,
0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2C, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6B, 0x65, 0x79, 0x3B, 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
0x69, 0x64, 0x65, 0x2C, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x64, 0x20,
0x6B, 0x65, 0x79, 0x2E
};
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASLogonMSCHAPv2
//
// DESCRIPTION
//
// Performs MS-CHAP v2 authentication.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASLogonMSCHAPv2(
IN PCWSTR UserName,
IN PCWSTR Domain,
IN PCSTR HashUserName,
IN PBYTE Challenge,
IN DWORD ChallengeLength,
IN PBYTE Response,
IN PBYTE PeerChallenge,
OUT PIAS_MSCHAP_V2_PROFILE Profile,
OUT PHANDLE Token
)
{
A_SHA_CTX context;
BYTE digest[A_SHA_DIGEST_LEN], masterKey[A_SHA_DIGEST_LEN];
BYTE computedChallenge[MSV1_0_CHALLENGE_LENGTH];
IAS_MSCHAP_PROFILE v1profile;
DWORD status;
/////////
// Compute the v2 challenge.
/////////
A_SHAInit(&context);
A_SHAUpdate(&context, PeerChallenge, 16);
A_SHAUpdate(&context, Challenge, ChallengeLength);
A_SHAUpdate(&context, (PBYTE)HashUserName, strlen(HashUserName));
A_SHAFinal(&context, digest);
memcpy(computedChallenge, digest, sizeof(computedChallenge));
/////////
// Authenticate the user.
/////////
status = IASLogonMSCHAP(
UserName,
Domain,
computedChallenge,
Response,
NULL,
&v1profile,
Token
);
if (status != NO_ERROR) { return status; }
/////////
// Generate authenticator response.
/////////
A_SHAInit(&context);
A_SHAUpdate(&context, v1profile.UserSessionKey, 16);
A_SHAUpdate(&context, Response, NT_RESPONSE_LENGTH);
A_SHAUpdate(&context, AuthMagic1, sizeof(AuthMagic1));
A_SHAFinal(&context, digest);
A_SHAInit(&context);
A_SHAUpdate(&context, digest, sizeof(digest));
A_SHAUpdate(&context, computedChallenge, sizeof(computedChallenge));
A_SHAUpdate(&context, AuthMagic2, sizeof(AuthMagic2));
A_SHAFinal(&context, digest);
memcpy(Profile->AuthResponse, digest, _AUTHENTICATOR_RESPONSE_LENGTH);
/////////
// Generate master key.
/////////
A_SHAInit(&context);
A_SHAUpdate(&context, v1profile.UserSessionKey, 16);
A_SHAUpdate(&context, Response, NT_RESPONSE_LENGTH);
A_SHAUpdate(&context, KeyMagic1, sizeof(KeyMagic1));
A_SHAFinal(&context, masterKey);
/////////
// Generate receive key.
/////////
A_SHAInit(&context);
A_SHAUpdate(&context, masterKey, 16);
A_SHAUpdate(&context, SHSpad1, sizeof(SHSpad1));
A_SHAUpdate(&context, KeyMagic2, sizeof(KeyMagic2));
A_SHAUpdate(&context, SHSpad2, sizeof(SHSpad2));
A_SHAFinal(&context, digest);
memcpy(Profile->RecvSessionKey, digest, MSV1_0_USER_SESSION_KEY_LENGTH);
/////////
// Generate send key.
/////////
A_SHAInit(&context);
A_SHAUpdate(&context, masterKey, 16);
A_SHAUpdate(&context, SHSpad1, sizeof(SHSpad1));
A_SHAUpdate(&context, KeyMagic3, sizeof(KeyMagic3));
A_SHAUpdate(&context, SHSpad2, sizeof(SHSpad2));
A_SHAFinal(&context, digest);
memcpy(Profile->SendSessionKey, digest, MSV1_0_USER_SESSION_KEY_LENGTH);
/////////
// Copy the logon domain.
/////////
memcpy(
Profile->LogonDomainName,
v1profile.LogonDomainName,
sizeof(Profile->LogonDomainName)
);
/////////
// Save the KickOffTime.
/////////
Profile->KickOffTime = v1profile.KickOffTime;
return NO_ERROR;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASChangePassword3
//
// DESCRIPTION
//
// Performs MS-CHAP v2 change password.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASChangePassword3(
IN PCWSTR UserName,
IN PCWSTR Domain,
IN PBYTE EncHash,
IN PBYTE EncPassword
)
{
return IASChangePassword2(
UserName,
Domain,
EncHash,
NULL,
EncPassword,
NULL,
FALSE
);
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetAliasMembership
//
// DESCRIPTION
//
// Determines alias membership in the local account and built-in domains.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetAliasMembership(
IN PSID UserSid,
IN PTOKEN_GROUPS GlobalGroups,
IN PIAS_LSA_ALLOC Allocator,
OUT PTOKEN_GROUPS *Groups,
OUT PDWORD ReturnLength
)
{
DWORD i, status, idx;
ULONG globalSidCount;
PSID *globalSids, sidBuffer;
PULONG accountAliases, builtinAliases;
ULONG accountAliasCount, builtinAliasCount;
ULONG buflen, groupCount;
//////////
// Form an array of the 'global' SIDs (global groups plus user).
//////////
globalSidCount = GlobalGroups->GroupCount + 1;
globalSids = (PSID*)_alloca(globalSidCount * sizeof(PSID));
// First the group SIDs ...
for (i = 0; i < GlobalGroups->GroupCount; ++i)
{
globalSids[i] = GlobalGroups->Groups[i].Sid;
}
// ... then the user SID.
globalSids[i] = UserSid;
//////////
// Lookup aliases in the account and built-in domains.
//////////
status = SamGetAliasMembership(
theAccountDomainHandle,
globalSidCount,
globalSids,
&accountAliasCount,
&accountAliases
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto exit;
}
status = SamGetAliasMembership(
theBuiltinDomainHandle,
globalSidCount,
globalSids,
&builtinAliasCount,
&builtinAliases
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto free_account_aliases;
}
//////////
// Allocate memory for the TOKEN_GROUPS struct.
//////////
// Space for the struct header.
buflen = FIELD_OFFSET(TOKEN_GROUPS, Groups);
// Space for the global groups.
groupCount = GlobalGroups->GroupCount;
for (i = 0; i < groupCount; ++i)
{
buflen += RtlLengthSid(GlobalGroups->Groups[i].Sid);
}
// Space for the aliases in the account domain.
groupCount += accountAliasCount;
buflen += theAccountSidLen * accountAliasCount;
// Space for the aliases in the builtin domain.
groupCount += builtinAliasCount;
buflen += theBuiltinSidLen * builtinAliasCount;
// Space for the SID_AND_ATTRIBUTES array.
buflen += sizeof(SID_AND_ATTRIBUTES) * groupCount;
*Groups = (PTOKEN_GROUPS)Allocator(buflen);
if (!*Groups)
{
status = ERROR_NOT_ENOUGH_MEMORY;
goto free_builtin_aliases;
}
*ReturnLength = buflen;
//////////
// Fill in the TOKEN_GROUPS struct.
//////////
(*Groups)->GroupCount = groupCount;
sidBuffer = (*Groups)->Groups + groupCount;
RtlCopySidAndAttributesArray(
GlobalGroups->GroupCount,
GlobalGroups->Groups,
buflen,
(*Groups)->Groups,
sidBuffer,
&sidBuffer,
&buflen
);
idx = GlobalGroups->GroupCount;
for (i = 0; i < accountAliasCount; ++i, ++idx)
{
IASInitializeChildSid(
sidBuffer,
theAccountDomainSid,
accountAliases[i]
);
(*Groups)->Groups[idx].Sid = sidBuffer;
(*Groups)->Groups[idx].Attributes = SE_GROUP_ENABLED;
sidBuffer = (PBYTE)sidBuffer + theAccountSidLen;
}
for (i = 0; i < builtinAliasCount; ++i, ++idx)
{
IASInitializeChildSid(
sidBuffer,
theBuiltinDomainSid,
builtinAliases[i]
);
(*Groups)->Groups[idx].Sid = sidBuffer;
(*Groups)->Groups[idx].Attributes = SE_GROUP_ENABLED;
sidBuffer = (PBYTE)sidBuffer + theBuiltinSidLen;
}
free_builtin_aliases:
SamFreeMemory(builtinAliases);
free_account_aliases:
SamFreeMemory(accountAliases);
exit:
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetGroupsForUser
//
// DESCRIPTION
//
// Allocated and initializes a TOKEN_GROUPS struct for the specified user.
//
///////////////////////////////////////////////////////////////////////////////
#define REQUIRED_USER_FIELDS \
( USER_ALL_USERACCOUNTCONTROL | \
USER_ALL_ACCOUNTEXPIRES | \
USER_ALL_PARAMETERS )
DWORD
WINAPI
IASGetGroupsForUser(
IN PCWSTR UserName,
IN PCWSTR Domain,
IN PIAS_LSA_ALLOC Allocator,
OUT PTOKEN_GROUPS *Groups,
OUT PDWORD ReturnLength,
OUT PLARGE_INTEGER SessionTimeout
)
{
DWORD status, i;
SAM_HANDLE hUser;
PSID userDomainSid;
ULONG userRid;
PUSER_ALL_INFORMATION uai;
PGROUP_MEMBERSHIP globalGroups;
ULONG globalGroupCount, globalSidLen;
PTOKEN_GROUPS tokenGroups;
PSID sidBuffer;
_ASSERT(SessionTimeout != NULL);
//////////
// Open the user.
//////////
status = IASSamOpenUser(
Domain,
UserName,
USER_LIST_GROUPS | USER_READ_ACCOUNT | USER_READ_LOGON,
0,
&userRid,
&userDomainSid,
&hUser
);
if (status != NO_ERROR) { goto exit; }
//////////
// Check the account restrictions.
//////////
status = SamQueryInformationUser(
hUser,
UserAllInformation,
(PVOID*)&uai
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto close_user;
}
if ((uai->WhichFields & REQUIRED_USER_FIELDS) != REQUIRED_USER_FIELDS)
{
status = ERROR_ACCESS_DENIED;
goto free_user_info;
}
if (uai->UserAccountControl & USER_ACCOUNT_DISABLED)
{
status = ERROR_ACCOUNT_DISABLED;
goto free_user_info;
}
if (uai->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)
{
status = ERROR_ACCOUNT_LOCKED_OUT;
goto free_user_info;
}
status = IASCheckAccountRestrictions(
&(uai->AccountExpires),
(PIAS_LOGON_HOURS)&(uai->LogonHours),
SessionTimeout
);
if (status != NO_ERROR) { goto free_user_info; }
//////////
// Get the user's global groups.
//////////
status = SamGetGroupsForUser(
hUser,
&globalGroups,
&globalGroupCount
);
if (!NT_SUCCESS(status))
{
status = RtlNtStatusToDosError(status);
goto close_user;
}
//////////
// Allocate memory for the TOKEN_GROUPS struct plus the user SID.
//////////
globalSidLen = IASLengthRequiredChildSid(userDomainSid);
tokenGroups =
(PTOKEN_GROUPS)_alloca(
FIELD_OFFSET(TOKEN_GROUPS, Groups) +
(sizeof(SID_AND_ATTRIBUTES) + globalSidLen) *
globalGroupCount +
globalSidLen
);
//////////
// Fill in the TOKEN_GROUPS struct.
//////////
tokenGroups->GroupCount = globalGroupCount;
sidBuffer = tokenGroups->Groups + globalGroupCount;
for (i = 0; i < globalGroupCount; ++i)
{
IASInitializeChildSid(
sidBuffer,
userDomainSid,
globalGroups[i].RelativeId
);
tokenGroups->Groups[i].Sid = sidBuffer;
tokenGroups->Groups[i].Attributes = globalGroups[i].Attributes;
sidBuffer = (PBYTE)sidBuffer + globalSidLen;
}
///////
// Compute the user SID.
///////
IASInitializeChildSid(
sidBuffer,
userDomainSid,
userRid
);
///////
// Expand the group membership locally.
///////
status = IASGetAliasMembership(
sidBuffer,
tokenGroups,
Allocator,
Groups,
ReturnLength
);
SamFreeMemory(globalGroups);
free_user_info:
SamFreeMemory(uai);
close_user:
RtlFreeHeap(RtlProcessHeap(), 0, userDomainSid);
SamCloseHandle(hUser);
exit:
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// GetSamUserParameters
//
// DESCRIPTION
//
// Retrieves the USER_PARAMETERS_INFORMATION for a user.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
GetSamUserParameters(
IN PCWSTR UserName,
IN PCWSTR Domain,
OUT PUSER_PARAMETERS_INFORMATION *UserParameters
)
{
DWORD status;
SAM_HANDLE hUser;
// Initialize the out parameter.
*UserParameters = NULL;
// Find the user.
status = IASSamOpenUser(
Domain,
UserName,
USER_READ_ACCOUNT,
0,
NULL,
NULL,
&hUser
);
if (status == NO_ERROR)
{
// Get the user's parameters.
status = SamQueryInformationUser(
hUser,
UserParametersInformation,
(PVOID*)UserParameters
);
if (!NT_SUCCESS(status)) { status = RtlNtStatusToDosError(status); }
SamCloseHandle(hUser);
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetUserParameters
//
// DESCRIPTION
//
// Returns the SAM UserParameters for a given user. The returned string
// must be freed through LocalFree.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetUserParameters(
IN PCWSTR UserName,
IN PCWSTR Domain,
OUT PWSTR *UserParameters
)
{
DWORD status;
SAM_HANDLE hUser;
PUSER_PARAMETERS_INFORMATION upi;
// Initialize the out parameter.
*UserParameters = NULL;
// Get the USER_PARAMETERS_INFORMATION.
status = GetSamUserParameters(
UserName,
Domain,
&upi
);
if (status != NO_ERROR) { return status; }
*UserParameters = (PWSTR)LocalAlloc(
LMEM_FIXED,
upi->Parameters.Length + sizeof(WCHAR)
);
if (*UserParameters)
{
memcpy(*UserParameters, upi->Parameters.Buffer, upi->Parameters.Length);
(*UserParameters)[upi->Parameters.Length / sizeof(WCHAR)] = L'\0';
}
else
{
status = ERROR_NOT_ENOUGH_MEMORY;
}
SamFreeMemory(upi);
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetRASUserInfo
//
// DESCRIPTION
//
// Basically a rewrite of RasAdminUserGetInfo.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetRASUserInfo(
IN PCWSTR UserName,
IN PCWSTR Domain,
OUT PRAS_USER_0 RasUser0
)
{
DWORD status;
PWSTR userParms;
status = IASGetUserParameters(
UserName,
Domain,
&userParms
);
if (status == NO_ERROR)
{
status = IASParmsQueryRasUser0(
userParms,
RasUser0
);
LocalFree(userParms);
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASValidateUserName
//
// DESCRIPTION
//
// Verifies that the input parameters represent a valid SAM account.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASValidateUserName(
IN PCWSTR UserName,
IN PCWSTR Domain
)
{
DWORD status;
PWCHAR attrs[1];
PLDAPMessage msg;
SAM_HANDLE hUser;
IAS_NTDS_RESULT result = { 0, 0 };
// For remote domains, we'll try LDAP first since it's faster.
if (!IASIsDomainLocal(Domain))
{
attrs[0] = NULL;
status = IASNtdsQueryUserAttributes(
Domain,
UserName,
LDAP_SCOPE_SUBTREE,
attrs,
&result
);
IASNtdsFreeResult(&result);
if (status != ERROR_DS_NOT_INSTALLED &&
status != ERROR_INVALID_DOMAIN_ROLE)
{
return status;
}
}
// Couldn't use the DS, so try SAM.
status = IASSamOpenUser(
Domain,
UserName,
USER_READ_ACCOUNT,
0,
NULL,
NULL,
&hUser
);
if (status == NO_ERROR) { SamCloseHandle(hUser); }
return status;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetDefaultDomain
//
// DESCRIPTION
//
// Returns the default domain. The returned string should be treated as
// read-only memory.
//
///////////////////////////////////////////////////////////////////////////////
PCWSTR
WINAPI
IASGetDefaultDomain( VOID )
{
return theDefaultDomain;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetDnsDomainName
//
// DESCRIPTION
//
// Returns the dns name of the domain
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetDnsDomainName(LPWSTR buffer, LPDWORD bufferByteSize)
{
DWORD result = NO_ERROR;
EnterCriticalSection(&critSec);
// i.e. assert that this function is not called after uninitialize or
// if initialize failed
_ASSERT(theDnsDomainName != 0);
if (bufferByteSize == 0 || buffer == 0)
{
result = ERROR_INSUFFICIENT_BUFFER;
}
else
{
HRESULT hr = StringCbCopyNW(
buffer,
*bufferByteSize,
theDnsDomainName->Buffer,
theDnsDomainName->Length
);
if (FAILED(hr))
{
result = HRESULT_CODE(hr);
}
}
// always set the necessary size
*bufferByteSize = theDnsDomainName->Length + sizeof(wchar_t); // for '\0'
// Failure could be for any reason: insufficient buffer size or
// anything else.
LeaveCriticalSection(&critSec);
return result;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASIsDomainLocal
//
// DESCRIPTION
//
// Returns TRUE if the specified domain resides on the local machine.
//
///////////////////////////////////////////////////////////////////////////////
BOOL
WINAPI
IASIsDomainLocal(
IN PCWSTR Domain
)
{
return (_wcsicmp(Domain, theAccountDomain) == 0) ? TRUE : FALSE;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetRole
//
// DESCRIPTION
//
// Returns the role of the local computer.
//
///////////////////////////////////////////////////////////////////////////////
IAS_ROLE
WINAPI
IASGetRole( VOID )
{
return ourRole;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetProductType
//
// DESCRIPTION
//
// Returns the product type of the local computer.
//
///////////////////////////////////////////////////////////////////////////////
IAS_PRODUCT_TYPE
WINAPI
IASGetProductType( VOID )
{
return ourProductType;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetGuestAccountName
//
// DESCRIPTION
//
// Returns the SAM account name of the guest account for the default
// domain. GuestAccount must be large enough to hold DNLEN + UNLEN + 2
// characters.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetGuestAccountName(
OUT PWSTR AccountName
)
{
wcscpy(AccountName, theGuestAccount);
return NO_ERROR;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASMapWin32Error
//
// DESCRIPTION
//
// Maps a Win32 error code to an IAS reason code. If the error can't be
// mapped, 'hrDefault' is returned. If 'hrDefault' equals -1, then
// HRESULT_FROM_WIN32 will be used to force a mapping.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT
WINAPI
IASMapWin32Error(
IN DWORD dwError,
IN HRESULT hrDefault
)
{
HRESULT hr = hrDefault;
switch (dwError)
{
case NO_ERROR:
hr = S_OK;
break;
case ERROR_ACCESS_DENIED:
hr = IAS_ACCESS_DENIED;
break;
case ERROR_NO_SUCH_DOMAIN:
hr = IAS_NO_SUCH_DOMAIN;
break;
case ERROR_NO_LOGON_SERVERS:
case RPC_S_SERVER_UNAVAILABLE:
case RPC_S_SERVER_TOO_BUSY:
case RPC_S_CALL_FAILED:
case EPT_S_NOT_REGISTERED:
hr = IAS_DOMAIN_UNAVAILABLE;
break;
case ERROR_INVALID_PASSWORD:
case ERROR_LOGON_FAILURE:
hr = IAS_AUTH_FAILURE;
break;
case ERROR_INVALID_LOGON_HOURS:
hr = IAS_INVALID_LOGON_HOURS;
break;
case ERROR_PASSWORD_EXPIRED:
case ERROR_PASSWORD_MUST_CHANGE:
hr = IAS_PASSWORD_MUST_CHANGE;
break;
case ERROR_ACCOUNT_RESTRICTION:
hr = IAS_ACCOUNT_RESTRICTION;
break;
case ERROR_ACCOUNT_DISABLED:
hr = IAS_ACCOUNT_DISABLED;
break;
case ERROR_ACCOUNT_EXPIRED:
hr = IAS_ACCOUNT_EXPIRED;
break;
case ERROR_ACCOUNT_LOCKED_OUT:
hr = IAS_ACCOUNT_LOCKED_OUT;
break;
case ERROR_NO_SUCH_USER:
case ERROR_NONE_MAPPED:
case NERR_UserNotFound:
hr = IAS_NO_SUCH_USER;
break;
case ERROR_ILL_FORMED_PASSWORD:
case ERROR_PASSWORD_RESTRICTION:
hr = IAS_CHANGE_PASSWORD_FAILURE;
break;
case ERROR_DS_NO_ATTRIBUTE_OR_VALUE:
hr = IAS_NO_CLEARTEXT_PASSWORD;
break;
case CRYPT_E_REVOKED:
hr = IAS_CRYPT_E_REVOKED;
break;
case CRYPT_E_NO_REVOCATION_DLL:
hr = IAS_CRYPT_E_NO_REVOCATION_DLL;
break;
case CRYPT_E_NO_REVOCATION_CHECK:
hr = IAS_CRYPT_E_NO_REVOCATION_CHECK;
break;
case CRYPT_E_REVOCATION_OFFLINE:
hr = IAS_CRYPT_E_REVOCATION_OFFLINE;
break;
case SEC_E_MESSAGE_ALTERED:
hr = IAS_SEC_E_MESSAGE_ALTERED;
break;
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
hr = IAS_SEC_E_NO_AUTHENTICATING_AUTHORITY;
break;
case SEC_E_INCOMPLETE_MESSAGE:
hr = IAS_SEC_E_INCOMPLETE_MESSAGE;
break;
case SEC_E_INCOMPLETE_CREDENTIALS:
hr = IAS_SEC_E_INCOMPLETE_CREDENTIALS;
break;
case SEC_E_TIME_SKEW:
hr = IAS_SEC_E_TIME_SKEW;
break;
case SEC_E_UNTRUSTED_ROOT:
hr = IAS_SEC_E_UNTRUSTED_ROOT;
break;
case SEC_E_ILLEGAL_MESSAGE:
hr = IAS_SEC_E_ILLEGAL_MESSAGE;
break;
case SEC_E_CERT_WRONG_USAGE:
hr = IAS_SEC_E_CERT_WRONG_USAGE;
break;
case SEC_E_CERT_EXPIRED:
hr = IAS_SEC_E_CERT_EXPIRED;
break;
case SEC_E_ALGORITHM_MISMATCH:
hr = IAS_SEC_E_ALGORITHM_MISMATCH;
break;
case SEC_E_SMARTCARD_LOGON_REQUIRED:
hr = IAS_SEC_E_SMARTCARD_LOGON_REQUIRED;
break;
case SEC_E_SHUTDOWN_IN_PROGRESS:
hr = IAS_SEC_E_SHUTDOWN_IN_PROGRESS;
break;
case SEC_E_MULTIPLE_ACCOUNTS:
hr = IAS_SEC_E_MULTIPLE_ACCOUNTS;
break;
case TRUST_E_PROVIDER_UNKNOWN:
hr = IAS_TRUST_E_PROVIDER_UNKNOWN;
break;
case TRUST_E_ACTION_UNKNOWN:
hr = IAS_TRUST_E_ACTION_UNKNOWN;
break;
case TRUST_E_SUBJECT_FORM_UNKNOWN:
hr = IAS_TRUST_E_SUBJECT_FORM_UNKNOWN;
break;
case TRUST_E_SUBJECT_NOT_TRUSTED:
hr = IAS_TRUST_E_SUBJECT_NOT_TRUSTED;
break;
case TRUST_E_NOSIGNATURE:
hr = IAS_TRUST_E_NOSIGNATURE;
break;
case CERT_E_EXPIRED:
hr = IAS_CERT_E_EXPIRED;
break;
case CERT_E_VALIDITYPERIODNESTING:
hr = IAS_CERT_E_VALIDITYPERIODNESTING;
break;
case CERT_E_ROLE:
hr = IAS_CERT_E_ROLE;
break;
case CERT_E_PATHLENCONST:
hr = IAS_CERT_E_PATHLENCONST;
break;
case CERT_E_CRITICAL:
hr = IAS_CERT_E_CRITICAL;
break;
case CERT_E_PURPOSE:
hr = IAS_CERT_E_PURPOSE;
break;
case CERT_E_ISSUERCHAINING:
hr = IAS_CERT_E_ISSUERCHAINING;
break;
case CERT_E_MALFORMED:
hr = IAS_CERT_E_MALFORMED;
break;
case CERT_E_UNTRUSTEDROOT:
hr = IAS_CERT_E_UNTRUSTEDROOT;
break;
case CERT_E_CHAINING:
hr = IAS_CERT_E_CHAINING;
break;
case TRUST_E_FAIL:
hr = IAS_TRUST_E_FAIL;
break;
case CERT_E_REVOKED:
hr = IAS_CERT_E_REVOKED ;
break;
case CERT_E_UNTRUSTEDTESTROOT:
hr = IAS_CERT_E_UNTRUSTEDTESTROOT;
break;
case CERT_E_REVOCATION_FAILURE:
hr = IAS_CERT_E_REVOCATION_FAILURE;
break;
case CERT_E_CN_NO_MATCH:
hr = IAS_CERT_E_CN_NO_MATCH;
break;
case CERT_E_WRONG_USAGE:
hr = IAS_CERT_E_WRONG_USAGE;
break;
case TRUST_E_EXPLICIT_DISTRUST:
hr = IAS_TRUST_E_EXPLICIT_DISTRUST;
break;
case CERT_E_UNTRUSTEDCA:
hr = IAS_CERT_E_UNTRUSTEDCA;
break;
case CERT_E_INVALID_POLICY:
hr = IAS_CERT_E_INVALID_POLICY;
break;
case CERT_E_INVALID_NAME:
hr = IAS_CERT_E_INVALID_NAME;
break;
case SEC_E_PKINIT_NAME_MISMATCH:
hr = IAS_SEC_E_PKINIT_NAME_MISMATCH;
break;
case SEC_E_OUT_OF_SEQUENCE:
hr = IAS_SEC_E_OUT_OF_SEQUENCE;
break;
case SEC_E_NO_CREDENTIALS:
hr = IAS_SEC_E_NO_CREDENTIALS;
break;
case NTE_BAD_UID:
case NTE_BAD_HASH:
case NTE_BAD_KEY:
case NTE_BAD_LEN:
case NTE_BAD_DATA:
case NTE_BAD_SIGNATURE:
case NTE_BAD_VER:
case NTE_BAD_ALGID:
case NTE_BAD_FLAGS:
case NTE_BAD_TYPE:
case NTE_BAD_KEY_STATE:
case NTE_BAD_HASH_STATE:
case NTE_NO_KEY:
case NTE_NO_MEMORY:
case NTE_EXISTS:
case NTE_PERM:
case NTE_NOT_FOUND:
case NTE_DOUBLE_ENCRYPT:
case NTE_BAD_PROVIDER:
case NTE_BAD_PROV_TYPE:
case NTE_BAD_PUBLIC_KEY:
case NTE_BAD_KEYSET:
case NTE_PROV_TYPE_NOT_DEF:
case NTE_PROV_TYPE_ENTRY_BAD:
case NTE_KEYSET_NOT_DEF:
case NTE_KEYSET_ENTRY_BAD:
case NTE_PROV_TYPE_NO_MATCH:
case NTE_SIGNATURE_FILE_BAD:
case SEC_I_CONTINUE_NEEDED:
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
case SEC_I_LOCAL_LOGON:
case SEC_E_BAD_PKGID:
case SEC_E_CONTEXT_EXPIRED:
case SEC_I_CONTEXT_EXPIRED:
case SEC_E_BUFFER_TOO_SMALL:
case SEC_I_INCOMPLETE_CREDENTIALS:
case SEC_I_RENEGOTIATE:
case SEC_E_WRONG_PRINCIPAL:
case SEC_I_NO_LSA_CONTEXT:
case SEC_E_ENCRYPT_FAILURE:
case SEC_E_DECRYPT_FAILURE:
case SEC_E_SECURITY_QOS_FAILED:
case SEC_E_UNFINISHED_CONTEXT_DELETED:
case SEC_E_NO_TGT_REPLY:
case SEC_E_NO_IP_ADDRESSES:
case SEC_E_WRONG_CREDENTIAL_HANDLE:
case SEC_E_CRYPTO_SYSTEM_INVALID:
case SEC_E_MAX_REFERRALS_EXCEEDED:
case SEC_E_MUST_BE_KDC:
case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
case SEC_E_TOO_MANY_PRINCIPALS:
case SEC_E_NO_PA_DATA:
case SEC_E_CERT_UNKNOWN:
hr = IAS_UNEXPECTED_EAP_ERROR;
break;
case ERROR_PROTOCOL_NOT_CONFIGURED:
hr = IAS_EAP_NEGOTIATION_FAILED;
break;
default:
// Default value already set above.
break;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASGetDcName
//
// DESCRIPTION
//
// Wrapper around DsGetDcNameW. Tries to do the right thing with regard
// to NETBIOS and DNS names.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
IASGetDcName(
IN LPCWSTR DomainName,
IN ULONG Flags,
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
)
{
DWORD status;
PDOMAIN_CONTROLLER_INFOW dci;
if (!(Flags & DS_IS_DNS_NAME)) { Flags |= DS_IS_FLAT_NAME; }
status = DsGetDcNameW(
NULL,
DomainName,
NULL,
NULL,
Flags,
DomainControllerInfo
);
if (status == NO_ERROR &&
!(Flags & DS_IS_DNS_NAME) &&
((*DomainControllerInfo)->Flags & DS_DS_FLAG))
{
// It's an NT5 DC, so we need the DNS name of the server.
Flags |= DS_RETURN_DNS_NAME;
// We always want a cache hit here.
Flags &= ~(ULONG)DS_FORCE_REDISCOVERY;
if (!DsGetDcNameW(
NULL,
DomainName,
NULL,
NULL,
Flags,
&dci
))
{
NetApiBufferFree(*DomainControllerInfo);
*DomainControllerInfo = dci;
}
}
return status;
}