Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1053 lines
28 KiB

/*
Copyright (c) 1992 Microsoft Corporation
Module Name:
client.c
Abstract:
This module contains the client impersonation code.
Author:
Jameel Hyder (microsoft!jameelh)
Revision History:
16 Jun 1992 Initial Version
Notes: Tab stop: 4
--*/
#define FILENUM FILE_CLIENT
#include <afp.h>
#include <client.h>
#include <access.h>
#include <secutil.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpImpersonateClient)
#pragma alloc_text( PAGE, AfpRevertBack)
#pragma alloc_text( PAGE, AfpGetChallenge)
#pragma alloc_text( PAGE, AfpLogonUser)
#endif
/*** AfpImpersonateClient
*
* Impersonates the remote client. The token representing the remote client
* is available in the SDA. If the SDA is NULL (i.e. server context) then
* impersonate the token that we have created for ourselves.
*/
VOID
AfpImpersonateClient(
IN PSDA pSda OPTIONAL
)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE Token;
PAGED_CODE( );
if (pSda != NULL)
{
Token = pSda->sda_UserToken;
}
else Token = AfpFspToken;
ASSERT(Token != NULL);
Status = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
(PVOID)&Token,
sizeof(Token));
ASSERT(NT_SUCCESS(Status));
}
/*** AfpRevertBack
*
* Revert back to the default thread context.
*/
VOID
AfpRevertBack(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE Handle = NULL;
PAGED_CODE( );
Status = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
(PVOID)&Handle,
sizeof(Handle));
ASSERT(NT_SUCCESS(Status));
}
#ifndef USE_OBSOLETE_LSA_API
/*** AfpGetChallenge
*
* Obtain a challenge token from the MSV1_0 package. This token is used by
* AfpLogin call.
*
* The following function modified so that we generate the challenge ourselves
* instead of making a call. This routine borrowed almost verbatim from
* the LM server code.
*/
BOOLEAN
AfpGetChallenge(
IN PSDA pSda
)
{
PMSV1_0_LM20_CHALLENGE_REQUEST ChallengeRequest;
PMSV1_0_LM20_CHALLENGE_RESPONSE ChallengeResponse;
ULONG Length;
NTSTATUS Status, StatusX;
union
{
LARGE_INTEGER time;
UCHAR bytes[8];
} u;
ULONG seed;
ULONG challenge[2];
ULONG result3;
PAGED_CODE( );
ChallengeRequest = NULL;
pSda->sda_Challenge = NULL;
//
// Create a pseudo-random 8-byte number by munging the system time
// for use as a random number seed.
//
// Start by getting the system time.
//
ASSERT( MSV1_0_CHALLENGE_LENGTH == 2 * sizeof(ULONG) );
KeQuerySystemTime( &u.time );
//
// To ensure that we don't use the same system time twice, add in the
// count of the number of times this routine has been called. Then
// increment the counter.
//
// *** Since we don't use the low byte of the system time (it doesn't
// take on enough different values, because of the timer
// resolution), we increment the counter by 0x100.
//
// *** We don't interlock the counter because we don't really care
// if it's not 100% accurate.
//
u.time.LowPart += EncryptionKeyCount;
EncryptionKeyCount += 0x100;
//
// Now use parts of the system time as a seed for the random
// number generator.
//
// *** Because the middle two bytes of the low part of the system
// time change most rapidly, we use those in forming the seed.
//
seed = ((u.bytes[1] + 1) << 0) |
((u.bytes[2] + 0) << 8) |
((u.bytes[2] - 1) << 16) |
((u.bytes[1] + 0) << 24);
//
// Now get two random numbers. RtlRandom does not return negative
// numbers, so we pseudo-randomly negate them.
//
challenge[0] = RtlRandom( &seed );
challenge[1] = RtlRandom( &seed );
result3 = RtlRandom( &seed );
if ( (result3 & 0x1) != 0 )
{
challenge[0] |= 0x80000000;
}
if ( (result3 & 0x2) != 0 )
{
challenge[1] |= 0x80000000;
}
// Allocate a buffer to hold the challenge and copy it in
if ((pSda->sda_Challenge = AfpAllocNonPagedMemory(MSV1_0_CHALLENGE_LENGTH)) != NULL)
{
RtlCopyMemory(pSda->sda_Challenge, challenge, MSV1_0_CHALLENGE_LENGTH);
}
return pSda->sda_Challenge != NULL;
}
/*** AfpLogonUser
*
* Attempt to login the user. The password is either encrypted or cleartext
* based on the UAM used. The UserName and domain is extracted out of the Sda.
*
* LOCKS: AfpStatisticsLock (SPIN)
*/
AFPSTATUS
AfpLogonUser(
IN PSDA pSda,
IN PANSI_STRING UserPasswd
)
{
NTSTATUS Status, SubStatus;
PUNICODE_STRING WSName;
ULONG ulUnused;
ULONG NtlmInTokenSize;
PNTLM_AUTHENTICATE_MESSAGE NtlmInToken = NULL;
PAUTHENTICATE_MESSAGE InToken = NULL;
ULONG InTokenSize;
PNTLM_ACCEPT_RESPONSE OutToken = NULL;
ULONG OutTokenSize;
ULONG AllocateSize;
SecBufferDesc InputToken;
SecBuffer InputBuffers[2];
SecBufferDesc OutputToken;
SecBuffer OutputBuffer;
CtxtHandle hNewContext;
TimeStamp Expiry;
ULONG BufferOffset;
PCHAR pTmp;
PAGED_CODE( );
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
#ifdef OPTIMIZE_GUEST_LOGONS
// 11/28/94 SueA: Now that there is a License Service to track the number
// of sessions via LsaLogonUser, we can no longer fake the guest tokens.
// Optimization for subsequent guest logons
// After the first guest logon, we save the token and do not free it till the
// server stops. All subsequent guest logons 'share' that token.
if (pSda->sda_ClientType == SDA_CLIENT_GUEST)
{
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
if (AfpGuestToken != NULL)
{
pSda->sda_UserToken = AfpGuestToken;
pSda->sda_UserSid = &AfpSidWorld;
pSda->sda_GroupSid = &AfpSidWorld; // Primary group of Guest is also 'World'
#ifdef INHERIT_DIRECTORY_PERMS
pSda->sda_UID = AfpIdWorld;
pSda->sda_GID = AfpIdWorld;
#else
ASSERT (AfpGuestSecDesc != NULL);
pSda->sda_pSecDesc = AfpGuestSecDesc;
#endif
AfpSwmrRelease(&AfpEtcMapLock);
return AFP_ERR_NONE;
}
else
{
AfpSwmrRelease(&AfpEtcMapLock);
}
}
#endif // OPTIMIZE_GUEST_LOGONS
WSName = &AfpDefaultWksta;
if (pSda->sda_WSName.Length != 0)
WSName = &pSda->sda_WSName;
//
// Figure out how big a buffer we need. We put all the messages
// in one buffer for efficiency's sake.
//
NtlmInTokenSize = sizeof(NTLM_AUTHENTICATE_MESSAGE);
NtlmInTokenSize = (NtlmInTokenSize + 3) & 0xfffffffc;
InTokenSize = sizeof(AUTHENTICATE_MESSAGE) +
pSda->sda_UserName.Length +
WSName->Length +
pSda->sda_DomainName.Length +
UserPasswd->Length +
9; // extra for byte aligning
InTokenSize = (InTokenSize + 3) & 0xfffffffc;
OutTokenSize = sizeof(NTLM_ACCEPT_RESPONSE);
OutTokenSize = (OutTokenSize + 3) & 0xfffffffc;
//
// Round this up to 8 byte boundary becaus the out token needs to be
// quad word aligned for the LARGE_INTEGER.
//
AllocateSize = ((NtlmInTokenSize + InTokenSize + 7) & 0xfffffff8) + OutTokenSize;
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
&InToken,
0L,
&AllocateSize,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
AFPLOG_ERROR(AFPSRVMSG_PAGED_POOL, Status, &AllocateSize,sizeof(AllocateSize), NULL);
#if DBG
DbgBreakPoint();
#endif
return(AFP_ERR_MISC);
}
NtlmInToken = (PNTLM_AUTHENTICATE_MESSAGE) ((PUCHAR) InToken + InTokenSize);
OutToken = (PNTLM_ACCEPT_RESPONSE) ((PUCHAR) (((ULONG) NtlmInToken + NtlmInTokenSize + 7) & 0xfffffff8));
RtlZeroMemory(InToken, InTokenSize + NtlmInTokenSize);
//
// set up the NtlmInToken first
//
if (pSda->sda_Challenge)
{
RtlCopyMemory(NtlmInToken->ChallengeToClient,
pSda->sda_Challenge,
MSV1_0_CHALLENGE_LENGTH );
}
NtlmInToken->ParameterControl = 0;
//
// Okay, now for the tought part - marshalling the AUTHENTICATE_MESSAGE
//
RtlCopyMemory(InToken->Signature,
NTLMSSP_SIGNATURE,
sizeof(NTLMSSP_SIGNATURE) );
InToken->MessageType = NtLmAuthenticate;
BufferOffset = sizeof(AUTHENTICATE_MESSAGE);
//
// LM password - case insensitive
//
pTmp = (PBYTE)InToken + BufferOffset;
*(LPWSTR)pTmp = L'\0';
InToken->LmChallengeResponse.Buffer = (PBYTE)BufferOffset;
InToken->LmChallengeResponse.Length = 1;
InToken->LmChallengeResponse.MaximumLength = sizeof(WCHAR);
InToken->NtChallengeResponse.Buffer = (PBYTE)BufferOffset;
InToken->NtChallengeResponse.Length = 0;
InToken->NtChallengeResponse.MaximumLength = sizeof(WCHAR);
InToken->DomainName.Buffer = (PBYTE)BufferOffset;
InToken->DomainName.Length = 0;
InToken->DomainName.MaximumLength = sizeof(WCHAR);
InToken->Workstation.Buffer = (PBYTE)BufferOffset;
InToken->Workstation.Length = 0;
InToken->Workstation.MaximumLength = sizeof(WCHAR);
InToken->UserName.Buffer = (PBYTE)BufferOffset;
InToken->UserName.Length = 0;
InToken->UserName.MaximumLength = sizeof(WCHAR);
if (pSda->sda_UserName.Length != 0)
{
ASSERT (UserPasswd->Length != 0);
if (pSda->sda_DomainName.Length != 0)
{
InToken->DomainName.Length = pSda->sda_DomainName.Length;
InToken->DomainName.MaximumLength = pSda->sda_DomainName.MaximumLength;
InToken->DomainName.Buffer = (PBYTE)BufferOffset;
RtlCopyMemory((PBYTE)InToken + BufferOffset,
(PBYTE)pSda->sda_DomainName.Buffer,
pSda->sda_DomainName.Length);
BufferOffset += pSda->sda_DomainName.Length;
BufferOffset = (BufferOffset + 3) & 0xfffffffc; // dword align it
}
InToken->LmChallengeResponse.Buffer = (PBYTE)BufferOffset;
InToken->LmChallengeResponse.Length = UserPasswd->Length;
InToken->LmChallengeResponse.MaximumLength = UserPasswd->MaximumLength;
RtlCopyMemory( (PBYTE)InToken + BufferOffset, UserPasswd->Buffer, UserPasswd->Length );
BufferOffset += UserPasswd->Length;
BufferOffset = (BufferOffset + 3) & 0xfffffffc; // dword align it
//
// Workstation Name
//
InToken->Workstation.Buffer = (PBYTE)BufferOffset;
InToken->Workstation.Length = WSName->Length;
InToken->Workstation.MaximumLength = WSName->MaximumLength;
RtlCopyMemory((PBYTE)InToken + BufferOffset,
WSName->Buffer,
WSName->Length);
BufferOffset += WSName->Length;
BufferOffset = (BufferOffset + 3) & 0xfffffffc; // dword align it
//
// User Name
//
InToken->UserName.Buffer = (PBYTE)BufferOffset;
InToken->UserName.Length = pSda->sda_UserName.Length;
InToken->UserName.MaximumLength = pSda->sda_UserName.MaximumLength;
RtlCopyMemory((PBYTE)InToken + BufferOffset,
pSda->sda_UserName.Buffer,
pSda->sda_UserName.Length);
BufferOffset += pSda->sda_UserName.Length;
}
InputToken.pBuffers = InputBuffers;
InputToken.cBuffers = 2;
InputToken.ulVersion = 0;
InputBuffers[0].pvBuffer = InToken;
InputBuffers[0].cbBuffer = InTokenSize;
InputBuffers[0].BufferType = SECBUFFER_TOKEN;
InputBuffers[1].pvBuffer = NtlmInToken;
InputBuffers[1].cbBuffer = NtlmInTokenSize;
InputBuffers[1].BufferType = SECBUFFER_TOKEN;
OutputToken.pBuffers = &OutputBuffer;
OutputToken.cBuffers = 1;
OutputToken.ulVersion = 0;
OutputBuffer.pvBuffer = OutToken;
OutputBuffer.cbBuffer = OutTokenSize;
OutputBuffer.BufferType = SECBUFFER_TOKEN;
Status = AcceptSecurityContext(&AfpSecHandle,
NULL,
&InputToken,
ASC_REQ_LICENSING,
SECURITY_NATIVE_DREP,
&hNewContext,
&OutputToken,
&ulUnused,
&Expiry );
if (NT_SUCCESS(Status))
{
AFPTIME CurrentTime;
if (pSda->sda_ClientType != SDA_CLIENT_GUEST)
{
AfpGetCurrentTimeInMacFormat(&CurrentTime);
// Get the kickoff time from the profile buffer. Round this to
// even # of SESSION_CHECK_TIME units
pSda->sda_tTillKickOff = (DWORD)(AfpConvertTimeToMacFormat(&OutToken->KickoffTime) - CurrentTime);
pSda->sda_tTillKickOff -= pSda->sda_tTillKickOff % SESSION_CHECK_TIME;
}
SubStatus = NtFreeVirtualMemory(NtCurrentProcess( ),
(PVOID *)&InToken,
&AllocateSize,
MEM_RELEASE);
ASSERT(NT_SUCCESS(SubStatus));
}
else // if (NT_SUCCESS(Status) != NO_ERROR)
{
NTSTATUS ExtErrCode = Status;
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpLogonUser: AcceptSecurityContext() failed with %X\n", Status));
SubStatus = NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID *)&InToken,
&AllocateSize,
MEM_RELEASE );
ASSERT(NT_SUCCESS(SubStatus));
// Set extended error codes here if using custom UAM or AFP 2.1
Status = AFP_ERR_USER_NOT_AUTH; // default
// The mac will map this to a session error dialog for each UAM.
// The dialog may be a little different for different versions of
// the mac OS and each UAM, but will always have something to do
// with getting the message across about no more sessions available.
if (ExtErrCode == STATUS_LICENSE_QUOTA_EXCEEDED)
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpLogonUser: License Quota Exceeded: returning ASP_SERVER_BUSY\n"));
return (ASP_SERVER_BUSY);
}
if ((pSda->sda_ClientVersion == AFP_VER_21) &&
(pSda->sda_ClientType != SDA_CLIENT_ENCRYPTED))
{
if ((ExtErrCode == STATUS_PASSWORD_EXPIRED) ||
(ExtErrCode == STATUS_PASSWORD_MUST_CHANGE))
Status = AFP_ERR_PWD_EXPIRED;
}
else if (pSda->sda_ClientType == SDA_CLIENT_ENCRYPTED)
{
if ((ExtErrCode == STATUS_PASSWORD_EXPIRED) ||
(ExtErrCode == STATUS_PASSWORD_MUST_CHANGE))
Status = AFP_ERR_PASSWORD_EXPIRED;
else if ((ExtErrCode == STATUS_ACCOUNT_DISABLED) ||
(ExtErrCode == STATUS_ACCOUNT_LOCKED_OUT))
Status = AFP_ERR_ACCOUNT_DISABLED;
else if (ExtErrCode == STATUS_INVALID_LOGON_HOURS)
Status = AFP_ERR_INVALID_LOGON_HOURS;
else if (ExtErrCode == STATUS_INVALID_WORKSTATION)
Status = AFP_ERR_INVALID_WORKSTATION;
}
return( Status );
}
//
// get the token out using the context
//
Status = QuerySecurityContextToken( &hNewContext, &pSda->sda_UserToken );
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpLogonUser: QuerySecurityContextToken() failed with %X\n", Status));
ASSERT(0);
pSda->sda_UserToken = NULL; // just paranoia
return(Status);
}
Status = DeleteSecurityContext( &hNewContext );
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpLogonUser: DeleteSecurityContext() failed with %X\n", Status));
}
Status = AfpGetUserAndPrimaryGroupSids(pSda);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpLogonUser: AfpGetUserAndPrimaryGroupSids() failed with %X\n", Status));
AFPLOG_ERROR(AFPSRVMSG_LOGON, Status, NULL, 0, NULL);
return( Status );
}
#ifdef INHERIT_DIRECTORY_PERMS
// Convert the user and group sids to IDs
AfpSidToMacId(pSda->sda_UserSid, &pSda->sda_UID);
AfpSidToMacId(pSda->sda_GroupSid, &pSda->sda_GID);
#else
// Make a security descriptor for user
Status = AfpMakeSecurityDescriptorForUser(pSda->sda_UserSid,
pSda->sda_GroupSid,
&pSda->sda_pSecDesc);
#endif
#ifdef OPTIMIZE_GUEST_LOGONS
if (pSda->sda_ClientType == SDA_CLIENT_GUEST)
{
// Save the guest login token and security descriptor
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
AfpGuestToken = pSda->sda_UserToken;
#ifdef INHERIT_DIRECTORY_PERMS
AfpSidToMacId(&AfpSidWorld, &AfpIdWorld);
#else
AfpGuestSecDesc = pSda->sda_pSecDesc;
#endif
AfpSwmrRelease(&AfpEtcMapLock);
}
#endif // OPTIMIZE_GUEST_LOGONS
return Status;
}
#else
/*** AfpGetChallenge
*
* Obtain a challenge token from the MSV1_0 package. This token is used by
* AfpLogin call.
*/
BOOLEAN
AfpGetChallenge(
IN PSDA pSda
)
{
PMSV1_0_LM20_CHALLENGE_REQUEST ChallengeRequest;
PMSV1_0_LM20_CHALLENGE_RESPONSE ChallengeResponse;
ULONG Length;
NTSTATUS Status, StatusX;
PAGED_CODE( );
ChallengeRequest = NULL;
pSda->sda_Challenge = NULL;
Length = sizeof(MSV1_0_LM20_CHALLENGE_REQUEST);
do
{
// Only the message type field in the ChallengeRequest structure
// needs to be filled. Also this buffer needs to be page aligned
// for LPC and hence cannot be allocated out of the Non-Paged Pool.
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
(PVOID *)&ChallengeRequest,
0,
&Length,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
AFPLOG_ERROR(AFPSRVMSG_PAGED_POOL, Status, &Length, sizeof(Length), NULL);
#if DBG
DbgBreakPoint();
#endif
break;
}
ChallengeRequest->MessageType = MsV1_0Lm20ChallengeRequest;
// Get the "Challenge" that clients will use to encrypt
// passwords.
Status = LsaCallAuthenticationPackage(AfpLsaHandle,
AfpAuthenticationPackage,
ChallengeRequest,
sizeof(MSV1_0_LM20_CHALLENGE_REQUEST),
(PVOID *)&ChallengeResponse,
&Length,
&StatusX);
if (!NT_SUCCESS(Status))
{
Status = StatusX;
AFPLOG_ERROR(AFPSRVMSG_LSA_CHALLENGE, Status, NULL, 0, NULL);
break;
}
// Allocate a buffer to hold the challenge and copy it in
if ((pSda->sda_Challenge = AfpAllocNonPagedMemory(MSV1_0_CHALLENGE_LENGTH)) != NULL)
{
RtlCopyMemory(pSda->sda_Challenge,
ChallengeResponse->ChallengeToClient,
MSV1_0_CHALLENGE_LENGTH);
}
// Free the LSA response buffer.
Status = LsaFreeReturnBuffer(ChallengeResponse);
ASSERT(NT_SUCCESS(Status));
} while (False);
// Free the paged aligned memory. We do not need it anymore
if (ChallengeRequest != NULL)
{
Status = NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID *)&ChallengeRequest, &Length,
MEM_RELEASE);
ASSERT( NT_SUCCESS(Status) );
}
return pSda->sda_Challenge != NULL;
}
/*** AfpLogonUser
*
* Attempt to login the user. The password is either encrypted or cleartext
* based on the UAM used. The UserName and domain is extracted out of the Sda.
*
* LOCKS: AfpStatisticsLock (SPIN)
*/
AFPSTATUS
AfpLogonUser(
IN PSDA pSda,
IN PANSI_STRING UserPasswd
)
{
DWORD UserInfoLength, RealUserInfoLength;
LUID LogonId;
STRING MachineName;
NTSTATUS Status, SubStatus;
PMSV1_0_LM20_LOGON UserInfo = NULL;
HANDLE UserToken;
TOKEN_SOURCE TokenSource;
PMSV1_0_LM20_LOGON_PROFILE ProfileBuffer;
QUOTA_LIMITS Quotas;
ULONG ProfileLength = 0;
PUNICODE_STRING WSName;
PBYTE pTmp;
PAGED_CODE( );
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
#ifdef OPTIMIZE_GUEST_LOGONS
// 11/28/94 SueA: Now that there is a License Service to track the number
// of sessions via LsaLogonUser, we can no longer fake the guest tokens.
// Optimization for subsequent guest logons
// After the first guest logon, we save the token and do not free it till the
// server stops. All subsequent guest logons 'share' that token.
if (pSda->sda_ClientType == SDA_CLIENT_GUEST)
{
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
if (AfpGuestToken != NULL)
{
pSda->sda_UserToken = AfpGuestToken;
pSda->sda_UserSid = &AfpSidWorld;
pSda->sda_GroupSid = &AfpSidWorld; // Primary group of Guest is also 'World'
#ifdef INHERIT_DIRECTORY_PERMS
pSda->sda_UID = AfpIdWorld;
pSda->sda_GID = AfpIdWorld;
#else
ASSERT (AfpGuestSecDesc != NULL);
pSda->sda_pSecDesc = AfpGuestSecDesc;
#endif
AfpSwmrRelease(&AfpEtcMapLock);
return AFP_ERR_NONE;
}
else
{
AfpSwmrRelease(&AfpEtcMapLock);
}
}
#endif // OPTIMIZE_GUEST_LOGONS
WSName = &AfpDefaultWksta;
if (pSda->sda_WSName.Length != 0)
WSName = &pSda->sda_WSName;
UserInfoLength = sizeof(MSV1_0_LM20_LOGON) +
(pSda->sda_UserName.Length + sizeof(WCHAR)) +
(WSName->Length + sizeof(WCHAR)) +
(pSda->sda_DomainName.Length + sizeof(WCHAR)) +
(UserPasswd->Length + sizeof(CHAR)) + 20;
/* This is some extra space for null strings */
// Save this as NtAllocateVirtualMemory will overwrite this
RealUserInfoLength = UserInfoLength;
do
{
// Also this buffer needs to be page aligned for LPC and hence
// cannot be allocated out of the Non-Paged Pool.
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
(PVOID *)&UserInfo,
0,
&UserInfoLength,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
AFPLOG_ERROR(AFPSRVMSG_PAGED_POOL, Status, &UserInfoLength,
sizeof(UserInfoLength), NULL);
Status = AFP_ERR_MISC;
#if DBG
DbgBreakPoint();
#endif
break;
}
pTmp = (PBYTE)(UserInfo + 1);
//
// Set up the user info structure. Following the MSV1_0_LM20_LOGON
// structure, there are the buffers for the unicode user name, the
// unicode workstation name, the password, and the user domain.
//
UserInfo->MessageType = MsV1_0Lm20Logon;
UserInfo->ParameterControl = 0; // Not used
// Make sure the buffer points to a NULL buffer
*((LPWSTR)pTmp) = 0;
MachineName.Length = 0;
MachineName.MaximumLength = sizeof(WCHAR);
MachineName.Buffer = pTmp;
UserInfo->Workstation.Length = 0;
UserInfo->Workstation.MaximumLength = 0;
UserInfo->Workstation.Buffer = (LPWSTR)pTmp;
// Setup for the GUEST case and fill up the details as needed
UserInfo->LogonDomainName.Length = 0;
UserInfo->LogonDomainName.MaximumLength = sizeof(WCHAR);
UserInfo->LogonDomainName.Buffer = (LPWSTR)pTmp;
UserInfo->UserName.Length = 0;
UserInfo->UserName.MaximumLength = 0;
UserInfo->UserName.Buffer = (LPWSTR)pTmp;
UserInfo->CaseSensitiveChallengeResponse.Length = 0;
UserInfo->CaseSensitiveChallengeResponse.MaximumLength = sizeof(WCHAR);
UserInfo->CaseSensitiveChallengeResponse.Buffer = pTmp;
UserInfo->CaseInsensitiveChallengeResponse.Length = 0;
UserInfo->CaseInsensitiveChallengeResponse.MaximumLength = sizeof(WCHAR);
UserInfo->CaseInsensitiveChallengeResponse.Buffer = pTmp;
// Get past the NULL
((LPWSTR)pTmp)++;
if (pSda->sda_UserName.Length != 0)
{
ASSERT (UserPasswd->Length != 0);
if (pSda->sda_DomainName.Length != 0)
{
UserInfo->LogonDomainName.Length =
pSda->sda_DomainName.Length;
UserInfo->LogonDomainName.MaximumLength =
pSda->sda_DomainName.MaximumLength;
UserInfo->LogonDomainName.Buffer = (PWSTR)pTmp;
RtlCopyMemory(UserInfo->LogonDomainName.Buffer,
pSda->sda_DomainName.Buffer,
pSda->sda_DomainName.Length);
pTmp += UserInfo->LogonDomainName.MaximumLength;
}
// Get the challenge if we are dealing with the custom UAM
if (pSda->sda_Challenge != NULL)
RtlCopyMemory(UserInfo->ChallengeToClient,\
pSda->sda_Challenge,
MSV1_0_CHALLENGE_LENGTH);
// Copy the workstation name
UserInfo->Workstation.Length = WSName->Length;
UserInfo->Workstation.MaximumLength = WSName->MaximumLength;
UserInfo->Workstation.Buffer = (PWSTR)pTmp;
RtlCopyMemory(UserInfo->Workstation.Buffer,
WSName->Buffer,
WSName->Length);
pTmp += WSName->MaximumLength;
// Copy the user name
UserInfo->UserName.Length = pSda->sda_UserName.Length;
UserInfo->UserName.MaximumLength = pSda->sda_UserName.MaximumLength;
UserInfo->UserName.Buffer = (PWSTR)pTmp;
RtlCopyMemory(UserInfo->UserName.Buffer,
pSda->sda_UserName.Buffer, pSda->sda_UserName.Length);
pTmp += UserInfo->UserName.MaximumLength;
// And finally the password
UserInfo->CaseInsensitiveChallengeResponse.Length =
UserPasswd->Length;
UserInfo->CaseInsensitiveChallengeResponse.MaximumLength =
UserPasswd->MaximumLength;
UserInfo->CaseInsensitiveChallengeResponse.Buffer = pTmp;
RtlCopyMemory(UserInfo->CaseInsensitiveChallengeResponse.Buffer,
UserPasswd->Buffer, UserPasswd->Length);
// pTmp += UserPasswd->MaximumLength;
}
RtlCopyMemory(&TokenSource.SourceName,
AFP_LOGON_PROCESS_NAME,
TOKEN_SOURCE_LENGTH);
NtAllocateLocallyUniqueId(&TokenSource.SourceIdentifier);
// Attempt to logon the user
Status = LsaLogonUser(AfpLsaHandle,
&MachineName,
Network,
AfpAuthenticationPackage | LSA_CALL_LICENSE_SERVER,
UserInfo,
RealUserInfoLength,
NULL,
&TokenSource,
(PVOID)&ProfileBuffer,
&ProfileLength,
&LogonId,
&UserToken,
&Quotas,
&SubStatus);
if (Status == STATUS_ACCOUNT_RESTRICTION)
Status = SubStatus;
if (NT_SUCCESS(Status))
{
pSda->sda_UserToken = UserToken;
if (ProfileLength != 0)
{
AFPTIME CurrentTime;
if (pSda->sda_ClientType != SDA_CLIENT_GUEST)
{
AfpGetCurrentTimeInMacFormat(&CurrentTime);
// Get the kickoff time from the profile buffer. Round this to
// even # of SESSION_CHECK_TIME units
pSda->sda_tTillKickOff = (DWORD)
(AfpConvertTimeToMacFormat(&ProfileBuffer->KickOffTime) - CurrentTime);
pSda->sda_tTillKickOff -= pSda->sda_tTillKickOff % SESSION_CHECK_TIME;
}
SubStatus = LsaFreeReturnBuffer(ProfileBuffer);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("AfpLogonUser: LsaFreeReturnBuffer %lx\n", SubStatus));
ASSERT (NT_SUCCESS(SubStatus));
}
}
if (!NT_SUCCESS(Status))
{
NTSTATUS ExtErrCode = Status;
// Set extended error codes here if using custom UAM or AFP 2.1
Status = AFP_ERR_USER_NOT_AUTH; // default
// The mac will map this to a session error dialog for each UAM.
// The dialog may be a little different for different versions of
// the mac OS and each UAM, but will always have something to do
// with getting the message across about no more sessions available.
if (ExtErrCode == STATUS_LICENSE_QUOTA_EXCEEDED)
{
Status = ASP_SERVER_BUSY;
break;
}
if ((pSda->sda_ClientVersion == AFP_VER_21) &&
(pSda->sda_ClientType != SDA_CLIENT_ENCRYPTED))
{
if ((ExtErrCode == STATUS_PASSWORD_EXPIRED) ||
(ExtErrCode == STATUS_PASSWORD_MUST_CHANGE))
Status = AFP_ERR_PWD_EXPIRED;
}
else if (pSda->sda_ClientType == SDA_CLIENT_ENCRYPTED)
{
if ((ExtErrCode == STATUS_PASSWORD_EXPIRED) ||
(ExtErrCode == STATUS_PASSWORD_MUST_CHANGE))
Status = AFP_ERR_PASSWORD_EXPIRED;
else if ((ExtErrCode == STATUS_ACCOUNT_DISABLED) ||
(ExtErrCode == STATUS_ACCOUNT_LOCKED_OUT))
Status = AFP_ERR_ACCOUNT_DISABLED;
else if (ExtErrCode == STATUS_INVALID_LOGON_HOURS)
Status = AFP_ERR_INVALID_LOGON_HOURS;
else if (ExtErrCode == STATUS_INVALID_WORKSTATION)
Status = AFP_ERR_INVALID_WORKSTATION;
}
break;
}
Status = AfpGetUserAndPrimaryGroupSids(pSda);
if (!NT_SUCCESS(Status))
{
AFPLOG_ERROR(AFPSRVMSG_LOGON, Status, NULL, 0, NULL);
break;
}
#ifdef INHERIT_DIRECTORY_PERMS
// Convert the user and group sids to IDs
AfpSidToMacId(pSda->sda_UserSid, &pSda->sda_UID);
AfpSidToMacId(pSda->sda_GroupSid, &pSda->sda_GID);
#else
// Make a security descriptor for user
Status = AfpMakeSecurityDescriptorForUser(pSda->sda_UserSid,
pSda->sda_GroupSid,
&pSda->sda_pSecDesc);
#endif
#ifdef OPTIMIZE_GUEST_LOGONS
if (pSda->sda_ClientType == SDA_CLIENT_GUEST)
{
// Save the guest login token and security descriptor
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
AfpGuestToken = pSda->sda_UserToken;
#ifdef INHERIT_DIRECTORY_PERMS
AfpSidToMacId(&AfpSidWorld, &AfpIdWorld);
#else
AfpGuestSecDesc = pSda->sda_pSecDesc;
#endif
AfpSwmrRelease(&AfpEtcMapLock);
}
#endif // OPTIMIZE_GUEST_LOGONS
} while (False);
if (UserInfo != NULL)
{
SubStatus = NtFreeVirtualMemory(NtCurrentProcess(),
(PVOID *)&UserInfo,
&UserInfoLength,
MEM_RELEASE);
ASSERT (NT_SUCCESS(SubStatus));
}
if (!NT_SUCCESS(Status))
{
INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_NumFailedLogins);
#ifndef INHERIT_DIRECTORY_PERMS
if (pSda->sda_pSecDesc != NULL)
{
if (pSda->sda_pSecDesc->Dacl != NULL)
AfpFreeMemory(pSda->sda_pSecDesc->Dacl);
AfpFreeMemory(pSda->sda_pSecDesc);
pSda->sda_pSecDesc = NULL;
}
#endif
if (pSda->sda_ClientType != SDA_CLIENT_GUEST)
{
if ((pSda->sda_UserSid != NULL) &&
(pSda->sda_UserSid != &AfpSidWorld))
{
AfpFreeMemory(pSda->sda_UserSid);
pSda->sda_UserSid = NULL;
}
if ((pSda->sda_GroupSid != NULL) &&
(pSda->sda_UserSid != &AfpSidWorld))
{
AfpFreeMemory(pSda->sda_GroupSid);
pSda->sda_GroupSid = NULL;
}
}
}
return Status;
}
#endif // #ifdef USE_OBSOLETE_LSA_API