|
|
///////////////////////////////////////////////////////////////////////////////
//
// 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 <ntlsa.h>
#include <kerberos.h>
#include <windows.h>
#include <ezlogon.h>
#include <iaslsa.h>
#include <iastrace.h>
CONST CHAR LOGON_PROCESS_NAME[] = "IAS"; CONST CHAR TOKEN_SOURCE_NAME[TOKEN_SOURCE_LENGTH] = "IAS";
//////////
// 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.
/////////////////////////////////////////////////////////////////////////////// //
// FUNCTION
//
// IASLogonInitialize
//
// DESCRIPTION
//
// Registers the logon process.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASLogonInitialize( VOID ) { DWORD status; BOOLEAN wasEnabled; LSA_STRING processName, packageName; LSA_OPERATIONAL_MODE opMode;
//////////
// 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; }
return NO_ERROR;
deregister: LsaDeregisterLogonProcess(theLogonProcess); theLogonProcess = NULL;
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); }
/////////////////////////////////////////////////////////////////////////////// //
// 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 KickOffTime ) { LONGLONG now, logonHoursExpiry; TIME_ZONE_INFORMATION tzi; SYSTEMTIME st; size_t msecOfWeek, msecPerUnit, idx, lastUnit, msecLeft; const size_t msecPerWeek = 1000 * 60 * 60 * 24 * 7;
GetSystemTimeAsFileTime((LPFILETIME)&now);
if (AccountExpires->QuadPart == 0) { // An expiration time of zero means 'never'.
KickOffTime->QuadPart = MAXLONGLONG; } else if (AccountExpires->QuadPart > now) { KickOffTime->QuadPart = AccountExpires->QuadPart; } else { 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 -= 60 * 10000000 * (LONGLONG)tzi.StandardBias; break;
case TIME_ZONE_ID_DAYLIGHT: // Bias is in minutes.
now -= 60 * 10000000 * (LONGLONG)tzi.DaylightBias; break;
default: return ERROR_INVALID_LOGON_HOURS; }
FileTimeToSystemTime((LPFILETIME)&now, &st);
// Number of milliseconds into the week.
msecOfWeek = st.wMilliseconds + st.wSecond * 1000 + st.wMinute * 1000 * 60 + st.wHour * 1000 * 60 * 60 + st.wDayOfWeek * 1000 * 60 * 60 * 24;
// Compute the index of the current time (our starting point).
msecPerUnit = msecPerWeek / LogonHours->UnitsPerWeek; idx = msecOfWeek / msecPerUnit;
// Number of units until we hit an unset bit.
lastUnit = 0;
while (lastUnit < LogonHours->UnitsPerWeek) { // Test the corresponding bit.
if ((LogonHours->LogonHours[idx / 8] & (0x1 << (idx % 8))) == 0) { break; }
++lastUnit; ++idx;
// Wrap around if necessary.
if (idx == LogonHours->UnitsPerWeek) { idx = 0; } }
if (lastUnit == LogonHours->UnitsPerWeek) { // All bits are set, so leave the KickOffTime alone.
} else if (lastUnit > 0) { // How many milliseconds left?
msecLeft = (lastUnit - 1) * msecPerUnit; msecLeft += msecPerUnit - (msecOfWeek % msecPerUnit);
// Add this to the current time to find out when logon hours expires.
logonHoursExpiry = now + (msecLeft * 10000i64);
// Is this more restrictive than the the current KickOffTime?
if (logonHoursExpiry < KickOffTime->QuadPart) { KickOffTime->QuadPart = logonHoursExpiry; } } else { // Current bit isn't set.
return ERROR_INVALID_LOGON_HOURS; }
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) && (response != NULL)) { LsaFreeReturnBuffer(response); }
return RtlNtStatusToDosError(status); }
|