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.
 
 
 
 
 
 

451 lines
11 KiB

///////////////////////////////////////////////////////////////////////////////
//
// 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);
}