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.
 
 
 
 
 
 

950 lines
24 KiB

//+-----------------------------------------------------------------------
//
// 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);
}