Windows2003-3790/base/boot/bootssp/ntlmssp.c
2020-09-30 16:53:55 +02:00

2178 lines
62 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
stub.c
Abstract:
NT LM Security Support Provider client stubs.
Author:
Cliff Van Dyke (CliffV) 29-Jun-1993
Environment: User Mode
Revision History:
--*/
#ifdef BLDR_KERNEL_RUNTIME
#include <bootdefs.h>
#endif
#ifdef WIN
#include <windows.h>
#include <ctype.h>
#endif
#include <security.h>
#include <ntlmsspi.h>
#include <crypt.h>
#include <ntlmssp.h>
#include <cred.h>
#include <debug.h>
#include <string.h>
#include <memory.h>
#include <cache.h>
#include <rpc.h>
#include <md5.h>
#include <context.h>
#include <stdio.h>
#include "crc32.h"
BOOL
__loadds
GetPassword(
PSSP_CREDENTIAL Credential,
int NeverPrompt
)
{
#ifdef BL_USE_LM_PASSWORD
if ((Credential->LmPassword != NULL) && (Credential->NtPassword != NULL)) {
return (TRUE);
}
#else
if (Credential->NtPassword != NULL) {
return (TRUE);
}
#endif
if (CacheGetPassword(Credential) == TRUE) {
return (TRUE);
}
return (FALSE);
}
SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfo(
IN SEC_CHAR SEC_FAR * PackageName,
OUT PSecPkgInfo SEC_FAR *PackageInfo
)
/*++
Routine Description:
This API is intended to provide basic information about Security
Packages themselves. This information will include the bounds on sizes
of authentication information, credentials and contexts.
?? This is a local routine rather than the real API call since the API
call has a bad interface that neither allows me to allocate the
buffer nor tells me how big the buffer is. Perhaps when the real API
is fixed, I'll make this the real API.
Arguments:
PackageName - Name of the package being queried.
PackageInfo - Returns a pointer to an allocated block describing the
security package. The allocated block must be freed using
FreeContextBuffer.
Return Value:
SEC_E_OK -- Call completed successfully
SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SEC_CHAR *Where;
//
// Ensure the correct package name was passed in.
//
if ( _fstrcmp( PackageName, NTLMSP_NAME ) != 0 ) {
return SEC_E_PACKAGE_UNKNOWN;
}
//
// Allocate a buffer for the PackageInfo
//
*PackageInfo = (PSecPkgInfo) SspAlloc (sizeof(SecPkgInfo) +
sizeof(NTLMSP_NAME) +
sizeof(NTLMSP_COMMENT) );
if ( *PackageInfo == NULL ) {
return SEC_E_INSUFFICIENT_MEMORY;
}
//
// Fill in the information.
//
(*PackageInfo)->fCapabilities = NTLMSP_CAPABILITIES;
(*PackageInfo)->wVersion = NTLMSP_VERSION;
(*PackageInfo)->wRPCID = RPC_C_AUTHN_WINNT;
(*PackageInfo)->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE;
Where = (SEC_CHAR *)((*PackageInfo)+1);
(*PackageInfo)->Name = Where;
_fstrcpy( Where, NTLMSP_NAME);
Where += _fstrlen(Where) + 1;
(*PackageInfo)->Comment = Where;
_fstrcpy( Where, NTLMSP_COMMENT);
Where += _fstrlen(Where) + 1;
return SEC_E_OK;
}
SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackages(
OUT PULONG PackageCount,
OUT PSecPkgInfo * PackageInfo
)
/*++
Routine Description:
This API returns a list of Security Packages available to client (i.e.
those that are either loaded or can be loaded on demand). The caller
must free the returned buffer with FreeContextBuffer. This API returns
a list of all the security packages available to a service. The names
returned can then be used to acquire credential handles, as well as
determine which package in the system best satisfies the requirements
of the caller. It is assumed that all available packages can be
included in the single call.
This is really a dummy API that just returns information about this
security package. It is provided to ensure this security package has the
same interface as the multiplexer DLL does.
Arguments:
PackageCount - Returns the number of packages supported.
PackageInfo - Returns an allocate array of structures
describing the security packages. The array must be freed
using FreeContextBuffer.
Return Value:
SEC_E_OK -- Call completed successfully
SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
//
// Get the information for this package.
//
SecStatus = QuerySecurityPackageInfo( NTLMSP_NAME,
PackageInfo );
if ( SecStatus != SEC_E_OK ) {
return SecStatus;
}
*PackageCount = 1;
return (SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandle(
IN SEC_CHAR * PrincipalName,
IN SEC_CHAR * PackageName,
IN ULONG CredentialUseFlags,
IN PLUID LogonId,
IN PVOID AuthData,
IN SEC_GET_KEY_FN GetKeyFunction,
IN PVOID GetKeyArgument,
OUT PCredHandle CredentialHandle,
OUT PTimeStamp Lifetime
)
/*++
Routine Description:
This API allows applications to acquire a handle to pre-existing
credentials associated with the user on whose behalf the call is made
i.e. under the identity this application is running. These pre-existing
credentials have been established through a system logon not described
here. Note that this is different from "login to the network" and does
not imply gathering of credentials.
Note for DOS we will ignore the previous note. On DOS we will gather
logon credentials through the AuthData parameter.
This API returns a handle to the credentials of a principal (user, client)
as used by a specific security package. This handle can then be used
in subsequent calls to the Context APIs. This API will not let a
process obtain a handle to credentials that are not related to the
process; i.e. we won't allow a process to grab the credentials of
another user logged into the same machine. There is no way for us
to determine if a process is a trojan horse or not, if it is executed
by the user.
Arguments:
PrincipalName - Name of the principal for whose credentials the handle
will reference. Note, if the process requesting the handle does
not have access to the credentials, an error will be returned.
A null string indicates that the process wants a handle to the
credentials of the user under whose security it is executing.
PackageName - Name of the package with which these credentials will
be used.
CredentialUseFlags - Flags indicating the way with which these
credentials will be used.
#define CRED_INBOUND 0x00000001
#define CRED_OUTBOUND 0x00000002
#define CRED_BOTH 0x00000003
#define CRED_OWF_PASSWORD 0x00000010
The credentials created with CRED_INBOUND option can only be used
for (validating incoming calls and can not be used for making accesses.
CRED_OWF_PASSWORD means that the password in AuthData has already
been through the OWF function.
LogonId - Pointer to NT style Logon Id which is a LUID. (Provided for
file system ; processes such as network redirectors.)
CredentialHandle - Returned credential handle.
Lifetime - Time that these credentials expire. The value returned in
this field depends on the security package.
Return Value:
STATUS_SUCCESS -- Call completed successfully
SEC_E_NO_SPM -- Security Support Provider is not running
SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package
SEC_E_PRINCIPAL_UNKNOWN -- No such principal
SEC_E_NOT_OWNER -- caller does not own the specified credentials
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CREDENTIAL Credential = NULL;
#ifdef DEBUGRPC_DETAIL
SspPrint(( SSP_API, "SspAcquireCredentialHandle Entered\n" ));
#endif
//
// Validate the arguments
//
if ( _fstrcmp( PackageName, NTLMSP_NAME ) != 0 ) {
return (SEC_E_PACKAGE_UNKNOWN);
}
if ( (CredentialUseFlags & SECPKG_CRED_OUTBOUND) &&
ARGUMENT_PRESENT(PrincipalName) && *PrincipalName != L'\0' ) {
return (SEC_E_PRINCIPAL_UNKNOWN);
}
if ( ARGUMENT_PRESENT(LogonId) ) {
return (SEC_E_PRINCIPAL_UNKNOWN);
}
if ( ARGUMENT_PRESENT(GetKeyFunction) ) {
return (SEC_E_PRINCIPAL_UNKNOWN);
}
if ( ARGUMENT_PRESENT(GetKeyArgument) ) {
return (SEC_E_PRINCIPAL_UNKNOWN);
}
//
// Ensure at least one Credential use bit is set.
//
if ( (CredentialUseFlags & (SECPKG_CRED_INBOUND|SECPKG_CRED_OUTBOUND)) == 0 ) {
SspPrint(( SSP_API,
"SspAcquireCredentialHandle: invalid credential use.\n" ));
SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
goto Cleanup;
}
//
// Allocate a credential block and initialize it.
//
Credential = SspCredentialAllocateCredential(CredentialUseFlags);
if ( Credential == NULL ) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
SecStatus = CacheSetCredentials( AuthData, Credential );
if (SecStatus != SEC_E_OK)
goto Cleanup;
//
// Return output parameters to the caller.
//
CredentialHandle->dwUpper = (ULONG_PTR)Credential;
CredentialHandle->dwLower = 0;
Lifetime->HighPart = 0;
Lifetime->LowPart = 0xffffffffL;
SecStatus = SEC_E_OK;
//
// Free and locally used resources.
//
Cleanup:
if ( SecStatus != SEC_E_OK ) {
if ( Credential != NULL ) {
SspFree( Credential );
}
}
#ifdef DEBUGRPC_DETAIL
SspPrint(( SSP_API, "SspAcquireCredentialHandle returns 0x%x\n", SecStatus ));
#endif
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandle(
IN PCredHandle CredentialHandle
)
/*++
Routine Description:
This API is used to notify the security system that the credentials are
no longer needed and allows the application to free the handle acquired
in the call described above. When all references to this credential
set has been removed then the credentials may themselves be removed.
Arguments:
CredentialHandle - Credential Handle obtained through
AcquireCredentialHandle.
Return Value:
STATUS_SUCCESS -- Call completed successfully
SEC_E_NO_SPM -- Security Support Provider is not running
SEC_E_INVALID_HANDLE -- Credential Handle is invalid
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CREDENTIAL Credential;
//
// Initialization
//
#ifdef DEBUGRPC_DETAIL
SspPrint(( SSP_API, "SspFreeCredentialHandle Entered\n" ));
#endif
//
// Find the referenced credential and delink it.
//
Credential = SspCredentialReferenceCredential(CredentialHandle, TRUE);
if ( Credential == NULL ) {
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
SspCredentialDereferenceCredential( Credential );
SspCredentialDereferenceCredential( Credential );
SecStatus = SEC_E_OK;
Cleanup:
#ifdef DEBUGRPC_DETAIL
SspPrint(( SSP_API, "SspFreeCredentialHandle returns 0x%x\n", SecStatus ));
#endif
return SecStatus;
}
BOOLEAN
SspGetTokenBuffer(
IN PSecBufferDesc TokenDescriptor OPTIONAL,
OUT PVOID * TokenBuffer,
OUT PULONG * TokenSize,
IN BOOLEAN ReadonlyOK
)
/*++
Routine Description:
This routine parses a Token Descriptor and pulls out the useful
information.
Arguments:
TokenDescriptor - Descriptor of the buffer containing (or to contain) the
token. If not specified, TokenBuffer and TokenSize will be returned
as NULL.
TokenBuffer - Returns a pointer to the buffer for the token.
TokenSize - Returns a pointer to the location of the size of the buffer.
ReadonlyOK - TRUE if the token buffer may be readonly.
Return Value:
TRUE - If token buffer was properly found.
--*/
{
ULONG i;
//
// If there is no TokenDescriptor passed in,
// just pass out NULL to our caller.
//
if ( !ARGUMENT_PRESENT( TokenDescriptor) ) {
*TokenBuffer = NULL;
*TokenSize = NULL;
return TRUE;
}
//
// Check the version of the descriptor.
//
if ( TokenDescriptor->ulVersion != 0 ) {
return FALSE;
}
//
// Loop through each described buffer.
//
for ( i=0; i<TokenDescriptor->cBuffers ; i++ ) {
PSecBuffer Buffer = &TokenDescriptor->pBuffers[i];
if ( (Buffer->BufferType & (~SECBUFFER_READONLY)) == SECBUFFER_TOKEN ) {
//
// If the buffer is readonly and readonly isn't OK,
// reject the buffer.
//
if ( !ReadonlyOK && (Buffer->BufferType & SECBUFFER_READONLY) ) {
return FALSE;
}
//
// Return the requested information
//
*TokenBuffer = Buffer->pvBuffer;
*TokenSize = &Buffer->cbBuffer;
return TRUE;
}
}
return FALSE;
}
SECURITY_STATUS
SspHandleFirstCall(
IN PCredHandle CredentialHandle,
IN OUT PCtxtHandle ContextHandle,
IN ULONG ContextReqFlags,
IN ULONG QoPFlags,
IN ULONG InputTokenSize,
IN PVOID InputToken,
IN OUT PULONG OutputTokenSize,
OUT PVOID OutputToken,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime
)
/*++
Routine Description:
Handle the First Call part of InitializeSecurityContext.
Arguments:
QoPFlags - Indicates security configuration
All other arguments same as for InitializeSecurityContext
Return Value:
STATUS_SUCCESS -- All OK
SEC_I_CALLBACK_NEEDED -- Caller should call again later
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CONTEXT Context = NULL;
PSSP_CREDENTIAL Credential = NULL;
NEGOTIATE_MESSAGE NegotiateMessage;
//
// Initialization
//
*ContextAttributes = 0;
//
// Get a pointer to the credential
//
Credential = SspCredentialReferenceCredential(
CredentialHandle,
FALSE );
if ( Credential == NULL ) {
SspPrint(( SSP_API,
"SspHandleFirstCall: invalid credential handle.\n" ));
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
if ( (Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0 ) {
SspPrint(( SSP_API, "SspHandleFirstCall: invalid credential use.\n" ));
SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
goto Cleanup;
}
//
// Allocate a new context
//
Context = SspContextAllocateContext();
if ( Context == NULL ) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Build a handle to the newly created context.
//
ContextHandle->dwUpper = (ULONG_PTR) Context;
ContextHandle->dwLower = 0;
//
// We don't support any options.
//
// Complain about those that require we do something.
//
if ( (ContextReqFlags & (ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_PROMPT_FOR_CREDS |
ISC_REQ_USE_SUPPLIED_CREDS )) != 0 ) {
SspPrint(( SSP_API,
"SspHandleFirstCall: invalid ContextReqFlags 0x%lx.\n",
ContextReqFlags ));
SecStatus = SEC_E_INVALID_CONTEXT_REQ;
goto Cleanup;
}
//
// If this is the first call,
// build a Negotiate message.
//
// Offer to talk Oem character set.
//
_fstrcpy(NegotiateMessage.Signature, NTLMSSP_SIGNATURE );
NegotiateMessage.MessageType = (ULONG)NtLmNegotiate;
if (QoPFlags & QOP_NTLMV2)
{
Context->NegotiateFlags =
NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_OEM |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_NTLM2 |
NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_128;
NegotiateMessage.NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_OEM |
NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_NTLM2 |
NTLMSSP_NEGOTIATE_56 |
NTLMSSP_NEGOTIATE_128;
}
else
{
NegotiateMessage.NegotiateFlags = NTLMSSP_NEGOTIATE_OEM |
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
if (Credential->Domain == NULL)
{
NegotiateMessage.NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
}
}
if ( *OutputTokenSize < sizeof(NEGOTIATE_MESSAGE) ) {
SecStatus = SEC_E_BUFFER_TOO_SMALL;
goto Cleanup;
}
if (ContextReqFlags & (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT)) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
NegotiateMessage.NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN |
NTLMSSP_NEGOTIATE_NT_ONLY;
}
if (ContextReqFlags & ISC_REQ_CONFIDENTIALITY) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
NegotiateMessage.NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL |
NTLMSSP_NEGOTIATE_NT_ONLY;
}
swaplong(NegotiateMessage.NegotiateFlags);
swaplong(NegotiateMessage.MessageType);
_fmemcpy(OutputToken, &NegotiateMessage, sizeof(NEGOTIATE_MESSAGE));
*OutputTokenSize = sizeof(NEGOTIATE_MESSAGE);
//
// Return output parameters to the caller.
//
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
Context->Credential = SspCredentialReferenceCredential(
CredentialHandle,
FALSE);
SecStatus = SEC_I_CALLBACK_NEEDED;
Context->State = NegotiateSentState;
//
// Free locally used resources.
//
Cleanup:
if ( Context != NULL ) {
if (SecStatus != SEC_I_CALLBACK_NEEDED) {
SspContextDereferenceContext( Context );
}
}
if ( Credential != NULL ) {
SspCredentialDereferenceCredential( Credential );
}
return SecStatus;
UNREFERENCED_PARAMETER( InputToken );
UNREFERENCED_PARAMETER( InputTokenSize );
}
SECURITY_STATUS
SspHandleChallengeMessage(
IN PLUID LogonId,
IN PCredHandle CredentialHandle,
IN OUT PCtxtHandle ContextHandle,
IN ULONG ContextReqFlags,
IN ULONG InputTokenSize,
IN PVOID InputToken,
IN OUT PULONG OutputTokenSize,
OUT PVOID OutputToken,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime
)
/*++
Routine Description:
Handle the Challenge message part of InitializeSecurityContext.
Arguments:
LogonId -- LogonId of the calling process.
All other arguments same as for InitializeSecurityContext
Return Value:
STATUS_SUCCESS - Message handled
SEC_I_CALLBACK_NEEDED -- Caller should call again later
SEC_E_INVALID_TOKEN -- Token improperly formatted
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
SEC_E_NO_CREDENTIALS -- There are no credentials for this client
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CONTEXT Context = NULL;
PSSP_CREDENTIAL Credential = NULL;
PCHALLENGE_MESSAGE ChallengeMessage = NULL;
PAUTHENTICATE_MESSAGE AuthenticateMessage = NULL;
ULONG AuthenticateMessageSize;
PCHAR Where;
#ifdef BL_USE_LM_PASSWORD
LM_RESPONSE LmResponse;
#endif
NT_RESPONSE NtResponse;
STRING32* pString;
//
// Initialization
//
*ContextAttributes = 0;
//
// Find the currently existing context.
//
Context = SspContextReferenceContext( ContextHandle, FALSE );
if ( Context == NULL ) {
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// If we have already sent the authenticate message, then this must be
// RPC calling Initialize a third time to re-authenticate a connection.
// This happens when a new interface is called over an existing
// connection. What we do here is build a NULL authenticate message
// that the server will recognize and also ignore.
//
if ( Context->State == AuthenticateSentState ) {
AUTHENTICATE_MESSAGE NullMessage;
//
// To make sure this is the intended meaning of the call, check
// that the input token is NULL.
//
if ( (InputTokenSize != 0) || (InputToken != NULL) ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( *OutputTokenSize < sizeof(NullMessage) ) {
SecStatus = SEC_E_BUFFER_TOO_SMALL;
} else {
_fstrcpy( NullMessage.Signature, NTLMSSP_SIGNATURE );
NullMessage.MessageType = NtLmAuthenticate;
swaplong(NullMessage.MessageType) ;
_fmemset(&NullMessage.LmChallengeResponse, 0, 5*sizeof(STRING));
*OutputTokenSize = sizeof(NullMessage);
_fmemcpy(OutputToken, &NullMessage, sizeof(NullMessage));
SecStatus = SEC_E_OK;
}
goto Cleanup;
}
if ( Context->State != NegotiateSentState ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"Context not in NegotiateSentState\n" ));
SecStatus = SEC_E_OUT_OF_SEQUENCE;
goto Cleanup;
}
//
// We don't support any options.
//
// Complain about those that require we do something.
//
if ( (ContextReqFlags & (ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_PROMPT_FOR_CREDS |
ISC_REQ_USE_SUPPLIED_CREDS )) != 0 ) {
SspPrint(( SSP_API,
"SspHandleFirstCall: invalid ContextReqFlags 0x%lx.\n",
ContextReqFlags ));
SecStatus = SEC_E_INVALID_CONTEXT_REQ;
goto Cleanup;
}
if (ContextReqFlags & (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT)) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
}
if (ContextReqFlags & ISC_REQ_CONFIDENTIALITY) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
}
//
// Ignore the Credential Handle.
//
// Since this is the second call,
// the credential is implied by the Context.
// We could double check that the Credential Handle is either NULL or
// correct. However, our implementation doesn't maintain a close
// association between the two (actually no association) so checking
// would require a lot of overhead.
//
UNREFERENCED_PARAMETER( CredentialHandle );
ASSERT(Context->Credential != NULL);
Credential = Context->Credential;
//
// Get the ChallengeMessage.
//
if ( InputTokenSize < sizeof(CHALLENGE_MESSAGE) ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"ChallengeMessage size wrong %ld\n",
InputTokenSize ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( InputTokenSize > NTLMSSP_MAX_MESSAGE_SIZE ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"InputTokenSize > NTLMSSP_MAX_MESSAGE_SIZE\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
ChallengeMessage = (PCHALLENGE_MESSAGE) InputToken;
swaplong(ChallengeMessage->MessageType) ;
swaplong(ChallengeMessage->NegotiateFlags) ;
if ( _fstrncmp( ChallengeMessage->Signature,
NTLMSSP_SIGNATURE,
sizeof(NTLMSSP_SIGNATURE)) != 0 ||
ChallengeMessage->MessageType != NtLmChallenge ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"InputToken has invalid NTLMSSP signature\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Only negotiate OEM
//
if ( !(ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2) && ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"ChallengeMessage bad NegotiateFlags (UNICODE) 0x%lx\n",
ChallengeMessage->NegotiateFlags ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Check whether the server negotiated ALWAYS_SIGN
//
if ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN ) {
Context->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
//
// Only negotiate NTLM
//
if ( ( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NETWARE ) &&
!( ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM ) ) {
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"ChallengeMessage bad NegotiateFlags (NETWARE) 0x%lx\n",
ChallengeMessage->NegotiateFlags ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
#if 0
//
// Make sure that if we are signing or sealing we only have to use the
// LM key
//
if ((Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) &&
!(ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY))
{
SspPrint(( SSP_API,
"SspHandleChallengeMessage: "
"ChallengeMessage bad NegotiateFlags (Sign or Seal but no LM key) 0x%lx\n",
ChallengeMessage->NegotiateFlags ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
#endif
if (!Credential || !Credential->Username)
{
SspPrint((SSP_CRITICAL, "SspHandleChallengeMessage no username\n"));
SecStatus = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
SspPrint((SSP_CRED, "User name: (%s)\n", Credential->Username));
SspPrint((SSP_CRED, "Domain name: (%s)\n", Credential->Domain));
SspPrint((SSP_CRED, "Workstation: (%s)\n", Credential->Workstation));
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
if (!Credential || !Credential->NtPassword)
{
SspPrint((SSP_CRITICAL, "No NtPassword\n"));
SecStatus = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
SecStatus = SsprHandleNtlmv2ChallengeMessage(
Credential,
InputTokenSize,
InputToken,
&Context->NegotiateFlags,
OutputTokenSize,
OutputToken,
&Context->UserSessionKey
);
if (SEC_E_BUFFER_TOO_SMALL == SecStatus)
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
}
if (SecStatus != SEC_E_OK)
{
goto Cleanup;
}
SspMakeNtlmv2SKeys(
&Context->UserSessionKey,
Context->NegotiateFlags,
0, // SendNonce
0, // RecvNonce
&Context->Ntlmv2SKeys
);
goto ReturnSuccess;
}
if (Credential->Domain == NULL) {
ASSERT(ChallengeMessage->TargetName.Length != 0);
Credential->Domain = SspAlloc(ChallengeMessage->TargetName.Length + 1);
if (Credential->Domain == NULL) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
pString = &ChallengeMessage->TargetName;
#if defined(_WIN64)
_fmemcpy(Credential->Domain, (PCHAR)ChallengeMessage + (ULONG)((__int64)pString->Buffer), pString->Length);
#else
_fmemcpy(Credential->Domain, (PCHAR)ChallengeMessage + (ULONG)pString->Buffer, pString->Length);
#endif
Credential->Domain[pString->Length] = '\0';
}
if (GetPassword(Credential, 0) == FALSE) {
SecStatus = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
#ifdef BL_USE_LM_PASSWORD
if (CalculateLmResponse((PLM_CHALLENGE)ChallengeMessage->Challenge, Credential->LmPassword, &LmResponse) == FALSE) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
#endif
if (CalculateNtResponse((PNT_CHALLENGE)ChallengeMessage->Challenge, Credential->NtPassword, &NtResponse) == FALSE) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Allocate an authenticate message. Change this #if 0 and the next one
// to send an LM challenge response also.
//
#ifdef BL_USE_LM_PASSWORD
AuthenticateMessageSize = sizeof(*AuthenticateMessage)+LM_RESPONSE_LENGTH+NT_RESPONSE_LENGTH;
#else
AuthenticateMessageSize = sizeof(*AuthenticateMessage)+NT_RESPONSE_LENGTH;
#endif
if (Credential->Domain != NULL) {
AuthenticateMessageSize += _fstrlen(Credential->Domain);
}
if (Credential->Username != NULL) {
AuthenticateMessageSize += _fstrlen(Credential->Username);
}
if (Credential->Workstation != NULL) {
AuthenticateMessageSize += _fstrlen(Credential->Workstation);
}
if ( AuthenticateMessageSize > *OutputTokenSize ) {
SecStatus = SEC_E_BUFFER_TOO_SMALL;
goto Cleanup;
}
AuthenticateMessage = (PAUTHENTICATE_MESSAGE) SspAlloc ((int)AuthenticateMessageSize );
if ( AuthenticateMessage == NULL ) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Build the authenticate message
//
_fstrcpy( AuthenticateMessage->Signature, NTLMSSP_SIGNATURE );
AuthenticateMessage->MessageType = NtLmAuthenticate;
swaplong(AuthenticateMessage->MessageType) ;
Where = (PCHAR)(AuthenticateMessage+1);
#ifdef BL_USE_LM_PASSWORD
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->LmChallengeResponse,
(PCHAR)&LmResponse,
LM_RESPONSE_LENGTH,
&Where);
#else
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->LmChallengeResponse,
NULL,
0,
&Where);
#endif
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->NtChallengeResponse,
(PCHAR)&NtResponse,
NT_RESPONSE_LENGTH,
&Where);
if (Credential->Domain != NULL) {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->DomainName,
Credential->Domain,
_fstrlen(Credential->Domain),
&Where);
} else {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->DomainName,
NULL, 0, &Where);
}
if (Credential->Username != NULL) {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->UserName,
Credential->Username,
_fstrlen(Credential->Username),
&Where);
} else {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->UserName,
NULL, 0, &Where);
}
if (Credential->Workstation != NULL) {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->Workstation,
Credential->Workstation,
_fstrlen(Credential->Workstation),
&Where);
} else {
SspCopyStringFromRaw( AuthenticateMessage,
&AuthenticateMessage->Workstation,
NULL, 0, &Where);
}
_fmemcpy(OutputToken, AuthenticateMessage, (int)AuthenticateMessageSize);
*OutputTokenSize = AuthenticateMessageSize;
//
// The session key is the password, so convert it to a rc4 key.
//
if (Context->NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN |
NTLMSSP_NEGOTIATE_SEAL)) {
#ifdef BL_USE_LM_PASSWORD
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) {
LM_RESPONSE SessionKey;
LM_OWF_PASSWORD LmKey;
UCHAR Key[LM_SESSION_KEY_LENGTH];
//
// The session key is the first 8 bytes of the challenge response,
// re-encrypted with the password with the second 8 bytes set to 0xbd
//
_fmemcpy(&LmKey,Credential->LmPassword,LM_SESSION_KEY_LENGTH);
_fmemset( (PUCHAR)(&LmKey) + LM_SESSION_KEY_LENGTH,
0xbd,
LM_OWF_PASSWORD_LENGTH - LM_SESSION_KEY_LENGTH);
if (CalculateLmResponse( (PLM_CHALLENGE) &LmResponse,
&LmKey,
&SessionKey) == FALSE) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
_fmemcpy(Key,&SessionKey,5);
ASSERT(LM_SESSION_KEY_LENGTH == 8);
//
// Put a well-known salt at the end of the key to limit
// the changing part to 40 bits.
//
Key[5] = 0xe5;
Key[6] = 0x38;
Key[7] = 0xb0;
Context->Rc4Key = SspAlloc(sizeof(struct RC4_KEYSTRUCT));
if (Context->Rc4Key == NULL)
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
rc4_key(Context->Rc4Key, LM_SESSION_KEY_LENGTH, Key);
Context->Nonce = 0;
} else
#endif
if (ChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NT_ONLY) {
MD5_CTX Md5Context;
USER_SESSION_KEY UserSessionKey;
if (AuthenticateMessage->NtChallengeResponse.Length != NT_RESPONSE_LENGTH) {
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
goto Cleanup;
}
CalculateUserSessionKeyNt(
&NtResponse,
Credential->NtPassword,
&UserSessionKey);
//
// The NT session key is made by MD5'ing the challenge response,
// user name, domain name, and nt user session key together.
//
_fmemset(&Md5Context, 0, sizeof(MD5_CTX));
MD5Init(
&Md5Context
);
MD5Update(
&Md5Context,
(PUCHAR)&NtResponse,
NT_RESPONSE_LENGTH
);
MD5Update(
&Md5Context,
Credential->Username,
_fstrlen(Credential->Username)
);
MD5Update(
&Md5Context,
Credential->Domain,
_fstrlen(Credential->Domain)
);
MD5Update(
&Md5Context,
(PUCHAR)&UserSessionKey,
NT_SESSION_KEY_LENGTH
);
MD5Final(
&Md5Context
);
ASSERT(MD5DIGESTLEN == NT_SESSION_KEY_LENGTH);
Context->Rc4Key = SspAlloc(sizeof(struct RC4_KEYSTRUCT));
if (Context->Rc4Key == NULL)
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
rc4_key(Context->Rc4Key, NT_SESSION_KEY_LENGTH, Md5Context.digest);
Context->Nonce = 0;
} else {
USER_SESSION_KEY UserSessionKey;
if (AuthenticateMessage->NtChallengeResponse.Length != NT_RESPONSE_LENGTH) {
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
goto Cleanup;
}
CalculateUserSessionKeyNt(
&NtResponse,
Credential->NtPassword,
&UserSessionKey);
Context->Rc4Key = SspAlloc(sizeof(struct RC4_KEYSTRUCT));
if (Context->Rc4Key == NULL)
{
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
rc4_key(Context->Rc4Key, NT_SESSION_KEY_LENGTH, (PUCHAR) &UserSessionKey);
Context->Nonce = 0;
}
}
ReturnSuccess:
//
// Return output parameters to the caller.
//
*ExpirationTime = SspContextGetTimeStamp( Context, TRUE );
SecStatus = SEC_E_OK;
//
// Free and locally used resources.
//
Cleanup:
if ( Context != NULL ) {
//
// Don't allow this context to be used again.
//
if ( SecStatus == SEC_E_OK ) {
Context->State = AuthenticateSentState;
} else {
Context->State = IdleState;
}
SspContextDereferenceContext( Context );
}
if ( AuthenticateMessage != NULL ) {
_fmemset(AuthenticateMessage, 0, AuthenticateMessageSize);
SspFree( AuthenticateMessage );
}
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
InitializeSecurityContext(
IN PCredHandle CredentialHandle,
IN PCtxtHandle OldContextHandle,
IN SEC_CHAR * TargetName,
IN ULONG ContextReqFlags,
IN ULONG Reserved1,
IN ULONG TargetDataRep,
IN PSecBufferDesc InputToken,
IN ULONG Reserved2,
OUT PCtxtHandle NewContextHandle,
OUT PSecBufferDesc OutputToken,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime
)
/*++
Routine Description:
This routine initiates the outbound security context from a credential
handle. This results in the establishment of a security context
between the application and a remote peer. The routine returns a token
which must be passed to the remote peer which in turn submits it to the
local security implementation via the AcceptSecurityContext() call.
The token generated should be considered opaque by all callers.
This function is used by a client to initialize an outbound context.
For a two leg security package, the calling sequence is as follows: The
client calls the function with OldContextHandle set to NULL and
InputToken set either to NULL or to a pointer to a security package
specific data structure. The package returns a context handle in
NewContextHandle and a token in OutputToken. The handle can then be
used for message APIs if desired.
The OutputToken returned here is sent across to target server which
calls AcceptSecuirtyContext() with this token as an input argument and
may receive a token which is returned to the initiator so it can call
InitializeSecurityContext() again.
For a three leg (mutual authentication) security package, the calling
sequence is as follows: The client calls the function as above, but the
package will return SEC_I_CALLBACK_NEEDED. The client then sends the
output token to the server and waits for the server's reply. Upon
receipt of the server's response, the client calls this function again,
with OldContextHandle set to the handle that was returned from the
first call. The token received from the server is supplied in the
InputToken parameter. If the server has successfully responded, then
the package will respond with success, or it will invalidate the
context.
Initialization of security context may require more than one call to
this function depending upon the underlying authentication mechanism as
well as the "choices" indicated via ContextReqFlags. The
ContextReqFlags and ContextAttributes are bit masks representing
various context level functions viz. delegation, mutual
authentication, confidentiality, replay detection and sequence
detection.
When ISC_REQ_PROMPT_FOR_CREDS flag is set the security package always
prompts the user for credentials, irrespective of whether credentials
are present or not. If user indicated that the supplied credentials be
used then they will be stashed (overwriting existing ones if any) for
future use. The security packages will always prompt for credentials
if none existed, this optimizes for the most common case before a
credentials database is built. But the security packages can be
configured to not do that. Security packages will ensure that they
only prompt to the interactive user, for other logon sessions, this
flag is ignored.
When ISC_REQ_USE_SUPPLIED_CREDS flag is set the security package always
uses the credentials supplied in the InitializeSecurityContext() call
via InputToken parameter. If the package does not have any credentials
available it will prompt for them and record it as indicated above.
It is an error to set both these flags simultaneously.
If the ISC_REQ_ALLOCATE_MEMORY was specified then the caller must free
the memory pointed to by OutputToken by calling FreeContextBuffer().
For example, the InputToken may be the challenge from a LAN Manager or
NT file server. In this case, the OutputToken would be the NTLM
encrypted response to the challenge. The caller of this API can then
take the appropriate response (case-sensitive v. case-insensitive) and
return it to the server for an authenticated connection.
Arguments:
CredentialHandle - Handle to the credentials to be used to
create the context.
OldContextHandle - Handle to the partially formed context, if this is
a second call (see above) or NULL if this is the first call.
TargetName - String indicating the target of the context. The name will
be security package specific. For example it will be a fully
qualified Cairo name for Kerberos package and can be UNC name or
domain name for the NTLM package.
ContextReqFlags - Requirements of the context, package specific.
#define ISC_REQ_DELEGATE 0x00000001
#define ISC_REQ_MUTUAL_AUTH 0x00000002
#define ISC_REQ_REPLAY_DETECT 0x00000004
#define ISC_REQ_SEQUENCE_DETECT 0x00000008
#define ISC_REQ_CONFIDENTIALITY 0x00000010
#define ISC_REQ_USE_SESSION_KEY 0x00000020
#define ISC_REQ_PROMT_FOR__CREDS 0x00000040
#define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080
#define ISC_REQ_ALLOCATE_MEMORY 0x00000100
#define ISC_REQ_USE_DCE_STYLE 0x00000200
Reserved1 - Reserved value, MBZ.
TargetDataRep - Long indicating the data representation (byte ordering, etc)
on the target. The constant SECURITY_NATIVE_DREP may be supplied
by the transport indicating that the native format is in use.
InputToken - Pointer to the input token. In the first call this
token can either be NULL or may contain security package specific
information.
Reserved2 - Reserved value, MBZ.
NewContextHandle - New context handle. If this is a second call, this
can be the same as OldContextHandle.
OutputToken - Buffer to receive the output token.
ContextAttributes -Attributes of the context established.
#define ISC_RET_DELEGATE 0x00000001
#define ISC_RET_MUTUAL_AUTH 0x00000002
#define ISC_RET_REPLAY_DETECT 0x00000004
#define ISC_RET_SEQUENCE_DETECT 0x00000008
#define ISC_REP_CONFIDENTIALITY 0x00000010
#define ISC_REP_USE_SESSION_KEY 0x00000020
#define ISC_REP_USED_COLLECTED_CREDS 0x00000040
#define ISC_REP_USED_SUPPLIED_CREDS 0x00000080
#define ISC_REP_ALLOCATED_MEMORY 0x00000100
#define ISC_REP_USED_DCE_STYLE 0x00000200
ExpirationTime - Expiration time of the context.
Return Value:
STATUS_SUCCESS - Message handled
SEC_I_CALLBACK_NEEDED -- Caller should call again later
SEC_E_NO_SPM -- Security Support Provider is not running
SEC_E_INVALID_TOKEN -- Token improperly formatted
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
SEC_E_NO_CREDENTIALS -- There are no credentials for this client
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
PVOID InputTokenBuffer;
PULONG InputTokenSize;
ULONG LocalInputTokenSize;
PVOID OutputTokenBuffer;
PULONG OutputTokenSize;
ULONG QopFlags;
//
// ideally pull these in from a global header.
//
extern ULONG AuthenticationType;
#define OSCHOICE_AUTHENETICATE_TYPE_NTLM_V1 0x00000001
#define OSCHOICE_AUTHENETICATE_TYPE_NTLM_V2 0x00000002
SspPrint((SSP_API, "SspInitializeSecurityContext Entered\n"));
//
// Check argument validity
//
if (!ARGUMENT_PRESENT(OutputToken)) {
return (ERROR_BAD_ARGUMENTS);
}
#ifdef notdef // ? RPC passes 0x10 or 0 here depending on attitude
if ( TargetDataRep != SECURITY_NATIVE_DREP ) {
return (STATUS_INVALID_PARAMETER);
}
#else // notdef
UNREFERENCED_PARAMETER( TargetDataRep );
#endif // notdef
if ( !SspGetTokenBuffer( InputToken,
&InputTokenBuffer,
&InputTokenSize,
TRUE ) ) {
return (SEC_E_INVALID_TOKEN);
}
if ( InputTokenSize == 0 ) {
InputTokenSize = &LocalInputTokenSize;
LocalInputTokenSize = 0;
}
if ( !SspGetTokenBuffer( OutputToken,
&OutputTokenBuffer,
&OutputTokenSize,
FALSE ) ) {
return (SEC_E_INVALID_TOKEN);
}
//
// If no previous context was passed in this is the first call.
//
if ( !ARGUMENT_PRESENT( OldContextHandle ) ) {
if ( !ARGUMENT_PRESENT( CredentialHandle ) ) {
return (SEC_E_INVALID_HANDLE);
}
if (AuthenticationType == OSCHOICE_AUTHENETICATE_TYPE_NTLM_V1) {
QopFlags = 0;
} else if (AuthenticationType == OSCHOICE_AUTHENETICATE_TYPE_NTLM_V2) {
QopFlags = QOP_NTLMV2;
}
return SspHandleFirstCall(
CredentialHandle,
NewContextHandle,
ContextReqFlags,
QopFlags,
*InputTokenSize,
InputTokenBuffer,
OutputTokenSize,
OutputTokenBuffer,
ContextAttributes,
ExpirationTime
);
//
// If context was passed in, continue where we left off.
//
} else {
*NewContextHandle = *OldContextHandle;
return SspHandleChallengeMessage(
NULL,
CredentialHandle,
NewContextHandle,
ContextReqFlags,
*InputTokenSize,
InputTokenBuffer,
OutputTokenSize,
OutputTokenBuffer,
ContextAttributes,
ExpirationTime
);
}
return (SecStatus);
}
SECURITY_STATUS SEC_ENTRY
DeleteSecurityContext (
PCtxtHandle ContextHandle
)
/*++
Routine Description:
Deletes the local data structures associated with the specified
security context and generates a token which is passed to a remote peer
so it too can remove the corresponding security context.
This API terminates a context on the local machine, and optionally
provides a token to be sent to the other machine. The OutputToken
generated by this call is to be sent to the remote peer (initiator or
acceptor). If the context was created with the I _REQ_ALLOCATE_MEMORY
flag, then the package will allocate a buffer for the output token.
Otherwise, it is the responsibility of the caller.
Arguments:
ContextHandle - Handle to the context to delete
TokenLength - Size of the output token (if any) that should be sent to
the process at the other end of the session.
Token - Pointer to the token to send.
Return Value:
SEC_E_OK - Call completed successfully
SEC_E_NO_SPM -- Security Support Provider is not running
SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CONTEXT Context = NULL;
//
// Initialization
//
SspPrint(( SSP_API, "SspDeleteSecurityContext Entered\n" ));
//
// Find the currently existing context (and delink it).
//
Context = SspContextReferenceContext( ContextHandle,
TRUE );
if ( Context == NULL ) {
SecStatus = SEC_E_INVALID_HANDLE;
goto cleanup;
} else {
SspContextDereferenceContext( Context );
SecStatus = SEC_E_OK;
}
cleanup:
if (Context != NULL) {
SspContextDereferenceContext(Context);
Context = NULL;
}
SspPrint(( SSP_API, "SspDeleteSecurityContext returns 0x%x\n", SecStatus ));
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
FreeContextBuffer (
void * ContextBuffer
)
/*++
Routine Description:
This API is provided to allow callers of security API such as
InitializeSecurityContext() for free the memory buffer allocated for
returning the outbound context token.
Arguments:
ContextBuffer - Address of the buffer to be freed.
Return Value:
SEC_E_OK - Call completed successfully
--*/
{
//
// The only allocated buffer that NtLmSsp currently returns to the caller
// is from EnumeratePackages. It uses LocalAlloc to allocate memory. If
// we ever need memory to be allocated by the service, we have to rethink
// how this routine distinguishes between to two types of allocated memory.
//
SspFree( ContextBuffer );
return (SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
ApplyControlToken (
PCtxtHandle ContextHandle,
PSecBufferDesc Input
)
{
#ifdef DEBUGRPC
SspPrint(( SSP_API, "ApplyContextToken Called\n" ));
#endif // DEBUGRPC
return SEC_E_UNSUPPORTED_FUNCTION;
UNREFERENCED_PARAMETER( ContextHandle );
UNREFERENCED_PARAMETER( Input );
}
void
SsprGenCheckSum(
IN PSecBuffer pMessage,
OUT PNTLMSSP_MESSAGE_SIGNATURE pSig
)
{
Crc32(pSig->CheckSum,pMessage->cbBuffer,pMessage->pvBuffer,&pSig->CheckSum);
}
SECURITY_STATUS SEC_ENTRY
MakeSignature(
IN OUT PCtxtHandle ContextHandle,
IN ULONG fQOP,
IN OUT PSecBufferDesc pMessage,
IN ULONG MessageSeqNo
)
{
PSSP_CONTEXT pContext;
PNTLMSSP_MESSAGE_SIGNATURE pSig;
int Signature;
ULONG i;
pContext = SspContextReferenceContext(ContextHandle,FALSE);
if (!pContext ||
(!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
&& (pContext->Rc4Key == NULL)
&& !(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
)
)
{
return(SEC_E_INVALID_HANDLE);
}
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
return SspNtlmv2MakeSignature(
&pContext->Ntlmv2SKeys,
pContext->NegotiateFlags,
fQOP,
MessageSeqNo,
pMessage
);
}
Signature = -1;
for (i = 0; i < pMessage->cBuffers; i++)
{
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
{
Signature = i;
break;
}
}
if (Signature == -1)
{
SspContextDereferenceContext(pContext);
return(SEC_E_INVALID_TOKEN);
}
pSig = pMessage->pBuffers[Signature].pvBuffer;
if (!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN))
{
_fmemset(pSig,0,NTLMSSP_MESSAGE_SIGNATURE_SIZE);
pSig->Version = NTLMSSP_SIGN_VERSION;
swaplong(pSig->Version) ; // MACBUG
SspContextDereferenceContext(pContext);
return(SEC_E_OK);
}
//
// required by CRC-32 algorithm
//
pSig->CheckSum = 0xffffffff;
for (i = 0; i < pMessage->cBuffers ; i++ )
{
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
!(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
{
SsprGenCheckSum(&pMessage->pBuffers[i], pSig);
}
}
//
// Required by CRC-32 algorithm
//
pSig->CheckSum ^= 0xffffffff;
pSig->Nonce = pContext->Nonce++;
pSig->Version = NTLMSSP_SIGN_VERSION; // MACBUG
swaplong(pSig->CheckSum) ;
swaplong(pSig->Nonce) ;
swaplong(pSig->Version) ;
rc4(pContext->Rc4Key, sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
(unsigned char SEC_FAR *) &pSig->RandomPad);
pMessage->pBuffers[Signature].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
SspContextDereferenceContext(pContext);
return(SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
VerifySignature(
IN OUT PCtxtHandle ContextHandle,
IN OUT PSecBufferDesc pMessage,
IN ULONG MessageSeqNo,
OUT PULONG pfQOP
)
{
PSSP_CONTEXT pContext;
PNTLMSSP_MESSAGE_SIGNATURE pSig;
NTLMSSP_MESSAGE_SIGNATURE Sig;
int Signature;
ULONG i;
UNREFERENCED_PARAMETER(pfQOP);
UNREFERENCED_PARAMETER(MessageSeqNo);
pContext = SspContextReferenceContext(ContextHandle,FALSE);
if (!pContext ||
(!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
&& (pContext->Rc4Key == NULL)
&& !(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
)
)
{
return(SEC_E_INVALID_HANDLE);
}
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
return SspNtlmv2VerifySignature(
&pContext->Ntlmv2SKeys,
pContext->NegotiateFlags,
MessageSeqNo,
pMessage,
pfQOP
);
}
Signature = -1;
for (i = 0; i < pMessage->cBuffers; i++)
{
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
{
Signature = i;
break;
}
}
if (Signature == -1)
{
SspContextDereferenceContext(pContext);
return(SEC_E_INVALID_TOKEN);
}
pSig = pMessage->pBuffers[Signature].pvBuffer;
swaplong(pSig->Version) ;
//
// Check if this is just a trailer and not a real signature
//
if (!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN))
{
SspContextDereferenceContext(pContext);
_fmemset(&Sig,0,NTLMSSP_MESSAGE_SIGNATURE_SIZE);
Sig.Version = NTLMSSP_SIGN_VERSION;
if (!_fmemcmp(&Sig,pSig,NTLMSSP_MESSAGE_SIGNATURE_SIZE))
{
return(SEC_E_OK);
}
return(SEC_E_MESSAGE_ALTERED);
}
Sig.CheckSum = 0xffffffff;
for (i = 0; i < pMessage->cBuffers ; i++ )
{
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
!(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
{
SsprGenCheckSum(&pMessage->pBuffers[i], &Sig);
}
}
Sig.CheckSum ^= 0xffffffff;
Sig.Nonce = pContext->Nonce++;
rc4(pContext->Rc4Key, sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
(unsigned char SEC_FAR *) &pSig->RandomPad);
SspContextDereferenceContext(pContext);
swaplong(pSig->CheckSum) ;
swaplong(pSig->Nonce) ;
if (pSig->CheckSum != Sig.CheckSum)
{
return(SEC_E_MESSAGE_ALTERED);
}
if (pSig->Nonce != Sig.Nonce)
{
return(SEC_E_OUT_OF_SEQUENCE);
}
return(SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
SealMessage(
IN OUT PCtxtHandle ContextHandle,
IN ULONG fQOP,
IN OUT PSecBufferDesc pMessage,
IN ULONG MessageSeqNo
)
{
PSSP_CONTEXT pContext;
PNTLMSSP_MESSAGE_SIGNATURE pSig;
int Signature;
ULONG i;
UNREFERENCED_PARAMETER(fQOP);
UNREFERENCED_PARAMETER(MessageSeqNo);
pContext = SspContextReferenceContext(ContextHandle, FALSE);
if (!pContext ||
(!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
&& (pContext->Rc4Key == NULL)
)
)
{
return(SEC_E_INVALID_HANDLE);
}
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
return SspNtlmv2SealMessage(
&pContext->Ntlmv2SKeys,
pContext->NegotiateFlags,
fQOP,
MessageSeqNo,
pMessage
);
}
Signature = -1;
for (i = 0; i < pMessage->cBuffers; i++)
{
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
{
Signature = i;
break;
}
}
if (Signature == -1)
{
SspContextDereferenceContext(pContext);
return(SEC_E_INVALID_TOKEN);
}
pSig = pMessage->pBuffers[Signature].pvBuffer;
//
// required by CRC-32 algorithm
//
pSig->CheckSum = 0xffffffff;
for (i = 0; i < pMessage->cBuffers ; i++ )
{
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
!(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
{
SsprGenCheckSum(&pMessage->pBuffers[i], pSig);
if (pMessage->pBuffers[i].cbBuffer) // rc4 fails with zero byte buffers
{
rc4(pContext->Rc4Key,
(int) pMessage->pBuffers[i].cbBuffer,
(PUCHAR) pMessage->pBuffers[i].pvBuffer );
}
}
}
//
// Required by CRC-32 algorithm
//
pSig->CheckSum ^= 0xffffffff;
pSig->Nonce = pContext->Nonce++;
pSig->Version = NTLMSSP_SIGN_VERSION; // MACBUG
swaplong(pSig->CheckSum) ;
swaplong(pSig->Nonce) ;
swaplong(pSig->Version) ;
rc4(pContext->Rc4Key, sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
(PUCHAR) &pSig->RandomPad);
pMessage->pBuffers[Signature].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
SspContextDereferenceContext(pContext);
return(SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
UnsealMessage(
IN OUT PCtxtHandle ContextHandle,
IN OUT PSecBufferDesc pMessage,
IN ULONG MessageSeqNo,
OUT PULONG pfQOP
)
{
PSSP_CONTEXT pContext;
PNTLMSSP_MESSAGE_SIGNATURE pSig;
NTLMSSP_MESSAGE_SIGNATURE Sig;
int Signature;
ULONG i;
UNREFERENCED_PARAMETER(pfQOP);
UNREFERENCED_PARAMETER(MessageSeqNo);
pContext = SspContextReferenceContext(ContextHandle, FALSE);
if (!pContext ||
(!(pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
&& (!pContext->Rc4Key)
)
)
{
return(SEC_E_INVALID_HANDLE);
}
if (pContext->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
{
return SspNtlmv2UnsealMessage(
&pContext->Ntlmv2SKeys,
pContext->NegotiateFlags,
MessageSeqNo,
pMessage,
pfQOP
);
}
Signature = -1;
for (i = 0; i < pMessage->cBuffers; i++)
{
if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
{
Signature = i;
break;
}
}
if (Signature == -1)
{
SspContextDereferenceContext(pContext);
return(SEC_E_INVALID_TOKEN);
}
pSig = pMessage->pBuffers[Signature].pvBuffer;
Sig.CheckSum = 0xffffffff;
for (i = 0; i < pMessage->cBuffers ; i++ )
{
if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
!(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
{
if (pMessage->pBuffers[i].cbBuffer)
{
rc4(pContext->Rc4Key,
(int) pMessage->pBuffers[i].cbBuffer,
(unsigned char *) pMessage->pBuffers[i].pvBuffer );
}
SsprGenCheckSum(&pMessage->pBuffers[i], &Sig);
}
}
Sig.CheckSum ^= 0xffffffff;
Sig.Nonce = pContext->Nonce++;
rc4(pContext->Rc4Key, sizeof(NTLMSSP_MESSAGE_SIGNATURE) - sizeof(ULONG),
(unsigned char *) &pSig->RandomPad);
SspContextDereferenceContext(pContext);
swaplong(pSig->Nonce) ;
swaplong(pSig->CheckSum) ;
if (pSig->Nonce != Sig.Nonce)
{
return(SEC_E_OUT_OF_SEQUENCE);
}
if (pSig->CheckSum != Sig.CheckSum)
{
return(SEC_E_MESSAGE_ALTERED);
}
return(SEC_E_OK);
}