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.
 
 
 
 
 
 

3716 lines
99 KiB

/*--
Copyright (c) 1994-1997 Microsoft Corporation
Module Name:
secpkg.c
Abstract:
Security package used for Netlogon's secure channel between two netlogon
processes.
Author:
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
05-Mar-1994 (MikeSw)
Created as user mode example SSPI
02-Nov-1997 (CliffV)
Converted to be the Netlogon security package.
--*/
//
// Common include files.
//
#include "logonsrv.h" // Include files common to entire service
#pragma hdrstop
//
// Include files specific to this .c file
//
#include <spseal.h>
//
// Authentication Data for the Netlogon Authentication Package.
//
// This is the auth data to pass to RpcBindingSetAuthInfo for the
// Netlogon authentication package.
//
typedef struct _NL_AUTH_DATA {
//
// Signature to identify that this is really AUTH Data
//
ULONG Signature;
#define NL_AUTH_DATA_SIGNATURE 0x29120227
//
// Size of this structure (in bytes)
//
ULONG Size;
//
// Information describing the session between the client and server.
//
SESSION_INFO SessionInfo;
//
// Domain name of the domain we're connecting to.
//
ULONG OemNetbiosDomainNameOffset;
ULONG OemNetbiosDomainNameLength;
ULONG Utf8DnsDomainNameOffset;
//
// Computer name of this machine
//
ULONG OemComputerNameOffset;
ULONG OemComputerNameLength;
ULONG Utf8ComputerNameOffset;
ULONG Utf8ComputerNameLength;
ULONG Utf8DnsHostNameOffset;
} NL_AUTH_DATA, *PNL_AUTH_DATA;
//
// A single credential
// Only the outbound side has a credential allocated. The inbound side simply
// returns a constant handle to the caller.
#define NL_AUTH_SERVER_CRED 0xfefefefe
typedef struct _NL_AUTH_CREDENTIAL {
//
// Handle identifying the credential
//
CredHandle CredentialHandle;
//
// Global list of all credentials
//
struct _NL_AUTH_CREDENTIAL *Next;
//
// Reference count
//
ULONG ReferenceCount;
//
// For a client side (outbound) credential,
// this is a pointer to the information from the client session structure
// representing the secure channel to the server.
//
PNL_AUTH_DATA ClientAuthData;
} NL_AUTH_CREDENTIAL, *PNL_AUTH_CREDENTIAL;
//
// A single context.
//
typedef struct _NL_AUTH_CONTEXT {
//
// Handle identifying the context
//
CtxtHandle ContextHandle;
//
// Global list of all contexts
//
struct _NL_AUTH_CONTEXT * Next;
LARGE_INTEGER Nonce;
ULONG ContextFlags;
//
// Information describing the session between the client and server.
//
SESSION_INFO SessionInfo;
enum {
Idle,
FirstInit,
FirstAccept,
SecondInit
} State;
//
// Flags
//
BOOLEAN Inbound;
} NL_AUTH_CONTEXT, *PNL_AUTH_CONTEXT;
#define BUFFERTYPE(_x_) ((_x_).BufferType & ~SECBUFFER_ATTRMASK)
//
// On the wire message transmitted from client to server during the bind.
//
typedef enum {
Negotiate,
NegotiateResponse
} NL_AUTH_MESSAGE_TYPE;
typedef struct _NL_AUTH_MESSAGE {
NL_AUTH_MESSAGE_TYPE MessageType;
ULONG Flags;
#define NL_AUTH_NETBIOS_DOMAIN_NAME 0x0001 // Netbios Domain name exists in buffer
#define NL_AUTH_NETBIOS_COMPUTER_NAME 0x0002 // Netbios Computer name exists in buffer
#define NL_AUTH_DNS_DOMAIN_NAME 0x0004 // DNS Domain name exists in buffer
#define NL_AUTH_DNS_HOST_NAME 0x0008 // DNS Host name exists in buffer
#define NL_AUTH_UTF8_NETBIOS_COMPUTER_NAME 0x0010 // UTF-8 Netbios Computer name exists in buffer
UCHAR Buffer[1];
} NL_AUTH_MESSAGE, *PNL_AUTH_MESSAGE;
//
// Signature for signed and sealed messages
//
#define NL_AUTH_ETYPE KERB_ETYPE_RC4_PLAIN_OLD // Encryption algorithm to use
#define NL_AUTH_CHECKSUM KERB_CHECKSUM_MD5_HMAC // Checksum algorithm to use
typedef struct _NL_AUTH_SIGNATURE {
BYTE SignatureAlgorithm[2]; // see below table for values
union {
BYTE SignFiller[4]; // filler, must be ff ff ff ff
struct {
BYTE SealAlgorithm[2];
BYTE SealFiller[2];
};
};
BYTE Flags[2];
#define NL_AUTH_SIGNED_BYTES 8 // Number of bytes in signature before SequenceNumber
#define NL_AUTH_SEQUENCE_SIZE 8
BYTE SequenceNumber[NL_AUTH_SEQUENCE_SIZE];
BYTE Checksum[8];
// Confounder must be the last field in the structure since it isn't sent on
// the wire if we're only signing the message.
#define NL_AUTH_CONFOUNDER_SIZE 8
BYTE Confounder[NL_AUTH_CONFOUNDER_SIZE];
} NL_AUTH_SIGNATURE, *PNL_AUTH_SIGNATURE;
#define PACKAGE_NAME NL_PACKAGE_NAME
#define PACKAGE_COMMENT L"Package for securing Netlogon's Secure Channel"
#define PACAKGE_CAPABILITIES (SECPKG_FLAG_TOKEN_ONLY | \
SECPKG_FLAG_MULTI_REQUIRED | \
SECPKG_FLAG_CONNECTION | \
SECPKG_FLAG_INTEGRITY | \
SECPKG_FLAG_PRIVACY)
#define PACKAGE_VERSION 1
#define PACKAGE_RPCID RPC_C_AUTHN_NETLOGON
#define PACKAGE_MAXTOKEN (sizeof(NL_AUTH_MESSAGE) + DNLEN + 1 + CNLEN + 1 + 2*(NL_MAX_DNS_LENGTH+1) )
#define PACKAGE_SIGNATURE_SIZE sizeof(NL_AUTH_SIGNATURE)
SecurityFunctionTableW SecTableW = {SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
EnumerateSecurityPackagesW,
NULL,
AcquireCredentialsHandleW,
FreeCredentialsHandle,
NULL, // LogonUser
InitializeSecurityContextW,
AcceptSecurityContext,
NULL,
DeleteSecurityContext,
NULL,
QueryContextAttributesW,
ImpersonateSecurityContext,
RevertSecurityContext,
MakeSignature,
VerifySignature,
FreeContextBuffer,
QuerySecurityPackageInfoW,
SealMessage,
UnsealMessage,
};
PNL_AUTH_CONTEXT ContextList;
PNL_AUTH_CREDENTIAL CredentialList;
//
// Id's to identify a context or credential.
// Access serialized by NlGlobalSecPkgCritSect
//
LARGE_INTEGER NextId = {0,0};
TimeStamp Forever = {0x7fffffff,0xfffffff};
TimeStamp Never = {0,0};
PVOID
NlBuildAuthData(
PCLIENT_SESSION ClientSession
)
/*++
Routine Description:
Allocate an authentication data structure suitable for passing to
RpcBindingSetAuthInfo
On Entry,
The caller must be a writer of the trust list entry.
The trust list entry must be authenticated.
Arguments:
ClientSession - Authenticated session describing the secure channel to a
DC.
Return Value:
Pointer to the AUTH_DATA. (Buffer should be freed by calling I_NetLogonFree.)
NULL: memory could not be allocated
--*/
{
ULONG Size;
PNL_AUTH_DATA ClientAuthData;
LPBYTE Where;
ULONG DnsDomainNameSize;
ULONG DnsHostNameSize;
NlAssert( ClientSession->CsReferenceCount > 0 );
NlAssert( ClientSession->CsFlags & CS_WRITER );
NlAssert( ClientSession->CsState == CS_AUTHENTICATED );
//
// Determine the size of the entry
//
DnsDomainNameSize =
(ClientSession->CsUtf8DnsDomainName != NULL ?
(strlen( ClientSession->CsUtf8DnsDomainName ) + 1) :
0);
#ifdef notdef
DnsHostNameSize =
(ClientSession->CsDomainInfo->DomUtf8DnsHostName != NULL ?
(strlen( ClientSession->CsDomainInfo->DomUtf8DnsHostName ) + 1) :
0);
#else // notdef
DnsHostNameSize = 0;
#endif // notdef
Size = sizeof(NL_AUTH_DATA) +
ClientSession->CsOemNetbiosDomainNameLength + 1 +
ClientSession->CsDomainInfo->DomOemComputerNameLength + 1 +
ClientSession->CsDomainInfo->DomUtf8ComputerNameLength + 1 +
DnsDomainNameSize +
DnsHostNameSize;
//
// Allocate the entry
//
ClientAuthData = NetpMemoryAllocate( Size );
if ( ClientAuthData == NULL ) {
return NULL;
}
Where = (LPBYTE) (ClientAuthData + 1);
RtlZeroMemory( ClientAuthData, sizeof(NL_AUTH_DATA) );
//
// Fill in the fixed length fields.
//
ClientAuthData->Signature = NL_AUTH_DATA_SIGNATURE;
ClientAuthData->Size = Size;
ClientAuthData->SessionInfo.SessionKey = ClientSession->CsSessionKey;
ClientAuthData->SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
//
// Copy the Netbios domain name of the domain hosted by the DC into the buffer
//
if ( ClientSession->CsOemNetbiosDomainNameLength != 0 ) {
ClientAuthData->OemNetbiosDomainNameOffset = (ULONG) (Where-(LPBYTE)ClientAuthData);
ClientAuthData->OemNetbiosDomainNameLength =
ClientSession->CsOemNetbiosDomainNameLength;
RtlCopyMemory( Where,
ClientSession->CsOemNetbiosDomainName,
ClientSession->CsOemNetbiosDomainNameLength + 1 );
Where += ClientAuthData->OemNetbiosDomainNameLength + 1;
}
//
// Copy the OEM Netbios computer name of this machine into the buffer.
//
// ???: Only copy the Netbios computername or DNS host name. Copy the
// one that was passed to the server on the NetServerReqChallenge.
//
if ( ClientSession->CsDomainInfo->DomOemComputerNameLength != 0 ) {
ClientAuthData->OemComputerNameOffset = (ULONG) (Where-(LPBYTE)ClientAuthData);
ClientAuthData->OemComputerNameLength =
ClientSession->CsDomainInfo->DomOemComputerNameLength;
RtlCopyMemory( Where,
ClientSession->CsDomainInfo->DomOemComputerName,
ClientSession->CsDomainInfo->DomOemComputerNameLength + 1);
Where += ClientAuthData->OemComputerNameLength + 1;
}
//
// Copy the UTF-8 Netbios computer name of this machine into the buffer.
//
if ( ClientSession->CsDomainInfo->DomUtf8ComputerNameLength != 0 ) {
ClientAuthData->Utf8ComputerNameOffset = (ULONG) (Where-(LPBYTE)ClientAuthData);
ClientAuthData->Utf8ComputerNameLength =
ClientSession->CsDomainInfo->DomUtf8ComputerNameLength;
RtlCopyMemory( Where,
ClientSession->CsDomainInfo->DomUtf8ComputerName,
ClientSession->CsDomainInfo->DomUtf8ComputerNameLength +1 );
Where += ClientAuthData->Utf8ComputerNameLength + 1;
}
//
// Copy the DNS domain name of the domain hosted by the DC into the buffer.
//
if ( ClientSession->CsUtf8DnsDomainName != NULL ) {
ClientAuthData->Utf8DnsDomainNameOffset = (ULONG) (Where-(LPBYTE)ClientAuthData);
RtlCopyMemory( Where, ClientSession->CsUtf8DnsDomainName, DnsDomainNameSize );
Where += DnsDomainNameSize;
}
//
// Copy the DNS host name name of this machine into the buffer.
//
// ???: Only copy the Netbios computername or DNS host name. Copy the
// one that was passed to the server on the NetServerReqChallenge.
//
#ifdef notdef
if ( ClientSession->CsDomainInfo->DomUtf8DnsHostName != NULL ) {
ClientAuthData->Utf8DnsHostNameOffset = (ULONG) (Where-(LPBYTE)ClientAuthData);
RtlCopyMemory( Where, ClientSession->CsDomainInfo->DomUtf8DnsHostName, DnsHostNameSize );
Where += DnsHostNameSize;
}
#endif // notdef
return ClientAuthData;
}
BOOL
NlEqualClientSessionKey(
PCLIENT_SESSION ClientSession,
PVOID ClientContext
)
/*++
Routine Description:
Checks whether the session key on the client session is equal
to the session key on the client context.
On Entry,
The caller must be a writer of the trust list entry.
Arguments:
ClientSession - Authenticated session describing the secure channel to a DC
ClientContext - Client context returned from a previous call to NlBuildAuthData
Return Value:
TRUE if the two session keys are equal.
FALSE, otherwise.
--*/
{
PNL_AUTH_DATA ClientAuthData = ClientContext;
if ( ClientAuthData == NULL ) {
return FALSE;
}
if ( RtlEqualMemory( &ClientSession->CsSessionKey,
&ClientAuthData->SessionInfo.SessionKey,
sizeof(ClientSession->CsSessionKey) ) ) {
return TRUE;
} else{
return FALSE;
}
}
BOOL
NlStartNetlogonCall(
VOID
)
/*++
Routine Description:
Start a procedure call from outside the Netlogon service into the
Netlogon Service.
Arguments:
None.
Return Value:
TRUE - Call is OK. (Caller must call NlEndNetlogonCall())
FALSE - Netlogon is not started.
--*/
{
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
EnterCriticalSection( &NlGlobalMsvCritSect );
if ( !NlGlobalMsvEnabled ) {
LeaveCriticalSection( &NlGlobalMsvCritSect );
return FALSE;
}
NlGlobalMsvThreadCount ++;
LeaveCriticalSection( &NlGlobalMsvCritSect );
return TRUE;
}
VOID
NlEndNetlogonCall(
VOID
)
/*++
Routine Description:
End a procedure call from outside the Netlogon service into the
Netlogon Service.
Arguments:
None.
Return Value:
None.
--*/
{
//
// Indicate that the calling thread has left netlogon.dll
//
EnterCriticalSection( &NlGlobalMsvCritSect );
NlGlobalMsvThreadCount --;
if ( NlGlobalMsvThreadCount == 0 && !NlGlobalMsvEnabled ) {
if ( !SetEvent( NlGlobalMsvTerminateEvent ) ) {
NlPrint((NL_CRITICAL, "Cannot set MSV termination event: %lu\n",
GetLastError() ));
}
}
LeaveCriticalSection( &NlGlobalMsvCritSect );
}
PNL_AUTH_CREDENTIAL
LocateCredential(
PCredHandle CredentialHandle
)
/*++
Routine Description:
Find a credential given its handle
Arguments:
CredentialHandle - Handle to the credential to locate
Return Value:
Pointer to the credential
--*/
{
PNL_AUTH_CREDENTIAL TestCredential;
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
for ( TestCredential = CredentialList;
TestCredential != NULL;
TestCredential = TestCredential->Next ) {
if ( TestCredential->CredentialHandle.dwUpper == CredentialHandle->dwUpper &&
TestCredential->CredentialHandle.dwLower == CredentialHandle->dwLower ) {
break;
}
}
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return(TestCredential);
}
BOOLEAN
DeleteCredential(
PCtxtHandle CredentialHandle
)
/*++
Routine Description:
Delete a credential given its handle
Arguments:
CredentialHandle - Handle to the credential to locate
Return Value:
TRUE: if credential existed.
--*/
{
PNL_AUTH_CREDENTIAL TestCredential, LastCredential;
//
// Find the credential.
//
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
LastCredential = NULL;
for ( TestCredential = CredentialList;
TestCredential != NULL ;
TestCredential = TestCredential->Next ) {
if ( TestCredential->CredentialHandle.dwUpper == CredentialHandle->dwUpper &&
TestCredential->CredentialHandle.dwLower == CredentialHandle->dwLower ) {
break;
}
LastCredential = TestCredential;
}
//
// If we found it,
// Dereference it.
//
if ( TestCredential != NULL ) {
TestCredential->ReferenceCount --;
//
// If this is the last dereference,
// delink it and delete it.
//
if ( TestCredential->ReferenceCount == 0 ) {
if (LastCredential != NULL) {
LastCredential->Next = TestCredential->Next;
} else {
NlAssert(CredentialList == TestCredential);
CredentialList = TestCredential->Next;
}
NlPrint(( NL_SESSION_MORE,
"DeleteCredential: %lx.%lx: credential freed\n",
CredentialHandle->dwUpper, CredentialHandle->dwLower ));
LocalFree(TestCredential);
} else {
NlPrint(( NL_SESSION_MORE,
"DeleteCredential: %lx.%lx: credential dereferenced: %ld\n",
CredentialHandle->dwUpper, CredentialHandle->dwLower,
TestCredential->ReferenceCount ));
}
} else {
NlPrint(( NL_SESSION_MORE,
"DeleteCredential: %lx.%lx: credential handle not found\n",
CredentialHandle->dwUpper, CredentialHandle->dwLower ));
}
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return( TestCredential == NULL ? FALSE : TRUE );
}
PNL_AUTH_CREDENTIAL
AllocateCredential(
IN PNL_AUTH_DATA ClientAuthData
)
/*++
Routine Description:
Allocate and initialize a credential (Client or server side)
Arguments:
ClientAuthData - The client auth data to capture.
Return Value:
Allocated credential. Delete this credential by DeleteCredential( Credential->CredentialHandle );
NULL if credential cannot be allocated.
--*/
{
PNL_AUTH_CREDENTIAL Credential;
//
// Determine if we already have a credential
//
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
for ( Credential = CredentialList;
Credential != NULL;
Credential = Credential->Next ) {
if ( ClientAuthData->Size == Credential->ClientAuthData->Size &&
RtlEqualMemory( ClientAuthData,
Credential->ClientAuthData,
ClientAuthData->Size ) ) {
//
// Return the existing credential to the caller.
//
Credential->ReferenceCount ++;
NlPrint(( NL_SESSION_MORE,
"AllocateCredential: %lx.%lx: credential referenced: %ld\n",
Credential->CredentialHandle.dwUpper,
Credential->CredentialHandle.dwLower,
Credential->ReferenceCount ));
goto Cleanup;
}
}
//
// Allocate a credential block.
//
Credential = (PNL_AUTH_CREDENTIAL)
LocalAlloc( LMEM_ZEROINIT,
sizeof(NL_AUTH_CREDENTIAL) +
ClientAuthData->Size );
if (Credential == NULL) {
goto Cleanup;
}
//
// Initialize the credential.
//
NextId.QuadPart ++;
Credential->CredentialHandle.dwUpper = NextId.HighPart;
Credential->CredentialHandle.dwLower = NextId.LowPart;
Credential->ReferenceCount = 1;
Credential->Next = CredentialList;
CredentialList = Credential;
//
// Capture a local copy of the credential
// The caller might free the one passed in to us.
//
Credential->ClientAuthData = (PNL_AUTH_DATA)
(((LPBYTE)Credential) + sizeof(NL_AUTH_CREDENTIAL));
RtlCopyMemory( Credential->ClientAuthData,
ClientAuthData,
ClientAuthData->Size );
NlPrint(( NL_SESSION_MORE,
"AllocateCredential: %lx.%lx: credential allocated\n",
Credential->CredentialHandle.dwUpper,
Credential->CredentialHandle.dwLower ));
Cleanup:
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return Credential;
}
PNL_AUTH_CONTEXT
LocateContext(
PCtxtHandle ContextHandle
)
/*++
Routine Description:
Find a context given its handle
Arguments:
ContextHandle - Handle to the context to locate
Return Value:
Pointer to the context
--*/
{
PNL_AUTH_CONTEXT TestContext;
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
for ( TestContext = ContextList;
TestContext != NULL;
TestContext = TestContext->Next ) {
if ( TestContext->ContextHandle.dwUpper == ContextHandle->dwUpper &&
TestContext->ContextHandle.dwLower == ContextHandle->dwLower ) {
break;
}
}
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return(TestContext);
}
BOOLEAN
DeleteContext(
PCtxtHandle ContextHandle
)
/*++
Routine Description:
Delete a context given its handle
Arguments:
ContextHandle - Handle to the context to locate
Return Value:
TRUE: Context existed
--*/
{
PNL_AUTH_CONTEXT TestContext, LastContext;
//
// Find the context.
//
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
LastContext = NULL;
for ( TestContext = ContextList;
TestContext != NULL ;
TestContext = TestContext->Next ) {
if ( TestContext->ContextHandle.dwUpper == ContextHandle->dwUpper &&
TestContext->ContextHandle.dwLower == ContextHandle->dwLower ) {
break;
}
LastContext = TestContext;
}
//
// If we found it,
// and it is no longer needed as a context or a credential,
// delink it and delete it.
//
if ( TestContext != NULL ) {
if (LastContext != NULL) {
LastContext->Next = TestContext->Next;
} else {
NlAssert(ContextList == TestContext);
ContextList = TestContext->Next;
}
NlPrint(( NL_SESSION_MORE,
"DeleteContext: %lx.%lx: context freed\n",
ContextHandle->dwUpper, ContextHandle->dwLower ));
LocalFree(TestContext);
} else {
NlPrint(( NL_SESSION_MORE,
"DeleteContext: %lx.%lx: context handle not found\n",
ContextHandle->dwUpper, ContextHandle->dwLower ));
}
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return( TestContext == NULL ? FALSE : TRUE );
}
PNL_AUTH_CONTEXT
AllocateContext(
IN ULONG fContextReq
)
/*++
Routine Description:
Allocate and initialize a context (Client or server side)
Arguments:
fContextReq - Context request flags
Return Value:
Allocated context. Delete this context by DeleteContext( Context->ContextHandle, FALSE );
NULL if context cannot be allocated.
--*/
{
PNL_AUTH_CONTEXT Context;
//
// Allocate a context block.
//
Context = (PNL_AUTH_CONTEXT) LocalAlloc( LMEM_ZEROINIT, sizeof(NL_AUTH_CONTEXT) );
if (Context == NULL) {
return NULL;
}
//
// Initialize the context.
//
Context->State = Idle;
Context->ContextFlags = fContextReq;
RtlEnterCriticalSection(&NlGlobalSecPkgCritSect);
NextId.QuadPart ++;
Context->ContextHandle.dwUpper = NextId.HighPart;
Context->ContextHandle.dwLower = NextId.LowPart;
Context->Next = ContextList;
ContextList = Context;
RtlLeaveCriticalSection(&NlGlobalSecPkgCritSect);
return Context;
}
PSecBuffer
LocateBuffer(PSecBufferDesc Buffer, ULONG MinimumSize)
/*++
Routine Description:
Arguments:
Standard.
Return Value:
--*/
{
ULONG Index;
if (Buffer == NULL) {
return(NULL);
}
for (Index = 0; Index < Buffer->cBuffers ; Index++) {
if ( BUFFERTYPE(Buffer->pBuffers[Index]) == SECBUFFER_TOKEN) {
//
// Do size checking
//
if (Buffer->pBuffers[Index].cbBuffer < MinimumSize) {
return(NULL);
}
return(&Buffer->pBuffers[Index]);
}
}
return(NULL);
}
PSecBuffer
LocateSecBuffer(PSecBufferDesc Buffer)
/*++
Routine Description:
Locate a buffer suitable for authentication
Arguments:
Standard.
Return Value:
--*/
{
return(LocateBuffer(Buffer, sizeof(NL_AUTH_MESSAGE)));
}
PSecBuffer
LocateSigBuffer(PSecBufferDesc Buffer)
/*++
Routine Description:
Locate a buffer suitable for a signature
Arguments:
Standard.
Return Value:
--*/
{
return(LocateBuffer(Buffer, PACKAGE_SIGNATURE_SIZE - NL_AUTH_CONFOUNDER_SIZE ));
}
PSecurityFunctionTableW SEC_ENTRY
InitSecurityInterfaceW(VOID)
/*++
Routine Description:
Initialization routine called by RPC to get pointers to all other routines.
Arguments:
None.
Return Value:
Pointer to function table.
--*/
{
NlPrint(( NL_SESSION_MORE,
"InitSecurityInterfaceW: called\n" ));
return(&SecTableW);
}
SECURITY_STATUS SEC_ENTRY
AcquireCredentialsHandleW(
LPWSTR pszPrincipal, // Name of principal
LPWSTR pszPackageName, // 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)
)
/*++
Routine Description:
Client and Server side routine to grab a credential handle.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
PNL_AUTH_CREDENTIAL Credential = NULL;
NlPrint(( NL_SESSION_MORE,
"AcquireCredentialsHandleW: called\n" ));
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
//
// Validate the input parameters
//
if ((fCredentialUse & (SECPKG_CRED_BOTH)) == 0) {
NlPrint(( NL_CRITICAL,
"AcquireCredentialHandle: Bad Credential Use 0x%lx.\n", fCredentialUse ));
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
if ((fCredentialUse & (SECPKG_CRED_BOTH)) == SECPKG_CRED_BOTH) {
NlPrint(( NL_CRITICAL,
"AcquireCredentialHandle: Bad Credential Use 0x%lx.\n", fCredentialUse ));
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
//
// Handle a client credential
//
if ((fCredentialUse & (SECPKG_CRED_BOTH)) == SECPKG_CRED_OUTBOUND) {
//
// Sanity check the client session
//
if ( pAuthData == NULL ) {
NlPrint(( NL_CRITICAL,
"AcquireCredentialHandle: NULL auth data\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( ((PNL_AUTH_DATA)pAuthData)->Signature != NL_AUTH_DATA_SIGNATURE ) {
NlPrint(( NL_CRITICAL,
"AcquireCredentialHandle: Invalid Signature on auth data\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Allocate a credential to remember the AuthData (ClientSession) in.
//
Credential = AllocateCredential( (PNL_AUTH_DATA)pAuthData );
if (Credential == NULL) {
NlPrint(( NL_CRITICAL,
"AcquireCredentialHandle: Cannot allocate context\n" ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Return to the caller.
//
*phCredential = Credential->CredentialHandle;
*ptsExpiry = Forever;
SecStatus = SEC_E_OK;
//
// Handle a server credential
//
// We don't need a credential on the server side.
// Silently succeed.
//
} else {
phCredential->dwUpper = NL_AUTH_SERVER_CRED; // Just return a constant
phCredential->dwLower = 0;
*ptsExpiry = Forever;
SecStatus = SEC_E_OK;
goto Cleanup;
}
Cleanup:
NlPrint(( NL_SESSION_MORE,
"AcquireCredentialsHandleW: %lx.%lx: returns 0x%lx\n",
phCredential->dwUpper, phCredential->dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
UNREFERENCED_PARAMETER( pvGetKeyArgument );
UNREFERENCED_PARAMETER( pGetKeyFn );
UNREFERENCED_PARAMETER( pAuthData );
UNREFERENCED_PARAMETER( pvLogonId );
UNREFERENCED_PARAMETER( pszPackageName );
UNREFERENCED_PARAMETER( pszPrincipal );
}
SECURITY_STATUS SEC_ENTRY
FreeCredentialsHandle(
PCredHandle phCredential // Handle to free
)
/*++
Routine Description:
Arguments:
Standard.
Return Value:
--*/
{
NlPrint(( NL_SESSION_MORE,
"FreeCredentialsHandle: %lx.%lx: called\n",
phCredential->dwUpper, phCredential->dwLower ));
//
// Don't require that Netlogon be running. Some credential handles are
// deleted as Netlogon is shutting down.
//
//
// Ignore server side credentials.
//
if ( phCredential->dwUpper == NL_AUTH_SERVER_CRED &&
phCredential->dwLower == 0 ) {
return(SEC_E_OK);
}
//
// For the Client side,
// Delete the credential.
//
if ( DeleteCredential( phCredential ) ) {
return(SEC_E_OK);
} else {
return(SEC_E_UNKNOWN_CREDENTIALS);
}
}
SECURITY_STATUS SEC_ENTRY
InitializeSecurityContextW(
PCredHandle phCredential, // Cred to base context
PCtxtHandle phContext, // Existing context (OPT)
LPWSTR pszTargetName, // 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)
)
/*++
Routine Description:
Client side routine to define a security context.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
NET_API_STATUS NetStatus;
PNL_AUTH_CONTEXT Context = NULL;
PNL_AUTH_CREDENTIAL Credential = NULL;
NL_AUTH_MESSAGE UNALIGNED *Message = NULL;
PSecBuffer OutputBuffer;
PSecBuffer InputBuffer;
WORD CompressOffset[10];
CHAR *CompressUtf8String[10];
ULONG CompressCount = 0;
ULONG Utf8StringSize;
LPBYTE Where;
NlPrint(( NL_SESSION_MORE,
"InitializeSecurityContext: %ws: called\n", pszTargetName ));
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
OutputBuffer = LocateSecBuffer(pOutput);
if (OutputBuffer == NULL) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Handle the first init call,
//
if (phContext == NULL) {
PNL_AUTH_DATA ClientAuthData;
//
// Find the credential and ensure it is outbound
//
if ( phCredential == NULL ) {
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
NlPrint(( NL_SESSION_MORE,
"InitializeSecurityContext: %lx.%lx: %ws: called with cred handle\n",
phCredential->dwUpper, phCredential->dwLower,
pszTargetName ));
//
// Locate the credential and make sure this is a client side call.
//
Credential = LocateCredential( phCredential );
if (Credential == NULL) {
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
ClientAuthData = Credential->ClientAuthData;
if ( ClientAuthData == NULL ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
NlAssert( ClientAuthData->Signature == NL_AUTH_DATA_SIGNATURE );
//
// Build an output token.
//
// This token simply tells the server side of the authentication
// package our computer name.
//
Message = (PNL_AUTH_MESSAGE) OutputBuffer->pvBuffer;
SmbPutUlong( &Message->MessageType, Negotiate );
Message->Flags = 0;
Where = &Message->Buffer[0];
//
// Copy the Netbios domain name of the domain hosted by the DC into the buffer
//
// OEM on the wire is bad. Luckily, if the DC is in a different locale,
// that DC also has a DNS domain name and we'll use that to find the
// hosted domain.
//
if ( ClientAuthData->OemNetbiosDomainNameLength != 0 ) {
strcpy( Where,
((LPBYTE)ClientAuthData) +
ClientAuthData->OemNetbiosDomainNameOffset );
Where += ClientAuthData->OemNetbiosDomainNameLength + 1;
Message->Flags |= NL_AUTH_NETBIOS_DOMAIN_NAME;
}
//
// Copy the computer name of this machine into the buffer.
//
// ???: Only copy the Netbios computername or DNS host name. Copy the
// one that was passed to the server on the NetServerReqChallenge.
//
// OEM on the wire is bad. So pass the UTF-8 version, too.
//
if ( ClientAuthData->OemComputerNameLength != 0 ) {
strcpy( Where,
((LPBYTE)ClientAuthData) +
ClientAuthData->OemComputerNameOffset );
Where += ClientAuthData->OemComputerNameLength + 1;
Message->Flags |= NL_AUTH_NETBIOS_COMPUTER_NAME;
}
//
// Copy the DNS domain name of the domain hosted by the DC into the buffer.
//
Utf8StringSize = 2*(NL_MAX_DNS_LENGTH+1);
CompressCount = 0; // No strings compressed yet.
if ( ClientAuthData->Utf8DnsDomainNameOffset != 0 ) {
NetStatus = NlpUtf8ToCutf8(
(LPBYTE)Message,
((LPBYTE)ClientAuthData) +
ClientAuthData->Utf8DnsDomainNameOffset,
FALSE,
&Where,
&Utf8StringSize,
&CompressCount,
CompressOffset,
CompressUtf8String );
if ( NetStatus != NO_ERROR ) {
NlPrint((NL_CRITICAL,
"Cannot pack DomainName into message %ld\n",
NetStatus ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Message->Flags |= NL_AUTH_DNS_DOMAIN_NAME;
}
//
// Copy the DNS host name name of this machine into the buffer.
//
// ???: Only copy the Netbios computername or DNS host name. Copy the
// one that was passed to the server on the NetServerReqChallenge.
//
if ( ClientAuthData->Utf8DnsHostNameOffset != 0 ) {
NetStatus = NlpUtf8ToCutf8(
(LPBYTE)Message,
((LPBYTE)ClientAuthData) +
ClientAuthData->Utf8DnsHostNameOffset,
FALSE,
&Where,
&Utf8StringSize,
&CompressCount,
CompressOffset,
CompressUtf8String );
if ( NetStatus != NO_ERROR ) {
NlPrint((NL_CRITICAL,
"Cannot pack dns host name into message %ld\n",
NetStatus ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Message->Flags |= NL_AUTH_DNS_HOST_NAME;
}
//
// Copy the UTF-8 netbios computer name of this machine into the buffer.
//
// ???: Only copy the Netbios computername or DNS host name. Copy the
// one that was passed to the server on the NetServerReqChallenge.
//
// OEM on the wire is bad. So pass the UTF-8 version, too.
//
if ( ClientAuthData->Utf8ComputerNameLength != 0 ) {
NetStatus = NlpUtf8ToCutf8(
(LPBYTE)Message,
((LPBYTE)ClientAuthData) +
ClientAuthData->Utf8ComputerNameOffset,
TRUE,
&Where,
&Utf8StringSize,
&CompressCount,
CompressOffset,
CompressUtf8String );
if ( NetStatus != NO_ERROR ) {
NlPrint((NL_CRITICAL,
"Cannot pack UTF-8 netbios computer name into message %ld\n",
NetStatus ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Message->Flags |= NL_AUTH_UTF8_NETBIOS_COMPUTER_NAME;
}
//
// Allocate a context.
//
Context = AllocateContext( fContextReq );
if ( Context == NULL) {
NlPrint(( NL_CRITICAL,
"InitializeSecurityContext: Cannot allocate context\n" ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Mark the context to indicate what state we're in.
//
Context->State = FirstInit;
Context->Inbound = FALSE;
//
// Grab the session key
//
Context->SessionInfo = ClientAuthData->SessionInfo;
//
// Ask the caller to call us back
//
OutputBuffer->cbBuffer = (DWORD)(Where - (LPBYTE)Message);
*phNewContext = Context->ContextHandle;
*pfContextAttr = fContextReq;
*ptsExpiry = Forever;
SecStatus = SEC_I_CONTINUE_NEEDED;
//
// Handle the second call
//
} else {
NlPrint(( NL_SESSION_MORE,
"InitializeSecurityContext: %lx.%lx: %ws: called with context handle\n",
phContext->dwUpper, phContext->dwLower,
pszTargetName ));
//
// This is the second call. Lookup the old context.
//
// Locate the context and make sure this is a client side call.
//
Context = LocateContext( phContext );
if (Context == NULL) {
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Ensure we're in the right state.
//
if ( Context->State != FirstInit ) {
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Check that the input message is what we expected.
//
InputBuffer = LocateSecBuffer(pInput);
if (InputBuffer == NULL) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Message = (PNL_AUTH_MESSAGE) InputBuffer->pvBuffer;
if ( InputBuffer->cbBuffer < sizeof(NL_AUTH_MESSAGE) ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( Message->MessageType != NegotiateResponse ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Context->State = SecondInit;
Context->Nonce.QuadPart = 0;
//
// Return to the caller.
//
OutputBuffer->cbBuffer = 0;
*pfContextAttr = fContextReq;
*ptsExpiry = Forever;
SecStatus = SEC_E_OK;
}
Cleanup:
NlPrint(( NL_SESSION_MORE,
"InitializeSecurityContext: returns 0x%lx\n", SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
UNREFERENCED_PARAMETER( Reserved2 );
UNREFERENCED_PARAMETER( TargetDataRep );
UNREFERENCED_PARAMETER( Reserved1 );
UNREFERENCED_PARAMETER( pszTargetName );
UNREFERENCED_PARAMETER( pInput );
}
SECURITY_STATUS SEC_ENTRY
AcceptSecurityContext(
PCredHandle phCredential, // Cred to base context
PCtxtHandle phContext, // Existing context (OPT)
PSecBufferDesc pInput, // Input buffer
unsigned long fContextReq, // Context Requirements
unsigned long TargetDataRep, // Target Data Rep
PCtxtHandle phNewContext, // (out) New context handle
PSecBufferDesc pOutput, // (inout) Output buffers
unsigned long SEC_FAR * pfContextAttr, // (out) Context attributes
PTimeStamp ptsExpiry // (out) Life span (OPT)
)
/*++
Routine Description:
Servert side routine to define a security context.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
PNL_AUTH_CONTEXT Context = NULL;
NL_AUTH_MESSAGE UNALIGNED *Message = NULL;
PSecBuffer OutputBuffer = NULL;
PSecBuffer InputBuffer;
LPBYTE Where;
LPSTR DnsDomainName = NULL;
LPSTR DnsHostName = NULL;
LPSTR Utf8ComputerName = NULL;
LPSTR OemDomainName = NULL;
LPWSTR UnicodeDomainName = NULL;
LPSTR OemComputerName = NULL;
LPWSTR UnicodeComputerName = NULL;
PDOMAIN_INFO DomainInfo = NULL;
PSERVER_SESSION ServerSession;
SESSION_INFO SessionInfo;
SecHandle CurrentHandle = {0};
NlPrint(( NL_SESSION_MORE,
"AcceptSecurityContext: called\n" ));
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
InputBuffer = LocateSecBuffer(pInput);
if (InputBuffer == NULL) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Make sure the output buffer exists.
//
OutputBuffer = LocateSecBuffer(pOutput);
if (OutputBuffer == NULL) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Handle the first server side call.
//
if (phContext == NULL) {
//
// Validate the credential handle.
//
if ( phCredential == NULL ||
phCredential->dwUpper != NL_AUTH_SERVER_CRED ||
phCredential->dwLower != 0 ) {
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
goto Cleanup;
}
CurrentHandle = *phCredential;
//
// Check that the input message is what we expected.
//
Message = (PNL_AUTH_MESSAGE) InputBuffer->pvBuffer;
if ( InputBuffer->cbBuffer < sizeof(NL_AUTH_MESSAGE) ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( Message->MessageType != Negotiate ) {
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Where = &Message->Buffer[0];
//
// Get Netbios hosted domain name from the buffer
//
if ( Message->Flags & NL_AUTH_NETBIOS_DOMAIN_NAME ) {
if ( !NetpLogonGetOemString(
Message,
InputBuffer->cbBuffer,
&Where,
DNLEN+1,
&OemDomainName ) ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: cannot get netbios domain name\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Get the Netbios client computer name from the message.
//
if ( Message->Flags & NL_AUTH_NETBIOS_COMPUTER_NAME ) {
//
// Get the computer name of the
if ( !NetpLogonGetOemString(
Message,
InputBuffer->cbBuffer,
&Where,
CNLEN+1,
&OemComputerName ) ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot parse computer name\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Get the domain name of the hosted domain from the message.
//
// Either get a utf-8 DNS domain name or an OEM netbios domain name
//
if ( Message->Flags & NL_AUTH_DNS_DOMAIN_NAME ) {
if ( !NetpLogonGetCutf8String(
Message,
InputBuffer->cbBuffer,
&Where,
&DnsDomainName ) ) {
NlPrint(( NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: DNS domain bad.\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Get the DNS client computer name from the message.
//
//
if ( Message->Flags & NL_AUTH_DNS_HOST_NAME ) {
if ( !NetpLogonGetCutf8String(
Message,
InputBuffer->cbBuffer,
&Where,
&DnsHostName ) ) {
NlPrint(( NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: DNS hostname bad.\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower
));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Ensure Netbios name isn't also present
//
if ( Message->Flags & NL_AUTH_NETBIOS_COMPUTER_NAME ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: both DNS '%s' and Netbios '%s' client name specified\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
DnsHostName,
OemComputerName ));
/* This isn't fatal */
}
}
//
// Get the UTF8 netbios computer name
//
if ( Message->Flags & NL_AUTH_UTF8_NETBIOS_COMPUTER_NAME ) {
if ( !NetpLogonGetCutf8String(
Message,
InputBuffer->cbBuffer,
&Where,
&Utf8ComputerName ) ) {
NlPrint(( NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: UTF8 computer name bad.\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower
));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Try to find the hosted domain using DNS
//
if ( DnsDomainName != NULL ) {
DomainInfo = NlFindDnsDomain( DnsDomainName,
NULL,
FALSE, // don't lookup NDNCs
TRUE, // check alias names
NULL ); // don't care if alias name matched
if ( DomainInfo == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot find domain %s\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
DnsDomainName ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Try to find hosted domain name using netbios.
//
} else {
//
// Make sure we were passed one.
//
if ( OemDomainName == NULL) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Neither DNS or netbios domain name specified (fatal)\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Convert to unicode
// Note: this is bogus since the clients OEM code page may be different
// than ours.
//
UnicodeDomainName = NetpAllocWStrFromStr( OemDomainName );
if ( UnicodeDomainName == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot alloc domain name %s\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
OemDomainName ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Look the name up.
//
DomainInfo = NlFindNetbiosDomain( UnicodeDomainName, FALSE );
if ( DomainInfo == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot find domain %ws (fatal)\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
UnicodeDomainName ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
}
//
// Get the name of the client machine.
//
// If the client computer passed us its DnsHostName,
// use that.
//
if ( DnsHostName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromUtf8Str( DnsHostName );
if ( UnicodeComputerName == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot alloc DNS computer name %s\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
DnsHostName ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// If the client computer passed us its Netbios name in UTF-8,
// use that.
//
} else if ( Utf8ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromUtf8Str( Utf8ComputerName );
if ( UnicodeComputerName == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot alloc utf8 computer name %s\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
Utf8ComputerName ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// If the client computer passed us its Netbios name in OEM,
// use that.
// OEM is bad since the clients code page might be different than ours.
//
} else if ( OemComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromStr( OemComputerName );
if ( UnicodeComputerName == NULL ) {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Cannot alloc oem computer name %s\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
OemComputerName ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// At this point it is fatal if we don't know the client computer name
//
} else {
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Don't know client computer name.\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Find the server session containing the session key
// and make a copy of it.
//
NlPrint((NL_SESSION_MORE,
"AcceptSecurityContext: %lx.%lx: from %ws\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
UnicodeComputerName ));
LOCK_SERVER_SESSION_TABLE( DomainInfo );
ServerSession = NlFindNamedServerSession( DomainInfo, UnicodeComputerName );
if (ServerSession == NULL) {
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: %lx.%lx: Can't NlFindNamedServerSession for %ws\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
UnicodeComputerName ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
SessionInfo.SessionKey = ServerSession->SsSessionKey;
SessionInfo.NegotiatedFlags = ServerSession->SsNegotiatedFlags;
//
// Indicate that this server session is being used
// to keep it alive.
//
ServerSession->SsCheck = 0;
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
//
// Build a new context.
//
Context = AllocateContext( fContextReq );
if (Context == NULL) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
Context->State = FirstAccept;
Context->Inbound = TRUE;
Context->SessionInfo = SessionInfo;
//
// Build an output token.
//
Message = (PNL_AUTH_MESSAGE) OutputBuffer->pvBuffer;
Message->MessageType = NegotiateResponse;
Message->Flags = 0;
Message->Buffer[0] = '\0';
OutputBuffer->cbBuffer = sizeof(NL_AUTH_MESSAGE);
//
// Tell the caller he need not call us back.
//
*phNewContext = Context->ContextHandle;
CurrentHandle = *phNewContext;
*pfContextAttr = fContextReq;
*ptsExpiry = Forever;
SecStatus = SEC_E_OK;
//
// We asked the caller to not call us back.
//
} else {
NlAssert( FALSE );
NlPrint((NL_CRITICAL,
"AcceptSecurityContext: Second accept called.\n" ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
Cleanup:
if ( DnsDomainName != NULL ) {
NetpMemoryFree( DnsDomainName );
}
if ( DnsHostName != NULL ) {
NetpMemoryFree( DnsHostName );
}
if ( Utf8ComputerName != NULL ) {
NetpMemoryFree( Utf8ComputerName );
}
if ( UnicodeComputerName != NULL ) {
NetApiBufferFree( UnicodeComputerName );
}
if ( UnicodeDomainName != NULL ) {
NetApiBufferFree( UnicodeDomainName );
}
if ( DomainInfo != NULL ) {
NlDereferenceDomain( DomainInfo );
}
NlPrint(( NL_SESSION_MORE,
"AcceptSecurityContext: %lx.%lx: returns 0x%lx\n",
CurrentHandle.dwUpper, CurrentHandle.dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
UNREFERENCED_PARAMETER( TargetDataRep );
UNREFERENCED_PARAMETER( pOutput );
}
SECURITY_STATUS SEC_ENTRY
DeleteSecurityContext(
PCtxtHandle phContext // Context to delete
)
/*++
Routine Description:
Routine to delete client or server side security context.
Arguments:
Standard.
Return Value:
--*/
{
NlPrint(( NL_SESSION_MORE,
"DeleteSecurityContext: %lx.%lx: called\n",
phContext->dwUpper, phContext->dwLower ));
//
// Don't require that Netlogon be running. Some security contexts are
// deleted as Netlogon is shutting down.
//
if ( DeleteContext( phContext )) {
return(SEC_E_OK);
} else {
return(SEC_E_INVALID_HANDLE);
}
}
SECURITY_STATUS SEC_ENTRY
EnumerateSecurityPackagesW(
unsigned long SEC_FAR * pcPackages, // Receives num. packages
PSecPkgInfoW SEC_FAR * ppPackageInfo // Receives array of info
)
/*++
Routine Description:
Routine to return a description of all the security packages implemented
by this DLL.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
SecStatus = QuerySecurityPackageInfoW(
PACKAGE_NAME,
ppPackageInfo
);
if (SecStatus == SEC_E_OK) {
*pcPackages = 1;
}
return(SecStatus);
}
SECURITY_STATUS SEC_ENTRY
QuerySecurityPackageInfoW(
LPWSTR pszPackageName, // Name of package
PSecPkgInfoW SEC_FAR * ppPackageInfo // Receives package info
)
/*++
Routine Description:
Routine to return the description of the named security package.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
PSecPkgInfoW PackageInfo;
ULONG PackageInfoSize;
PUCHAR Where;
if (_wcsicmp(pszPackageName, PACKAGE_NAME)) {
SecStatus = SEC_E_SECPKG_NOT_FOUND;
goto Cleanup;
}
PackageInfoSize = sizeof(SecPkgInfoW) +
(wcslen(PACKAGE_NAME) + 1 +
wcslen(PACKAGE_COMMENT) + 1) * sizeof(WCHAR);
PackageInfo = (PSecPkgInfoW) LocalAlloc(0,PackageInfoSize);
if (PackageInfo == NULL) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
PackageInfo->fCapabilities = PACAKGE_CAPABILITIES;
PackageInfo->wVersion = PACKAGE_VERSION;
PackageInfo->wRPCID = PACKAGE_RPCID;
PackageInfo->cbMaxToken = PACKAGE_MAXTOKEN;
Where = (PUCHAR) (PackageInfo + 1);
PackageInfo->Name = (LPWSTR) Where;
Where += (wcslen(PACKAGE_NAME) + 1) * sizeof(WCHAR);
wcscpy(PackageInfo->Name, PACKAGE_NAME);
PackageInfo->Comment = (LPWSTR) Where;
Where += (wcslen(PACKAGE_COMMENT) + 1) * sizeof(WCHAR);
wcscpy(PackageInfo->Comment, PACKAGE_COMMENT);
NlAssert((Where - (PBYTE) PackageInfo) == (LONG) PackageInfoSize);
*ppPackageInfo = PackageInfo;
SecStatus = SEC_E_OK;
Cleanup:
NlPrint(( NL_SESSION_MORE,
"QuerySecurityPackageInfo: returns 0x%lx\n", SecStatus ));
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
FreeContextBuffer(
void SEC_FAR * pvContextBuffer
)
/*++
Routine Description:
Routine to free a context buffer.
Arguments:
Standard.
Return Value:
--*/
{
LocalFree(pvContextBuffer);
return(SEC_E_OK);
}
SECURITY_STATUS SEC_ENTRY
ImpersonateSecurityContext(
PCtxtHandle phContext // Context to impersonate
)
/*++
Routine Description:
Server side routine to impersonate an authenticated user.
Arguments:
Standard.
Return Value:
--*/
{
NTSTATUS Status;
Status = RtlImpersonateSelf(SecurityImpersonation);
if (NT_SUCCESS(Status)) {
return(SEC_E_OK);
} else {
return(SEC_E_NO_IMPERSONATION);
}
UNREFERENCED_PARAMETER( phContext );
}
SECURITY_STATUS SEC_ENTRY
RevertSecurityContext(
PCtxtHandle phContext // Context from which to re
)
/*++
Routine Description:
Server side routine to undo a previous imperonation.
Arguments:
Standard.
Return Value:
--*/
{
RevertToSelf();
return(SEC_E_OK);
UNREFERENCED_PARAMETER( phContext );
}
SECURITY_STATUS SEC_ENTRY
QueryContextAttributesW(
PCtxtHandle phContext, // Context to query
unsigned long ulAttribute, // Attribute to query
void SEC_FAR * pBuffer // Buffer for attributes
)
/*++
Routine Description:
Routine to return information about a context.
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
PNL_AUTH_CONTEXT Context;
PSecPkgContext_Sizes ContextSizes;
PSecPkgContext_NamesW ContextNames;
PSecPkgContext_Lifespan ContextLifespan;
PSecPkgContext_DceInfo ContextDceInfo;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
//
// Locate the context and make sure this is a client side call.
//
Context = LocateContext( phContext );
if (Context == NULL) {
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
//
//
switch(ulAttribute) {
case SECPKG_ATTR_SIZES:
ContextSizes = (PSecPkgContext_Sizes) pBuffer;
ContextSizes->cbMaxSignature = PACKAGE_SIGNATURE_SIZE;
if ((Context->ContextFlags & ISC_REQ_CONFIDENTIALITY) != 0) {
ContextSizes->cbSecurityTrailer = PACKAGE_SIGNATURE_SIZE;
ContextSizes->cbBlockSize = 1;
} else {
ContextSizes->cbSecurityTrailer = 0;
ContextSizes->cbBlockSize = 0;
}
ContextSizes->cbMaxToken = PACKAGE_MAXTOKEN;
break;
#ifdef notdef // Only support the ones RPC uses
case SECPKG_ATTR_NAMES:
ContextNames = (PSecPkgContext_Names) pBuffer;
ContextNames->sUserName = (LPWSTR) LocalAlloc(0,sizeof(L"dummy user"));
if (ContextNames->sUserName == NULL) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
wcscpy(ContextNames->sUserName, L"dummy user");
break;
case SECPKG_ATTR_LIFESPAN:
ContextLifespan = (PSecPkgContext_Lifespan) pBuffer;
ContextLifespan->tsStart = Never;
ContextLifespan->tsExpiry = Forever;
break;
case SECPKG_ATTR_DCE_INFO:
ContextDceInfo = (PSecPkgContext_DceInfo) pBuffer;
ContextDceInfo->AuthzSvc = 0;
ContextDceInfo->pPac = (PVOID) LocalAlloc(0,sizeof(L"dummy user"));
if (ContextDceInfo->pPac == NULL) {
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
wcscpy((LPWSTR) ContextDceInfo->pPac, L"dummy user");
break;
#endif // notdef // Only support the ones RPC uses
default:
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
goto Cleanup;
}
SecStatus = SEC_E_OK;
Cleanup:
NlPrint(( NL_SESSION_MORE,
"QueryContextAttributes: %lx.%lx: %ld returns 0x%lx\n",
phContext->dwUpper, phContext->dwLower,
ulAttribute,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
UNREFERENCED_PARAMETER( pBuffer );
}
SECURITY_STATUS
KerbMapNtStatusToSecStatus(
IN NTSTATUS Status
)
{
SECURITY_STATUS SecStatus;
//
// Check for security status and let them through
//
if (HRESULT_FACILITY(Status) == FACILITY_SECURITY )
{
return(Status);
}
switch(Status) {
case STATUS_SUCCESS:
SecStatus = SEC_E_OK;
break;
case STATUS_INSUFFICIENT_RESOURCES:
case STATUS_NO_MEMORY:
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
break;
case STATUS_NETLOGON_NOT_STARTED:
case STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
case STATUS_NO_LOGON_SERVERS:
case STATUS_NO_SUCH_DOMAIN:
SecStatus = SEC_E_NO_AUTHENTICATING_AUTHORITY;
break;
case STATUS_NO_SUCH_LOGON_SESSION:
SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
break;
case STATUS_INVALID_PARAMETER:
SecStatus = SEC_E_INVALID_TOKEN;
break;
case STATUS_PRIVILEGE_NOT_HELD:
SecStatus = SEC_E_NOT_OWNER;
break;
case STATUS_INVALID_HANDLE:
SecStatus = SEC_E_INVALID_HANDLE;
break;
case STATUS_BUFFER_TOO_SMALL:
// ???: there should be a better code
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
break;
case STATUS_NOT_SUPPORTED:
SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
break;
case STATUS_OBJECT_NAME_NOT_FOUND:
SecStatus = SEC_E_TARGET_UNKNOWN;
break;
case STATUS_LOGON_FAILURE:
case STATUS_NO_SUCH_USER:
case STATUS_ACCOUNT_DISABLED:
case STATUS_ACCOUNT_RESTRICTION:
case STATUS_ACCOUNT_LOCKED_OUT:
case STATUS_WRONG_PASSWORD:
case STATUS_ACCOUNT_EXPIRED:
case STATUS_PASSWORD_EXPIRED:
SecStatus = SEC_E_LOGON_DENIED;
break;
case STATUS_NO_TRUST_SAM_ACCOUNT:
SecStatus = SEC_E_TARGET_UNKNOWN;
break;
case STATUS_BAD_NETWORK_PATH:
case STATUS_TRUST_FAILURE:
case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
// ???: what should this be?
SecStatus = SEC_E_NO_AUTHENTICATING_AUTHORITY;
break;
case STATUS_NAME_TOO_LONG:
case STATUS_ILL_FORMED_PASSWORD:
// ???: what should this be?
SecStatus = SEC_E_INVALID_TOKEN;
break;
case STATUS_INTERNAL_ERROR:
SecStatus = SEC_E_INTERNAL_ERROR;
break;
default:
NlPrint(( NL_CRITICAL, "\n\n\n Unable to map error code 0x%x\n\n\n\n",Status));
SecStatus = SEC_E_INTERNAL_ERROR;
}
return(SecStatus);
}
SECURITY_STATUS
NlpSignOrSeal(
IN PCtxtHandle phContext,
IN ULONG fQOP,
IN OUT PSecBufferDesc MessageBuffers,
IN ULONG MessageSeqNo,
IN BOOLEAN SealIt
)
/*++
Routine Description:
Common routine to sign or seal a message (Client or server side)
Arguments:
Standard.
SealIt - True to seal the message.
FALSE to sign the message.
Return Value:
--*/
{
SECURITY_STATUS SecStatus = SEC_E_OK;
NTSTATUS Status = STATUS_SUCCESS;
PCHECKSUM_FUNCTION Check;
PCRYPTO_SYSTEM CryptSystem;
PCHECKSUM_BUFFER CheckBuffer = NULL;
PCRYPT_STATE_BUFFER CryptBuffer = NULL;
ULONG Index;
PSecBuffer SignatureBuffer = NULL;
PNL_AUTH_SIGNATURE Signature;
UCHAR LocalChecksum[24]; // ??? need better constant
NETLOGON_SESSION_KEY EncryptionSessionKey;
ULONG OutputSize;
PNL_AUTH_CONTEXT Context;
BOOLEAN ChecksumOnly;
//
// Locate the context.
//
Context = LocateContext( phContext );
if (Context == NULL) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Cannot LocateContext\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Find a buffer to put the signature in.
//
SignatureBuffer = LocateSigBuffer( MessageBuffers );
if (SignatureBuffer == NULL) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: No signature buffer found\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if ( SealIt ) {
if ( SignatureBuffer->cbBuffer < sizeof(PACKAGE_SIGNATURE_SIZE) ) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: buffer too small for sealing\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
}
//
// Build the signature.
Signature = (PNL_AUTH_SIGNATURE) SignatureBuffer->pvBuffer;
Signature->SignatureAlgorithm[0] = (UCHAR) NL_AUTH_CHECKSUM;
Signature->SignatureAlgorithm[1] = 0;
if ( SealIt ) {
Signature->SealAlgorithm[0] = (UCHAR) NL_AUTH_ETYPE;
Signature->SealAlgorithm[1] = 0;
memset(Signature->SealFiller, 0xff, sizeof(Signature->SealFiller));
NlGenerateRandomBits( Signature->Confounder, NL_AUTH_CONFOUNDER_SIZE );
SignatureBuffer->cbBuffer = PACKAGE_SIGNATURE_SIZE;
} else {
memset(Signature->SignFiller, 0xff, sizeof(Signature->SignFiller));
SignatureBuffer->cbBuffer = PACKAGE_SIGNATURE_SIZE - NL_AUTH_CONFOUNDER_SIZE;
}
Signature->Flags[0] = 0;
Signature->Flags[1] = 0;
Signature->SequenceNumber[0] = (UCHAR) ((Context->Nonce.LowPart & 0xff000000) >> 24);
Signature->SequenceNumber[1] = (UCHAR) ((Context->Nonce.LowPart & 0x00ff0000) >> 16);
Signature->SequenceNumber[2] = (UCHAR) ((Context->Nonce.LowPart & 0x0000ff00) >> 8);
Signature->SequenceNumber[3] = (UCHAR) (Context->Nonce.LowPart & 0x000000ff);
Signature->SequenceNumber[4] = (UCHAR) ((Context->Nonce.HighPart & 0xff000000) >> 24);
Signature->SequenceNumber[5] = (UCHAR) ((Context->Nonce.HighPart & 0x00ff0000) >> 16);
Signature->SequenceNumber[6] = (UCHAR) ((Context->Nonce.HighPart & 0x0000ff00) >> 8);
Signature->SequenceNumber[7] = (UCHAR) (Context->Nonce.HighPart & 0x000000ff);
if ( !Context->Inbound ) {
Signature->SequenceNumber[4] |= 0x80; // Discriminate between inbound and outbound messages
}
Context->Nonce.QuadPart ++;
//
// Initialize the checksum routines.
//
Status = CDLocateCheckSum( (ULONG)NL_AUTH_CHECKSUM, &Check);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to load checksum routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert(Check->CheckSumSize <= sizeof(LocalChecksum));
NlPrint(( NL_ENCRYPT,
"NlpSignOrSeal: %lx.%lx: Session Key: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, &Context->SessionInfo.SessionKey, sizeof( Context->SessionInfo.SessionKey) );
Status = Check->InitializeEx(
(LPBYTE)&Context->SessionInfo.SessionKey,
sizeof( Context->SessionInfo.SessionKey),
0, // no message type
&CheckBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to initialize checksum routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Locate the encryption routines.
//
Status = CDLocateCSystem( (ULONG)NL_AUTH_ETYPE, &CryptSystem);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to load crypt system: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Sum first several bytes of the signature
//
Check->Sum( CheckBuffer,
NL_AUTH_SIGNED_BYTES,
(PUCHAR)Signature );
//
// Sum and encrypt the confounder
//
if ( SealIt ) {
//
// Sum the confounder
//
Check->Sum(
CheckBuffer,
NL_AUTH_CONFOUNDER_SIZE,
Signature->Confounder );
//
// Create the encryption key by xoring the session key with 0xf0f0f0f0
//
for ( Index=0; Index < sizeof(EncryptionSessionKey); Index++ ) {
((LPBYTE)(&EncryptionSessionKey))[Index] =
((LPBYTE)(&Context->SessionInfo.SessionKey))[Index] ^0xf0f0f0f0;
}
//
// Pass the key to the encryption routines.
//
Status = CryptSystem->Initialize(
(LPBYTE)&EncryptionSessionKey,
sizeof( EncryptionSessionKey ),
0, // no message type
&CryptBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to initialize crypt routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Set the initial vector to ensure the key is different for each message
//
Status = CryptSystem->Control(
CRYPT_CONTROL_SET_INIT_VECT,
CryptBuffer,
Signature->SequenceNumber,
sizeof(Signature->SequenceNumber) );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to set IV: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Encrypt the confounder
//
Status = CryptSystem->Encrypt(
CryptBuffer,
Signature->Confounder,
NL_AUTH_CONFOUNDER_SIZE,
Signature->Confounder,
&OutputSize );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to encrypt confounder: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert( OutputSize == NL_AUTH_CONFOUNDER_SIZE );
}
//
// Sum and encrypt the caller's message.
//
for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) {
if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) &&
(MessageBuffers->pBuffers[Index].cbBuffer != 0)) {
ChecksumOnly = ((MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM) != 0);
Check->Sum(
CheckBuffer,
MessageBuffers->pBuffers[Index].cbBuffer,
(PBYTE) MessageBuffers->pBuffers[Index].pvBuffer );
//
// Now encrypt the buffer
//
if ( SealIt && !ChecksumOnly ) {
Status = CryptSystem->Encrypt(
CryptBuffer,
(PUCHAR) MessageBuffers->pBuffers[Index].pvBuffer,
MessageBuffers->pBuffers[Index].cbBuffer,
(PUCHAR) MessageBuffers->pBuffers[Index].pvBuffer,
&OutputSize );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to encrypt buffer: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert(OutputSize == MessageBuffers->pBuffers[Index].cbBuffer);
}
}
}
//
// Finish the checksum
//
(void) Check->Finalize(CheckBuffer, LocalChecksum);
#ifdef notdef
Status = Check->Finish(&CheckBuffer);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,"NlpSignOrSeal: Failed to finish checksum: 0x%x\n", Status));
goto Cleanup;
}
CheckBuffer = NULL;
#endif // notdef
//
// Copy the checksum into the message.
//
NlAssert( sizeof(LocalChecksum) >= sizeof(Signature->Checksum) );
RtlCopyMemory( Signature->Checksum, LocalChecksum, sizeof(Signature->Checksum) );
//
// Always encrypt the sequence number, using the checksum as the IV
//
if ( SealIt ) {
CryptSystem->Discard( &CryptBuffer );
CryptBuffer = NULL;
}
Status = CryptSystem->Initialize(
(LPBYTE)&Context->SessionInfo.SessionKey,
sizeof( Context->SessionInfo.SessionKey),
0, // no message type
&CryptBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed initialize crypt routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Set the initial vector
//
NlPrint(( NL_ENCRYPT,
"NlpSignOrSeal: %lx.%lx: IV: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->Checksum, sizeof(Signature->Checksum) );
Status = CryptSystem->Control(
CRYPT_CONTROL_SET_INIT_VECT,
CryptBuffer,
Signature->Checksum,
sizeof(Signature->Checksum) );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to set IV: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Now encrypt the sequence number
//
NlPrint(( NL_ENCRYPT,
"NlpSignOrSeal: %lx.%lx: Clear Seq: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->SequenceNumber, sizeof(Signature->SequenceNumber) );
Status = CryptSystem->Encrypt(
CryptBuffer,
Signature->SequenceNumber,
sizeof(Signature->SequenceNumber),
Signature->SequenceNumber,
&OutputSize
);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpSignOrSeal: %lx.%lx: Failed to encrypt sequence number: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlPrint(( NL_ENCRYPT,
"NlpSignOrSeal: %lx.%lx: Encrypted Seq: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->SequenceNumber, sizeof(Signature->SequenceNumber) );
NlAssert(OutputSize == sizeof(Signature->SequenceNumber));
Cleanup:
if (CryptBuffer != NULL) {
CryptSystem->Discard(&CryptBuffer);
}
if (CheckBuffer != NULL) {
Check->Finish(&CheckBuffer);
}
if ( SecStatus == SEC_E_OK ) {
SecStatus = KerbMapNtStatusToSecStatus(Status);
}
return SecStatus;
UNREFERENCED_PARAMETER( MessageSeqNo );
UNREFERENCED_PARAMETER( fQOP );
}
SECURITY_STATUS
NlpVerifyOrUnseal(
IN PCtxtHandle phContext,
IN PSecBufferDesc MessageBuffers,
IN ULONG MessageSequenceNumber,
OUT PULONG QualityOfProtection,
IN BOOLEAN UnsealIt
)
/*++
Routine Description:
Common routine to verify or unseal a message (Client or server side)
Arguments:
Standard.
UnSealIt - True to unseal the message.
FALSE to verify the message.
Return Value:
--*/
{
SECURITY_STATUS SecStatus = SEC_E_OK;
NTSTATUS Status = STATUS_SUCCESS;
PCHECKSUM_FUNCTION Check;
PCRYPTO_SYSTEM CryptSystem;
PCHECKSUM_BUFFER CheckBuffer = NULL;
PCRYPT_STATE_BUFFER CryptBuffer = NULL;
ULONG Index;
PSecBuffer SignatureBuffer = NULL;
PNL_AUTH_SIGNATURE Signature;
UCHAR LocalChecksum[24]; // ??? need better constant
BYTE LocalNonce[ NL_AUTH_SEQUENCE_SIZE ];
NETLOGON_SESSION_KEY EncryptionSessionKey;
BOOLEAN ChecksumOnly;
ULONG OutputSize;
PNL_AUTH_CONTEXT Context;
//
// Locate the context.
//
Context = LocateContext( phContext );
if (Context == NULL) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Cannot LocateContext\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_INVALID_HANDLE;
goto Cleanup;
}
//
// Find a buffer to put the signature in.
//
SignatureBuffer = LocateSigBuffer( MessageBuffers );
if (SignatureBuffer == NULL) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: No signature buffer found\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Verify the signature.
//
Signature = (PNL_AUTH_SIGNATURE) SignatureBuffer->pvBuffer;
if ( Signature->SignatureAlgorithm[0] != (BYTE)NL_AUTH_CHECKSUM ||
Signature->SignatureAlgorithm[1] != 0 ) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: signature alg different\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
if ( UnsealIt ) {
if ( SignatureBuffer->cbBuffer < sizeof(PACKAGE_SIGNATURE_SIZE) ) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: buffer too small for sealing\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
if ( Signature->SealAlgorithm[0] != (BYTE)NL_AUTH_ETYPE ||
Signature->SealAlgorithm[1] != 0 ) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: seal alg different\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
if ( *((USHORT UNALIGNED *)Signature->SealFiller) != 0xffff) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Filler different\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
} else {
if ( *((ULONG UNALIGNED *) Signature->SignFiller) != 0xffffffff) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Filler different\n",
phContext->dwUpper, phContext->dwLower ));
SecStatus = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
}
//
// Verify the sequence number.
//
// It is sent encrypted using the checksum as the IV, so decrypt it before
// checking it.
//
// Locate the encryption routines.
//
Status = CDLocateCSystem( (ULONG)NL_AUTH_ETYPE, &CryptSystem);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to load crypt system: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlPrint(( NL_ENCRYPT,
"NlpVerifyOrUnseal: %lx.%lx: Session Key: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, &Context->SessionInfo.SessionKey, sizeof( Context->SessionInfo.SessionKey) );
Status = CryptSystem->Initialize(
(LPBYTE)&Context->SessionInfo.SessionKey,
sizeof( Context->SessionInfo.SessionKey),
0, // no message type
&CryptBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed initialize crypt routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Set the initial vector
//
NlPrint(( NL_ENCRYPT,
"NlpVerifyOrUnseal: %lx.%lx: IV: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->Checksum, sizeof(Signature->Checksum) );
Status = CryptSystem->Control(
CRYPT_CONTROL_SET_INIT_VECT,
CryptBuffer,
Signature->Checksum,
sizeof(Signature->Checksum) );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to set IV: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Now decrypt the sequence number
//
NlPrint(( NL_ENCRYPT,
"NlpVerifyOrUnseal: %lx.%lx: Encrypted Seq: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->SequenceNumber, sizeof(Signature->SequenceNumber) );
Status = CryptSystem->Decrypt(
CryptBuffer,
Signature->SequenceNumber,
sizeof(Signature->SequenceNumber),
Signature->SequenceNumber,
&OutputSize );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Cannot decrypt sequence number: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlPrint(( NL_ENCRYPT,
"NlpVerifyOrUnseal: %lx.%lx: Clear Seq: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature->SequenceNumber, sizeof(Signature->SequenceNumber) );
LocalNonce[0] = (UCHAR) ((Context->Nonce.LowPart & 0xff000000) >> 24);
LocalNonce[1] = (UCHAR) ((Context->Nonce.LowPart & 0x00ff0000) >> 16);
LocalNonce[2] = (UCHAR) ((Context->Nonce.LowPart & 0x0000ff00) >> 8);
LocalNonce[3] = (UCHAR) (Context->Nonce.LowPart & 0x000000ff);
LocalNonce[4] = (UCHAR) ((Context->Nonce.HighPart & 0xff000000) >> 24);
LocalNonce[5] = (UCHAR) ((Context->Nonce.HighPart & 0x00ff0000) >> 16);
LocalNonce[6] = (UCHAR) ((Context->Nonce.HighPart & 0x0000ff00) >> 8);
LocalNonce[7] = (UCHAR) (Context->Nonce.HighPart & 0x000000ff);
if ( Context->Inbound ) {
LocalNonce[4] |= 0x80; // Discriminate between inbound and outbound messages
}
if (!RtlEqualMemory( LocalNonce,
Signature->SequenceNumber,
NL_AUTH_SEQUENCE_SIZE )) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Out of sequence\n",
phContext->dwUpper, phContext->dwLower ));
NlPrint(( NL_CRITICAL,"NlpVerifyOrUnseal: Local Sequence: " ));
NlpDumpBuffer(NL_CRITICAL, LocalNonce, NL_AUTH_SEQUENCE_SIZE );
NlPrint(( NL_CRITICAL,"NlpVerifyOrUnseal: Remote Sequence: " ));
NlpDumpBuffer(NL_CRITICAL, Signature->SequenceNumber, NL_AUTH_SEQUENCE_SIZE );
Status = SEC_E_OUT_OF_SEQUENCE;
goto Cleanup;
}
Context->Nonce.QuadPart ++;
//
// Now compute the checksum and verify it
//
//
// Initialize the checksum routines.
//
Status = CDLocateCheckSum( (ULONG)NL_AUTH_CHECKSUM, &Check);
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to load checksum routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert(Check->CheckSumSize <= sizeof(LocalChecksum));
Status = Check->InitializeEx(
(LPBYTE)&Context->SessionInfo.SessionKey,
sizeof( Context->SessionInfo.SessionKey),
0, // no message type
&CheckBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to initialize checksum routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Sum first several bytes of the signature
//
NlPrint(( NL_ENCRYPT,
"NlpVerifyOrUnseal: %lx.%lx: First Several of signature: ",
phContext->dwUpper, phContext->dwLower ));
NlpDumpBuffer(NL_ENCRYPT, Signature, NL_AUTH_SIGNED_BYTES );
Check->Sum( CheckBuffer,
NL_AUTH_SIGNED_BYTES,
(PUCHAR)Signature );
//
// Sum and decrypt the confounder
//
if ( UnsealIt ) {
//
// Discard the previous CryptBuffer
//
CryptSystem->Discard( &CryptBuffer );
CryptBuffer = NULL;
//
// Create the encryption key by xoring the session key with 0xf0f0f0f0
//
for ( Index=0; Index < sizeof(EncryptionSessionKey); Index++ ) {
((LPBYTE)(&EncryptionSessionKey))[Index] =
((LPBYTE)(&Context->SessionInfo.SessionKey))[Index] ^0xf0f0f0f0;
}
//
// Pass the key to the encryption routines.
//
Status = CryptSystem->Initialize(
(LPBYTE)&EncryptionSessionKey,
sizeof( EncryptionSessionKey ),
0, // no message type
&CryptBuffer );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to initialize crypt routines: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Set the initial vector to ensure the key is different for each message
//
Status = CryptSystem->Control(
CRYPT_CONTROL_SET_INIT_VECT,
CryptBuffer,
Signature->SequenceNumber,
sizeof(Signature->SequenceNumber) );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to set IV: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
//
// Decrypt the confounder
//
Status = CryptSystem->Decrypt(
CryptBuffer,
Signature->Confounder,
NL_AUTH_CONFOUNDER_SIZE,
Signature->Confounder,
&OutputSize );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to encrypt confounder: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert( OutputSize == NL_AUTH_CONFOUNDER_SIZE );
//
// Sum the decrypted confounder
//
Check->Sum(
CheckBuffer,
NL_AUTH_CONFOUNDER_SIZE,
Signature->Confounder );
}
//
// Sum and decrypt the caller's message.
//
for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) {
if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) &&
(!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) &&
(MessageBuffers->pBuffers[Index].cbBuffer != 0)) {
//
// Now decrypt the buffer
//
ChecksumOnly = ((MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM) != 0);
if ( UnsealIt && !ChecksumOnly ) {
Status = CryptSystem->Decrypt(
CryptBuffer,
(PUCHAR) MessageBuffers->pBuffers[Index].pvBuffer,
MessageBuffers->pBuffers[Index].cbBuffer,
(PUCHAR) MessageBuffers->pBuffers[Index].pvBuffer,
&OutputSize );
if (!NT_SUCCESS(Status)) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Failed to encrypt buffer: 0x%x\n",
phContext->dwUpper, phContext->dwLower,
Status));
goto Cleanup;
}
NlAssert(OutputSize == MessageBuffers->pBuffers[Index].cbBuffer);
}
//
// Checksum the decrypted buffer.
//
Check->Sum(
CheckBuffer,
MessageBuffers->pBuffers[Index].cbBuffer,
(PBYTE) MessageBuffers->pBuffers[Index].pvBuffer );
}
}
//
// Finish the checksum
//
(void) Check->Finalize(CheckBuffer, LocalChecksum);
if (!RtlEqualMemory(
LocalChecksum,
Signature->Checksum,
sizeof(Signature->Checksum) )) {
NlPrint(( NL_CRITICAL,
"NlpVerifyOrUnseal: %lx.%lx: Checksum mismatch\n",
phContext->dwUpper, phContext->dwLower ));
Status = SEC_E_MESSAGE_ALTERED;
goto Cleanup;
}
Cleanup:
if (CheckBuffer != NULL) {
Check->Finish(&CheckBuffer);
}
if (CryptBuffer != NULL) {
CryptSystem->Discard(&CryptBuffer);
}
if ( SecStatus == SEC_E_OK ) {
SecStatus = KerbMapNtStatusToSecStatus(Status);
}
return SecStatus;
UNREFERENCED_PARAMETER( QualityOfProtection );
UNREFERENCED_PARAMETER( MessageSequenceNumber );
}
SECURITY_STATUS SEC_ENTRY
MakeSignature( PCtxtHandle phContext,
ULONG fQOP,
PSecBufferDesc pMessage,
ULONG MessageSeqNo)
/*++
Routine Description:
Routine to sign a message (Client or server side)
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
SecStatus = NlpSignOrSeal(
phContext,
fQOP,
pMessage,
MessageSeqNo,
FALSE ); // Just sign the message
NlPrint(( NL_SESSION_MORE,
"MakeSignature: %lx.%lx: returns 0x%lx\n",
phContext->dwUpper, phContext->dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
VerifySignature(PCtxtHandle phContext,
PSecBufferDesc pMessage,
ULONG MessageSeqNo,
ULONG * pfQOP)
/*++
Routine Description:
Routine to verify a signed message (Client or server side)
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
SecStatus = NlpVerifyOrUnseal(
phContext,
pMessage,
MessageSeqNo,
pfQOP,
FALSE ); // Just verify the signature
NlPrint(( NL_SESSION_MORE,
"VerifySignature: %lx.%lx: returns 0x%lx\n",
phContext->dwUpper, phContext->dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
SealMessage( PCtxtHandle phContext,
ULONG fQOP,
PSecBufferDesc pMessage,
ULONG MessageSeqNo)
/*++
Routine Description:
Routine to encrypt a message (Client or server side)
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
SecStatus = NlpSignOrSeal(
phContext,
fQOP,
pMessage,
MessageSeqNo,
TRUE ); // Seal the message
NlPrint(( NL_SESSION_MORE,
"SealMessage: %lx.%lx: returns 0x%lx\n",
phContext->dwUpper, phContext->dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
}
SECURITY_STATUS SEC_ENTRY
UnsealMessage( PCtxtHandle phContext,
PSecBufferDesc pMessage,
ULONG MessageSeqNo,
ULONG * pfQOP)
/*++
Routine Description:
Routine to un-encrypt a message (client or server side)
Arguments:
Standard.
Return Value:
--*/
{
SECURITY_STATUS SecStatus;
//
// If caller is calling when the netlogon service isn't running,
// tell it so.
//
if ( !NlStartNetlogonCall() ) {
return SEC_E_SECPKG_NOT_FOUND;
}
SecStatus = NlpVerifyOrUnseal(
phContext,
pMessage,
MessageSeqNo,
pfQOP,
TRUE ); // unseal the message
NlPrint(( NL_SESSION_MORE,
"UnsealMessage: %lx.%lx: returns 0x%lx\n",
phContext->dwUpper, phContext->dwLower,
SecStatus ));
// Let netlogon service exit.
NlEndNetlogonCall();
return SecStatus;
}