407 lines
9.6 KiB
C
407 lines
9.6 KiB
C
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Copyright (c) 1998, 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";
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////// //
|
||
|
// 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
|
||
|
)
|
||
|
{
|
||
|
LARGE_INTEGER now;
|
||
|
TIME_ZONE_INFORMATION tzi;
|
||
|
SYSTEMTIME st;
|
||
|
DWORD unit;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|