|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// ezlogon.c
//
// SYNOPSIS
//
// Defines the IAS wrapper around LsaLogonUser
//
// MODIFICATION HISTORY
//
// 08/15/1998 Original version.
// 09/09/1998 Fix AV when logon domain doesn't match user domain.
// 10/02/1998 NULL out handle when LsaLogonUser fails.
// 10/11/1998 Use SubStatus for STATUS_ACCOUNT_RESTRICTION.
// 10/22/1998 PIAS_LOGON_HOURS is now a mandatory parameter.
// 01/28/1999 Remove LogonDomainName check.
// 04/19/1999 Add IASPurgeTicketCache.
//
///////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <lm.h>
#include <sha.h>
#include <ntsam.h>
#include <ntsamp.h>
#include <dsgetdc.h>
#include <ntlsa.h>
#include <kerberos.h>
#include <windows.h>
#include <ezlogon.h>
#include <malloc.h>
//#include <iaslsa.h>
//#define NT_RESPONSE_LENGTH 24
//#define LM_RESPONSE_LENGTH 24
//////////
// Handle to the IAS Logon Process.
//////////
LSA_HANDLE theLogonProcess;
//////////
// The MSV1_0 authentication package.
//////////
ULONG theMSV1_0_Package;
CONST CHAR LOGON_PROCESS_NAME[] = "CHAP"; CONST CHAR TOKEN_SOURCE_NAME[TOKEN_SOURCE_LENGTH] = "CHAP";
// Number of milliseconds in a week.
#define MSEC_PER_WEEK (1000 * 60 * 60 * 24 * 7)
//////////
// Misc. global variables used for logons.
//////////
LSA_HANDLE theLogonProcess; // The handle for the logon process.
ULONG theMSV1_0_Package; // The MSV1_0 authentication package.
ULONG theKerberosPackage; // The Kerberos authentication package.
STRING theOriginName; // The origin of the logon requests.
TOKEN_SOURCE theSourceContext; // The source context of the logon requests.
//////////
// Domain names.
//////////
WCHAR theAccountDomain [DNLEN + 1]; // Local account domain.
WCHAR theRegistryDomain[DNLEN + 1]; // Registry override for default domain.
//////////
// SID's
//////////
PSID theAccountDomainSid; PSID theBuiltinDomainSid;
//////////
// UNC name of the local computer.
//////////
WCHAR theLocalServer[CNLEN + 3];
SECURITY_QUALITY_OF_SERVICE QOS = { sizeof(SECURITY_QUALITY_OF_SERVICE), // Length
SecurityImpersonation, // ImpersonationLevel
SECURITY_DYNAMIC_TRACKING, // ContextTrackingMode
FALSE // EffectiveOnly
};
OBJECT_ATTRIBUTES theObjectAttributes = { sizeof(OBJECT_ATTRIBUTES), // Length
NULL, // RootDirectory
NULL, // ObjectName
0, // Attributes
NULL, // SecurityDescriptor
&QOS // SecurityQualityOfService
};
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASLogonInitialize
//
// DESCRIPTION
//
// Registers the logon process.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASLogonInitialize( VOID ) { DWORD status; BOOLEAN wasEnabled; LSA_STRING processName, packageName; PPOLICY_ACCOUNT_DOMAIN_INFO padi = NULL; LSA_OPERATIONAL_MODE opMode; DWORD cbData = 0; LSA_HANDLE hLsa; //////////
// Enable SE_TCB_PRIVILEGE.
//////////
status = RtlAdjustPrivilege( SE_TCB_PRIVILEGE, TRUE, FALSE, &wasEnabled ); if (!NT_SUCCESS(status)) { goto exit; }
//////////
// Register as a logon process.
//////////
RtlInitString( &processName, LOGON_PROCESS_NAME );
status = LsaRegisterLogonProcess( &processName, &theLogonProcess, &opMode ); if (!NT_SUCCESS(status)) { goto exit; }
//////////
// Lookup the MSV1_0 authentication package.
//////////
RtlInitString( &packageName, MSV1_0_PACKAGE_NAME );
status = LsaLookupAuthenticationPackage( theLogonProcess, &packageName, &theMSV1_0_Package ); if (!NT_SUCCESS(status)) { goto deregister; }
//////////
// Lookup the Kerberos authentication package.
//////////
RtlInitString( &packageName, MICROSOFT_KERBEROS_NAME_A );
status = LsaLookupAuthenticationPackage( theLogonProcess, &packageName, &theKerberosPackage ); if (!NT_SUCCESS(status)) { goto deregister; }
//////////
// Initialize the source context.
//////////
memcpy(theSourceContext.SourceName, TOKEN_SOURCE_NAME, TOKEN_SOURCE_LENGTH); status = NtAllocateLocallyUniqueId( &theSourceContext.SourceIdentifier ); if (!NT_SUCCESS(status)) { goto deregister; }
/////////
/// Initialize the account domain and local domain
////////
wcscpy(theLocalServer, L"\\\\"); cbData = CNLEN + 1; if (!GetComputerNameW(theLocalServer + 2, &cbData)) { return GetLastError(); }
//////////
// Open a handle to the LSA.
//////////
status = LsaOpenPolicy( NULL, &theObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &hLsa ); if (!NT_SUCCESS(status)) { goto deregister; }
//////////
// Get the account domain information.
//////////
status = LsaQueryInformationPolicy( hLsa, PolicyAccountDomainInformation, (PVOID*)&padi ); LsaClose(hLsa); if (!NT_SUCCESS(status)) { goto deregister; }
// Save the domain name.
wcsncpy(theAccountDomain, padi->DomainName.Buffer, DNLEN); _wcsupr(theAccountDomain);
if ( padi ) LsaFreeMemory(padi);
return NO_ERROR;
deregister: if ( padi ) LsaFreeMemory(padi); LsaDeregisterLogonProcess(theLogonProcess); theLogonProcess = NULL; //setup the logon domain for the machine
exit: return RtlNtStatusToDosError(status); }
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASLogonShutdown
//
// DESCRIPTION
//
// Deregisters the logon process.
//
///////////////////////////////////////////////////////////////////////////////
VOID WINAPI IASLogonShutdown( VOID ) { LsaDeregisterLogonProcess(theLogonProcess); theLogonProcess = NULL; }
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASInitAuthInfo
//
// DESCRIPTION
//
// Initializes the fields common to all MSV1_0_LM20* structs.
//
///////////////////////////////////////////////////////////////////////////////
VOID WINAPI IASInitAuthInfo( IN PVOID AuthInfo, IN DWORD FixedLength, IN PCWSTR UserName, IN PCWSTR Domain, OUT PBYTE* Data ) { PMSV1_0_LM20_LOGON logon;
// Zero out the fixed data.
memset(AuthInfo, 0, FixedLength);
// Set Data to point just past the fixed struct.
*Data = FixedLength + (PBYTE)AuthInfo;
// This cast is safe since all LM20 structs have the same initial fields.
logon = (PMSV1_0_LM20_LOGON)AuthInfo;
// We always do Network logons.
logon->MessageType = MsV1_0NetworkLogon;
// Copy in the strings common to all logons.
IASInitUnicodeString(logon->LogonDomainName, *Data, Domain); IASInitUnicodeString(logon->UserName, *Data, UserName); IASInitUnicodeString(logon->Workstation, *Data, L""); }
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASLogonUser
//
// DESCRIPTION
//
// Wrapper around LsaLogonUser.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASLogonUser( IN PVOID AuthInfo, IN ULONG AuthInfoLength, OUT PMSV1_0_LM20_LOGON_PROFILE *Profile, OUT PHANDLE Token ) { NTSTATUS status, SubStatus; PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer; ULONG ProfileBufferLength; LUID LogonId; QUOTA_LIMITS Quotas;
// Make sure the OUT arguments are NULL.
*Token = NULL; ProfileBuffer = NULL;
status = LsaLogonUser( theLogonProcess, &theOriginName, Network, theMSV1_0_Package, AuthInfo, AuthInfoLength, NULL, &theSourceContext, &ProfileBuffer, &ProfileBufferLength, &LogonId, Token, &Quotas, &SubStatus );
if (!NT_SUCCESS(status)) { // For account restrictions, we can get a more descriptive error
// from the SubStatus.
if (status == STATUS_ACCOUNT_RESTRICTION && !NT_SUCCESS(SubStatus)) { status = SubStatus; }
// Sometimes LsaLogonUser returns an invalid handle value on failure.
*Token = NULL; }
if (Profile) { // Return the profile if requested ...
*Profile = ProfileBuffer; } else if (ProfileBuffer) { // ... otherwise free it.
LsaFreeReturnBuffer(ProfileBuffer); }
return RtlNtStatusToDosError(status); }
//
// All MSCHAP Related stuff goes here
//
///////////////////////////////////////////////////////////////////////////////
//
// 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
//
// 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);
__try { // Allocate a buffer on the stack.
authInfo = (PMSV1_0_LM20_LOGON)_alloca(authLength);
} __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) { _resetstkoflw(); }
// 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;
// NOTE Workaround for LSA IA64 WINBUG # 126930 6/13/2000 IA64:
// LsaLogonUser succeeds but returns NULL LogonDomainName.
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
//
// 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) );
return NO_ERROR; }
DWORD WINAPI IASGetSendRecvSessionKeys( PBYTE pbUserSessionKey, DWORD dwUserSessionKeyLen, PBYTE pbResponse, DWORD dwResponseLen, OUT PBYTE pbSendKey, OUT PBYTE pbRecvKey ) { DWORD dwRetCode = NO_ERROR; A_SHA_CTX context; BYTE digest[A_SHA_DIGEST_LEN], masterKey[A_SHA_DIGEST_LEN];
/////////
// Generate master key.
/////////
A_SHAInit(&context); A_SHAUpdate(&context, pbUserSessionKey, dwUserSessionKeyLen); A_SHAUpdate(&context, pbResponse, dwResponseLen); 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(pbRecvKey, 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(pbSendKey, digest, MSV1_0_USER_SESSION_KEY_LENGTH); return dwRetCode; }
#if 0
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASCheckAccountRestrictions
//
// DESCRIPTION
//
// Checks whether an account can be used for logon.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASCheckAccountRestrictions( IN PLARGE_INTEGER AccountExpires, IN PIAS_LOGON_HOURS LogonHours, OUT PLARGE_INTEGER SessionTimeout ) { LARGE_INTEGER now; TIME_ZONE_INFORMATION tzi; SYSTEMTIME st; DWORD unit; LARGE_INTEGER KickoffTime; LARGE_INTEGER LogoffTime; ULONG LogoffUnitsIntoWeek; USHORT i; ULONG LogoffMsIntoWeek; ULONG MillisecondsPerUnit; ULONG DeltaMs; ULONG CurrentUnitsIntoWeek; LARGE_INTEGER Delta100Ns;
_ASSERT(SessionTimeout != NULL); SessionTimeout->QuadPart = MAXLONGLONG; KickoffTime.QuadPart = MAXLONGLONG; LogoffTime.QuadPart = MAXLONGLONG;
GetSystemTimeAsFileTime((LPFILETIME)&now);
// An expiration time of zero means 'never'.
if ((AccountExpires->QuadPart != 0) && (AccountExpires->QuadPart < now.QuadPart)) { return ERROR_ACCOUNT_EXPIRED; }
// If LogonHours is empty, then we're done.
if (LogonHours->UnitsPerWeek == 0) { return NO_ERROR; }
// The LogonHours array does not account for bias.
switch (GetTimeZoneInformation(&tzi)) { case TIME_ZONE_ID_UNKNOWN: case TIME_ZONE_ID_STANDARD: // Bias is in minutes.
now.QuadPart -= 60 * 10000000 * (LONGLONG)tzi.StandardBias; break;
case TIME_ZONE_ID_DAYLIGHT: // Bias is in minutes.
now.QuadPart -= 60 * 10000000 * (LONGLONG)tzi.DaylightBias; break;
default: return ERROR_INVALID_LOGON_HOURS; }
FileTimeToSystemTime( (LPFILETIME)&now, &st );
// Number of milliseconds into the week.
unit = st.wMilliseconds + st.wSecond * 1000 + st.wMinute * 1000 * 60 + st.wHour * 1000 * 60 * 60 + st.wDayOfWeek * 1000 * 60 * 60 * 24;
// Convert this to 'units'.
unit /= (MSEC_PER_WEEK / (DWORD)LogonHours->UnitsPerWeek);
// Test the appropriate bit.
if ((LogonHours->LogonHours[unit / 8 ] & (1 << (unit % 8))) == 0) { return ERROR_INVALID_LOGON_HOURS; } else { //
// Determine the next time that the user is NOT supposed to be logged
// in, and return that as LogoffTime.
//
i = 0; LogoffUnitsIntoWeek = unit;
do { ++i; LogoffUnitsIntoWeek = ( LogoffUnitsIntoWeek + 1 ) % LogonHours->UnitsPerWeek; } while ( ( i <= LogonHours->UnitsPerWeek) && ( LogonHours->LogonHours[ LogoffUnitsIntoWeek / 8 ] & ( 0x01 << ( LogoffUnitsIntoWeek % 8 ) ) ) );
if ( i > LogonHours->UnitsPerWeek ) { //
// All times are allowed, so there's no logoff
// time. Return forever for both LogoffTime and
// KickoffTime.
//
LogoffTime.QuadPart = MAXLONGLONG; KickoffTime.QuadPart = MAXLONGLONG; } else { //
// LogoffUnitsIntoWeek points at which time unit the
// user is to log off. Calculate actual time from
// the unit, and return it.
//
// CurrentTimeFields already holds the current
// time for some time during this week; just adjust
// to the logoff time during this week and convert
// to time format.
//
MillisecondsPerUnit = MSEC_PER_WEEK / LogonHours->UnitsPerWeek; LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek;
if ( LogoffMsIntoWeek < unit ) { DeltaMs = MSEC_PER_WEEK - ( unit - LogoffMsIntoWeek ); } else { DeltaMs = LogoffMsIntoWeek - unit; }
Delta100Ns.QuadPart = (LONGLONG) DeltaMs * 10000;
LogoffTime.QuadPart = min(now.QuadPart + Delta100Ns.QuadPart, LogoffTime.QuadPart); } // Get the minimum of the three values
KickoffTime.QuadPart = min(LogoffTime.QuadPart, KickoffTime.QuadPart); KickoffTime.QuadPart = min(KickoffTime.QuadPart, AccountExpires->QuadPart);
// store the result
SessionTimeout->QuadPart = KickoffTime.QuadPart; } return NO_ERROR; }
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASPurgeTicketCache
//
// DESCRIPTION
//
// Purges the Kerberos ticket cache.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASPurgeTicketCache( VOID ) { KERB_PURGE_TKT_CACHE_REQUEST request; NTSTATUS status, subStatus; PVOID response; ULONG responseLength;
memset(&request, 0, sizeof(request)); request.MessageType = KerbPurgeTicketCacheMessage;
response = NULL; responseLength = 0; subStatus = 0;
status = LsaCallAuthenticationPackage( theLogonProcess, theKerberosPackage, &request, sizeof(request), &response, &responseLength, &subStatus ); if (NT_SUCCESS(status)) { LsaFreeReturnBuffer(response); }
return RtlNtStatusToDosError(status); } #endif
///////////////////////////////////////////////////////////////////////////////
//
// 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; }
///////////////////////////////////////////////////////////////////////////////
//
// 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; }
///////////////////////////////////////////////////////////////////////////////
//
// 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 ); }
|