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