|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1997
//
// File: stubs.cxx
//
// Contents: user-mode stubs for security API
//
//
// History: 3/5/94 MikeSw Created
// 12/15/97 AdamBa Modified from security\lsa\security\ntlm
//
//------------------------------------------------------------------------
#include <rdrssp.h>
#include <nturtl.h>
#include <align.h>
#include "nlp.h"
static CredHandle NullCredential = {0,0};
#define NTLMSSP_REQUIRED_NEGOTIATE_FLAGS ( NTLMSSP_NEGOTIATE_UNICODE | \
NTLMSSP_REQUEST_INIT_RESPONSE )
NTSTATUS MspLm20GetChallengeResponse ( IN PVOID ProtocolSubmitBuffer, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferSize, IN BOOLEAN OwfPasswordProvided );
//+-------------------------------------------------------------------------
//
// Function: AcquireCredentialsHandleK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleK( PSECURITY_STRING pssPrincipal, // Name of principal
PSECURITY_STRING pssPackageName, // Name of package
unsigned long fCredentialUse, // Flags indicating use
void SEC_FAR * pvLogonId, // Pointer to logon ID
void SEC_FAR * pAuthData, // Package specific data
SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func
void SEC_FAR * pvGetKeyArgument, // Value to pass to GetKey()
PCredHandle phCredential, // (out) Cred Handle
PTimeStamp ptsExpiry // (out) Lifetime (optional)
) { SECURITY_STATUS scRet; SECURITY_STRING Principal; TimeStamp OptionalTimeStamp; UNICODE_STRING PackageName; PMS_LOGON_CREDENTIAL LogonCredential;
if (!pssPackageName) { return(SEC_E_SECPKG_NOT_FOUND); }
//
// We don't accept principal names either.
//
if (pssPrincipal) { return(SEC_E_UNKNOWN_CREDENTIALS); }
//
// Make sure they want the NTLM security package
//
RtlInitUnicodeString( &PackageName, NTLMSP_NAME );
if (!RtlEqualUnicodeString( pssPackageName, &PackageName, TRUE)) { return(SEC_E_SECPKG_NOT_FOUND); }
#if 0
//
// For the moment, only accept OWF passwords. This is the
// easiest for now since there is no place to record the
// flag otherwise. The password provided is assumed to
// be the LM and NT OWF passwords concatenated together.
//
if ((fCredentialUse & SECPKG_CRED_OWF_PASSWORD) == 0) { return(SEC_E_UNSUPPORTED_FUNCTION); } #endif
//
// The credential handle is the logon id
//
if (fCredentialUse & SECPKG_CRED_OUTBOUND) { if (pvLogonId != NULL) { LogonCredential = (PMS_LOGON_CREDENTIAL)SecAllocate(sizeof(MS_LOGON_CREDENTIAL));
if (LogonCredential == NULL) { return(SEC_E_INSUFFICIENT_MEMORY); }
LogonCredential->LogonId = *((PLUID)pvLogonId); LogonCredential->CredentialUse = fCredentialUse;
*(PMS_LOGON_CREDENTIAL *)phCredential = LogonCredential; } else { return(SEC_E_UNKNOWN_CREDENTIALS); }
} else if (fCredentialUse & SECPKG_CRED_INBOUND) { //
// For inbound credentials, we will accept a logon id but
// we don't require it.
//
if (pvLogonId != NULL) { LogonCredential = (PMS_LOGON_CREDENTIAL)SecAllocate(sizeof(MS_LOGON_CREDENTIAL));
if (LogonCredential == NULL) { return(SEC_E_INSUFFICIENT_MEMORY); }
LogonCredential->LogonId = *((PLUID)pvLogonId); LogonCredential->CredentialUse = fCredentialUse; *(PMS_LOGON_CREDENTIAL *)phCredential = LogonCredential; } else { *phCredential = NullCredential; }
} else { return(SEC_E_UNSUPPORTED_FUNCTION); }
return(SEC_E_OK);
}
//+-------------------------------------------------------------------------
//
// Function: FreeCredentialsHandleK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY FreeCredentialsHandleK( PCredHandle phCredential // Handle to free
) { if ((phCredential != NULL) && (!RtlEqualMemory(phCredential, &NullCredential, sizeof(NullCredential)))) {
PMS_LOGON_CREDENTIAL LogonCredential = *((PMS_LOGON_CREDENTIAL *)phCredential);
if (LogonCredential != NULL) { SecFree(LogonCredential); *phCredential = NullCredential; }
}
return(SEC_E_OK); }
VOID PutString( OUT PSTRING32 Destination, IN PSTRING Source, IN PVOID Base, IN OUT PUCHAR * Where ) { Destination->Buffer = (ULONG)((ULONG_PTR) *Where - (ULONG_PTR) Base); Destination->Length = Source->Length; Destination->MaximumLength = Source->Length;
RtlCopyMemory( *Where, Source->Buffer, Source->Length ); *Where += Source->Length; }
//+-------------------------------------------------------------------------
//
// Function: InitializeSecurityContextK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY InitializeSecurityContextK( PCredHandle phCredential, // Cred to base context
PCtxtHandle phContext, // Existing context (OPT)
PSECURITY_STRING pssTargetName, // Name of target
unsigned long fContextReq, // Context Requirements
unsigned long Reserved1, // Reserved, MBZ
unsigned long TargetDataRep, // Data rep of target
PSecBufferDesc pInput, // Input Buffers
unsigned long Reserved2, // Reserved, MBZ
PCtxtHandle phNewContext, // (out) New Context handle
PSecBufferDesc pOutput, // (inout) Output Buffers
unsigned long SEC_FAR * pfContextAttr, // (out) Context attrs
PTimeStamp ptsExpiry // (out) Life span (OPT)
) { SECURITY_STATUS scRet; PMSV1_0_GETCHALLENRESP_REQUEST ChallengeRequest = NULL; ULONG ChallengeRequestSize; PMSV1_0_GETCHALLENRESP_RESPONSE ChallengeResponse = NULL; ULONG ChallengeResponseSize; PCHALLENGE_MESSAGE ChallengeMessage = NULL; ULONG ChallengeMessageSize; PNTLM_CHALLENGE_MESSAGE NtlmChallengeMessage = NULL; ULONG NtlmChallengeMessageSize; PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL; ULONG AuthenticateMessageSize; PNTLM_INITIALIZE_RESPONSE NtlmInitializeResponse = NULL; UNICODE_STRING PasswordToUse; UNICODE_STRING UserNameToUse; UNICODE_STRING DomainNameToUse; ULONG ParameterControl = USE_PRIMARY_PASSWORD | RETURN_PRIMARY_USERNAME | RETURN_PRIMARY_LOGON_DOMAINNAME;
NTSTATUS FinalStatus = STATUS_SUCCESS; PUCHAR Where; PSecBuffer AuthenticationToken = NULL; PSecBuffer InitializeResponseToken = NULL; BOOLEAN UseSuppliedCreds = FALSE;
RtlInitUnicodeString( &PasswordToUse, NULL );
RtlInitUnicodeString( &UserNameToUse, NULL );
RtlInitUnicodeString( &DomainNameToUse, NULL );
//
// Check for valid sizes, pointers, etc.:
//
if (!phCredential) { return(SEC_E_INVALID_HANDLE); }
//
// Locate the buffers with the input data
//
if (!GetTokenBuffer( pInput, 0, // get the first security token
(PVOID *) &ChallengeMessage, &ChallengeMessageSize, TRUE // may be readonly
)) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; }
//
// If we are using supplied creds, get them now too.
//
if (fContextReq & ISC_REQ_USE_SUPPLIED_CREDS) { if (!GetTokenBuffer( pInput, 1, // get the second security token
(PVOID *) &NtlmChallengeMessage, &NtlmChallengeMessageSize, TRUE // may be readonly
)) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; } else { UseSuppliedCreds = TRUE; }
}
//
// Get the output tokens
//
if (!GetSecurityToken( pOutput, 0, &AuthenticationToken) || !GetSecurityToken( pOutput, 1, &InitializeResponseToken ) ) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; }
//
// Make sure the sizes are o.k.
//
if ((ChallengeMessageSize < sizeof(CHALLENGE_MESSAGE)) || (UseSuppliedCreds && !(NtlmChallengeMessageSize < sizeof(NTLM_CHALLENGE_MESSAGE)))) { scRet = SEC_E_INVALID_TOKEN; }
//
// Make sure the caller wants us to allocate memory:
//
if (!(fContextReq & ISC_REQ_ALLOCATE_MEMORY)) { scRet = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; }
//
// KB: allow calls requesting PROMPT_FOR_CREDS to go through.
// We won't prompt, but we will setup a context properly.
// This is OK because PROMPT_FOR_CREDS flag doesnt' do anything
// in the NTLM package
//
// if ((fContextReq & ISC_REQ_PROMPT_FOR_CREDS) != 0)
// {
// scRet = SEC_E_UNSUPPORTED_FUNCTION;
// goto Cleanup;
// }
//
// Verify the validity of the challenge message.
//
if (strncmp( ChallengeMessage->Signature, NTLMSSP_SIGNATURE, sizeof(NTLMSSP_SIGNATURE))) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; }
if (ChallengeMessage->MessageType != NtLmChallenge) { scRet = SEC_E_INVALID_TOKEN; goto Cleanup; }
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_REQUIRED_NEGOTIATE_FLAGS) != NTLMSSP_REQUIRED_NEGOTIATE_FLAGS) { scRet = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; }
if ((ChallengeMessage->NegotiateFlags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) != 0) { ParameterControl |= RETURN_NON_NT_USER_SESSION_KEY; }
if ((fContextReq & ISC_REQ_USE_SUPPLIED_CREDS) != 0) { if ( NtlmChallengeMessage->Password.Buffer != 0) { ParameterControl &= ~USE_PRIMARY_PASSWORD; PasswordToUse.Length = NtlmChallengeMessage->Password.Length; PasswordToUse.MaximumLength = NtlmChallengeMessage->Password.MaximumLength; PasswordToUse.Buffer = (LPWSTR) (NtlmChallengeMessage->Password.Buffer + (PCHAR) NtlmChallengeMessage); }
if (NtlmChallengeMessage->UserName.Length != 0) { UserNameToUse.Length = NtlmChallengeMessage->UserName.Length; UserNameToUse.MaximumLength = NtlmChallengeMessage->UserName.MaximumLength; UserNameToUse.Buffer = (LPWSTR) (NtlmChallengeMessage->UserName.Buffer + (PCHAR) NtlmChallengeMessage); ParameterControl &= ~RETURN_PRIMARY_USERNAME; } if (NtlmChallengeMessage->DomainName.Length != 0) { DomainNameToUse.Length = NtlmChallengeMessage->DomainName.Length; DomainNameToUse.MaximumLength = NtlmChallengeMessage->DomainName.MaximumLength; DomainNameToUse.Buffer = (LPWSTR) (NtlmChallengeMessage->DomainName.Buffer + (PCHAR) NtlmChallengeMessage); ParameterControl &= ~RETURN_PRIMARY_LOGON_DOMAINNAME; }
}
//
// Package up the parameter for a call to the LSA.
//
ChallengeRequestSize = sizeof(MSV1_0_GETCHALLENRESP_REQUEST) + PasswordToUse.Length + UserNameToUse.Length + DomainNameToUse.Length;
ChallengeRequest = SecAllocate(ChallengeRequestSize); if (ChallengeRequest == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; }
//
// Build the challenge request message.
//
ChallengeRequest->MessageType = MsV1_0Lm20GetChallengeResponse; ChallengeRequest->ParameterControl = ParameterControl; if (RtlEqualMemory(phCredential, &NullCredential, sizeof(NullCredential))) { ChallengeRequest->LogonId = *((PLUID)&NullCredential); } else { ChallengeRequest->LogonId = (*((PMS_LOGON_CREDENTIAL *)phCredential))->LogonId; } RtlCopyMemory( ChallengeRequest->ChallengeToClient, ChallengeMessage->Challenge, MSV1_0_CHALLENGE_LENGTH ); if ((ParameterControl & USE_PRIMARY_PASSWORD) == 0) { //
// We assume the user specified SECPKG_CRED_OWF_PASSWORD when
// AcquireSecurityContext was called, so the password is the
// LM and NT OWF passwords concatenated together.
//
ChallengeRequest->Password.Buffer = (LPWSTR) (ChallengeRequest + 1); RtlCopyMemory( ChallengeRequest->Password.Buffer, PasswordToUse.Buffer, PasswordToUse.Length ); ChallengeRequest->Password.Length = PasswordToUse.Length; ChallengeRequest->Password.MaximumLength = PasswordToUse.Length;
//
// need user name in NTLMv2
//
ChallengeRequest->UserName.Buffer = (PWSTR) (((UCHAR*) ChallengeRequest->Password.Buffer) + ChallengeRequest->Password.MaximumLength);
RtlCopyMemory( ChallengeRequest->UserName.Buffer, UserNameToUse.Buffer, UserNameToUse.Length ); ChallengeRequest->UserName.Length = UserNameToUse.Length; ChallengeRequest->UserName.MaximumLength = UserNameToUse.Length;
//
// need logon domain in NTLMv2
//
ChallengeRequest->LogonDomainName.Buffer = (PWSTR) (((UCHAR*) ChallengeRequest->UserName.Buffer) + ChallengeRequest->UserName.MaximumLength);
RtlCopyMemory( ChallengeRequest->LogonDomainName.Buffer, DomainNameToUse.Buffer, DomainNameToUse.Length ); ChallengeRequest->LogonDomainName.Length = DomainNameToUse.Length; ChallengeRequest->LogonDomainName.MaximumLength = DomainNameToUse.Length; }
FinalStatus = MspLm20GetChallengeResponse( ChallengeRequest, ChallengeRequestSize, &ChallengeResponse, &ChallengeResponseSize, (BOOLEAN)((RtlEqualMemory(phCredential, &NullCredential, sizeof(NullCredential))) ? TRUE : ((*((PMS_LOGON_CREDENTIAL *)phCredential))->CredentialUse & SECPKG_CRED_OWF_PASSWORD) != 0x0) );
if (!NT_SUCCESS(FinalStatus)) { scRet = FinalStatus; goto Cleanup; }
ASSERT(ChallengeResponse->MessageType == MsV1_0Lm20GetChallengeResponse); //
// Now prepare the output message.
//
if (UserNameToUse.Buffer == NULL) { UserNameToUse = ChallengeResponse->UserName; } if (DomainNameToUse.Buffer == NULL) { DomainNameToUse = ChallengeResponse->LogonDomainName; }
AuthenticateMessageSize = sizeof(AUTHENTICATE_MESSAGE) + UserNameToUse.Length + DomainNameToUse.Length + ChallengeResponse->CaseSensitiveChallengeResponse.Length + ChallengeResponse->CaseInsensitiveChallengeResponse.Length;
AuthenticateMessage = (PAUTHENTICATE_MESSAGE) SecAllocate(AuthenticateMessageSize); if (AuthenticateMessage == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; }
Where = (PUCHAR) (AuthenticateMessage + 1); RtlCopyMemory( AuthenticateMessage->Signature, NTLMSSP_SIGNATURE, sizeof(NTLMSSP_SIGNATURE) ); AuthenticateMessage->MessageType = NtLmAuthenticate;
PutString( &AuthenticateMessage->LmChallengeResponse, &ChallengeResponse->CaseInsensitiveChallengeResponse, AuthenticateMessage, &Where );
PutString( &AuthenticateMessage->NtChallengeResponse, &ChallengeResponse->CaseSensitiveChallengeResponse, AuthenticateMessage, &Where );
PutString( &AuthenticateMessage->DomainName, (PSTRING) &DomainNameToUse, AuthenticateMessage, &Where );
PutString( &AuthenticateMessage->UserName, (PSTRING) &UserNameToUse, AuthenticateMessage, &Where );
//
// KB. no workstation name to fill in. This is
// OK because the workstation name is only used
// in loopback detection, and this is not relevant
// to this implementation of NTLM.
//
AuthenticateMessage->Workstation.Length = 0; AuthenticateMessage->Workstation.MaximumLength = 0; AuthenticateMessage->Workstation.Buffer = 0;
//
// Build the initialize response.
//
NtlmInitializeResponse = (PNTLM_INITIALIZE_RESPONSE) SecAllocate(sizeof(NTLM_INITIALIZE_RESPONSE)); if (NtlmInitializeResponse == NULL) { scRet = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; }
RtlCopyMemory( NtlmInitializeResponse->UserSessionKey, ChallengeResponse->UserSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH );
RtlCopyMemory( NtlmInitializeResponse->LanmanSessionKey, ChallengeResponse->LanmanSessionKey, MSV1_0_LANMAN_SESSION_KEY_LENGTH );
//
// Fill in the output buffers now.
//
AuthenticationToken->pvBuffer = AuthenticateMessage; AuthenticationToken->cbBuffer = AuthenticateMessageSize; InitializeResponseToken->pvBuffer = NtlmInitializeResponse; InitializeResponseToken->cbBuffer = sizeof(NTLM_INITIALIZE_RESPONSE);
//
// Make a local context for this
//
scRet = NtlmInitKernelContext( NtlmInitializeResponse->UserSessionKey, NtlmInitializeResponse->LanmanSessionKey, NULL, // no token,
phNewContext );
if (!NT_SUCCESS(scRet)) { goto Cleanup; } scRet = SEC_E_OK;
Cleanup:
if (ChallengeRequest != NULL) { SecFree(ChallengeRequest); }
if (ChallengeResponse != NULL) { ExFreePool( ChallengeResponse ); }
if (!NT_SUCCESS(scRet)) { if (AuthenticateMessage != NULL) { SecFree(AuthenticateMessage); } if (NtlmInitializeResponse != NULL) { SecFree(NtlmInitializeResponse); } } return(scRet); }
//+-------------------------------------------------------------------------
//
// Function: DeleteSecurityContextK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY DeleteSecurityContextK( PCtxtHandle phContext // Context to delete
) { SECURITY_STATUS scRet;
// For now, just delete the LSA context:
if (!phContext) { return(SEC_E_INVALID_HANDLE); }
scRet = NtlmDeleteKernelContext(phContext);
return(scRet);
}
#if 0
//+-------------------------------------------------------------------------
//
// Function: EnumerateSecurityPackagesK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesK( unsigned long SEC_FAR * pcPackages, // Receives num. packages
PSecPkgInfo SEC_FAR * ppPackageInfo // Receives array of info
) { ULONG PackageInfoSize; PSecPkgInfoW PackageInfo = NULL; PUCHAR Where;
//
// Figure out the size of the returned data
//
PackageInfoSize = sizeof(SecPkgInfoW) + sizeof(NTLMSP_NAME) + sizeof(NTLMSP_COMMENT);
PackageInfo = (PSecPkgInfoW) SecAllocate(PackageInfoSize);
if (PackageInfo == NULL) { return(SEC_E_INSUFFICIENT_MEMORY); }
//
// Fill in the fixed length fields
//
PackageInfo->fCapabilities = SECPKG_FLAG_CONNECTION | SECPKG_FLAG_TOKEN_ONLY; PackageInfo->wVersion = NTLMSP_VERSION; PackageInfo->wRPCID = NTLMSP_RPCID; PackageInfo->cbMaxToken = NTLMSSP_MAX_MESSAGE_SIZE;
//
// Fill in the fields
//
Where = (PUCHAR) (PackageInfo+1); PackageInfo->Name = (LPWSTR) Where; RtlCopyMemory( PackageInfo->Name, NTLMSP_NAME, sizeof(NTLMSP_NAME) ); Where += sizeof(NTLMSP_NAME);
PackageInfo->Comment = (LPWSTR) Where; RtlCopyMemory( PackageInfo->Comment, NTLMSP_COMMENT, sizeof(NTLMSP_COMMENT) ); Where += sizeof(NTLMSP_COMMENT);
*pcPackages = 1; *ppPackageInfo = PackageInfo; return(SEC_E_OK); }
//+-------------------------------------------------------------------------
//
// Function: QuerySecurityPackageInfoK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoK( PSECURITY_STRING pssPackageName, // Name of package
PSecPkgInfo * ppPackageInfo // Receives package info
) {
UNICODE_STRING PackageName; ULONG PackageCount;
RtlInitUnicodeString( &PackageName, NTLMSP_NAME );
if (!RtlEqualUnicodeString( pssPackageName, &PackageName, TRUE // case insensitive
)) { return(SEC_E_SECPKG_NOT_FOUND); }
return(EnumerateSecurityPackagesK(&PackageCount,ppPackageInfo));
}
#endif
//+-------------------------------------------------------------------------
//
// Function: FreeContextBufferK
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
SECURITY_STATUS SEC_ENTRY FreeContextBufferK( void SEC_FAR * pvContextBuffer ) { SecFree(pvContextBuffer);
return(SEC_E_OK); }
|