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.
 
 
 
 
 
 

886 lines
26 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
subauth.cxx
Abstract:
subauth
Author:
Larry Zhu (LZhu) December 1, 2001 Created
Environment:
User Mode
Revision History:
--*/
#include "precomp.hxx"
#pragma hdrstop
#include <lmcons.h>
#include <logonmsv.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include "subauth.hxx"
NTSTATUS
NTAPI
Msv1_0SubAuthenticationRoutineEx(
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID pLogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION pUserAll,
IN SAM_HANDLE UserHandle,
IN OUT PMSV1_0_VALIDATION_INFO pValidationInfo,
OUT PULONG pActionsPerformed
)
{
TNtStatus Status;
DebugPrintf(SSPI_LOG, "Msv1_0SubAuthenticationRoutine in msvsubauth.dll: LogonLevel %#x, validating UserName %wZ, UserId %#x(%d)\n",
LogonLevel, &pUserAll->UserName, pUserAll->UserId, pUserAll->UserId);
Status DBGCHK = Msv1_0SubAuthenticationRoutine(
LogonLevel,
pLogonInformation,
Flags,
pUserAll,
&pValidationInfo->WhichFields,
&pValidationInfo->UserFlags,
&pValidationInfo->Authoritative,
&pValidationInfo->LogoffTime,
&pValidationInfo->KickoffTime
);
if (NT_SUCCESS(Status))
{
pValidationInfo->UserId = pUserAll->UserId;
*pActionsPerformed = MSV1_0_SUBAUTH_PASSWORD;
}
return Status;
}
NTSTATUS
NTAPI
Msv1_0SubAuthenticationRoutine(
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID pLogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION pUserAll,
OUT PULONG pWhichFields,
OUT PULONG pUserFlags,
OUT PBOOLEAN Authoritative,
OUT PLARGE_INTEGER pLogoffTime,
OUT PLARGE_INTEGER pKickoffTime
)
{
TNtStatus Status = STATUS_SUCCESS;
ULONG UserAccountControl;
LARGE_INTEGER LogonTime;
LARGE_INTEGER PasswordDateSet;
UNICODE_STRING LocalWorkstation;
PNETLOGON_NETWORK_INFO LogonNetworkInfo;
DebugPrintf(SSPI_LOG, "Msv1_0SubAuthenticationRoutine in msvsubauth.dll: LogonLevel %#x, validating UserName %wZ, UserId %#x(%d)\n",
LogonLevel, &pUserAll->UserName, pUserAll->UserId, pUserAll->UserId);
//
// Check whether the SubAuthentication package supports this type
// of logon.
//
*Authoritative = TRUE;
*pUserFlags = 0;
*pWhichFields = 0;
(VOID) NtQuerySystemTime(&LogonTime);
switch (LogonLevel)
{
case NetlogonInteractiveInformation:
case NetlogonServiceInformation:
//
// This SubAuthentication package only supports network logons.
//
Status DBGCHK = STATUS_INVALID_INFO_CLASS;
break;
case NetlogonNetworkInformation:
//
// This SubAuthentication package doesn't support access via machine
// accounts.
//
UserAccountControl = USER_NORMAL_ACCOUNT;
//
// Local user (Temp Duplicate) accounts are only used on the machine
// being directly logged onto.
// (Nor are interactive or service logons allowed to them.)
//
if ((Flags & MSV1_0_PASSTHRU) == 0)
{
UserAccountControl |= USER_TEMP_DUPLICATE_ACCOUNT;
}
LogonNetworkInfo = (PNETLOGON_NETWORK_INFO) pLogonInformation;
break;
default:
*Authoritative = TRUE;
Status DBGCHK = STATUS_INVALID_INFO_CLASS;
}
//
// If the account type isn't allowed,
// Treat this as though the User Account doesn't exist.
//
if (NT_SUCCESS(Status) && (UserAccountControl & pUserAll->UserAccountControl) == 0)
{
*Authoritative = FALSE;
Status DBGCHK = STATUS_NO_SUCH_USER;
}
//
// This SubAuthentication package doesn't allow guest logons.
//
if (NT_SUCCESS(Status) && (Flags & MSV1_0_GUEST_LOGON))
{
*Authoritative = FALSE;
Status DBGCHK = STATUS_NO_SUCH_USER;
}
//
// Ensure the account isn't locked out.
//
if (NT_SUCCESS(Status) && (pUserAll->UserId != DOMAIN_USER_RID_ADMIN &&
(pUserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED)))
{
//
// Since the UI strongly encourages admins to disable user
// accounts rather than delete them. Treat disabled acccount as
// non-authoritative allowing the search to continue for other
// accounts by the same name.
//
if (pUserAll->UserAccountControl & USER_ACCOUNT_DISABLED)
{
*Authoritative = FALSE;
}
else
{
*Authoritative = TRUE;
}
Status DBGCHK = STATUS_ACCOUNT_LOCKED_OUT;
}
//
// Check the password.
//
if (NT_SUCCESS(Status) && FALSE /* VALIDATE THE USER'S PASSWORD HERE */)
{
Status DBGCHK = STATUS_WRONG_PASSWORD;
//
// Since the UI strongly encourages admins to disable user
// accounts rather than delete them. Treat disabled acccount as
// non-authoritative allowing the search to continue for other
// accounts by the same name.
//
if (pUserAll->UserAccountControl & USER_ACCOUNT_DISABLED)
{
*Authoritative = FALSE;
}
else
{
*Authoritative = TRUE;
}
}
//
// Prevent some things from effecting the Administrator user
//
if (NT_SUCCESS(Status))
{
if (pUserAll->UserId == DOMAIN_USER_RID_ADMIN)
{
//
// The administrator account doesn't have a forced logoff time.
//
pLogoffTime->HighPart = 0x7FFFFFFF;
pLogoffTime->LowPart = 0xFFFFFFFF;
pKickoffTime->HighPart = 0x7FFFFFFF;
pKickoffTime->LowPart = 0xFFFFFFFF;
}
else
{
//
// Check if the account is disabled.
//
if (pUserAll->UserAccountControl & USER_ACCOUNT_DISABLED)
{
//
// Since the UI strongly encourages admins to disable user
// accounts rather than delete them. Treat disabled acccount as
// non-authoritative allowing the search to continue for other
// accounts by the same name.
//
*Authoritative = FALSE;
Status DBGCHK = STATUS_ACCOUNT_DISABLED;
}
//
// Check if the account has expired.
//
if (NT_SUCCESS(Status) && (pUserAll->AccountExpires.QuadPart != 0) &&
(LogonTime.QuadPart >= pUserAll->AccountExpires.QuadPart))
{
*Authoritative = TRUE;
Status DBGCHK = STATUS_ACCOUNT_EXPIRED;
}
//
// The password is valid, check to see if the password is expired.
// (SAM will have appropriately set PasswordMustChange to reflect
// USER_DONT_EXPIRE_PASSWORD)
//
// If the password checked above is not the SAM password, you may
// want to consider not checking the SAM password expiration times here.
//
if (NT_SUCCESS(Status) && (LogonTime.QuadPart >= pUserAll->PasswordMustChange.QuadPart))
{
if (pUserAll->PasswordLastSet.QuadPart == 0)
{
Status DBGCHK = STATUS_PASSWORD_MUST_CHANGE;
}
else
{
Status DBGCHK = STATUS_PASSWORD_EXPIRED;
}
*Authoritative = TRUE;
}
//
// Validate the workstation the user logged on from.
//
// Ditch leading \\ on workstation name before passing it to SAM.
//
if (NT_SUCCESS(Status))
{
LocalWorkstation = LogonNetworkInfo->Identity.Workstation;
if (LocalWorkstation.Length > 0 &&
LocalWorkstation.Buffer[0] == L'\\' &&
LocalWorkstation.Buffer[1] == L'\\')
{
LocalWorkstation.Buffer += 2;
LocalWorkstation.Length -= 2 * sizeof(WCHAR);
LocalWorkstation.MaximumLength -= 2 * sizeof(WCHAR);
}
//
// To validate the user's logon hours as SAM does it, use this code,
// otherwise, supply your own checks below this code.
//
Status DBGCHK = AccountRestrictions(
pUserAll->UserId,
&LocalWorkstation,
(PUNICODE_STRING) &pUserAll->WorkStations,
&pUserAll->LogonHours,
pLogoffTime,
pKickoffTime
);
}
//
// Validate if the user can log on from this workstation.
// (Supply subauthentication package specific code here.)
if (NT_SUCCESS(Status) && LogonNetworkInfo->Identity.Workstation.Buffer == NULL)
{
Status DBGCHK = STATUS_INVALID_WORKSTATION;
*Authoritative = TRUE;
}
}
}
//
// The user is valid.
//
if (NT_SUCCESS(Status))
{
*Authoritative = TRUE;
Status DBGCHK = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
SampMatchworkstation(
IN PUNICODE_STRING pLogonWorkStation,
IN PUNICODE_STRING pWorkStations
)
{
TNtStatus NtStatus = STATUS_INVALID_WORKSTATION;
PWCHAR pWorkStationName;
UNICODE_STRING Unicode;
UNICODE_STRING WorkStationsListCopy;
PWCHAR pTmpBuffer;
//
// Local workstation is always allowed
// If WorkStations field is 0 everybody is allowed
//
if ((pLogonWorkStation == NULL) ||
(pLogonWorkStation->Length == 0) ||
(pWorkStations->Length == 0))
{
return STATUS_SUCCESS;
}
//
// WorkStationApiList points to our current location in the list of
// WorkStations.
//
WorkStationsListCopy.Buffer = (PWSTR) new CHAR[pWorkStations->Length];
NtStatus DBGCHK = WorkStationsListCopy.Buffer ? STATUS_SUCCESS : STATUS_NO_MEMORY;
if (NT_SUCCESS(NtStatus))
{
WorkStationsListCopy.MaximumLength = pWorkStations->Length;
RtlCopyMemory(WorkStationsListCopy.Buffer, pWorkStations->Buffer, pWorkStations->Length);
//
// wcstok requires a string the first time it's called, and NULL
// for all subsequent calls. Use a temporary variable so we
// can do this.
//
pTmpBuffer = WorkStationsListCopy.Buffer;
while (pWorkStationName = wcstok(pTmpBuffer, L","))
{
pTmpBuffer = NULL;
RtlInitUnicodeString(&Unicode, pWorkStationName);
if (EqualComputerName(&Unicode, pLogonWorkStation))
{
NtStatus DBGCHK = STATUS_SUCCESS;
break;
}
}
}
RtlFreeUnicodeString(&WorkStationsListCopy);
return NtStatus;
}
NTSTATUS
AccountRestrictions(
IN ULONG UserRid,
IN PUNICODE_STRING pLogonWorkStation,
IN PUNICODE_STRING pWorkStations,
IN PLOGON_HOURS pLogonHours,
OUT PLARGE_INTEGER pLogoffTime,
OUT PLARGE_INTEGER pKickoffTime
)
{
TNtStatus NtStatus = STATUS_SUCCESS;
static BOOLEAN GetForceLogoff = TRUE;
static LARGE_INTEGER ForceLogoff = {0x7fffffff, 0xFFFFFFF};
#define MILLISECONDS_PER_WEEK 7 * 24 * 60 * 60 * 1000
SYSTEMTIME CurrentTimeFields;
LARGE_INTEGER CurrentTime;
LARGE_INTEGER CurrentUTCTime;
LARGE_INTEGER MillisecondsIntoWeekXUnitsPerWeek;
LARGE_INTEGER LargeUnitsIntoWeek;
LARGE_INTEGER Delta100Ns;
ULONG CurrentMsIntoWeek;
ULONG LogoffMsIntoWeek;
ULONG DeltaMs;
ULONG MillisecondsPerUnit;
ULONG CurrentUnitsIntoWeek;
ULONG LogoffUnitsIntoWeek;
USHORT i;
TIME_ZONE_INFORMATION TimeZoneInformation;
DWORD TimeZoneId;
LARGE_INTEGER BiasIn100NsUnits;
LONG BiasInMinutes;
//
// Only check for users other than the builtin ADMIN
//
if (UserRid != DOMAIN_USER_RID_ADMIN)
{
//
// Scan to make sure the workstation being logged into is in the
// list of valid workstations - or if the list of valid workstations
// is null, which means that all are valid.
//
NtStatus DBGCHK = SampMatchworkstation(pLogonWorkStation, pWorkStations);
if (NT_SUCCESS(NtStatus))
{
//
// Check to make sure that the current time is a valid time to log
// on in the LogonHours.
//
// We need to validate the time taking into account whether we are
// in daylight savings time or standard time. Thus, if the logon
// hours specify that we are able to log on between 9am and 5pm,
// this means 9am to 5pm standard time during the standard time
// period, and 9am to 5pm daylight savings time when in the
// daylight savings time. Since the logon hours stored by SAM are
// independent of daylight savings time, we need to add in the
// difference between standard time and daylight savings time to
// the current time before checking whether this time is a valid
// time to log on. Since this difference (or bias as it is called)
// is actually held in the form
//
// Standard time = Daylight savings time + Bias
//
// the Bias is a negative number. Thus we actually subtract the
// signed Bias from the Current Time.
//
// First, get the Time Zone Information.
//
TimeZoneId = GetTimeZoneInformation(&TimeZoneInformation);
//
// Next, get the appropriate bias (signed integer in minutes) to subtract from
// the Universal Time Convention (UTC) time returned by NtQuerySystemTime
// to get the local time. The bias to be used depends whether we're
// in Daylight Savings time or Standard Time as indicated by the
// TimeZoneId parameter.
//
// local time = UTC time - bias in 100Ns units
//
switch (TimeZoneId)
{
case TIME_ZONE_ID_UNKNOWN:
//
// There is no differentiation between standard and
// daylight savings time. Proceed as for Standard Time
//
BiasInMinutes = TimeZoneInformation.StandardBias;
break;
case TIME_ZONE_ID_STANDARD:
BiasInMinutes = TimeZoneInformation.StandardBias;
break;
case TIME_ZONE_ID_DAYLIGHT:
BiasInMinutes = TimeZoneInformation.DaylightBias;
break;
default:
//
// Something is wrong with the time zone information. Fail
// the logon request.
//
NtStatus DBGCHK = STATUS_INVALID_LOGON_HOURS;
break;
}
if (NT_SUCCESS(NtStatus))
{
//
// Convert the Bias from minutes to 100ns units
//
BiasIn100NsUnits.QuadPart = ((LONGLONG)BiasInMinutes) * 60 * 10000000;
//
// Get the UTC time in 100Ns units used by Windows Nt. This
// time is GMT.
//
NtStatus DBGCHK = NtQuerySystemTime(&CurrentUTCTime);
}
if (NT_SUCCESS(NtStatus))
{
CurrentTime.QuadPart = CurrentUTCTime.QuadPart -
BiasIn100NsUnits.QuadPart;
FileTimeToSystemTime((PFILETIME)&CurrentTime, &CurrentTimeFields);
CurrentMsIntoWeek = (((( CurrentTimeFields.wDayOfWeek * 24 ) +
CurrentTimeFields.wHour ) * 60 +
CurrentTimeFields.wMinute ) * 60 +
CurrentTimeFields.wSecond ) * 1000 +
CurrentTimeFields.wMilliseconds;
MillisecondsIntoWeekXUnitsPerWeek.QuadPart =
((LONGLONG)CurrentMsIntoWeek) *
((LONGLONG)pLogonHours->UnitsPerWeek);
LargeUnitsIntoWeek.QuadPart =
MillisecondsIntoWeekXUnitsPerWeek.QuadPart / ((ULONG) MILLISECONDS_PER_WEEK);
CurrentUnitsIntoWeek = LargeUnitsIntoWeek.LowPart;
if ( !( pLogonHours->LogonHours[ CurrentUnitsIntoWeek / 8] &
( 0x01 << ( CurrentUnitsIntoWeek % 8 ) ) ) )
{
NtStatus DBGCHK = STATUS_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 = CurrentUnitsIntoWeek;
do
{
i++;
LogoffUnitsIntoWeek = (LogoffUnitsIntoWeek + 1) % pLogonHours->UnitsPerWeek;
}
while ( (i <= pLogonHours->UnitsPerWeek) &&
( pLogonHours->LogonHours[LogoffUnitsIntoWeek / 8] & (0x01 << (LogoffUnitsIntoWeek % 8)) ) );
if (i > pLogonHours->UnitsPerWeek)
{
//
// All times are allowed, so there's no logoff
// time. Return forever for both pLogoffTime and
// KickoffTime.
//
pLogoffTime->HighPart = 0x7FFFFFFF;
pLogoffTime->LowPart = 0xFFFFFFFF;
pKickoffTime->HighPart = 0x7FFFFFFF;
pKickoffTime->LowPart = 0xFFFFFFFF;
}
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 = MILLISECONDS_PER_WEEK / pLogonHours->UnitsPerWeek;
LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek;
if (LogoffMsIntoWeek < CurrentMsIntoWeek)
{
DeltaMs = MILLISECONDS_PER_WEEK - (CurrentMsIntoWeek - LogoffMsIntoWeek) ;
}
else
{
DeltaMs = LogoffMsIntoWeek - CurrentMsIntoWeek;
}
Delta100Ns.QuadPart = (LONGLONG) DeltaMs * 10000;
pLogoffTime->QuadPart = CurrentUTCTime.QuadPart + Delta100Ns.QuadPart;
//
// Grab the domain's ForceLogoff time.
//
if (GetForceLogoff)
{
NET_API_STATUS NetStatus;
LPUSER_MODALS_INFO_0 UserModals0;
NetStatus = NetUserModalsGet(NULL,
0,
(PBYTE *)&UserModals0);
if (NetStatus == 0)
{
GetForceLogoff = FALSE;
ForceLogoff = NetpSecondsToDeltaTime(UserModals0->usrmod0_force_logoff);
NetApiBufferFree(UserModals0);
}
}
//
// Subtract Domain->ForceLogoff from LogoffTime, and return
// that as KickoffTime. Note that Domain->ForceLogoff is a
// negative delta. If its magnitude is sufficiently large
// (in fact, larger than the difference between LogoffTime
// and the largest positive large integer), we'll get overflow
// resulting in a KickOffTime that is negative. In this
// case, reset the KickOffTime to this largest positive
// large integer (i.e. "never") value.
//
pKickoffTime->QuadPart = pLogoffTime->QuadPart - ForceLogoff.QuadPart;
if (pKickoffTime->QuadPart < 0)
{
pKickoffTime->HighPart = 0x7FFFFFFF;
pKickoffTime->LowPart = 0xFFFFFFFF;
}
}
}
}
}
}
else
{
//
// Never kick administrators off
//
pLogoffTime->HighPart = 0x7FFFFFFF;
pLogoffTime->LowPart = 0xFFFFFFFF;
pKickoffTime->HighPart = 0x7FFFFFFF;
pKickoffTime->LowPart = 0xFFFFFFFF;
}
return NtStatus;
}
LARGE_INTEGER
NetpSecondsToDeltaTime(
IN ULONG Seconds
)
{
LARGE_INTEGER DeltaTime;
LARGE_INTEGER LargeSeconds;
LARGE_INTEGER Answer;
//
// Special case TIMEQ_FOREVER (return a full scale negative)
//
if (Seconds == TIMEQ_FOREVER)
{
DeltaTime.LowPart = 0;
DeltaTime.HighPart = (LONG) 0x80000000;
}
else
{
//
// Convert seconds to 100ns units simply by multiplying by 10000000.
//
// Convert to delta time by negating.
//
LargeSeconds.LowPart = Seconds;
LargeSeconds.HighPart = 0;
Answer.QuadPart = LargeSeconds.QuadPart * 10000000;
if (Answer.QuadPart < 0)
{
DeltaTime.LowPart = 0;
DeltaTime.HighPart = (LONG) 0x80000000;
}
else
{
DeltaTime.QuadPart = -Answer.QuadPart;
}
}
return DeltaTime;
}
BOOLEAN
EqualComputerName(
IN PUNICODE_STRING pString1,
IN PUNICODE_STRING pString2
)
{
WCHAR szComputer1[CNLEN + 1] = {0};
WCHAR szComputer2[CNLEN + 1] = {0};
CHAR szOemComputer1[CNLEN + 1] = {0};
CHAR szOemComputer2[CNLEN + 1] = {0};
//
// Make sure the names are not too long
//
if ((pString1->Length > CNLEN*sizeof(WCHAR)) ||
(pString2->Length > CNLEN*sizeof(WCHAR)))
{
return FALSE;
}
//
// Copy them to null terminated strings
//
RtlCopyMemory(
szComputer1,
pString1->Buffer,
pString1->Length
);
szComputer1[pString1->Length/sizeof(WCHAR)] = L'\0';
RtlCopyMemory(
szComputer2,
pString2->Buffer,
pString2->Length
);
szComputer2[pString2->Length/sizeof(WCHAR)] = L'\0';
//
// Convert the computer names to OEM
//
if (!CharToOemW(
szComputer1,
szOemComputer1
))
{
return FALSE;
}
if (!CharToOemW(
szComputer2,
szOemComputer2
))
{
return FALSE;
}
//
// Do a case insensitive comparison of the oem computer names.
//
if (_stricmp(szOemComputer1, szOemComputer2) == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
NTSTATUS NTAPI
Msv1_0SubAuthenticationRoutineGeneric(
IN PVOID SubmitBuffer,
IN ULONG SubmitBufferLength,
OUT PULONG ReturnBufferLength,
OUT PVOID *ReturnBuffer
)
{
TNtStatus Status;
SspiPrint(SSPI_LOG, TEXT("Msv1_0SubAuthenticationRoutineGeneric\n"));
Status DBGCHK = (ReturnBufferLength && ReturnBuffer ) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
if (NT_SUCCESS(Status))
{
*ReturnBuffer = LocalAlloc(0, SubmitBufferLength);
if (*ReturnBuffer)
{
*ReturnBufferLength = SubmitBufferLength;
RtlCopyMemory(*ReturnBuffer, SubmitBuffer, SubmitBufferLength);
}
else
{
*ReturnBufferLength = 0;
}
SspiPrintHex(SSPI_LOG, TEXT("SubauthInfo"), SubmitBufferLength, SubmitBuffer);
}
return Status;
}
NTSTATUS
NTAPI
Msv1_0SubAuthenticationFilter(
IN NETLOGON_LOGON_INFO_CLASS LogonLevel,
IN PVOID pLogonInformation,
IN ULONG Flags,
IN PUSER_ALL_INFORMATION pUserAll,
OUT PULONG pWhichFields,
OUT PULONG pUserFlags,
OUT PBOOLEAN pAuthoritative,
OUT PLARGE_INTEGER pLogoffTime,
OUT PLARGE_INTEGER pKickoffTime
)
{
return Msv1_0SubAuthenticationRoutine(
LogonLevel,
pLogonInformation,
Flags,
pUserAll,
pWhichFields,
pUserFlags,
pAuthoritative,
pLogoffTime,
pKickoffTime
);
}