/*-- 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 // // 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; }