1137 lines
28 KiB
C
1137 lines
28 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
access.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains routines for interfacing to the security
|
|||
|
system in NT.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 30-Oct-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#define BugCheckFileId SRV_FILE_ACCESS
|
|||
|
|
|||
|
#if DBG
|
|||
|
ULONG SrvLogonCount = 0;
|
|||
|
ULONG SrvNullLogonCount = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
#define ROUND_UP_COUNT(Count,Pow2) \
|
|||
|
( ((Count)+(Pow2)-1) & (~((Pow2)-1)) )
|
|||
|
|
|||
|
typedef struct _LOGON_INFO {
|
|||
|
PWCH WorkstationName;
|
|||
|
ULONG WorkstationNameLength;
|
|||
|
PWCH DomainName;
|
|||
|
ULONG DomainNameLength;
|
|||
|
PWCH UserName;
|
|||
|
ULONG UserNameLength;
|
|||
|
PCHAR CaseInsensitivePassword;
|
|||
|
ULONG CaseInsensitivePasswordLength;
|
|||
|
PCHAR CaseSensitivePassword;
|
|||
|
ULONG CaseSensitivePasswordLength;
|
|||
|
CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH];
|
|||
|
LUID LogonId;
|
|||
|
CtxtHandle Token;
|
|||
|
BOOLEAN HaveHandle;
|
|||
|
LARGE_INTEGER KickOffTime;
|
|||
|
LARGE_INTEGER LogOffTime;
|
|||
|
USHORT Action;
|
|||
|
BOOLEAN GuestLogon;
|
|||
|
BOOLEAN EncryptedLogon;
|
|||
|
BOOLEAN NtSmbs;
|
|||
|
BOOLEAN IsNullSession;
|
|||
|
BOOLEAN IsAdmin;
|
|||
|
CHAR NtUserSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
|
|||
|
CHAR LanManSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
|
|||
|
} LOGON_INFO, *PLOGON_INFO;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DoUserLogon (
|
|||
|
IN PLOGON_INFO LogonInfo
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ULONG SrvHaveCreds = 0;
|
|||
|
#define HAVEKERBEROS 1
|
|||
|
#define HAVENTLM 2
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, SrvValidateUser )
|
|||
|
#pragma alloc_text( PAGE, DoUserLogon )
|
|||
|
#pragma alloc_text( PAGE, SrvIsAdmin )
|
|||
|
#pragma alloc_text( PAGE, SrvFreeSecurityContexts )
|
|||
|
#pragma alloc_text( PAGE, AcquireLMCredentials )
|
|||
|
#pragma alloc_text( PAGE, SrvValidateBlob )
|
|||
|
#pragma alloc_text( PAGE, SrvIsKerberosAvailable )
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvValidateUser (
|
|||
|
OUT CtxtHandle *Token,
|
|||
|
IN PSESSION Session OPTIONAL,
|
|||
|
IN PCONNECTION Connection OPTIONAL,
|
|||
|
IN PUNICODE_STRING UserName OPTIONAL,
|
|||
|
IN PCHAR CaseInsensitivePassword,
|
|||
|
IN CLONG CaseInsensitivePasswordLength,
|
|||
|
IN PCHAR CaseSensitivePassword OPTIONAL,
|
|||
|
IN CLONG CaseSensitivePasswordLength,
|
|||
|
OUT PUSHORT Action OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Validates a username/password combination by interfacing to the
|
|||
|
security subsystem.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Session - A pointer to a session block so that this routine can
|
|||
|
insert a user token.
|
|||
|
|
|||
|
Connection - A pointer to the connection this user is on.
|
|||
|
|
|||
|
UserName - ASCIIZ string corresponding to the user name to validate.
|
|||
|
|
|||
|
CaseInsensitivePassword - ASCII (not ASCIIZ) string containing
|
|||
|
password for the user.
|
|||
|
|
|||
|
CaseInsensitivePasswordLength - Length of Password, in bytes.
|
|||
|
This includes the null terminator when the password is not
|
|||
|
encrypted.
|
|||
|
|
|||
|
CaseSensitivePassword - a mixed case, Unicode version of the password.
|
|||
|
This is only supplied by NT clients; for downlevel clients,
|
|||
|
it will be NULL.
|
|||
|
|
|||
|
CaseSensitivePasswordLength - the length of the case-sensitive password.
|
|||
|
|
|||
|
Action - This is part of the sessionsetupandx response.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS from the security system.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
LOGON_INFO logonInfo;
|
|||
|
PPAGED_CONNECTION pagedConnection;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Load input parameters for DoUserLogon into the LOGON_INFO struct.
|
|||
|
//
|
|||
|
// If this is the server's initialization attempt at creating a null
|
|||
|
// session, then the Connection and Session pointers will be NULL.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(Connection) ) {
|
|||
|
|
|||
|
pagedConnection = Connection->PagedConnection;
|
|||
|
|
|||
|
logonInfo.WorkstationName =
|
|||
|
pagedConnection->ClientMachineNameString.Buffer;
|
|||
|
logonInfo.WorkstationNameLength =
|
|||
|
pagedConnection->ClientMachineNameString.Length;
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
logonInfo.EncryptionKey,
|
|||
|
pagedConnection->EncryptionKey,
|
|||
|
MSV1_0_CHALLENGE_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
logonInfo.NtSmbs = CLIENT_CAPABLE_OF( NT_SMBS, Connection );
|
|||
|
|
|||
|
ASSERT( ARGUMENT_PRESENT(Session) );
|
|||
|
|
|||
|
logonInfo.DomainName = Session->UserDomain.Buffer;
|
|||
|
logonInfo.DomainNameLength = Session->UserDomain.Length;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ASSERT( !ARGUMENT_PRESENT(Session) );
|
|||
|
|
|||
|
logonInfo.WorkstationName = StrNull;
|
|||
|
logonInfo.WorkstationNameLength = 0;
|
|||
|
|
|||
|
logonInfo.NtSmbs = FALSE;
|
|||
|
|
|||
|
logonInfo.DomainName = StrNull;
|
|||
|
logonInfo.DomainNameLength = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(UserName) ) {
|
|||
|
logonInfo.UserName = UserName->Buffer;
|
|||
|
logonInfo.UserNameLength = UserName->Length;
|
|||
|
} else {
|
|||
|
logonInfo.UserName = StrNull;
|
|||
|
logonInfo.UserNameLength = 0;
|
|||
|
}
|
|||
|
|
|||
|
logonInfo.CaseSensitivePassword = CaseSensitivePassword;
|
|||
|
logonInfo.CaseSensitivePasswordLength = CaseSensitivePasswordLength;
|
|||
|
|
|||
|
logonInfo.CaseInsensitivePassword = CaseInsensitivePassword;
|
|||
|
logonInfo.CaseInsensitivePasswordLength = CaseInsensitivePasswordLength;
|
|||
|
logonInfo.HaveHandle = FALSE;
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(Action) ) {
|
|||
|
logonInfo.Action = *Action;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attempt the logon.
|
|||
|
//
|
|||
|
|
|||
|
status = DoUserLogon( &logonInfo );
|
|||
|
|
|||
|
//
|
|||
|
// Before checking the status, copy the user token. This will be
|
|||
|
// NULL if the logon failed.
|
|||
|
//
|
|||
|
|
|||
|
*Token = logonInfo.Token;
|
|||
|
|
|||
|
if ( NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The logon succeeded. Save output data.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(Session) ) {
|
|||
|
|
|||
|
Session->LogonId = logonInfo.LogonId;
|
|||
|
|
|||
|
Session->KickOffTime = logonInfo.KickOffTime;
|
|||
|
Session->LogOffTime = logonInfo.LogOffTime;
|
|||
|
|
|||
|
Session->GuestLogon = logonInfo.GuestLogon;
|
|||
|
Session->EncryptedLogon = logonInfo.EncryptedLogon;
|
|||
|
Session->IsNullSession = logonInfo.IsNullSession;
|
|||
|
Session->IsAdmin = logonInfo.IsAdmin;
|
|||
|
|
|||
|
Session->HaveHandle = logonInfo.HaveHandle;
|
|||
|
|
|||
|
Session->UserHandle = logonInfo.Token;
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
Session->NtUserSessionKey,
|
|||
|
logonInfo.NtUserSessionKey,
|
|||
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
RtlCopyMemory(
|
|||
|
Session->LanManSessionKey,
|
|||
|
logonInfo.LanManSessionKey,
|
|||
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(Action) ) {
|
|||
|
*Action = logonInfo.Action;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // SrvValidateUser
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DoUserLogon (
|
|||
|
IN PLOGON_INFO LogonInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Validates a username/password combination by interfacing to the
|
|||
|
security subsystem.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
LogonInfo - Pointer to a block containing in/out information about
|
|||
|
the logon.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS from the security system.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status, subStatus, freeStatus;
|
|||
|
ULONG actualUserInfoBufferLength;
|
|||
|
ULONG oldSessionCount;
|
|||
|
LUID LogonId;
|
|||
|
ULONG Catts;
|
|||
|
LARGE_INTEGER Expiry;
|
|||
|
ULONG BufferOffset;
|
|||
|
SecBufferDesc InputToken;
|
|||
|
SecBuffer InputBuffers[2];
|
|||
|
SecBufferDesc OutputToken;
|
|||
|
SecBuffer OutputBuffer;
|
|||
|
PNTLM_AUTHENTICATE_MESSAGE NtlmInToken = NULL;
|
|||
|
PAUTHENTICATE_MESSAGE InToken = NULL;
|
|||
|
PNTLM_ACCEPT_RESPONSE OutToken = NULL;
|
|||
|
ULONG NtlmInTokenSize;
|
|||
|
ULONG InTokenSize;
|
|||
|
ULONG OutTokenSize;
|
|||
|
ULONG AllocateSize;
|
|||
|
|
|||
|
ULONG profileBufferLength;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
LogonInfo->IsNullSession = FALSE;
|
|||
|
LogonInfo->IsAdmin = FALSE;
|
|||
|
|
|||
|
#if DBG
|
|||
|
SrvLogonCount++;
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If this is a null session request, use the cached null session
|
|||
|
// token, which was created during server startup.
|
|||
|
//
|
|||
|
|
|||
|
if ( (LogonInfo->UserNameLength == 0) &&
|
|||
|
(LogonInfo->CaseSensitivePasswordLength == 0) &&
|
|||
|
( (LogonInfo->CaseInsensitivePasswordLength == 0) ||
|
|||
|
( (LogonInfo->CaseInsensitivePasswordLength == 1) &&
|
|||
|
(*LogonInfo->CaseInsensitivePassword == '\0') ) ) ) {
|
|||
|
|
|||
|
LogonInfo->IsNullSession = TRUE;
|
|||
|
#if DBG
|
|||
|
SrvNullLogonCount++;
|
|||
|
#endif
|
|||
|
|
|||
|
if ( !CONTEXT_NULL(SrvNullSessionToken) ) {
|
|||
|
|
|||
|
LogonInfo->HaveHandle = TRUE;
|
|||
|
LogonInfo->Token = SrvNullSessionToken;
|
|||
|
|
|||
|
LogonInfo->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
|
|||
|
LogonInfo->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
|
|||
|
|
|||
|
LogonInfo->GuestLogon = FALSE;
|
|||
|
LogonInfo->EncryptedLogon = FALSE;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is the main body of the Cairo logon user code
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// First make sure we have a credential handle
|
|||
|
//
|
|||
|
|
|||
|
if ((SrvHaveCreds & HAVENTLM) == 0) {
|
|||
|
|
|||
|
status = AcquireLMCredentials();
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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) +
|
|||
|
LogonInfo->UserNameLength +
|
|||
|
LogonInfo->WorkstationNameLength +
|
|||
|
LogonInfo->DomainNameLength +
|
|||
|
LogonInfo->CaseInsensitivePasswordLength +
|
|||
|
ROUND_UP_COUNT(LogonInfo->CaseSensitivePasswordLength, sizeof(USHORT));
|
|||
|
|
|||
|
|
|||
|
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) ) {
|
|||
|
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvValidateUser: NtAllocateVirtualMemory failed: %X\n.",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
SrvLogError(
|
|||
|
SrvDeviceObject,
|
|||
|
EVENT_SRV_NO_VIRTUAL_MEMORY,
|
|||
|
status,
|
|||
|
&actualUserInfoBufferLength,
|
|||
|
sizeof(ULONG),
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Zero the input tokens
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
InToken,
|
|||
|
InTokenSize + NtlmInTokenSize
|
|||
|
);
|
|||
|
|
|||
|
NtlmInToken = (PNTLM_AUTHENTICATE_MESSAGE) ((PUCHAR) InToken + InTokenSize);
|
|||
|
OutToken = (PNTLM_ACCEPT_RESPONSE) ((PUCHAR) (((ULONG) NtlmInToken + NtlmInTokenSize + 7) & 0xfffffff8));
|
|||
|
|
|||
|
//
|
|||
|
// First set up the NtlmInToken, since it is the easiest.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
NtlmInToken->ChallengeToClient,
|
|||
|
LogonInfo->EncryptionKey,
|
|||
|
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
|
|||
|
//
|
|||
|
|
|||
|
InToken->LmChallengeResponse.Buffer = (PCHAR) BufferOffset;
|
|||
|
InToken->LmChallengeResponse.Length =
|
|||
|
InToken->LmChallengeResponse.MaximumLength =
|
|||
|
(USHORT) LogonInfo->CaseInsensitivePasswordLength;
|
|||
|
|
|||
|
RtlCopyMemory( BufferOffset + (PCHAR) InToken,
|
|||
|
LogonInfo->CaseInsensitivePassword,
|
|||
|
LogonInfo->CaseInsensitivePasswordLength);
|
|||
|
|
|||
|
BufferOffset += ROUND_UP_COUNT(LogonInfo->CaseInsensitivePasswordLength, sizeof(USHORT));
|
|||
|
|
|||
|
//
|
|||
|
// NT password - case sensitive
|
|||
|
//
|
|||
|
|
|||
|
InToken->NtChallengeResponse.Buffer = (PCHAR) BufferOffset;
|
|||
|
InToken->NtChallengeResponse.Length =
|
|||
|
InToken->NtChallengeResponse.MaximumLength =
|
|||
|
(USHORT) LogonInfo->CaseSensitivePasswordLength;
|
|||
|
|
|||
|
RtlCopyMemory( BufferOffset + (PCHAR) InToken,
|
|||
|
LogonInfo->CaseSensitivePassword,
|
|||
|
LogonInfo->CaseSensitivePasswordLength);
|
|||
|
|
|||
|
BufferOffset += LogonInfo->CaseSensitivePasswordLength;
|
|||
|
|
|||
|
//
|
|||
|
// Domain Name
|
|||
|
//
|
|||
|
|
|||
|
InToken->DomainName.Buffer = (PCHAR) BufferOffset;
|
|||
|
InToken->DomainName.Length =
|
|||
|
InToken->DomainName.MaximumLength =
|
|||
|
(USHORT) LogonInfo->DomainNameLength;
|
|||
|
|
|||
|
RtlCopyMemory( BufferOffset + (PCHAR) InToken,
|
|||
|
LogonInfo->DomainName,
|
|||
|
LogonInfo->DomainNameLength);
|
|||
|
|
|||
|
BufferOffset += LogonInfo->DomainNameLength;
|
|||
|
|
|||
|
//
|
|||
|
// Workstation Name
|
|||
|
//
|
|||
|
|
|||
|
InToken->Workstation.Buffer = (PCHAR) BufferOffset;
|
|||
|
InToken->Workstation.Length =
|
|||
|
InToken->Workstation.MaximumLength =
|
|||
|
(USHORT) LogonInfo->WorkstationNameLength;
|
|||
|
|
|||
|
RtlCopyMemory( BufferOffset + (PCHAR) InToken,
|
|||
|
LogonInfo->WorkstationName,
|
|||
|
LogonInfo->WorkstationNameLength);
|
|||
|
|
|||
|
BufferOffset += LogonInfo->WorkstationNameLength;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// User Name
|
|||
|
//
|
|||
|
|
|||
|
InToken->UserName.Buffer = (PCHAR) BufferOffset;
|
|||
|
InToken->UserName.Length =
|
|||
|
InToken->UserName.MaximumLength =
|
|||
|
(USHORT) LogonInfo->UserNameLength;
|
|||
|
|
|||
|
RtlCopyMemory( BufferOffset + (PCHAR) InToken,
|
|||
|
LogonInfo->UserName,
|
|||
|
LogonInfo->UserNameLength);
|
|||
|
|
|||
|
BufferOffset += LogonInfo->UserNameLength;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Setup all the buffers properly
|
|||
|
//
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
SrvStatistics.SessionLogonAttempts++;
|
|||
|
|
|||
|
status = AcceptSecurityContext(
|
|||
|
&SrvLmLsaHandle,
|
|||
|
NULL,
|
|||
|
&InputToken,
|
|||
|
0,
|
|||
|
SECURITY_NATIVE_DREP,
|
|||
|
&LogonInfo->Token,
|
|||
|
&OutputToken,
|
|||
|
&Catts,
|
|||
|
(PTimeStamp) &Expiry
|
|||
|
);
|
|||
|
|
|||
|
status = MapSecurityError( status );
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
|
|||
|
LogonInfo->Token.dwLower = 0;
|
|||
|
LogonInfo->Token.dwUpper = 0;
|
|||
|
|
|||
|
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvValidateUser: LsaLogonUser failed: %X",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
freeStatus = NtFreeVirtualMemory(
|
|||
|
NtCurrentProcess( ),
|
|||
|
(PVOID *)&InToken,
|
|||
|
&AllocateSize,
|
|||
|
MEM_RELEASE
|
|||
|
);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(freeStatus));
|
|||
|
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LogonInfo->KickOffTime = OutToken->KickoffTime;
|
|||
|
LogonInfo->LogOffTime = Expiry;
|
|||
|
LogonInfo->GuestLogon = (BOOLEAN)(OutToken->UserFlags & LOGON_GUEST);
|
|||
|
LogonInfo->EncryptedLogon = (BOOLEAN)!(OutToken->UserFlags & LOGON_NOENCRYPTION);
|
|||
|
LogonInfo->LogonId = OutToken->LogonId;
|
|||
|
LogonInfo->HaveHandle = TRUE;
|
|||
|
|
|||
|
if ( (OutToken->UserFlags & LOGON_USED_LM_PASSWORD) &&
|
|||
|
LogonInfo->NtSmbs ) {
|
|||
|
|
|||
|
ASSERT( MSV1_0_USER_SESSION_KEY_LENGTH >=
|
|||
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH );
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
LogonInfo->NtUserSessionKey,
|
|||
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
LogonInfo->NtUserSessionKey,
|
|||
|
OutToken->LanmanSessionKey,
|
|||
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Turn on bit 1 to tell the client that we are using
|
|||
|
// the lm session key instead of the user session key.
|
|||
|
//
|
|||
|
|
|||
|
LogonInfo->Action |= SMB_SETUP_USE_LANMAN_KEY;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
LogonInfo->NtUserSessionKey,
|
|||
|
OutToken->UserSessionKey,
|
|||
|
MSV1_0_USER_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
LogonInfo->LanManSessionKey,
|
|||
|
OutToken->LanmanSessionKey,
|
|||
|
MSV1_0_LANMAN_SESSION_KEY_LENGTH
|
|||
|
);
|
|||
|
|
|||
|
freeStatus = NtFreeVirtualMemory(
|
|||
|
NtCurrentProcess( ),
|
|||
|
(PVOID *)&InToken,
|
|||
|
&AllocateSize,
|
|||
|
MEM_RELEASE
|
|||
|
);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(freeStatus));
|
|||
|
|
|||
|
//
|
|||
|
// Note whether or not this user is an administrator
|
|||
|
//
|
|||
|
|
|||
|
LogonInfo->IsAdmin = SrvIsAdmin( LogonInfo->Token );
|
|||
|
|
|||
|
//
|
|||
|
// One last check: Is our session count being exceeded?
|
|||
|
// We will let the session be exceeded by 1 iff the client
|
|||
|
// is an administrator.
|
|||
|
//
|
|||
|
|
|||
|
if( LogonInfo->IsNullSession == FALSE ) {
|
|||
|
|
|||
|
oldSessionCount = ExInterlockedAddUlong(
|
|||
|
&SrvStatistics.CurrentNumberOfSessions,
|
|||
|
1,
|
|||
|
&GLOBAL_SPIN_LOCK(Statistics)
|
|||
|
);
|
|||
|
|
|||
|
if ( oldSessionCount >= SrvMaxUsers ) {
|
|||
|
if( oldSessionCount != SrvMaxUsers || !LogonInfo->IsAdmin ) {
|
|||
|
|
|||
|
ExInterlockedAddUlong(
|
|||
|
&SrvStatistics.CurrentNumberOfSessions,
|
|||
|
(ULONG)-1,
|
|||
|
&GLOBAL_SPIN_LOCK(Statistics)
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
DeleteSecurityContext( &LogonInfo->Token );
|
|||
|
RtlZeroMemory( &LogonInfo->Token, sizeof( LogonInfo->Token ) );
|
|||
|
|
|||
|
status = STATUS_REQUEST_NOT_ACCEPTED;
|
|||
|
goto error_exit;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
error_exit:
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // DoUserLogon
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SrvIsAdmin(
|
|||
|
CtxtHandle Handle
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns TRUE if the user represented by Handle is an
|
|||
|
administrator
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Handle - Represents the user we're interested in
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the user is an administrator. FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|||
|
ACCESS_MASK GrantedAccess;
|
|||
|
GENERIC_MAPPING Mapping = { FILE_GENERIC_READ,
|
|||
|
FILE_GENERIC_WRITE,
|
|||
|
FILE_GENERIC_EXECUTE,
|
|||
|
FILE_ALL_ACCESS
|
|||
|
};
|
|||
|
HANDLE NullHandle = NULL;
|
|||
|
BOOLEAN retval = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Impersonate the client
|
|||
|
//
|
|||
|
status = ImpersonateSecurityContext( &Handle );
|
|||
|
|
|||
|
if( !NT_SUCCESS( status ) )
|
|||
|
return FALSE;
|
|||
|
|
|||
|
SeCaptureSubjectContext( &SubjectContext );
|
|||
|
|
|||
|
retval = SeAccessCheck( &SrvAdminSecurityDescriptor,
|
|||
|
&SubjectContext,
|
|||
|
FALSE,
|
|||
|
FILE_GENERIC_READ,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
&Mapping,
|
|||
|
UserMode,
|
|||
|
&GrantedAccess,
|
|||
|
&status );
|
|||
|
|
|||
|
SeReleaseSubjectContext( &SubjectContext );
|
|||
|
|
|||
|
//
|
|||
|
// Revert back to our original identity
|
|||
|
//
|
|||
|
NtSetInformationThread( NtCurrentThread( ),
|
|||
|
ThreadImpersonationToken,
|
|||
|
&NullHandle,
|
|||
|
sizeof(NullHandle)
|
|||
|
);
|
|||
|
return retval;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvValidateBlob(
|
|||
|
IN PSESSION Session,
|
|||
|
IN PCONNECTION Connection,
|
|||
|
IN PUNICODE_STRING UserName,
|
|||
|
IN OUT PCHAR Blob,
|
|||
|
IN OUT ULONG *BlobLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Validates a Kerberos Blob sent from the client
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Session - A pointer to a session block so that this routine can
|
|||
|
insert a user token.
|
|||
|
|
|||
|
Connection - A pointer to the connection this user is on.
|
|||
|
|
|||
|
UserName - ASCIIZ string corresponding to the user name to validate.
|
|||
|
|
|||
|
Blob - The Blob to validate and the place to return the output
|
|||
|
Blob. Note this means that this string space has to be
|
|||
|
long enough to hold the maximum length Blob.
|
|||
|
|
|||
|
BlobLength - The length of the aforementioned Blob
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS from the security system.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
ULONG Catts;
|
|||
|
LARGE_INTEGER Expiry;
|
|||
|
PUCHAR AllocateMemory = NULL;
|
|||
|
ULONG AllocateLength = *BlobLength;
|
|||
|
BOOLEAN virtualMemoryAllocated = FALSE;
|
|||
|
SecBufferDesc InputToken;
|
|||
|
SecBuffer InputBuffer;
|
|||
|
SecBufferDesc OutputToken;
|
|||
|
SecBuffer OutputBuffer;
|
|||
|
ULONG oldSessionCount;
|
|||
|
|
|||
|
AllocateLength += 16;
|
|||
|
|
|||
|
Status = NtAllocateVirtualMemory(
|
|||
|
NtCurrentProcess(),
|
|||
|
&AllocateMemory,
|
|||
|
0,
|
|||
|
&AllocateLength,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
INTERNAL_ERROR( ERROR_LEVEL_UNEXPECTED,
|
|||
|
"Could not allocate Blob Memory %lC\n",
|
|||
|
Status,
|
|||
|
NULL);
|
|||
|
goto get_out;
|
|||
|
}
|
|||
|
|
|||
|
virtualMemoryAllocated = TRUE;
|
|||
|
|
|||
|
|
|||
|
if ( (SrvHaveCreds & HAVEKERBEROS) == 0 ) { // Need to get cred handle first
|
|||
|
|
|||
|
UNICODE_STRING Kerb;
|
|||
|
|
|||
|
Kerb.Length = Kerb.MaximumLength = 16;
|
|||
|
Kerb.Buffer = (LPWSTR) AllocateMemory;
|
|||
|
RtlCopyMemory( Kerb.Buffer, MICROSOFT_KERBEROS_NAME, 16);
|
|||
|
|
|||
|
Status = AcquireCredentialsHandle(
|
|||
|
NULL, // Default principal
|
|||
|
(PSECURITY_STRING) &Kerb,
|
|||
|
SECPKG_CRED_INBOUND, // Need to define this
|
|||
|
NULL, // No LUID
|
|||
|
NULL, // no AuthData
|
|||
|
NULL, // no GetKeyFn
|
|||
|
NULL, // no GetKeyArg
|
|||
|
&SrvKerberosLsaHandle,
|
|||
|
(PTimeStamp)&Expiry
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
Status = MapSecurityError(Status);
|
|||
|
goto get_out;
|
|||
|
}
|
|||
|
SrvHaveCreds |= HAVEKERBEROS;
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory( AllocateMemory, Blob, *BlobLength );
|
|||
|
InputToken.pBuffers = &InputBuffer;
|
|||
|
InputToken.cBuffers = 1;
|
|||
|
InputToken.ulVersion = 0;
|
|||
|
InputBuffer.pvBuffer = AllocateMemory;
|
|||
|
InputBuffer.cbBuffer = *BlobLength;
|
|||
|
InputBuffer.BufferType = SECBUFFER_TOKEN;
|
|||
|
|
|||
|
OutputToken.pBuffers = &OutputBuffer;
|
|||
|
OutputToken.cBuffers = 1;
|
|||
|
OutputToken.ulVersion = 0;
|
|||
|
OutputBuffer.pvBuffer = AllocateMemory;
|
|||
|
OutputBuffer.cbBuffer = *BlobLength;
|
|||
|
OutputBuffer.BufferType = SECBUFFER_TOKEN;
|
|||
|
|
|||
|
SrvStatistics.SessionLogonAttempts++;
|
|||
|
|
|||
|
Status = AcceptSecurityContext(
|
|||
|
&SrvKerberosLsaHandle,
|
|||
|
(PCtxtHandle)NULL,
|
|||
|
&InputToken,
|
|||
|
ASC_REQ_EXTENDED_ERROR, // fContextReq
|
|||
|
SECURITY_NATIVE_DREP,
|
|||
|
&Session->UserHandle,
|
|||
|
&OutputToken,
|
|||
|
&Catts,
|
|||
|
(PTimeStamp)&Expiry
|
|||
|
);
|
|||
|
|
|||
|
Status = MapSecurityError( Status );
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status)
|
|||
|
||
|
|||
|
(Catts & ASC_RET_EXTENDED_ERROR) )
|
|||
|
{
|
|||
|
*BlobLength = OutputBuffer.cbBuffer;
|
|||
|
RtlCopyMemory( Blob, AllocateMemory, *BlobLength );
|
|||
|
|
|||
|
//
|
|||
|
// BUGBUG
|
|||
|
// All of the following values need to come from someplace
|
|||
|
// And while we're at it, get the LogonId as well
|
|||
|
//
|
|||
|
|
|||
|
if(NT_SUCCESS(Status))
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Note whether or not this user is an administrator
|
|||
|
//
|
|||
|
|
|||
|
Session->IsAdmin = SrvIsAdmin( Session->UserHandle );
|
|||
|
|
|||
|
//
|
|||
|
// fiddle with the session structures iff the
|
|||
|
// security context was actually accepted
|
|||
|
//
|
|||
|
|
|||
|
Session->HaveHandle = TRUE;
|
|||
|
Session->KickOffTime = Expiry;
|
|||
|
Session->LogOffTime = Expiry;
|
|||
|
Session->GuestLogon = FALSE; // No guest logon this way
|
|||
|
Session->EncryptedLogon = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// See if the session count is being exceeded. We'll allow it only
|
|||
|
// if the new client is an administrator
|
|||
|
//
|
|||
|
oldSessionCount = ExInterlockedAddUlong(
|
|||
|
&SrvStatistics.CurrentNumberOfSessions,
|
|||
|
1,
|
|||
|
&GLOBAL_SPIN_LOCK(Statistics)
|
|||
|
);
|
|||
|
|
|||
|
if ( oldSessionCount >= SrvMaxUsers ) {
|
|||
|
if( oldSessionCount != SrvMaxUsers || !SrvIsAdmin( Session->UserHandle ) ) {
|
|||
|
|
|||
|
ExInterlockedAddUlong(
|
|||
|
&SrvStatistics.CurrentNumberOfSessions,
|
|||
|
(ULONG)-1,
|
|||
|
&GLOBAL_SPIN_LOCK(Statistics)
|
|||
|
);
|
|||
|
|
|||
|
DeleteSecurityContext( &Session->UserHandle );
|
|||
|
Session->HaveHandle = FALSE;
|
|||
|
|
|||
|
Status = STATUS_REQUEST_NOT_ACCEPTED;
|
|||
|
goto get_out;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*BlobLength = 0;
|
|||
|
}
|
|||
|
|
|||
|
get_out:
|
|||
|
|
|||
|
if (virtualMemoryAllocated) {
|
|||
|
(VOID)NtFreeVirtualMemory(
|
|||
|
NtCurrentProcess(),
|
|||
|
&AllocateMemory,
|
|||
|
&AllocateLength,
|
|||
|
MEM_DECOMMIT
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
} // SrvValidateBlob
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvFreeSecurityContexts (
|
|||
|
IN PSESSION Session
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Releases any context obtained via the LSA
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
IN PSESSION Session : The session
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if ( Session->HaveHandle ) {
|
|||
|
if ( !CONTEXT_EQUAL( Session->UserHandle, SrvNullSessionToken ) ) {
|
|||
|
ExInterlockedAddUlong(
|
|||
|
&SrvStatistics.CurrentNumberOfSessions,
|
|||
|
(ULONG)-1,
|
|||
|
&GLOBAL_SPIN_LOCK(Statistics)
|
|||
|
);
|
|||
|
DeleteSecurityContext( &Session->UserHandle );
|
|||
|
}
|
|||
|
}
|
|||
|
Session->HaveHandle = FALSE;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // SrvFreeSecurityContexts
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
AcquireLMCredentials (
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
UNICODE_STRING Ntlm;
|
|||
|
PUCHAR AllocateMemory = NULL;
|
|||
|
ULONG AllocateLength = 8;
|
|||
|
NTSTATUS status;
|
|||
|
TimeStamp Expiry;
|
|||
|
|
|||
|
status = NtAllocateVirtualMemory(
|
|||
|
NtCurrentProcess(),
|
|||
|
&AllocateMemory,
|
|||
|
0,
|
|||
|
&AllocateLength,
|
|||
|
MEM_COMMIT,
|
|||
|
PAGE_READWRITE
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
Ntlm.Length = Ntlm.MaximumLength = 8;
|
|||
|
Ntlm.Buffer = (LPWSTR)AllocateMemory,
|
|||
|
RtlCopyMemory( Ntlm.Buffer, L"NTLM", 8 );
|
|||
|
|
|||
|
status = AcquireCredentialsHandle(
|
|||
|
NULL, // Default principal
|
|||
|
(PSECURITY_STRING) &Ntlm,
|
|||
|
SECPKG_CRED_INBOUND, // Need to define this
|
|||
|
NULL, // No LUID
|
|||
|
NULL, // No AuthData
|
|||
|
NULL, // No GetKeyFn
|
|||
|
NULL, // No GetKeyArg
|
|||
|
&SrvLmLsaHandle,
|
|||
|
&Expiry
|
|||
|
);
|
|||
|
|
|||
|
(VOID)NtFreeVirtualMemory(
|
|||
|
NtCurrentProcess(),
|
|||
|
&AllocateMemory,
|
|||
|
&AllocateLength,
|
|||
|
MEM_DECOMMIT
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
status = MapSecurityError(status);
|
|||
|
return status;
|
|||
|
}
|
|||
|
SrvHaveCreds |= HAVENTLM;
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // AcquireLMCredentials
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SrvIsKerberosAvailable(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Checks whether Kerberos is one of the supported security packages.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if Kerberos is available, FALSE if otherwise or error.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
ULONG PackageCount, Index;
|
|||
|
PSecPkgInfoW Packages;
|
|||
|
BOOLEAN FoundKerberos = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Get the list of packages from the security driver
|
|||
|
//
|
|||
|
|
|||
|
Status = EnumerateSecurityPackages(
|
|||
|
&PackageCount,
|
|||
|
&Packages
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Loop through the list looking for Kerberos
|
|||
|
//
|
|||
|
|
|||
|
for (Index = 0; Index < PackageCount ; Index++ ) {
|
|||
|
if (!_wcsicmp(Packages[Index].Name, MICROSOFT_KERBEROS_NAME_W)) {
|
|||
|
FoundKerberos = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FreeContextBuffer(Packages);
|
|||
|
return(FoundKerberos);
|
|||
|
|
|||
|
}
|