//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 2000 // // File: userapi.cxx // // Contents: User-mode APIs to the NtDigest security package // // Main user mode entry points into this dll: // SpUserModeInitialize // SpInstanceInit // SpDeleteUserModeContext // SpInitUserModeContext // SpMakeSignature // SpVerifySignature // SpSealMessage // SpUnsealMessage // SpGetContextToken // SpQueryContextAttributes // SpCompleteAuthToken // SpFormatCredentials // SpMarshallSupplementalCreds // SpExportSecurityContext // SpImportSecurityContext // // Helper functions: // SspCreateTokenDacl // SspMapContext (this is called in Lsa mode) // // History: ChandanS 26-Jul-1996 Stolen from kerberos\client2\userapi.cxx // KDamour 18Mar00 Stolen from NTLM userapi.cxx // //------------------------------------------------------------------------ // // This area is still under determination as to support for userlevel functions // #include "global.h" #include // For sprintf #if DBG #define TEMPSIZE 4000 #endif // Winsock-ish host/network byte order converters for short and long integers. // #define htons(x) ((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00)) #define htonl(x) ((((x) >> 24) & 0x000000FFL) | \ (((x) >> 8) & 0x0000FF00L) | \ (((x) << 8) & 0x00FF0000L) | \ (((x) << 24) & 0xFF000000L)) //+------------------------------------------------------------------------- // // Function: SpUserModeInitialize // // Synopsis: Initialize an the Digest DLL in a client's // address space also called in LSA // // Effects: // // Arguments: LsaVersion - Version of the security dll loading the package // PackageVersion - Version of the Digest package // UserFunctionTable - Receives a copy of Digests's user mode // function table // pcTables - Receives count of tables returned. // // Requires: // // Returns: STATUS_SUCCESS - normal completion // STATUS_INVALID_PARAMETER - LsaVersion specified is incorrect // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SEC_ENTRY SpUserModeInitialize( IN ULONG LsaVersion, OUT PULONG PackageVersion, OUT PSECPKG_USER_FUNCTION_TABLE * UserFunctionTable, OUT PULONG pcTables ) { #if DBG DebugInitialize(); #endif DebugLog((DEB_TRACE_FUNC, "SpUserModeInitialize: Entering\n" )); NTSTATUS Status = STATUS_SUCCESS; if (LsaVersion != SECPKG_INTERFACE_VERSION) { Status = STATUS_INVALID_PARAMETER; goto CleanUp; } *PackageVersion = SECPKG_INTERFACE_VERSION; g_NtDigestUserFuncTable.InstanceInit = SpInstanceInit; g_NtDigestUserFuncTable.MakeSignature = SpMakeSignature; g_NtDigestUserFuncTable.VerifySignature = SpVerifySignature; g_NtDigestUserFuncTable.SealMessage = SpSealMessage; g_NtDigestUserFuncTable.UnsealMessage = SpUnsealMessage; g_NtDigestUserFuncTable.GetContextToken = SpGetContextToken; g_NtDigestUserFuncTable.QueryContextAttributes = SpQueryContextAttributes; g_NtDigestUserFuncTable.CompleteAuthToken = SpCompleteAuthToken; g_NtDigestUserFuncTable.InitUserModeContext = SpInitUserModeContext; g_NtDigestUserFuncTable.DeleteUserModeContext = SpDeleteUserModeContext; g_NtDigestUserFuncTable.FormatCredentials = SpFormatCredentials; g_NtDigestUserFuncTable.MarshallSupplementalCreds = SpMarshallSupplementalCreds; g_NtDigestUserFuncTable.ExportContext = SpExportSecurityContext; g_NtDigestUserFuncTable.ImportContext = SpImportSecurityContext; *UserFunctionTable = &g_NtDigestUserFuncTable; *pcTables = 1; CleanUp: DebugLog((DEB_TRACE_FUNC, "SpUserModeInitialize: Leaving Status 0x%x\n", Status)); return(Status); } //+------------------------------------------------------------------------- // // Function: SpInstanceInit // // Synopsis: Initialize an instance of the NtDigest package in a client's // address space. Also called once in LSA // // Effects: // // Arguments: Version - Version of the security dll loading the package // and it is Unused and Un-initialized // FunctionTable - Contains helper routines for use by NtDigest // and it is fixed static // UserFunctions - Receives a copy of NtDigest's user mode // function table - NOPE - has No information at all // // Requires: // // Returns: STATUS_SUCCESS // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpInstanceInit( IN ULONG Version, IN PSECPKG_DLL_FUNCTIONS DllFunctionTable, OUT PVOID * UserFunctionTable ) { DebugLog((DEB_TRACE_FUNC, "SpInstanceInit: Entering\n" )); NTSTATUS Status = STATUS_SUCCESS; // Save the Alloc/Free functions // Check if called in LSA or from Usermode - LSA calls SPInitialize then SPInstanceInit if (g_NtDigestState != NtDigestLsaMode) { g_NtDigestState = NtDigestUserMode; // indicate in user address space } g_UserFunctions = DllFunctionTable; // Need to initialize Crypto stuff and nonce creations Status = NonceInitialize(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInstanceInit: Error from NonceInitialize is %d\n", Status)); goto CleanUp; } // // Init the UserMode Context stuff // Status = UserCtxtHandlerInit(); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SpInstanceInit: Error from UserCtxtHandlerInit 0x%x\n", Status)); goto CleanUp; } // // Read in the registry values for SSP configuration - in user mode space // SPLoadRegOptions(); CleanUp: DebugLog((DEB_TRACE_FUNC, "SpInstanceInit: Leaving Status = 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpDeleteUserModeContext // // Synopsis: Deletes a user mode context by unlinking it and then // dereferencing it. // // Effects: // // Arguments: ContextHandle - Lsa context handle of the context to delete // // Requires: // // Returns: STATUS_SUCCESS on success, STATUS_INVALID_HANDLE if the // context can't be located // // Notes: // If this is an exported context, send a flag back to the LSA so that // Lsa does not call the SecpDeleteSecurityContext in the lsa process // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpDeleteUserModeContext( IN ULONG_PTR ContextHandle ) { DebugLog((DEB_TRACE_FUNC, "SpDeleteUserModeContext: Entering ContextHandle 0x%lx\n", ContextHandle )); PDIGEST_USERCONTEXT pUserContext = NULL; NTSTATUS Status = STATUS_SUCCESS; // // Find the currently existing user context and delink it // so that another context cannot Reference it before we // Dereference this one. // Status = UserCtxtHandlerHandleToContext(ContextHandle, TRUE, &pUserContext); if (!NT_SUCCESS(Status)) { // // pContext is legally NULL when we are dealing with an incomplete // context. This can often be the case when the second call to // InitializeSecurityContext() fails. // /// Status = STATUS_INVALID_HANDLE; Status = STATUS_SUCCESS; DebugLog((DEB_WARN, "SpDeleteUserModeContext: UserCtxtHandlerHandleToContext not found 0x%x\n", Status )); goto CleanUp; } // Now deference if (pUserContext != NULL) { Status = UserCtxtHandlerRelease(pUserContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpDeleteUserModeContext: DereferenceUserContext error Status 0x%x\n", Status )); } } CleanUp: DebugLog((DEB_TRACE_FUNC, "SpDeleteUserModeContext: Leaving ContextHandle 0x%lx status 0x%x\n", ContextHandle, Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpInitUserModeContext // // Synopsis: Creates a user-mode context from a packed LSA mode context // // Effects: // // Arguments: ContextHandle - Lsa mode context handle for the context // PackedContext - A marshalled buffer containing the LSA // mode context. // // Requires: // // Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpInitUserModeContext( IN ULONG_PTR ContextHandle, IN PSecBuffer PackedContext ) { NTSTATUS Status = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; PDIGEST_PACKED_USERCONTEXT pPackedUserContext = NULL; UINT Length = 0; DebugLog((DEB_TRACE_FUNC, "SpInitUserModeContext: Entering ContextHandle 0x%lx\n", ContextHandle )); ASSERT(PackedContext); // If Marshalled data is too small for holding a Client Context - reject it if (PackedContext->cbBuffer < sizeof(DIGEST_PACKED_USERCONTEXT)) { Status = STATUS_INVALID_PARAMETER; DebugLog((DEB_ERROR, "SpInitUserModeContext: ContextData size < DIGEST_PACKED_USERCONTEXT\n" )); goto CleanUp; } pPackedUserContext = (PDIGEST_PACKED_USERCONTEXT) DigestAllocateMemory(PackedContext->cbBuffer); if (!pPackedUserContext) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "SpInitUserModeContext: DigestAllocateMemory for Packed Copy returns NULL\n" )); goto CleanUp; } // Copy the Packed User Context from LSA to local memory so it wil be long word aligned memcpy(pPackedUserContext, PackedContext->pvBuffer, PackedContext->cbBuffer); // Now we will unpack this transfered LSA context into UserMode space Context List pContext = (PDIGEST_USERCONTEXT) DigestAllocateMemory( sizeof(DIGEST_USERCONTEXT) ); if (!pContext) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "SpInitUserModeContext: DigestAllocateMemory returns NULL\n" )); goto CleanUp; } Status = UserCtxtInit(pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpInitUserModeContext: UserContextInit error 0x%x\n", Status)); goto CleanUp; } // Store the location of the context in the LSA pContext->LsaContext = ContextHandle; Status = DigestUnpackContext(pPackedUserContext, pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpInitUserModeContext: DigestUnpackContext error 0x%x\n", Status)); goto CleanUp; } UserContextPrint(pContext); Status = UserCtxtHandlerInsertCred(pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpInitUserModeContext: UserCtxtHandlerInsertCred error status 0x%x\n", Status)); goto CleanUp; } DebugLog((DEB_TRACE, "SpInitUserModeContext: (RefCount) UserContextInit created & listed 0x%x\n", pContext)); CleanUp: if (!NT_SUCCESS(Status)) { if (pContext != NULL) { // Release the User context on error if allocated UserCtxtFree(pContext); pContext = NULL; } } if (pPackedUserContext) { DigestFreeMemory(pPackedUserContext); pPackedUserContext = NULL; } // Let FreeContextBuffer handle freeing the virtual allocs if (PackedContext->pvBuffer != NULL) { FreeContextBuffer(PackedContext->pvBuffer); PackedContext->pvBuffer = NULL; PackedContext->cbBuffer = 0; } DebugLog((DEB_TRACE_FUNC, "SpInitUserModeContext: Leaving status 0x%x\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpMakeSignature // // Synopsis: Signs a message buffer by calculating a checksum over all // the non-read only data buffers and encrypting the checksum // along with a nonce. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // QualityOfProtection - Unused flags. // MessageBuffers - Contains an array of buffers to sign and // to store the signature. // MessageSequenceNumber - Sequence number for this message, // only used in datagram cases. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found. // STATUS_BUFFER_TOO_SMALL - the signature buffer is too small // to hold the signature // // Returns: // // Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c , // routine SspHandleSignMessage. It's possible that // bugs got copied too // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpMakeSignature( IN ULONG_PTR ContextHandle, IN ULONG fQOP, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; BOOL bServer = FALSE; DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // Are we in SASL or HTTP mode DebugLog((DEB_TRACE_FUNC, "SpMakeSignature:Entering ContextHandle 0x%lx\n", ContextHandle )); UNREFERENCED_PARAMETER(fQOP); Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SpMakeSignature: Could not find ContextHandle\n" )); goto CleanUp; } UserContextPrint(pContext); // Check to see if Integrity is negotiated for SC bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; if ((pContext->typeDigest == SASL_CLIENT) || (pContext->typeDigest == SASL_SERVER)) { typeDigestMode = DIGESTMODE_SASL; } else { typeDigestMode = DIGESTMODE_HTTP; } if (typeDigestMode == DIGESTMODE_HTTP) { DebugLog((DEB_TRACE, "SpMakeSignature: HTTP SignMessage selected\n")); Status = DigestUserHTTPHelper( pContext, eSign, pMessage, MessageSeqNo ); } else { if ((bServer && !(pContext->ContextReq & ASC_REQ_INTEGRITY)) || (!bServer && !(pContext->ContextReq & ISC_REQ_INTEGRITY)) ) { Status = SEC_E_QOP_NOT_SUPPORTED; DebugLog((DEB_ERROR, "SpMakeSignature: Did not negotiate INTEGRITY\n" )); goto CleanUp; } DebugLog((DEB_TRACE, "SpMakeSignature: SASL SignMessage selected\n")); Status = DigestUserSignHelper( pContext, pMessage, MessageSeqNo ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpMakeSignature: DigestUserHTTP/SASLSignHelper returns %lx\n", Status )); goto CleanUp; } CleanUp: if (pContext != NULL) { SubStatus = UserCtxtHandlerRelease(pContext); // Don't destroy previous status if (NT_SUCCESS(Status)) { Status = SubStatus; } } DebugLog((DEB_TRACE_FUNC, "SpMakeSignature:Leaving status 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpVerifySignature // // Synopsis: Verifies a signed message buffer by calculating the Digest Access // for data bufferswith the current Security Context state. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // MessageBuffers - Contains an array of signed buffers and // a signature buffer. // MessageSequenceNumber - Unused ULONG // QualityOfProtection - Unused flags. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found or was too small. // // Returns: // // Notes: This routine should be called AFTER you have a valid security context // from (usually) acceptsecuritycontext. The usermode context has a nonce // count that is automatically incremented for each successful verify signature // function call. Therefore, calling this functio with the same noncecount // will return a failed status message. // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpVerifySignature( IN ULONG_PTR ContextHandle, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo, OUT PULONG pfQOP ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; BOOL bServer = FALSE; DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // Are we in SASL or HTTP mode DebugLog((DEB_TRACE_FUNC, "SpVerifySignature:Entering ContextHandle 0x%lx\n", ContextHandle )); // Reset output flags if (pfQOP) { *pfQOP = 0; } Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SpVerifySignature: Could not find ContextHandle\n" )); goto CleanUp; } UserContextPrint(pContext); // Check to see if Integrity is negotiated for SC bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; if ((pContext->typeDigest == SASL_CLIENT) || (pContext->typeDigest == SASL_SERVER)) { typeDigestMode = DIGESTMODE_SASL; } else { typeDigestMode = DIGESTMODE_HTTP; } if (typeDigestMode == DIGESTMODE_HTTP) { DebugLog((DEB_TRACE, "SpVerifySignature: HTTP VerifyMessage selected\n")); Status = DigestUserHTTPHelper( pContext, eVerify, pMessage, MessageSeqNo ); } else { if ((bServer && !(pContext->ContextReq & ASC_REQ_INTEGRITY)) || (!bServer && !(pContext->ContextReq & ISC_REQ_INTEGRITY)) ) { Status = SEC_E_QOP_NOT_SUPPORTED; DebugLog((DEB_ERROR, "SpVerifySignature: Did not negotiate INTEGRITY\n" )); goto CleanUp; } else { DebugLog((DEB_TRACE, "SpVerifySignature: SASL VerifyMessage selected\n")); Status = DigestUserVerifyHelper( pContext, pMessage, MessageSeqNo ); } } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpVerifySignature: DigestUserHTTP/SASLSignHelper returns %lx\n", Status )); goto CleanUp; } CleanUp: if (pContext != NULL) { SubStatus = UserCtxtHandlerRelease(pContext); // Don't destroy previous status if (NT_SUCCESS(Status)) { Status = SubStatus; } } DebugLog((DEB_TRACE_FUNC, "SpVerifySignature:Leaving status 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpSealMessage // // Synopsis: Verifies a signed message buffer by calculating a checksum over all // the non-read only data buffers and encrypting the checksum // along with a nonce. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // MessageBuffers - Contains an array of signed buffers and // a signature buffer. // MessageSequenceNumber - Sequence number for this message, // only used in datagram cases. // QualityOfProtection - Unused flags. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found or was too small. // // Returns: // // Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c , // routine SspHandleSealMessage. It's possible that // bugs got copied too // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpSealMessage( IN ULONG_PTR ContextHandle, IN ULONG fQOP, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; BOOL bServer = FALSE; DebugLog((DEB_TRACE_FUNC, "SpSealMessage:Entering ContextHandle 0x%lx\n", ContextHandle )); UNREFERENCED_PARAMETER(fQOP); Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SpSealMessage: Could not find ContextHandle\n" )); goto CleanUp; } UserContextPrint(pContext); // Check to see if Confidentiality is negotiated for SC bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; if ((bServer && !(pContext->ContextReq & ASC_RET_CONFIDENTIALITY)) || (!bServer && !(pContext->ContextReq & ISC_RET_CONFIDENTIALITY)) ) { // Since CONFIDENTIALITY not negoiated - check if integrity selected if ((bServer && (pContext->ContextReq & ASC_RET_INTEGRITY)) || (!bServer && (pContext->ContextReq & ISC_RET_INTEGRITY)) ) { DebugLog((DEB_TRACE, "SpSealMessage: No Confidentiality selected - use Integrity ONLY\n")); // Just call the Sign routine only Status = DigestUserSignHelper( pContext, pMessage, MessageSeqNo ); } else { DebugLog((DEB_ERROR, "SpSealMessage: Neither Confidentiality nor Integrity selected\n")); Status = SEC_E_QOP_NOT_SUPPORTED; DebugLog((DEB_ERROR, "SpSealMessage: Did not negotiate CONFIDENTIALITY\n" )); goto CleanUp; } } else { // Use SignHelper for both SASL - HTTP not speced Status = DigestUserSealHelper( pContext, pMessage, MessageSeqNo ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpSealMessage: DigestUserSASLHelper returns %lx\n", Status )); goto CleanUp; } CleanUp: if (pContext != NULL) { SubStatus = UserCtxtHandlerRelease(pContext); // Don't destroy previous status if (NT_SUCCESS(Status)) { Status = SubStatus; } } DebugLog((DEB_TRACE_FUNC, "SpSealMessage:Leaving status 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpUnsealMessage // // Synopsis: Verifies a signed message buffer by calculating a checksum over all // the non-read only data buffers and encrypting the checksum // along with a nonce. // // Effects: // // Arguments: ContextHandle - Handle of the context to use to sign the // message. // MessageBuffers - Contains an array of signed buffers and // a signature buffer. // MessageSequenceNumber - Sequence number for this message, // only used in datagram cases. // QualityOfProtection - Unused flags. // // Requires: STATUS_INVALID_HANDLE - the context could not be found or // was not configured for message integrity. // STATUS_INVALID_PARAMETER - the signature buffer could not // be found or was too small. // // Returns: // // Notes: This was stolen from net\svcdlls\ntlmssp\client\sign.c , // routine SspHandleUnsealMessage. It's possible that // bugs got copied too // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpUnsealMessage( IN ULONG_PTR ContextHandle, IN PSecBufferDesc pMessage, IN ULONG MessageSeqNo, OUT PULONG pfQOP ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; BOOL bServer; // acting as the server ? DebugLog((DEB_TRACE_FUNC, "SpUnsealMessage:Entering ContextHandle 0x%lx\n", ContextHandle )); // Reset output flags if (pfQOP) { *pfQOP = 0; } Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SpUnsealMessage: Could not find ContextHandle\n" )); goto CleanUp; } UserContextPrint(pContext); // Check to see if Confidentiality is negotiated for SC bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; if ((bServer && !(pContext->ContextReq & ASC_RET_CONFIDENTIALITY)) || (!bServer && !(pContext->ContextReq & ISC_RET_CONFIDENTIALITY)) ) { if ((bServer && (pContext->ContextReq & ASC_RET_INTEGRITY)) || (!bServer && (pContext->ContextReq & ISC_RET_INTEGRITY)) ) { DebugLog((DEB_TRACE, "SpUnsealMessage: No Confidentiality selected - use Integrity ONLY\n")); Status = DigestUserVerifyHelper( pContext, pMessage, MessageSeqNo ); // signal QOP was only for integrity if (pfQOP) { *pfQOP = SIGN_ONLY; } } else { DebugLog((DEB_ERROR, "SpUnsealMessage: Neither Confidentiality nor Integrity selected\n")); Status = SEC_E_QOP_NOT_SUPPORTED; DebugLog((DEB_ERROR, "SpUnsealMessage: Did not negotiate CONFIDENTIALITY\n" )); goto CleanUp; } } else { Status = DigestUserUnsealHelper( pContext, pMessage, MessageSeqNo ); } if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpUnsealMessage: DigestUserSASLHelper returns %lx\n", Status )); goto CleanUp; } CleanUp: if (pContext != NULL) { SubStatus = UserCtxtHandlerRelease(pContext); // Don't destroy previous status if (NT_SUCCESS(Status)) { Status = SubStatus; } } DebugLog((DEB_TRACE_FUNC, "SpUnsealMessage:Leaving status 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpGetContextToken // // Synopsis: returns a pointer to the token for a server-side context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: Used in ImpersonateSecurityContext SSPI Call // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpGetContextToken( IN ULONG_PTR ContextHandle, OUT PHANDLE ImpersonationToken ) { DebugLog((DEB_TRACE_FUNC, "SpGetContextToken: Entering ContextHandle 0x%lx\n", ContextHandle )); NTSTATUS Status = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpGetContextToken: UserCtxtHandlerHandleToContext error 0x%x\n", Status)); } DebugLog((DEB_TRACE, "SpGetContextToken: Client ImpersonationToken 0x%lx\n", pContext->ClientTokenHandle )); if (pContext && pContext->ClientTokenHandle) { *ImpersonationToken = pContext->ClientTokenHandle; goto CleanUp; } Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SpGetContextToken: no token handle\n" )); CleanUp: if (pContext != NULL) { Status = UserCtxtHandlerRelease(pContext); } DebugLog((DEB_TRACE_FUNC, "SpGetContextToken: Leaving Status 0x%lx\n", Status )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpQueryContextAttributes // // Synopsis: Querys attributes of the specified context // This API allows a customer of the security // services to determine certain attributes of // the context. These are: sizes, names, and lifespan. // // Effects: // // Arguments: // // ContextHandle - Handle to the context to query. // // Attribute - Attribute to query. // // // Buffer - Buffer to copy the data into. The buffer must // be large enough to fit the queried attribute. // // // Requires: // // Returns: // // STATUS_SUCCESS - Call completed successfully // // STATUS_INVALID_HANDLE -- Credential/Context Handle is invalid // STATUS_NOT_SUPPORTED -- Function code is not supported // // Notes: // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpQueryContextAttributes( IN ULONG_PTR ContextHandle, IN ULONG Attribute, IN OUT PVOID Buffer ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT pContext = NULL; DebugLog((DEB_TRACE_FUNC, "SpQueryContextAttributes: Entering ContextHandle 0x%lx\n", ContextHandle )); PSecPkgContext_Sizes ContextSizes = NULL; PSecPkgContext_Flags ContextFlags = NULL; PSecPkgContext_DceInfo ContextDceInfo = NULL; PSecPkgContext_Names ContextNames = NULL; PSecPkgContext_PackageInfo PackageInfo = NULL; PSecPkgContext_NegotiationInfo NegInfo = NULL; PSecPkgContext_PasswordExpiry PasswordExpires = NULL; PSecPkgContext_KeyInfo KeyInfo = NULL; PSecPkgContext_AccessToken AccessToken = NULL; PSecPkgContext_StreamSizes StreamSizes = NULL; ULONG PackageInfoSize = 0; BOOL bServer = FALSE; LPWSTR pszEncryptAlgorithmName = NULL; LPWSTR pszSignatureAlgorithmName = NULL; DWORD dwBytes = 0; ULONG ulMaxMessage = 0; DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // Are we in SASL or HTTP mode Status = UserCtxtHandlerHandleToContext(ContextHandle, FALSE, &pContext); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SpQueryContextAttributes: HandleToContext error 0x%x\n", Status)); Status = STATUS_INVALID_HANDLE; goto CleanUp; } // Check to see if Integrity is negotiated for SC bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; if ((pContext->typeDigest == SASL_CLIENT) || (pContext->typeDigest == SASL_SERVER)) { typeDigestMode = DIGESTMODE_SASL; } else { typeDigestMode = DIGESTMODE_HTTP; } // // Handle each of the various queried attributes // DebugLog((DEB_TRACE, "SpQueryContextAttributes : 0x%lx\n", Attribute )); switch ( Attribute) { case SECPKG_ATTR_SIZES: ContextSizes = (PSecPkgContext_Sizes) Buffer; ZeroMemory(ContextSizes, sizeof(SecPkgContext_Sizes)); ContextSizes->cbMaxToken = NTDIGEST_SP_MAX_TOKEN_SIZE; if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP has signature the same as token in Authentication Header info ContextSizes->cbMaxSignature = NTDIGEST_SP_MAX_TOKEN_SIZE; } else { // SASL has specialized signature block ContextSizes->cbMaxSignature = MAC_BLOCK_SIZE + MAX_PADDING; } if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { ContextSizes->cbBlockSize = DES_BLOCKSIZE; ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else if ((pContext->typeCipher == CIPHER_RC4) || (pContext->typeCipher == CIPHER_RC4_40) || (pContext->typeCipher == CIPHER_RC4_56)) { ContextSizes->cbBlockSize = RC4_BLOCKSIZE; ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else { ContextSizes->cbBlockSize = 0; if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP has signature the same as token in Authentication Header info ContextSizes->cbSecurityTrailer = 0; } else { // SASL has specialized signature block ContextSizes->cbSecurityTrailer = MAC_BLOCK_SIZE + MAX_PADDING; // handle Auth-int case } } break; case SECPKG_ATTR_DCE_INFO: ContextDceInfo = (PSecPkgContext_DceInfo) Buffer; ZeroMemory(ContextDceInfo, sizeof(SecPkgContext_DceInfo)); ContextDceInfo->AuthzSvc = 0; break; case SECPKG_ATTR_NAMES: ContextNames = (PSecPkgContext_Names) Buffer; ZeroMemory(ContextNames, sizeof(SecPkgContext_Names)); if (pContext->ustrAccountName.Length && pContext->ustrAccountName.Buffer) { dwBytes = pContext->ustrAccountName.Length + sizeof(WCHAR); ContextNames->sUserName = (LPWSTR)g_UserFunctions->AllocateHeap(dwBytes); if (ContextNames->sUserName) { ZeroMemory(ContextNames->sUserName, dwBytes); memcpy(ContextNames->sUserName, pContext->ustrAccountName.Buffer, pContext->ustrAccountName.Length); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; } break; case SECPKG_ATTR_PACKAGE_INFO: case SECPKG_ATTR_NEGOTIATION_INFO: // // Return the information about this package. This is useful for // callers who used SPNEGO and don't know what package they got. // if ((Attribute == SECPKG_ATTR_NEGOTIATION_INFO) && (g_fParameter_Negotiate == FALSE)) { Status = STATUS_NOT_SUPPORTED; goto CleanUp; } PackageInfo = (PSecPkgContext_PackageInfo) Buffer; ZeroMemory(PackageInfo, sizeof(SecPkgContext_PackageInfo)); PackageInfoSize = sizeof(SecPkgInfoW) + sizeof(WDIGEST_SP_NAME) + sizeof(NTDIGEST_SP_COMMENT); PackageInfo->PackageInfo = (PSecPkgInfoW) g_UserFunctions->AllocateHeap(PackageInfoSize); if (PackageInfo->PackageInfo == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CleanUp; } PackageInfo->PackageInfo->Name = (LPWSTR) (PackageInfo->PackageInfo + 1); PackageInfo->PackageInfo->Comment = (LPWSTR) ((((PBYTE) PackageInfo->PackageInfo->Name)) + sizeof(WDIGEST_SP_NAME)); wcscpy( PackageInfo->PackageInfo->Name, WDIGEST_SP_NAME ); wcscpy( PackageInfo->PackageInfo->Comment, NTDIGEST_SP_COMMENT ); PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; PackageInfo->PackageInfo->wRPCID = RPC_C_AUTHN_DIGEST; PackageInfo->PackageInfo->fCapabilities = NTDIGEST_SP_CAPS; PackageInfo->PackageInfo->cbMaxToken = NTDIGEST_SP_MAX_TOKEN_SIZE; if ( Attribute == SECPKG_ATTR_NEGOTIATION_INFO ) { NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ; NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ; } break; case SECPKG_ATTR_PASSWORD_EXPIRY: PasswordExpires = (PSecPkgContext_PasswordExpiry) Buffer; if (pContext->Expires.QuadPart != 0) { PasswordExpires->tsPasswordExpires = pContext->Expires; } else Status = STATUS_NOT_SUPPORTED; break; case SECPKG_ATTR_KEY_INFO: KeyInfo = (PSecPkgContext_KeyInfo) Buffer; ZeroMemory(KeyInfo, sizeof(SecPkgContext_KeyInfo)); if (typeDigestMode == DIGESTMODE_HTTP) { // HTTP mode KeyInfo->SignatureAlgorithm = CALG_MD5; } else { // SASL mode KeyInfo->KeySize = 128; // All modes use a 128 bit key - may have less entropy though (i.e. rc4-XX) KeyInfo->SignatureAlgorithm = CALG_HMAC; pszSignatureAlgorithmName = WSTR_CIPHER_HMAC_MD5; switch (pContext->typeCipher) { case CIPHER_RC4: case CIPHER_RC4_40: case CIPHER_RC4_56: KeyInfo->KeySize = 16 * 8; // All modes use a 128 bit key - may have less entropy though (i.e. rc4-XX) KeyInfo->SignatureAlgorithm = CALG_RC4; pszEncryptAlgorithmName = WSTR_CIPHER_RC4; break; case CIPHER_DES: KeyInfo->KeySize = 7 * 8; KeyInfo->SignatureAlgorithm = CALG_DES; pszEncryptAlgorithmName = WSTR_CIPHER_DES; break; case CIPHER_3DES: KeyInfo->KeySize = 14 * 8; KeyInfo->SignatureAlgorithm = CALG_3DES_112; pszEncryptAlgorithmName = WSTR_CIPHER_3DES; break; } if (pszEncryptAlgorithmName) { KeyInfo->sEncryptAlgorithmName = (LPWSTR) g_UserFunctions->AllocateHeap(sizeof(WCHAR) * (wcslen(pszEncryptAlgorithmName) + 1)); if (KeyInfo->sEncryptAlgorithmName != NULL) { wcscpy( KeyInfo->sEncryptAlgorithmName, pszEncryptAlgorithmName ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if (pszSignatureAlgorithmName) { KeyInfo->sSignatureAlgorithmName = (LPWSTR) g_UserFunctions->AllocateHeap(sizeof(WCHAR) * (wcslen(pszSignatureAlgorithmName) + 1)); if (KeyInfo->sSignatureAlgorithmName != NULL) { wcscpy( KeyInfo->sSignatureAlgorithmName, pszSignatureAlgorithmName ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } } break; case SECPKG_ATTR_STREAM_SIZES: StreamSizes = (PSecPkgContext_StreamSizes) Buffer; ZeroMemory(StreamSizes, sizeof(SecPkgContext_StreamSizes)); if (typeDigestMode == DIGESTMODE_HTTP) { } else { // SASL ulMaxMessage = pContext->ulRecvMaxBuf; if (pContext->ulSendMaxBuf < ulMaxMessage) { ulMaxMessage = pContext->ulSendMaxBuf; } StreamSizes->cbMaximumMessage = ulMaxMessage - (MAC_BLOCK_SIZE + MAX_PADDING); } if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { StreamSizes->cbBlockSize = DES_BLOCKSIZE; StreamSizes->cbTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } else if ((pContext->typeCipher == CIPHER_RC4) || (pContext->typeCipher == CIPHER_RC4_40) || (pContext->typeCipher == CIPHER_RC4_56)) { StreamSizes->cbBlockSize = RC4_BLOCKSIZE; StreamSizes->cbTrailer = MAC_BLOCK_SIZE + MAX_PADDING; } break; case SECPKG_ATTR_ACCESS_TOKEN: AccessToken = (PSecPkgContext_AccessToken) Buffer; // // ClientTokenHandle can be NULL, for instance: // 1. client side context. // 2. incomplete server context. // Token is not duped - caller must not CloseHandle AccessToken->AccessToken = (void*)pContext->ClientTokenHandle; break; default: Status = STATUS_NOT_SUPPORTED; break; } CleanUp: if (!NT_SUCCESS(Status)) { switch (Attribute) { case SECPKG_ATTR_NAMES: if (ContextNames != NULL && ContextNames->sUserName ) { g_UserFunctions->FreeHeap(ContextNames->sUserName); ContextNames->sUserName = NULL; } break; case SECPKG_ATTR_DCE_INFO: if (ContextDceInfo != NULL && ContextDceInfo->pPac) { g_UserFunctions->FreeHeap(ContextDceInfo->pPac); ContextDceInfo->pPac = NULL; } break; case SECPKG_ATTR_KEY_INFO: if (KeyInfo != NULL && KeyInfo->sEncryptAlgorithmName) { g_UserFunctions->FreeHeap(KeyInfo->sEncryptAlgorithmName); KeyInfo->sEncryptAlgorithmName = NULL; } if (KeyInfo != NULL && KeyInfo->sSignatureAlgorithmName) { g_UserFunctions->FreeHeap(KeyInfo->sSignatureAlgorithmName); KeyInfo->sSignatureAlgorithmName = NULL; } break; } } if (pContext != NULL) { SubStatus = UserCtxtHandlerRelease(pContext); } DebugLog((DEB_TRACE_FUNC, "SpQueryContextAttributes: Leaving ContextHandle 0x%lx\n", ContextHandle )); return(Status); } //+------------------------------------------------------------------------- // // Function: SpCompleteAuthToken // // Synopsis: Completes a context - used to perform user mode verification of // challenge response for non-persistent connections re-established via ASC // call. // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpCompleteAuthToken( IN ULONG_PTR ContextHandle, IN PSecBufferDesc InputBuffer ) { NTSTATUS Status = STATUS_SUCCESS; ULONG ulQOP = 0; DebugLog((DEB_TRACE_FUNC, "SpCompleteAuthToken: Entering ContextHandle 0x%lx\n", ContextHandle )); Status = SpVerifySignature(ContextHandle, InputBuffer, 0, &ulQOP); DebugLog((DEB_TRACE_FUNC, "SpCompleteAuthToken: Leaving ContextHandle 0x%lx Status = 0x%x\n", ContextHandle, Status)); return(Status); } NTSTATUS NTAPI SpFormatCredentials( IN PSecBuffer Credentials, OUT PSecBuffer FormattedCredentials ) { UNREFERENCED_PARAMETER (Credentials); UNREFERENCED_PARAMETER (FormattedCredentials); DebugLog((DEB_TRACE_FUNC, "SpFormatCredentials: Entering/Leaving\n")); return(SEC_E_UNSUPPORTED_FUNCTION); } NTSTATUS NTAPI SpMarshallSupplementalCreds( IN ULONG CredentialSize, IN PUCHAR Credentials, OUT PULONG MarshalledCredSize, OUT PVOID * MarshalledCreds ) { UNREFERENCED_PARAMETER (CredentialSize); UNREFERENCED_PARAMETER (Credentials); UNREFERENCED_PARAMETER (MarshalledCredSize); UNREFERENCED_PARAMETER (MarshalledCreds); DebugLog((DEB_TRACE_FUNC, "SpMarshallSupplementalCreds: Entering/Leaving\n")); return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: NtDigestMakePackedContext // // Synopsis: Maps a context to the caller's address space // // Effects: // // Arguments: Context - The context to map // MappedContext - Set to TRUE on success // ContextData - Receives a buffer in the caller's address space // with the mapped context. // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NtDigestMakePackedContext( IN PDIGEST_USERCONTEXT Context, OUT PBOOLEAN MappedContext, OUT PSecBuffer ContextData, IN ULONG Flags ) { NTSTATUS Status = STATUS_SUCCESS; PDIGEST_USERCONTEXT PackedContext = NULL; ULONG ContextSize = 0, ContextNameSize = 0; DebugLog((DEB_TRACE_FUNC, "NtDigestMakePackedContext: Entering/Leaving\n")); return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: SpExportSecurityContext // // Synopsis: Exports a security context to another process // // Effects: Allocates memory for output // // Arguments: ContextHandle - handle to context to export // Flags - Flags concerning duplication. Allowable flags: // SECPKG_CONTEXT_EXPORT_DELETE_OLD - causes old context // to be deleted. // PackedContext - Receives serialized context to be freed with // FreeContextBuffer // TokenHandle - Optionally receives handle to context's token. // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SpExportSecurityContext( IN ULONG_PTR ContextHandle, IN ULONG Flags, OUT PSecBuffer PackedContext, OUT PHANDLE TokenHandle ) { PDIGEST_USERCONTEXT Context = NULL, pvContext = NULL; NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; ULONG ContextSize = 0; BOOLEAN MappedContext = FALSE; DebugLog((DEB_TRACE_FUNC, "SpExportSecurityContext:Entering/Leaving ContextHandle 0x%x\n", ContextHandle )); return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: NtDigestCreateUserModeContext // // Synopsis: Creates a user-mode context to support impersonation and // message integrity and privacy // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NtDigestCreateUserModeContext( IN ULONG_PTR ContextHandle, IN HANDLE Token, IN PSecBuffer MarshalledContext, OUT PDIGEST_USERCONTEXT * NewContext ) { NTSTATUS Status = STATUS_SUCCESS; DebugLog((DEB_TRACE_FUNC, "NtDigestCreateUserModeContext: Entering/Leaving ContextHandle 0x%x\n", ContextHandle )); return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: SpImportSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SpImportSecurityContext( IN PSecBuffer PackedContext, IN HANDLE Token, OUT PULONG_PTR ContextHandle ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDIGEST_USERCONTEXT Context = NULL; DebugLog((DEB_TRACE_FUNC, "SpImportSecurityContext: Entering/Leaving ContextHandle 0x%x\n", ContextHandle)); return(SEC_E_UNSUPPORTED_FUNCTION); } /*++ RoutineDescription: Gets the TOKEN_USER from an open token Arguments: Token - Handle to a token open for TOKEN_QUERY access Return Value: STATUS_INSUFFICIENT_RESOURCES - not enough memory to complete the function. Errors from NtQueryInformationToken. --*/ NTSTATUS SspGetTokenUser( HANDLE Token, PTOKEN_USER * pTokenUser ) { PTOKEN_USER LocalTokenUser = NULL; NTSTATUS Status = STATUS_SUCCESS; ULONG TokenUserSize = 0; DebugLog((DEB_TRACE_FUNC, "SspGetTokenUser: Entering Token 0x%x pTokenUser 0x%x\n", Token, pTokenUser)); // // Query the token user. First pass in NULL to get back the // required size. // Status = NtQueryInformationToken( Token, TokenUser, NULL, 0, &TokenUserSize ); if (Status != STATUS_BUFFER_TOO_SMALL) { ASSERT(Status != STATUS_SUCCESS); DebugLog((DEB_ERROR, "SspGetTokenUser: NtQueryInformationToken (1st call) returns 0x%lx for Token 0x%x\n", Status, Token )); goto CleanUp; } // // Now allocate the required ammount of memory and try again. // LocalTokenUser = (PTOKEN_USER) DigestAllocateMemory(TokenUserSize); if (LocalTokenUser == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CleanUp; } Status = NtQueryInformationToken( Token, TokenUser, LocalTokenUser, TokenUserSize, &TokenUserSize ); if (NT_SUCCESS(Status)) { *pTokenUser = LocalTokenUser; } else { DigestFreeMemory(LocalTokenUser); DebugLog((DEB_ERROR, "SspGetTokenUser: NtQueryInformationToken (2nd call) returns 0x%lx for Token 0x%x\n", Status, Token )); } CleanUp: DebugLog((DEB_TRACE_FUNC, "SspGetTokenUser: Leaving Token 0x%x with Status 0x%x\n", Token, Status)); return(Status); } /*++ RoutineDescription: Create a local context for a real context Don't link it to out list of local contexts. Called inside LSA to prep packed Context buffer to send to UserMode addr space Arguments: pLsaContext - pointer to a Context in LSA to map over to User space pDigest - pointer to digest auth parameters - may be NULL and use Context instead ContextData - packed Context information to send to usermode process Return Value: --*/ NTSTATUS SspMapDigestContext( IN PDIGEST_CONTEXT pLsaContext, // LSA Context IN PDIGEST_PARAMETER pDigest, OUT PSecBuffer ContextData ) { NTSTATUS Status = STATUS_SUCCESS; PDIGEST_PACKED_USERCONTEXT pPackedUserCtxt = NULL; // Return buffer to on good auth to UserMode addr space USHORT cbLenNeeded = 0; PUCHAR pucLoc = NULL; HANDLE hTemp = NULL; int iAuth = 0; DebugLog((DEB_TRACE_FUNC, "SspMapContext: Entering for LSA context %lx\n", pLsaContext)); ASSERT(ContextData); ASSERT(pLsaContext); if (!pLsaContext) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "SspMapContext: pLsaContext invalid\n")); goto CleanUp; } // Copy over only selected fields cbLenNeeded = sizeof(DIGEST_PACKED_USERCONTEXT); if (pDigest) { cbLenNeeded += pDigest->refstrParam[MD5_AUTH_USERNAME].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_REALM].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_NONCE].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_CNONCE].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_ALGORITHM].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_QOP].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_AUTHZID].Length; cbLenNeeded += pDigest->refstrParam[MD5_AUTH_OPAQUE].Length; } else { cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_USERNAME].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_REALM].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_NONCE].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_CNONCE].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_ALGORITHM].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_QOP].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_AUTHZID].Length; cbLenNeeded += pLsaContext->strDirective[MD5_AUTH_OPAQUE].Length; } cbLenNeeded += pLsaContext->strSessionKey.Length; cbLenNeeded += pLsaContext->ustrAccountName.Length; DebugLog((DEB_TRACE, "SspMapContext: Packed Digest will be %d bytes \n", cbLenNeeded)); // DigestAllocateMemory will use g_LsaFunctions->AllocateLsaHeap() pPackedUserCtxt = (PDIGEST_PACKED_USERCONTEXT)g_LsaFunctions->AllocateLsaHeap(cbLenNeeded); if (!pPackedUserCtxt) { // Failed to allocate memory to send info to usermode space ContextData->cbBuffer = 0; Status = SEC_E_INSUFFICIENT_MEMORY; DebugLog((DEB_ERROR, "SspMapContext: out of memory on usermode contextdata\n")); goto CleanUp; } // Now initialize the UserMode Context struct to return ZeroMemory(pPackedUserCtxt, cbLenNeeded); pPackedUserCtxt->Expires = pLsaContext->PasswordExpires; pPackedUserCtxt->typeAlgorithm = (ULONG)pLsaContext->typeAlgorithm; pPackedUserCtxt->typeCipher = (ULONG)pLsaContext->typeCipher; pPackedUserCtxt->typeCharset = (ULONG)pLsaContext->typeCharset; pPackedUserCtxt->typeDigest = (ULONG)pLsaContext->typeDigest; pPackedUserCtxt->typeQOP = (ULONG)pLsaContext->typeQOP; pPackedUserCtxt->ulSendMaxBuf = pLsaContext->ulSendMaxBuf; pPackedUserCtxt->ulRecvMaxBuf = pLsaContext->ulRecvMaxBuf; pPackedUserCtxt->ContextReq = (ULONG)pLsaContext->ContextReq; pPackedUserCtxt->CredentialUseFlags = (ULONG)pLsaContext->CredentialUseFlags; // Now mark that there is data for these items ONLY non-zero items will be written out!!! if (pDigest) { pPackedUserCtxt->uDigestLen[MD5_AUTH_USERNAME] = (ULONG)pDigest->refstrParam[MD5_AUTH_USERNAME].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_REALM] = (ULONG)pDigest->refstrParam[MD5_AUTH_REALM].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_NONCE] = (ULONG)pDigest->refstrParam[MD5_AUTH_NONCE].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_CNONCE] = (ULONG)pDigest->refstrParam[MD5_AUTH_CNONCE].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_ALGORITHM] = (ULONG)pDigest->refstrParam[MD5_AUTH_ALGORITHM].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_QOP] = (ULONG)pDigest->refstrParam[MD5_AUTH_QOP].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_AUTHZID] = (ULONG)pDigest->refstrParam[MD5_AUTH_AUTHZID].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_OPAQUE] = (ULONG)pDigest->refstrParam[MD5_AUTH_OPAQUE].Length; } else { pPackedUserCtxt->uDigestLen[MD5_AUTH_USERNAME] = (ULONG)pLsaContext->strDirective[MD5_AUTH_USERNAME].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_REALM] = (ULONG)pLsaContext->strDirective[MD5_AUTH_REALM].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_NONCE] = (ULONG)pLsaContext->strDirective[MD5_AUTH_NONCE].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_CNONCE] = (ULONG)pLsaContext->strDirective[MD5_AUTH_CNONCE].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_ALGORITHM] = (ULONG)pLsaContext->strDirective[MD5_AUTH_ALGORITHM].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_QOP] = (ULONG)pLsaContext->strDirective[MD5_AUTH_QOP].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_AUTHZID] = (ULONG)pLsaContext->strDirective[MD5_AUTH_AUTHZID].Length; pPackedUserCtxt->uDigestLen[MD5_AUTH_OPAQUE] = (ULONG)pLsaContext->strDirective[MD5_AUTH_OPAQUE].Length; } pPackedUserCtxt->uSessionKeyLen = (ULONG)pLsaContext->strSessionKey.Length; pPackedUserCtxt->uAccountNameLen = (ULONG)pLsaContext->ustrAccountName.Length; // dup token if it exists if (pLsaContext->TokenHandle != NULL) { Status = g_LsaFunctions->DuplicateHandle( pLsaContext->TokenHandle, &(hTemp)); if (!NT_SUCCESS(Status)) { if (pPackedUserCtxt) { DigestFreeMemory(pPackedUserCtxt); } ContextData->cbBuffer = 0; DebugLog((DEB_ERROR, "SspMapContext: DuplicateHandle returns 0x%lx\n", Status)); goto CleanUp; } // Must pack the HANDLE into a fixed size structure for IA64 and i32 formats pPackedUserCtxt->ClientTokenHandle = (ULONG) ((ULONG_PTR)hTemp); DebugLog((DEB_TRACE, "SspMapContext: DuplicateHandle successful ClientTokenHandle 0x%x\n", pPackedUserCtxt->ClientTokenHandle)); } // Now copy over the string data elements pucLoc = &(pPackedUserCtxt->ucData); if (pDigest) { for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if (pPackedUserCtxt->uDigestLen[iAuth]) { memcpy(pucLoc, pDigest->refstrParam[iAuth].Buffer, pPackedUserCtxt->uDigestLen[iAuth]); pucLoc += pPackedUserCtxt->uDigestLen[iAuth]; } } } else { for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if (pPackedUserCtxt->uDigestLen[iAuth]) { memcpy(pucLoc, pLsaContext->strDirective[iAuth].Buffer, pPackedUserCtxt->uDigestLen[iAuth]); pucLoc += pPackedUserCtxt->uDigestLen[iAuth]; } } } if (pPackedUserCtxt->uSessionKeyLen) { memcpy(pucLoc, pLsaContext->strSessionKey.Buffer, pPackedUserCtxt->uSessionKeyLen); pucLoc += pPackedUserCtxt->uSessionKeyLen; } if (pPackedUserCtxt->uAccountNameLen) { memcpy(pucLoc, pLsaContext->ustrAccountName.Buffer, pPackedUserCtxt->uAccountNameLen); pucLoc += pPackedUserCtxt->uAccountNameLen; } ContextData->pvBuffer = pPackedUserCtxt; ContextData->cbBuffer = cbLenNeeded; ContextData->BufferType = SECBUFFER_TOKEN; CleanUp: DebugLog((DEB_TRACE_FUNC, "SspMapContext: Leaving LsaContext %lx Status 0x%x\n", pLsaContext, Status)); return(Status); } //+-------------------------------------------------------------------- // // Function: DigestUserHTTPHelper // // Synopsis: Process a SecBuffer with a given User Security Context // Used with HTTP for auth after initial ASC/ISC exchange // // Arguments: pContext - UserMode Context for the security state // Op - operation to perform on the Sec buffers // pMessage - sec buffers to processs and return output // // Returns: NTSTATUS // // Notes: // //--------------------------------------------------------------------- NTSTATUS NTAPI DigestUserHTTPHelper( IN PDIGEST_USERCONTEXT pContext, IN eSignSealOp Op, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; ULONG ulSeqNo = 0; PSecBuffer pChalRspInputToken = NULL; PSecBuffer pMethodInputToken = NULL; PSecBuffer pURIInputToken = NULL; PSecBuffer pHEntityInputToken = NULL; PSecBuffer pFirstOutputToken = NULL; DIGEST_PARAMETER Digest; PDIGEST_CONTEXT pNewContext = NULL; // keep pointer to release new context on error int iTemp = 0; int iAuth = 0; char *cptr = NULL; char szNCOverride[2*NCNUM]; // Overrides the provided NC if non-zero using only NCNUM digits STRING strURI; UNICODE_STRING refustrURI; BOOL fDefChars = FALSE; ZeroMemory(&strURI, sizeof(strURI)); ZeroMemory(&refustrURI, sizeof(refustrURI)); DebugLog((DEB_TRACE_FUNC, "DigestUserHTTPHelper: Entering \n")); DigestInit(&Digest); if (pSecBuff->cBuffers < 1) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: Not enough input buffers 0x%x\n", Status)); goto CleanUp; } pChalRspInputToken = &(pSecBuff->pBuffers[0]); if (!ContextIsTokenOK(pChalRspInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: ContextIsTokenOK (ChalRspInputToken) failed 0x%x\n", Status)); goto CleanUp; } // We have input in the SECBUFFER 0th location - parse it Status = DigestParser2(pChalRspInputToken, MD5_AUTH_NAMES, MD5_AUTH_LAST, &Digest); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserHTTPHelper: DigestParser error 0x%x\n", Status)); goto CleanUp; } // Now determine all of the other buffers DebugLog((DEB_TRACE, "DigestUserHTTPHelper: pContext->ContextReq 0x%lx \n", pContext->ContextReq)); DebugLog((DEB_TRACE, "DigestUserHTTPHelper: HTTP SecBuffer Format\n")); // Retrieve the information from the SecBuffers & check proper formattting if (pSecBuff->cBuffers < 4) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: Not enough input buffers 0x%x\n", Status)); goto CleanUp; } pMethodInputToken = &(pSecBuff->pBuffers[1]); if (!ContextIsTokenOK(pMethodInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { // Check to make sure that string is present Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: ContextIsTokenOK (MethodInputToken) failed 0x%x\n", Status)); goto CleanUp; } pURIInputToken = &(pSecBuff->pBuffers[2]); if (!ContextIsTokenOK(pURIInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: ContextIsTokenOK (URIInputToken) failed 0x%x\n", Status)); goto CleanUp; } pHEntityInputToken = &(pSecBuff->pBuffers[3]); if (!ContextIsTokenOK(pHEntityInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: ContextIsTokenOK (HEntityInputToken) failed 0x%x\n", Status)); goto CleanUp; } // Take care of the output buffer if (Op == eSign) { if (pSecBuff->cBuffers < 5) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: No Output Buffers %d\n", Status)); goto CleanUp; } pFirstOutputToken = &(pSecBuff->pBuffers[4]); if (!ContextIsTokenOK(pFirstOutputToken, 0)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper, ContextIsTokenOK (FirstOutputToken) failed 0x%x\n", Status)); goto CleanUp; } // Reset output buffer if (pFirstOutputToken && (pFirstOutputToken->pvBuffer) && (pFirstOutputToken->cbBuffer >= 1)) { cptr = (char *)pFirstOutputToken->pvBuffer; *cptr = '\0'; } } else { pFirstOutputToken = NULL; // There is no output buffer } // Verify that there is a valid Method provided if (!pMethodInputToken->pvBuffer || !pMethodInputToken->cbBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: Method SecBuffer must have valid method string status 0x%x\n", Status)); goto CleanUp; } iTemp = strlencounted((char *)pMethodInputToken->pvBuffer, pMethodInputToken->cbBuffer); if (!iTemp) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: Method SecBuffer must have valid method string status 0x%x\n", Status)); goto CleanUp; } Digest.refstrParam[MD5_AUTH_METHOD].Length = (USHORT)iTemp; Digest.refstrParam[MD5_AUTH_METHOD].MaximumLength = (unsigned short)(pMethodInputToken->cbBuffer); Digest.refstrParam[MD5_AUTH_METHOD].Buffer = (char *)pMethodInputToken->pvBuffer; // refernce memory - no alloc!!!! // Check to see if we have H(Entity) data to utilize if (pHEntityInputToken->cbBuffer) { // Verify that there is a valid Method provided if (!pHEntityInputToken->pvBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: HEntity SecBuffer must have valid string status 0x%x\n", Status)); goto CleanUp; } iTemp = strlencounted((char *)pHEntityInputToken->pvBuffer, pHEntityInputToken->cbBuffer); if ((iTemp != 0) && (iTemp != (MD5_HASH_BYTESIZE * 2))) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: HEntity SecBuffer must have valid MD5 Hash data 0x%x\n", Status)); goto CleanUp; } if (iTemp) { Digest.refstrParam[MD5_AUTH_HENTITY].Length = (USHORT)iTemp; Digest.refstrParam[MD5_AUTH_HENTITY].MaximumLength = (unsigned short)(pHEntityInputToken->cbBuffer); Digest.refstrParam[MD5_AUTH_HENTITY].Buffer = (char *)pHEntityInputToken->pvBuffer; // refernce memory - no alloc!!!! } } // Import the URI if it is a sign otherwise verify URI match if verify if (Op == eSign) { // Pull in the URI provided in SecBuffer if (!pURIInputToken || !pURIInputToken->cbBuffer || !pURIInputToken->pvBuffer) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: URI SecBuffer must have valid string 0x%x\n", Status)); goto CleanUp; } iTemp = 0; if (PBUFFERTYPE(pURIInputToken) == SECBUFFER_PKG_PARAMS) { iTemp = strlencounted((char *)pURIInputToken->pvBuffer, pURIInputToken->cbBuffer); if (iTemp > 0) { Status = StringCharDuplicate(&strURI, (char *)pURIInputToken->pvBuffer, (USHORT)iTemp); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserHTTPHelper: StringCharDuplicate error 0x%x\n", Status)); goto CleanUp; } } } else { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserHTTPHelper: URI buffer type invalid error %d\n", Status)); goto CleanUp; } StringReference(&(Digest.refstrParam[MD5_AUTH_URI]), &strURI); // refernce memory - no alloc!!!! } // If we have a NonceCount in the MessageSequenceNumber then use that if (MessageSeqNo) { ulSeqNo = MessageSeqNo; } else { ulSeqNo = pContext->ulNC + 1; // Else use the next sequence number } sprintf(szNCOverride, "%0.8x", ulSeqNo); // Buffer is twice as big as we need (for safety) so just clip out first 8 characters szNCOverride[NCNUM] = '\0'; // clip to 8 digits DebugLog((DEB_TRACE, "DigestUserHTTPHelper: Message Sequence NC is %s\n", szNCOverride)); Digest.refstrParam[MD5_AUTH_NC].Length = (USHORT)NCNUM; Digest.refstrParam[MD5_AUTH_NC].MaximumLength = (unsigned short)(NCNUM+1); Digest.refstrParam[MD5_AUTH_NC].Buffer = (char *)szNCOverride; // refernce memory - no alloc!!!! // Now link in the stored context values into the digest if this is a SignMessage // If there are values there from the input auth line then override them with context's value if (Op == eSign) { for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if (pContext->strParam[iAuth].Length) { // Link in only if passed into the user context from the LSA context Digest.refstrParam[iAuth].Length = pContext->strParam[iAuth].Length; Digest.refstrParam[iAuth].MaximumLength = pContext->strParam[iAuth].MaximumLength; Digest.refstrParam[iAuth].Buffer = pContext->strParam[iAuth].Buffer; // reference memory - no alloc!!!! } } } DebugLog((DEB_TRACE, "DigestUserHTTPHelper: Digest inputs processing completed\n")); Status = DigestUserProcessParameters(pContext, &Digest, pFirstOutputToken); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserHTTPHelper: DigestUserProcessParameters error 0x%x\n", Status)); goto CleanUp; } // For testing if nonce is stale, but digest calc still verified if (Op == eVerify) { if (NT_SUCCESS(Status) && NonceIsExpired(&(Digest.refstrParam[MD5_AUTH_NONCE]))) { Status = SEC_E_CONTEXT_EXPIRED; DebugLog((DEB_TRACE, "DigestUserHTTPHelper: NONCE is out of date. Flag stale return Status 0x%x\n", Status)); } } pContext->ulNC = ulSeqNo; // Everything verified so increment to next nonce count CleanUp: DigestFree(&Digest); StringFree(&strURI); DebugLog((DEB_TRACE_FUNC, "DigestUserHTTPHelper: Leaving Status 0x%x\n", Status)); return(Status); } //+-------------------------------------------------------------------- // // Function: DigestUserSignHelper // // Synopsis: Process a SecBuffer with a given User Security Context // Used with SASL section 2.3 RFC // // Arguments: pContext - UserMode Context for the security state // Op - operation to perform on the Sec buffers // pMessage - sec buffers to processs and return output // // // Returns: NTSTATUS // // Notes: // //--------------------------------------------------------------------- NTSTATUS NTAPI DigestUserSignHelper( IN PDIGEST_USERCONTEXT pContext, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDWORD pdwSeqNum = NULL; // points to the Sequence number to use PSecBuffer pSecBufToken = NULL; PSecBuffer pSecBufData = NULL; PSecBuffer pSecBufPad = NULL; PSecBuffer pSecBufHMAC = NULL; // Points to the HMAC appended to the data block PSecBuffer pSecBufMsg = NULL; // Points to the data section BOOL bServer = FALSE; SASL_MAC_BLOCK MacBlock; DWORD dwSeqNumber = 0; STRING strcSignKeyConst; // pointer to a constant valued string ULONG Index = 0; DebugLog((DEB_TRACE_FUNC, "DigestUserSignHelper: Entering \n")); ZeroMemory(&MacBlock, sizeof(SASL_MAC_BLOCK)); RtlInitString(&strcSignKeyConst, NULL); bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < pSecBuff->cBuffers ; Index++ ) { if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_TOKEN) { pSecBufToken = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_DATA) { pSecBufData = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_PADDING) { pSecBufPad = &pSecBuff->pBuffers[Index]; } } if ((!pSecBufPad) || (!pSecBufPad->cbBuffer)) { // If no SECBUFFER_PADDING, use SECBUFFER_TOKEN pSecBufHMAC = pSecBufToken; } else { pSecBufHMAC = pSecBufPad; if (pSecBufToken) { pSecBufToken->cbBuffer = 0; } } if (!ContextIsTokenOK(pSecBufHMAC, 0) || (pSecBufHMAC->cbBuffer < MAC_BLOCK_SIZE)) { Status = SEC_E_BUFFER_TOO_SMALL; DebugLog((DEB_ERROR, "DigestUserSignHelper: ContextIsTokenOK (SignatureToken) failed 0x%x\n", Status)); goto CleanUp; } if (!ContextIsTokenOK(pSecBufData, 0)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserSignHelper: ContextIsTokenOK (SecBufMsg) failed 0x%x\n", Status)); goto CleanUp; } // Determine the sequence number & Constant Key Sring to utilize acting as the server if (bServer) { pdwSeqNum = &(pContext->dwSendSeqNum); RtlInitString(&strcSignKeyConst, SASL_S2C_SIGN_KEY); DebugLog((DEB_TRACE, "DigestUserSignHelper: Signing in Server Mode (Message StoC) SeqNum %d\n", *pdwSeqNum)); } else { // acting as the client pdwSeqNum = &(pContext->dwSendSeqNum); RtlInitString(&strcSignKeyConst, SASL_C2S_SIGN_KEY); DebugLog((DEB_TRACE, "DigestUserSignHelper: Signing in Client Mode (Message CtoS) SeqNum %d\n", *pdwSeqNum)); } Status = CalculateSASLHMAC(pContext, TRUE, &strcSignKeyConst, *pdwSeqNum, (PBYTE)pSecBufData->pvBuffer, pSecBufData->cbBuffer, &MacBlock); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSignHelper: Error in CalculateSASLHMAC status 0x%x\n", Status)); goto CleanUp; } // Write the calculated MAC block out to the SecBuffer memcpy(pSecBufHMAC->pvBuffer, &MacBlock, MAC_BLOCK_SIZE); DebugLog((DEB_TRACE, "DigestUserSignHelper: Wrote out the calculated MAC Block.\n")); pSecBufHMAC->cbBuffer = MAC_BLOCK_SIZE; // indicate number of bytes we used for padding and HMAC block // completed all tasks down to here. Need to update the sequence number (*pdwSeqNum)++; DebugLog((DEB_TRACE, "DigestUserSignHelper: Updated SeqNum to %d\n", *pdwSeqNum)); CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUserSignHelper: Leaving Status 0x%x\n", Status)); return(Status); } //+-------------------------------------------------------------------- // // Function: DigestUserVerifyHelper // // Synopsis: Process a SecBuffer with a given User Security Context // Used with SASL section 2.3 RFC // // Arguments: pContext - UserMode Context for the security state // Op - operation to perform on the Sec buffers // pMessage - sec buffers to processs and return output // // // Returns: NTSTATUS // // Notes: // //--------------------------------------------------------------------- NTSTATUS NTAPI DigestUserVerifyHelper( IN PDIGEST_USERCONTEXT pContext, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDWORD pdwSeqNum = NULL; // points to the Sequence number to use PBYTE pMsgHMAC = NULL; // Location of the HMAC in the message PSecBuffer pSecBufData = NULL; PSecBuffer pSecBufStream = NULL; PSecBuffer pSecBufHMAC = NULL; // Points to the HMAC appended to the data block PSecBuffer pSecBufMsg = NULL; // Points to the data section BOOL bServer = FALSE; SASL_MAC_BLOCK MacBlock; SASL_MAC_BLOCK TokenMacBlock; DWORD dwSeqNumber = 0; STRING strcSignKeyConst; ULONG cbSecBufMsgIntegrity = 0; // Number of bytes in message to calc HMAC on ULONG Index = 0; #if DBG char szTemp[TEMPSIZE]; ZeroMemory(szTemp, TEMPSIZE); #endif DebugLog((DEB_TRACE_FUNC, "DigestUserVerifyHelper: Entering \n")); ZeroMemory(&MacBlock, sizeof(SASL_MAC_BLOCK)); ZeroMemory(&TokenMacBlock, sizeof(SASL_MAC_BLOCK)); RtlInitString(&strcSignKeyConst, NULL); bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < pSecBuff->cBuffers ; Index++ ) { if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_DATA) { pSecBufData = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_STREAM) { pSecBufStream = &pSecBuff->pBuffers[Index]; } } // Must be for decrypt/verify if ((!pSecBufStream) || (!pSecBufStream->cbBuffer)) { // If no SECBUFFER_STREAM, use SECBUFFER_DATA pSecBufMsg = pSecBufData; } else { pSecBufMsg = pSecBufStream; } if ((!ContextIsTokenOK(pSecBufMsg, 0)) || (pSecBufMsg->cbBuffer < MAC_BLOCK_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserVerifyHelper: ContextIsTokenOK (SecBufMsg) decrypt/verify failed 0x%x\n", Status)); goto CleanUp; } // Strip off the MsgType and the Sequence Number cbSecBufMsgIntegrity = pSecBufMsg->cbBuffer - (MAC_BLOCK_SIZE); // Determine the sequence number to utilize acting as the server if (bServer) { pdwSeqNum = &(pContext->dwRecvSeqNum); RtlInitString(&strcSignKeyConst, SASL_C2S_SIGN_KEY); DebugLog((DEB_TRACE, "DigestUserVerifyHelper: Verifying in Server Mode (Message CtoS) SeqNum %d\n", *pdwSeqNum)); } else { // acting as the client pdwSeqNum = &(pContext->dwRecvSeqNum); RtlInitString(&strcSignKeyConst, SASL_S2C_SIGN_KEY); DebugLog((DEB_TRACE, "DigestUserVerifyHelper: Verifying in Client Mode (Message StoC) SeqNum %d\n", *pdwSeqNum)); } Status = CalculateSASLHMAC(pContext, FALSE, &strcSignKeyConst, *pdwSeqNum, (PBYTE)pSecBufMsg->pvBuffer, cbSecBufMsgIntegrity, &MacBlock); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserVerifyHelper: Error in CalculateSASLHMAC status 0x%x\n", Status)); goto CleanUp; } DebugLog((DEB_TRACE, "DigestUserVerifyHelper: Ready to compare MacBlocks\n")); // Check validity of MAC block ONLY do not write it out pMsgHMAC = (PBYTE)pSecBufMsg->pvBuffer + cbSecBufMsgIntegrity; memcpy(&TokenMacBlock, pMsgHMAC, MAC_BLOCK_SIZE); if (MacBlock.dwSeqNumber != TokenMacBlock.dwSeqNumber) { Status = SEC_E_OUT_OF_SEQUENCE; DebugLog((DEB_ERROR, "DigestUserVerifyHelper: SASL MAC blocks out of sequence. Failed verify. Status 0x%x\n", Status)); #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&TokenMacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: Token's HMAC-MD5 block %s\n", szTemp)); ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&MacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: TComputed HMAC-MD5 block %s\n", szTemp)); #endif goto CleanUp; } if (memcmp(&MacBlock, &TokenMacBlock, MAC_BLOCK_SIZE)) { Status = SEC_E_MESSAGE_ALTERED; DebugLog((DEB_ERROR, "DigestUserVerifyHelper: SASL MAC blocks do not match. Failed verify. Status 0x%x\n", Status)); #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&TokenMacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: Token's HMAC-MD5 block %s\n", szTemp)); ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&MacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: TComputed HMAC-MD5 block %s\n", szTemp)); #endif goto CleanUp; } else { DebugLog((DEB_TRACE, "DigestUserVerifyHelper: SASL MAC blocks match!\n")); } // completed all tasks down to here. Need to update the sequence number (*pdwSeqNum)++; // Update the Data information (without the attached HMAC info block if (pSecBufData) { pSecBufData->cbBuffer = pSecBufMsg->cbBuffer - MAC_BLOCK_SIZE; pSecBufData->pvBuffer = pSecBufMsg->pvBuffer; } DebugLog((DEB_TRACE, "DigestUserVerifyHelper: Updated SeqNum to %d\n", *pdwSeqNum)); CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUserVerifyHelper: Leaving Status 0x%x\n", Status)); return(Status); } //+-------------------------------------------------------------------- // // Function: DigestUserSealHelper // // Synopsis: Process a SecBuffer with a given User Security Context // Used with SASL section 2.3 RFC // // Arguments: pContext - UserMode Context for the security state // Op - operation to perform on the Sec buffers // pMessage - sec buffers to processs and return output // // // Returns: NTSTATUS // // Notes: // //--------------------------------------------------------------------- NTSTATUS NTAPI DigestUserSealHelper( IN PDIGEST_USERCONTEXT pContext, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDWORD pdwSeqNum = NULL; // points to the Sequence number to use PSecBuffer pSecBufToken = NULL; PSecBuffer pSecBufData = NULL; PSecBuffer pSecBufPad = NULL; PSecBuffer pSecBufHMAC = NULL; // Points to the HMAC appended to the data block PSecBuffer pSecBufMsg = NULL; // Points to the data section BOOL bServer = FALSE; SASL_MAC_BLOCK MacBlock; DWORD dwSeqNumber = 0; STRING strcSignKeyConst; STRING strcSealKeyConst; PUCHAR pbIV = NULL; BYTE bKcTempData[MD5_HASH_BYTESIZE]; // Message integrity keys RFC 2831 sec 2.3 ULONG Index = 0; USHORT cbHA1n = 0; // Number of bytes for Ha1 in Kcc/Kcs DWORD cbKey = 0; // Number of bytes of Kcc/Kcs to use for the key DWORD cbKeyNoParity = 0; // Number of bytes of Kcc/Kcs to use for the key with no parity DWORD cbTempKey = 0; ULONG cbBlockSize = RC4_BLOCKSIZE; // Blocksize for the given cipher ULONG cbPrefixPadding = 0; // number of bytes needed for padding out to blocksize ULONG cbBlocks = 0; PBYTE pHMACTemp = NULL; ALG_ID Algid = 0; DebugLog((DEB_TRACE_FUNC, "DigestUserSealHelper: Entering \n")); ZeroMemory(&MacBlock, sizeof(SASL_MAC_BLOCK)); RtlInitString(&strcSignKeyConst, NULL); RtlInitString(&strcSealKeyConst, NULL); bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < pSecBuff->cBuffers ; Index++ ) { if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_TOKEN) { pSecBufToken = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_DATA) { pSecBufData = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_PADDING) { pSecBufPad = &pSecBuff->pBuffers[Index]; } } if ((!pSecBufPad) || (!pSecBufPad->cbBuffer)) { // If no SECBUFFER_PADDING, use SECBUFFER_TOKEN pSecBufHMAC = pSecBufToken; } else { pSecBufHMAC = pSecBufPad; if (pSecBufToken) { pSecBufToken->cbBuffer = 0; } } if (!ContextIsTokenOK(pSecBufHMAC, 0) || (pSecBufHMAC->cbBuffer < (MAC_BLOCK_SIZE + MAX_PADDING))) { Status = SEC_E_BUFFER_TOO_SMALL; DebugLog((DEB_ERROR, "DigestUserSealHelper: ContextIsTokenOK (SignatureToken) failed 0x%x\n", Status)); goto CleanUp; } if (!ContextIsTokenOK(pSecBufData, 0)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserSealHelper: ContextIsTokenOK (SecBufMsg) failed 0x%x\n", Status)); goto CleanUp; } // Determine the sequence number & Constant Key Sring to utilize acting as the server if (bServer) { pdwSeqNum = &(pContext->dwSendSeqNum); RtlInitString(&strcSignKeyConst, SASL_S2C_SIGN_KEY); RtlInitString(&strcSealKeyConst, SASL_S2C_SEAL_KEY); DebugLog((DEB_TRACE, "DigestUserSealHelper: Signing in Server Mode (Message StoC) SeqNum %d\n", *pdwSeqNum)); } else { // acting as the client pdwSeqNum = &(pContext->dwSendSeqNum); RtlInitString(&strcSignKeyConst, SASL_C2S_SIGN_KEY); RtlInitString(&strcSealKeyConst, SASL_C2S_SEAL_KEY); DebugLog((DEB_TRACE, "DigestUserSealHelper: Signing in Client Mode (Message CtoS) SeqNum %d\n", *pdwSeqNum)); } // Based on the Cypher selected - establish the byte count parameters - magic numbers from RFC if (pContext->typeCipher == CIPHER_RC4) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_RC4_40) { cbHA1n = 5; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_RC4_56) { cbHA1n = 7; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_DES) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 8; // number of bytes to use from Kcc/Kcs cbKeyNoParity = 7; cbBlockSize = DES_BLOCKSIZE; // DES uses a blocksize of 8 Algid = CALG_DES; } else if (pContext->typeCipher == CIPHER_3DES) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs cbKeyNoParity = 14; cbBlockSize = DES_BLOCKSIZE; // DES uses a blocksize of 8 Algid = CALG_3DES_112; } else { Status = SEC_E_CRYPTO_SYSTEM_INVALID; DebugLog((DEB_ERROR, "DigestUserSealHelper: ContextIsTokenOK (SecBufMsg) failed 0x%x\n", Status)); goto CleanUp; } // If the cipher is not a stream cipher - the place prefix padding before SASL MAC // Modified to include padding based on message datasize + the 10 byte HMAC if (cbBlockSize != 1) { cbBlocks = (pSecBufData->cbBuffer + SASL_MAC_HMAC_SIZE) / cbBlockSize; // integer divison cbPrefixPadding = cbBlockSize - ((pSecBufData->cbBuffer + SASL_MAC_HMAC_SIZE) - (cbBlockSize * cbBlocks)); if (!cbPrefixPadding) { cbPrefixPadding = cbBlockSize; // if padding is zero set it to the blocksize - i.e. always pad } DebugLog((DEB_TRACE, "DigestUserSealHelper: DataSize %lu BlockSize %lu Padding %lu\n", pSecBufData->cbBuffer, cbBlockSize, cbPrefixPadding)); } Status = CalculateSASLHMAC(pContext, TRUE, &strcSignKeyConst, *pdwSeqNum, (PBYTE)pSecBufData->pvBuffer, pSecBufData->cbBuffer, &MacBlock); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in CalculateSASLHMAC status 0x%x\n", Status)); goto CleanUp; } // Write the calculated MAC block out to the SecBuffer // Put the padding as the prefix pHMACTemp = (PBYTE)pSecBufHMAC->pvBuffer; memset(pHMACTemp, cbPrefixPadding, cbPrefixPadding); memcpy(pHMACTemp + cbPrefixPadding, &MacBlock, MAC_BLOCK_SIZE); DebugLog((DEB_TRACE, "DigestUserSealHelper: Wrote out the calculated MAC Block.\n")); pSecBufHMAC->cbBuffer = MAC_BLOCK_SIZE + cbPrefixPadding; // indicate number of bytes we used for padding and HMAC block // Completed the Integrity calculation, now encrypt the data if requested // Encrypt the message, padding and first SASL_MAC_HMAC_SIZE (10) bytes of HMAC (the integrity value) // Compute Kc for encryption (seal) & generate Cryptkey if (pContext->hSealCryptKey == NULL) { ASSERT(*pdwSeqNum == 0); // Should be first call into package // Compute on first time call to encrypt - save for other sequence numbers Status = CalculateKc(pContext->bSessionKey, cbHA1n, &strcSealKeyConst, pContext->bKcSealHashData); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in CalculateKc status 0x%x\n", Status)); goto CleanUp; } // code to expand the DES key into multiple of 8 bytes (key with parity) if ((pContext->typeCipher == CIPHER_DES) || (pContext->typeCipher == CIPHER_3DES)) { Status = AddDESParity(pContext->bKcSealHashData, cbKeyNoParity, bKcTempData, &cbTempKey); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in AddDESParity status 0x%x\n", Status)); goto CleanUp; } // replace with DES parity version ASSERT(cbKey == cbTempKey); memcpy(pContext->bSealKey, bKcTempData, cbTempKey); pbIV = &(pContext->bKcSealHashData[8]); } else { memcpy(pContext->bSealKey, pContext->bKcSealHashData, MD5_HASH_BYTESIZE); pbIV = NULL; } // generate symmetric key from the cleartext Status = CreateSymmetricKey(Algid, cbKey, pContext->bSealKey, pbIV, &pContext->hSealCryptKey); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in CalculateKc status 0x%x\n", Status)); goto CleanUp; } } if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { DebugLog((DEB_TRACE, "DigestUserSealHelper: 3DES/DES Encryption\n")); // Specify IV - take only the last 8 bytes per RFC 2831 sect 2.4 Status = EncryptData2(pContext->hSealCryptKey, cbBlockSize, pSecBufData->cbBuffer, (PUCHAR)pSecBufData->pvBuffer, (cbPrefixPadding + SASL_MAC_HMAC_SIZE), pHMACTemp); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in EncryptData status 0x%x\n", Status)); goto CleanUp; } } else { Status = EncryptData2(pContext->hSealCryptKey, cbBlockSize, pSecBufData->cbBuffer, (PUCHAR)pSecBufData->pvBuffer, (cbPrefixPadding + SASL_MAC_HMAC_SIZE), pHMACTemp); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserSealHelper: Error in EncryptData status 0x%x\n", Status)); goto CleanUp; } } DebugLog((DEB_TRACE, "DigestUserSealHelper: Data encrypted\n")); // completed all tasks down to here. Need to update the sequence number (*pdwSeqNum)++; DebugLog((DEB_TRACE, "DigestUserSealHelper: Updated SeqNum to %d\n", *pdwSeqNum)); CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUserSealHelper: Leaving Status 0x%x\n", Status)); return(Status); } //+-------------------------------------------------------------------- // // Function: DigestUserUnsealHelper // // Synopsis: Process a SecBuffer with a given User Security Context // Used with SASL section 2.3 RFC // // Arguments: pContext - UserMode Context for the security state // Op - operation to perform on the Sec buffers // pMessage - sec buffers to processs and return output // // // Returns: NTSTATUS // // Notes: // //--------------------------------------------------------------------- NTSTATUS NTAPI DigestUserUnsealHelper( IN PDIGEST_USERCONTEXT pContext, IN OUT PSecBufferDesc pSecBuff, IN ULONG MessageSeqNo ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS SubStatus = STATUS_SUCCESS; PDWORD pdwSeqNum = NULL; // points to the Sequence number to use PSecBuffer pSecBufData = NULL; PSecBuffer pSecBufStream = NULL; PSecBuffer pSecBufHMAC = NULL; // Points to the HMAC appended to the data block PSecBuffer pSecBufMsg = NULL; // Points to the data section BOOL bServer = FALSE; SASL_MAC_BLOCK MacBlock; SASL_MAC_BLOCK TokenMacBlock; // Extract the HMAC block imbedded in the message DWORD dwSeqNumber = 0; STRING strcSignKeyConst; STRING strcSealKeyConst; PBYTE pMsgHMAC = NULL; BYTE bKcTempData[MD5_HASH_BYTESIZE]; // Message integrity keys RFC 2831 sec 2.3 PUCHAR pbIV = NULL; ULONG Index = 0; USHORT cbHA1n = 0; // Number of bytes for Ha1 in Kcc/Kcs DWORD cbKey = 0; // Number of bytes of Kcc/Kcs to use for the key DWORD cbKeyNoParity = 0; // Number of bytes of Kcc/Kcs to use for the key with no parity DWORD cbTempKey = 0; ULONG cbBlockSize = 1; // Blocksize for the given cipher UCHAR cbPrefixPadding = 0; // number of bytes needed for padding out to blocksize ULONG cbMsg = 0; // number of bytes in the actual message PBYTE pHMACTemp = NULL; PBYTE pMsgPadding = NULL; // Location of a padding byte ALG_ID Algid = 0; ULONG cbSecBufMsgPrivacy = 0; // Number of bytes to decrypt (unseal) #if DBG char szTemp[TEMPSIZE]; ULONG iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); #endif DebugLog((DEB_TRACE_FUNC, "DigestUserUnsealHelper: Entering\n")); ZeroMemory(&MacBlock, sizeof(MacBlock)); ZeroMemory(&TokenMacBlock, sizeof(TokenMacBlock)); RtlInitString(&strcSignKeyConst, NULL); RtlInitString(&strcSealKeyConst, NULL); bServer = pContext->CredentialUseFlags & DIGEST_CRED_INBOUND; // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < pSecBuff->cBuffers ; Index++ ) { if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_DATA) { pSecBufData = &pSecBuff->pBuffers[Index]; } if (BUFFERTYPE(pSecBuff->pBuffers[Index]) == SECBUFFER_STREAM) { pSecBufStream = &pSecBuff->pBuffers[Index]; } } // Must be for decrypt/verify if ((!pSecBufStream) || (!pSecBufStream->cbBuffer)) { // If no SECBUFFER_STREAM, use SECBUFFER_DATA pSecBufMsg = pSecBufData; } else { pSecBufMsg = pSecBufStream; } if ((!ContextIsTokenOK(pSecBufMsg, 0)) || (pSecBufMsg->cbBuffer < MAC_BLOCK_SIZE)) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: ContextIsTokenOK (SecBufMsg) decrypt/verify failed 0x%x\n", Status)); goto CleanUp; } // Strip off the MsgType and the Sequence Number cbSecBufMsgPrivacy = pSecBufMsg->cbBuffer - (SASL_MAC_MSG_SIZE + SASL_MAC_SEQ_SIZE); if (!ContextIsTokenOK(pSecBufMsg, 0) || (pSecBufMsg->cbBuffer < MAC_BLOCK_SIZE)) { Status = SEC_E_BUFFER_TOO_SMALL; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: ContextIsTokenOK (SignatureToken) failed 0x%x\n", Status)); goto CleanUp; } // Determine the sequence number & Constant Key Sring to utilize acting as the server if (bServer) { pdwSeqNum = &(pContext->dwRecvSeqNum); RtlInitString(&strcSignKeyConst, SASL_C2S_SIGN_KEY); RtlInitString(&strcSealKeyConst, SASL_C2S_SEAL_KEY); DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Signing in Server Mode (Message StoC) SeqNum %d\n", *pdwSeqNum)); } else { // acting as the client pdwSeqNum = &(pContext->dwRecvSeqNum); RtlInitString(&strcSignKeyConst, SASL_S2C_SIGN_KEY); RtlInitString(&strcSealKeyConst, SASL_S2C_SEAL_KEY); DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Signing in Client Mode (Message CtoS) SeqNum %d\n", *pdwSeqNum)); } // Based on the Cypher selected - establish the byte count parameters - magic numbers from RFC if (pContext->typeCipher == CIPHER_RC4) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_RC4_40) { cbHA1n = 5; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_RC4_56) { cbHA1n = 7; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs Algid = CALG_RC4; } else if (pContext->typeCipher == CIPHER_DES) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 8; // number of bytes to use from Kcc/Kcs cbKeyNoParity = 7; cbBlockSize = 8; // DES uses a blocksize of 8 Algid = CALG_DES; } else if (pContext->typeCipher == CIPHER_3DES) { cbHA1n = 16; // RFC 2831 sect 2.4 cbKey = 16; // number of bytes to use from Kcc/Kcs cbKeyNoParity = 14; cbBlockSize = 8; // DES uses a blocksize of 8 Algid = CALG_3DES_112; } else { Status = SEC_E_CRYPTO_SYSTEM_INVALID; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: ContextIsTokenOK (SecBufMsg) failed 0x%x\n", Status)); goto CleanUp; } // Decrypt the message, padding and first SASL_MAC_HMAC_SIZE (10) bytes of HMAC (the integrity value) // Compute Kc for encryption (seal) if (pContext->hUnsealCryptKey == NULL) { ASSERT(*pdwSeqNum == 0); // Compute on first time call to encrypt - save for other sequence numbers Status = CalculateKc(pContext->bSessionKey, cbHA1n, &strcSealKeyConst, pContext->bKcUnsealHashData); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in CalculateKc status 0x%x\n", Status)); goto CleanUp; } // code to expand the DES key into multiple of 8 bytes (key with parity) if ((pContext->typeCipher == CIPHER_DES) || (pContext->typeCipher == CIPHER_3DES)) { Status = AddDESParity(pContext->bKcUnsealHashData, cbKeyNoParity, bKcTempData, &cbTempKey); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in AddDESParity status 0x%x\n", Status)); goto CleanUp; } // replace with DES parity version ASSERT(cbKey == cbTempKey); memcpy(pContext->bUnsealKey, bKcTempData, cbKey); pbIV = &(pContext->bKcUnsealHashData[8]); } else { // For RC4 ciphers memcpy(pContext->bUnsealKey, pContext->bKcUnsealHashData, MD5_HASH_BYTESIZE); pbIV = NULL; } // generate the symmetric key from the cleartext Status = CreateSymmetricKey(Algid, cbKey, pContext->bUnsealKey, pbIV, &pContext->hUnsealCryptKey); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in CalculateKc status 0x%x\n", Status)); goto CleanUp; } } if ((pContext->typeCipher == CIPHER_3DES) || (pContext->typeCipher == CIPHER_DES)) { // Specify IV - take only the last 8 bytes per RFC 2831 sect 2.4 Status = DecryptData(pContext->hUnsealCryptKey, cbSecBufMsgPrivacy, (PUCHAR)pSecBufMsg->pvBuffer); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in DecryptData status 0x%x\n", Status)); goto CleanUp; } // Padding length is indicated in the actual padding - get the pad byte near HMAC if (pSecBufMsg->cbBuffer < (MAC_BLOCK_SIZE + 1)) { Status = STATUS_INTERNAL_ERROR; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Determining padding not enough space status 0x%x\n", Status)); goto CleanUp; } pMsgPadding = (PBYTE)pSecBufMsg->pvBuffer + (pSecBufMsg->cbBuffer - (MAC_BLOCK_SIZE + 1)); #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); if ((MAC_BLOCK_SIZE + 1) < iTempLen) { iTempLen = (MAC_BLOCK_SIZE + 1); } BinToHex(pMsgPadding, iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "DecryptData: HMAC & padding byte Data bytes (%dof%d bytes) %s\n", iTempLen, (MAC_BLOCK_SIZE + 1), szTemp)); } DebugLog((DEB_TRACE, "DecryptData: MAC block size %d bytes\n", MAC_BLOCK_SIZE)); #endif cbPrefixPadding = *pMsgPadding; if (cbPrefixPadding > MAX_PADDING) { Status = STATUS_INTERNAL_ERROR; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Padding selected (%d) too large status 0x%x\n", cbPrefixPadding, Status)); goto CleanUp; } if (pSecBufMsg->cbBuffer < (MAC_BLOCK_SIZE + cbPrefixPadding)) { Status = STATUS_INTERNAL_ERROR; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Message incorrect length status 0x%x\n", Status)); goto CleanUp; } cbMsg = pSecBufMsg->cbBuffer - (MAC_BLOCK_SIZE + cbPrefixPadding); DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Padding found to be %d bytes\n", cbPrefixPadding)); } else { Status = DecryptData(pContext->hUnsealCryptKey, cbSecBufMsgPrivacy, (PUCHAR)pSecBufMsg->pvBuffer); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in EncryptData status 0x%x\n", Status)); goto CleanUp; } // There is no padding on stream ciphers, so just remove the SASL HMAC block cbMsg = pSecBufMsg->cbBuffer - MAC_BLOCK_SIZE; DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Stream Cipher - No padding\n")); } // Locate the beginning of the message pMsgHMAC = (PBYTE)pSecBufMsg->pvBuffer + (pSecBufMsg->cbBuffer - MAC_BLOCK_SIZE); Status = CalculateSASLHMAC(pContext, FALSE, &strcSignKeyConst, *pdwSeqNum, (PBYTE)pSecBufMsg->pvBuffer, cbMsg, &MacBlock); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "DigestUserUnsealHelper: Error in CalculateSASLHMAC status 0x%x\n", Status)); goto CleanUp; } DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Ready to compare MacBlocks\n")); // Check validity of MAC block ONLY do not write it out memcpy(&TokenMacBlock, pMsgHMAC, MAC_BLOCK_SIZE); if (MacBlock.dwSeqNumber != TokenMacBlock.dwSeqNumber) { Status = SEC_E_OUT_OF_SEQUENCE; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: SASL MAC blocks out of sequence. Failed verify. Status 0x%x\n", Status)); #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&TokenMacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: Token's HMAC-MD5 block %s\n", szTemp)); ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&MacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: TComputed HMAC-MD5 block %s\n", szTemp)); #endif goto CleanUp; } if (memcmp(&MacBlock, &TokenMacBlock, MAC_BLOCK_SIZE)) { Status = SEC_E_MESSAGE_ALTERED; DebugLog((DEB_ERROR, "DigestUserUnsealHelper: SASL MAC blocks do not match. Failed verify. Status 0x%x\n", Status)); #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&TokenMacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: Token's HMAC-MD5 block %s\n", szTemp)); ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&MacBlock, MAC_BLOCK_SIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: TComputed HMAC-MD5 block %s\n", szTemp)); #endif goto CleanUp; } else { DebugLog((DEB_TRACE, "DigestUserUnsealHelper: SASL MAC blocks match!\n")); } // Write out to SECBUFFERDATA the length and location of message if (pSecBufData) { pSecBufData->cbBuffer = cbMsg; pSecBufData->pvBuffer = pSecBufMsg->pvBuffer; } // completed all tasks down to here. Need to update the sequence number (*pdwSeqNum)++; DebugLog((DEB_TRACE, "DigestUserUnsealHelper: Updated SeqNum to %d\n", *pdwSeqNum)); CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUserUnsealHelper: Leaving Status 0x%x\n", Status)); return(Status); } // Process the Digest information with the context info and generate any output token info NTSTATUS NTAPI DigestUserProcessParameters( IN PDIGEST_USERCONTEXT pContext, IN PDIGEST_PARAMETER pDigest, OUT PSecBuffer pFirstOutputToken) { NTSTATUS Status = STATUS_SUCCESS; ULONG ulNonceCount = 0; DebugLog((DEB_TRACE_FUNC, "DigestUserProcessParameters: Entering\n")); // Some common input verification tests // We must have a noncecount specified since we specified a qop in the Challenge // If we decide to support no noncecount modes then we need to make sure that qop is not specified if (pDigest->refstrParam[MD5_AUTH_NC].Length) { Status = RtlCharToInteger(pDigest->refstrParam[MD5_AUTH_NC].Buffer, HEXBASE, &ulNonceCount); if (!NT_SUCCESS(Status)) { Status = STATUS_INVALID_PARAMETER; DebugLog((DEB_ERROR, "DigestUserProcessParameters: Nonce Count badly formatted\n")); goto CleanUp; } } // Check nonceCount is incremented to preclude replay if (!(ulNonceCount > pContext->ulNC)) { // We failed to verify next noncecount Status = SEC_E_OUT_OF_SEQUENCE; DebugLog((DEB_ERROR, "DigestUserProcessParameters: NonceCount failed to increment!\n")); goto CleanUp; } // Since we are in UserMode we MUST have a sessionkey to use - if non then can not process if (!pContext->strSessionKey.Length) { Status = SEC_E_NO_AUTHENTICATING_AUTHORITY; // indicate that we needed a call to ASC or ISC first DebugLog((DEB_ERROR, "DigestUserProcessParameters: No Session Key contained in UserContext\n")); goto CleanUp; } // Copy the SessionKey from the Context into the Digest Structure to verify against // This will have Digest Auth routines use the SessionKey rather than recompute H(A1) StringFree(&(pDigest->strSessionKey)); Status = StringDuplicate(&(pDigest->strSessionKey), &(pContext->strSessionKey)); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserProcessParameters: Failed to copy over SessionKey\n")); goto CleanUp; } // Set the type of Digest Parameters we are to process pDigest->typeDigest = pContext->typeDigest; pDigest->typeQOP = pContext->typeQOP; pDigest->typeAlgorithm = pContext->typeAlgorithm; pDigest->typeCharset = pContext->typeCharset; DigestPrint(pDigest); // No check locally that Digest is authentic Status = DigestCalculation(pDigest, NULL); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserProcessParameters: Oh no we FAILED Authentication!!!!\n")); goto CleanUp; } // Send to output buffer only if there is an output buffer // This allows this routine to be used in UserMode if (pFirstOutputToken) { Status = DigestCreateChalResp(pDigest, NULL, pFirstOutputToken); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "DigestUserProcessParameters: Failed to create Output String\n")); goto CleanUp; } } CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUserProcessParameters: Leaving Status 0x%x\n", Status)); return(Status); } // Unpack the context from LSA mode into the User mode Context NTSTATUS DigestUnpackContext( IN PDIGEST_PACKED_USERCONTEXT pPackedUserContext, OUT PDIGEST_USERCONTEXT pContext) { NTSTATUS Status = STATUS_SUCCESS; PUCHAR pucLoc = NULL; USHORT uNumWChars = 0; int iAuth = 0; DebugLog((DEB_TRACE_FUNC, "DigestUnpackContext: Entering\n")); ASSERT(pContext); // // If TokenHandle is NULL, we are being called as // as an effect of InitializeSecurityContext, else we are // being called because of AcceptSecurityContext // if (pPackedUserContext->ClientTokenHandle != NULL) { DebugLog((DEB_TRACE, "DigestUnpackContext: Called from ASC\n" )); pContext->ClientTokenHandle = (HANDLE) ((ULONG_PTR)pPackedUserContext->ClientTokenHandle); if (FAILED(SspCreateTokenDacl(pContext->ClientTokenHandle))) { Status = STATUS_INVALID_HANDLE; DebugLog((DEB_ERROR, "DigestUnpackContext: SspCreateTokenDacl failed\n" )); goto CleanUp; } } else { DebugLog((DEB_TRACE, "DigestUnpackContext: Called from ISC\n" )); } // // Copy over all of the other fields - some data might be binary so // use RtlCopyMemory(Dest, Src, len) // pContext->Expires = pPackedUserContext->Expires; pContext->typeAlgorithm = (ALGORITHM_TYPE)pPackedUserContext->typeAlgorithm; pContext->typeCharset = (CHARSET_TYPE)pPackedUserContext->typeCharset; pContext->typeCipher = (CIPHER_TYPE)pPackedUserContext->typeCipher; pContext->typeDigest = (DIGEST_TYPE)pPackedUserContext->typeDigest; pContext->typeQOP = (QOP_TYPE)pPackedUserContext->typeQOP; pContext->ulSendMaxBuf = pPackedUserContext->ulSendMaxBuf; pContext->ulRecvMaxBuf = pPackedUserContext->ulRecvMaxBuf; pContext->ulNC = 1; // Force to one to account for ISC/ASC first message verify pContext->lReferences = 1; pContext->ContextReq = pPackedUserContext->ContextReq; pContext->CredentialUseFlags = pPackedUserContext->CredentialUseFlags; // Now check on the strings attached pucLoc = &(pPackedUserContext->ucData); for (iAuth = 0; iAuth < MD5_AUTH_LAST; iAuth++) { if (pPackedUserContext->uDigestLen[iAuth]) { Status = StringAllocate(&(pContext->strParam[iAuth]), (USHORT)pPackedUserContext->uDigestLen[iAuth]); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestUnpackContext: DigestAllocateMemory for Params returns NULL\n" )); goto CleanUp; } memcpy(pContext->strParam[iAuth].Buffer, pucLoc, (USHORT)pPackedUserContext->uDigestLen[iAuth]); pContext->strParam[iAuth].Length = (USHORT)pPackedUserContext->uDigestLen[iAuth]; pucLoc += (USHORT)pPackedUserContext->uDigestLen[iAuth]; // DebugLog((DEB_TRACE, "DigestUnpackContext: Param[%d] is length %d - %.50s\n", // iAuth, pPackedUserContext->uDigestLen[iAuth], pContext->strParam[iAuth].Buffer )); } } // Now do the SessionKey if (pPackedUserContext->uSessionKeyLen) { Status = StringAllocate(&(pContext->strSessionKey), (USHORT)pPackedUserContext->uSessionKeyLen); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestUnpackContext: DigestAllocateMemory for SessionKey returns NULL\n" )); goto CleanUp; } memcpy(pContext->strSessionKey.Buffer, pucLoc, pPackedUserContext->uSessionKeyLen); pContext->strSessionKey.Length = (USHORT)pPackedUserContext->uSessionKeyLen; pucLoc += (USHORT)pPackedUserContext->uSessionKeyLen; } // Now do the AccountName if (pPackedUserContext->uAccountNameLen) { uNumWChars = (USHORT)pPackedUserContext->uAccountNameLen / sizeof(WCHAR); Status = UnicodeStringAllocate(&(pContext->ustrAccountName), uNumWChars); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; DebugLog((DEB_ERROR, "DigestUnpackContext: DigestAllocateMemory for AccountName returns NULL\n" )); goto CleanUp; } memcpy(pContext->ustrAccountName.Buffer, pucLoc, pPackedUserContext->uAccountNameLen); pContext->ustrAccountName.Length = (USHORT)pPackedUserContext->uAccountNameLen; pucLoc += (USHORT)pPackedUserContext->uAccountNameLen; } // Now determine the binary version of the SessionKey from HEX() version ASSERT(pContext->strSessionKey.Length == MD5_HASH_HEX_SIZE); HexToBin(pContext->strSessionKey.Buffer, MD5_HASH_HEX_SIZE, pContext->bSessionKey); #if DBG char szTemp[TEMPSIZE]; ZeroMemory(szTemp, TEMPSIZE); BinToHex(pContext->bSessionKey, MD5_HASH_BYTESIZE, szTemp); DebugLog((DEB_TRACE, "DigestUnpackContext: verify SessionKey %Z is binary %s\n", &(pContext->strSessionKey), szTemp)); #endif CleanUp: DebugLog((DEB_TRACE_FUNC, "DigestUnpackContext: Leaving Status 0x%x\n", Status)); return(Status); } // Printout the fields present in usercontext pContext NTSTATUS UserContextPrint(PDIGEST_USERCONTEXT pContext) { NTSTATUS Status = STATUS_SUCCESS; int i = 0; if (!pContext) { return (STATUS_INVALID_PARAMETER); } DebugLog((DEB_TRACE_FUNC, "UserContext: Entering for Context Handle at 0x%x\n", pContext)); DebugLog((DEB_TRACE, "UserContext: NC %ld\n", pContext->ulNC)); DebugLog((DEB_TRACE, "UserContext: LSA Context 0x%x\n", pContext->LsaContext)); if (pContext->typeDigest == DIGEST_CLIENT) { DebugLog((DEB_TRACE, "UserContext: DIGEST_CLIENT\n")); } if (pContext->typeDigest == DIGEST_SERVER) { DebugLog((DEB_TRACE, "UserContext: DIGEST_SERVER\n")); } if (pContext->typeDigest == SASL_SERVER) { DebugLog((DEB_TRACE, "UserContext: SASL_SERVER\n")); } if (pContext->typeDigest == SASL_CLIENT) { DebugLog((DEB_TRACE, "UserContext: SASL_CLIENT\n")); } if (pContext->typeQOP == AUTH) { DebugLog((DEB_TRACE, "UserContext: QOP: AUTH\n")); } if (pContext->typeQOP == AUTH_INT) { DebugLog((DEB_TRACE, "UserContext: QOP: AUTH_INT\n")); } if (pContext->typeQOP == AUTH_CONF) { DebugLog((DEB_TRACE, "UserContext: QOP: AUTH_CONF\n")); } if (pContext->typeAlgorithm == MD5) { DebugLog((DEB_TRACE, "UserContext: Algorithm: MD5\n")); } if (pContext->typeAlgorithm == MD5_SESS) { DebugLog((DEB_TRACE, "UserContext: Algorithm: MD5_SESS\n")); } if (pContext->typeCharset == ISO_8859_1) { DebugLog((DEB_TRACE, "UserContext: Charset: ISO 8859-1\n")); } if (pContext->typeCharset == UTF_8) { DebugLog((DEB_TRACE, "UserContext: Charset: UTF-8\n")); } if (pContext->typeCipher == CIPHER_RC4) { DebugLog((DEB_TRACE, "UserContext: Cipher: CIPHER_RC4\n")); } else if (pContext->typeCipher == CIPHER_RC4_40) { DebugLog((DEB_TRACE, "UserContext: Cipher: CIPHER_RC4_40\n")); } else if (pContext->typeCipher == CIPHER_RC4_56) { DebugLog((DEB_TRACE, "UserContext: Cipher: CIPHER_RC4_56\n")); } else if (pContext->typeCipher == CIPHER_DES) { DebugLog((DEB_TRACE, "UserContext: Cipher: CIPHER_DES\n")); } else if (pContext->typeCipher == CIPHER_3DES) { DebugLog((DEB_TRACE, "UserContext: Cipher: CIPHER_3DES\n")); } DebugLog((DEB_TRACE, "UserContext: ContextReq 0x%lx CredentialUseFlags 0x%x\n", pContext->ContextReq, pContext->CredentialUseFlags)); for (i=0; i < MD5_AUTH_LAST;i++) { if (pContext->strParam[i].Buffer && pContext->strParam[i].Length) { DebugLog((DEB_TRACE, "UserContext: Digest[%d] = \"%Z\"\n", i, &pContext->strParam[i])); } } if (pContext->strSessionKey.Length) { DebugLog((DEB_TRACE, "UserContext: SessionKey %Z\n", &pContext->strSessionKey)); } if (pContext->ustrAccountName.Length) { DebugLog((DEB_TRACE, "UserContext: AccountName %wZ\n", &pContext->ustrAccountName)); } DebugLog((DEB_TRACE_FUNC, "UserContext: Leaving\n")); return(Status); } // CryptoAPI function support NTSTATUS SEC_ENTRY CreateSymmetricKey( IN ALG_ID Algid, IN DWORD cbKey, IN UCHAR *pbKey, IN UCHAR *pbIV, OUT HCRYPTKEY *phKey ) { NTSTATUS Status = STATUS_SUCCESS; PLAINTEXTBLOB PlainBlob; DebugLog((DEB_TRACE_FUNC, "CreateSymmetricKey: Entering\n")); ASSERT(*phKey == NULL); ZeroMemory(&PlainBlob, sizeof(PlainBlob)); if (cbKey > MD5_HASH_BYTESIZE) { DebugLog((DEB_ERROR, "CreateSymmetricKey: Shared key too long\n")); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } #if DBG char szTemp[TEMPSIZE]; // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex(pbKey, cbKey, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CreateSymmetricKey: Creating symmetric for %s\n", szTemp)); } #endif PlainBlob.Blob.bType = PLAINTEXTKEYBLOB; PlainBlob.Blob.bVersion = CUR_BLOB_VERSION; PlainBlob.Blob.reserved = 0; PlainBlob.Blob.aiKeyAlg = Algid; memcpy(PlainBlob.bKey, pbKey, cbKey); PlainBlob.dwKeyLen = cbKey; // import thw simpleblob to get a handle to the symmetric key if (!CryptImportKey(g_hCryptProv, (BYTE *)&PlainBlob, sizeof(PlainBlob), 0, 0, phKey)) { DebugLog((DEB_ERROR, "CreateSymmetricKey: CryptImportKey failed error 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; } if ((Algid == CALG_DES) || (Algid == CALG_3DES_112)) { if (!pbIV) { DebugLog((DEB_WARN, "CreateSymmetricKey: No IV selected for DES\n")); } else { #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex(pbIV, 8, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CreateSymmetricKey: IV bytes set to %s\n", szTemp)); } #endif if (!CryptSetKeyParam(*phKey, KP_IV, pbIV, 0)) { DebugLog((DEB_ERROR, "CreateSymmetricKey:CryptSetKeyParam() failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } } } CleanUp: DebugLog((DEB_TRACE_FUNC, "CreateSymmetricKey: Leaving status 0x%x\n", Status)); return(Status); } //+------------------------------------------------------------------------- // // Function: EncryptData2 // // Synopsis: Encrypt a data buffer (broken into two pieces DATA and Signature) // // Effects: no global effect. // // Arguments: // // IN Algid -- Encryption algorithm to utilize // IN pbIV -- DES salt (if any provided) // IN hKey -- symmetric key to utilize // IN cbBlocklength -- natural block length for encoding (RC will be 1 and DES will be 8) // IN cbData -- number of data bytes to encrypt // IN pbData -- pointer to data bytes to encrypt // IN cbSignature -- number of signature bytes to encrypt after Data is encrypted // IN pbSignature -- number of bytes in signature to encrypt // // Requires: no global requirements // // Returns: STATUS_SUCCESS, or resource error // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SEC_ENTRY EncryptData2( IN HCRYPTKEY hKey, IN ULONG cbBlocklength, IN ULONG cbData, IN OUT UCHAR *pbData, IN ULONG cbSignature, IN OUT UCHAR *pbSignature ) { DWORD dwBytesEncrypt = 0; ULONG cbTemp = 0; NTSTATUS Status = STATUS_SUCCESS; ULONG cbBlocks = 0; ULONG cbDataExtra = 0; ULONG cbCnt = 0; // number of bytes to alloc for merged data PBYTE pbBuff2 = NULL; // temp alloc for merge of extra bytes & signature PBYTE pbDataExtra = NULL; // location for start of extra memory bytes DebugLog((DEB_TRACE_FUNC, "EncryptData2: Entering %lu bytes\n", cbData)); DebugLog((DEB_TRACE, "EncryptData2: Blocklength %lu\n", cbBlocklength)); DebugLog((DEB_TRACE, "EncryptData2: Signature block %lu bytes\n", cbSignature)); // Check if encrypting 1 or two buffers if (!cbSignature) { DebugLog((DEB_TRACE, "EncryptData2: one buffer only - direct encode\n")); DebugLog((DEB_TRACE, "EncryptData2: buffer %lu bytes\n", cbData)); // Only one buffer utilized dwBytesEncrypt = cbData; if (!CryptEncrypt(hKey, 0, FALSE, 0, pbData, &dwBytesEncrypt, cbData)) { DebugLog((DEB_ERROR, "EncryptData2:CryptEncrypt one buffer failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } } else { // We have two buffers to encrypt // Identify if there are extra bytes beyond blocksize for cipher cbBlocks = cbData / cbBlocklength; // integer division cbDataExtra = cbData - (cbBlocklength * cbBlocks); DebugLog((DEB_TRACE, "EncryptData2: number of cipher blocks %lu number extra bytes %lu\n", cbBlocks, cbDataExtra)); if (cbDataExtra) { DebugLog((DEB_TRACE, "EncryptData2: merge signature - encrypt two buffers & replace\n")); // extra data bytes starting memory location pbDataExtra = pbData + (cbBlocklength * cbBlocks); // There are bytes outside a multiple of the cipher block size // create temp buffer for extra bytes and HMAC cbCnt = cbDataExtra + cbSignature; DebugLog((DEB_TRACE, "EncryptData2: merge block size %lu bytes\n", cbCnt)); pbBuff2 = (PBYTE)DigestAllocateMemory(cbCnt + MAX_PADDING); if (!pbBuff2) { DebugLog((DEB_ERROR, "EncryptData2:out of memory\n")); Status = SEC_E_INSUFFICIENT_MEMORY; goto CleanUp; } // copy over the bytes to temp buffer memcpy(pbBuff2, pbDataExtra, cbDataExtra); memcpy(pbBuff2 + cbDataExtra, pbSignature, cbSignature); // encrypt any multiples of blocklength in the data section (first buffer) if (cbBlocks) { dwBytesEncrypt = cbBlocklength * cbBlocks; DebugLog((DEB_TRACE, "EncryptData2: buffer 1 %lu bytes\n", dwBytesEncrypt)); if (!CryptEncrypt(hKey, 0, FALSE, 0, pbData, &dwBytesEncrypt, dwBytesEncrypt)) { DebugLog((DEB_ERROR, "EncryptData2:CryptEncrypt first buffer (blocklength) failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } } // encrypt the temp buffer - copy back to original locations afterwards dwBytesEncrypt = cbCnt; DebugLog((DEB_TRACE, "EncryptData2: buffer 2 encrypt %lu bytes in %lu byte buffer\n", dwBytesEncrypt, cbCnt + MAX_PADDING)); if (!CryptEncrypt(hKey, 0, FALSE, 0, pbBuff2, &dwBytesEncrypt, cbCnt + MAX_PADDING)) { DebugLog((DEB_ERROR, "EncryptData2:CryptEncrypt second buffer (blocklength) failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } memcpy(pbSignature, pbBuff2 + cbDataExtra, cbSignature); memcpy(pbDataExtra, pbBuff2, cbDataExtra); } else { DebugLog((DEB_TRACE, "EncryptData2: multiple of cipher blocksize - encrypt two buffers directly\n")); // encrypt data buffer and then signature buffer - data buffer is multiple of blocksize if (cbData) { dwBytesEncrypt = cbData; DebugLog((DEB_TRACE, "EncryptData2: buffer 1 %lu bytes\n", dwBytesEncrypt)); if (!CryptEncrypt(hKey, 0, FALSE, 0, pbData, &dwBytesEncrypt, cbData)) { DebugLog((DEB_ERROR, "EncryptData2:CryptEncrypt first buffer (blocklength) failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } DebugLog((DEB_TRACE, "EncryptData2: needed %lu bytes for encrypted buffer 1\n", dwBytesEncrypt)); } // Final encrypt of signature buffer dwBytesEncrypt = cbSignature; DebugLog((DEB_TRACE, "EncryptData2: buffer 2 %lu bytes\n", dwBytesEncrypt)); // We do our own padding so we must have Final=FALSE so CAPI will not add in one if (!CryptEncrypt(hKey, 0, FALSE, 0, pbSignature, &dwBytesEncrypt, cbSignature)) { DebugLog((DEB_ERROR, "EncryptData2:CryptEncrypt second buffer (blocklength) failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } DebugLog((DEB_TRACE, "EncryptData2: needed %lu bytes for encrypted buffer 2\n", dwBytesEncrypt)); } } goto CleanUp; CleanUp: if (pbBuff2) { DigestFreeMemory(pbBuff2); pbBuff2 = NULL; } DebugLog((DEB_TRACE_FUNC, "EncryptData2: Leaving status 0x%x\n", Status)); return(Status); } NTSTATUS SEC_ENTRY DecryptData( IN HCRYPTKEY hKey, IN ULONG cbData, IN OUT UCHAR *pbData ) { ULONG cb = cbData; ULONG cbTemp = 0; NTSTATUS Status = STATUS_SUCCESS; #if DBG char szTemp[TEMPSIZE]; ULONG iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); #endif DebugLog((DEB_TRACE_FUNC, "DecryptData: Entering %lu bytes at 0x%x\n", cbData, pbData)); #if DBG // Now convert the Hash to Hex - for TESTING ONLY iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); if (cbData < iTempLen) { iTempLen = cbData; } BinToHex(pbData, iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "DecryptData: Encrypted Data bytes (%dof%d bytes) %s\n", iTempLen, cbData, szTemp)); } iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); if (cbData < iTempLen) { iTempLen = cbData; } BinToHex((pbData + cbData - iTempLen), iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "DecryptData: Encrypted end of buffer (%dof%d bytes) %s\n", iTempLen, cbData, szTemp)); } #endif // import the simpleblob to get a handle to the symmetric key if (!CryptDecrypt(hKey, 0, FALSE, 0, pbData, &cb)) { DebugLog((DEB_ERROR, "DecryptData:CryptCreateHash() failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } #if DBG DebugLog((DEB_ERROR, "DecryptData: Decrypted number of bytes %lu\n", cb)); // Now convert the Hash to Hex - for TESTING ONLY iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); if (cb < iTempLen) { iTempLen = cb; } BinToHex(pbData, iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "DecryptData: Decrypted Data bytes (%dof%d bytes) %s\n", iTempLen, cbData, szTemp)); } iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); if (cb < iTempLen) { iTempLen = cb; } BinToHex((pbData + cb - iTempLen), iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "DecryptData: Decrypted end of buffer (%dof%d bytes) %s\n", iTempLen, cbData, szTemp)); } #endif CleanUp: DebugLog((DEB_TRACE_FUNC, "DecryptData: Leaving status 0x%x\n", Status)); return(Status); } NTSTATUS SEC_ENTRY CalculateSASLHMAC( IN PDIGEST_USERCONTEXT pContext, IN BOOL fSign, IN PSTRING pstrSignKeyConst, IN DWORD dwSeqNum, // Sequence number to process IN PBYTE pData, // location of data to HMAC IN ULONG cbData, // How many bytes of data to process OUT PSASL_MAC_BLOCK pMacBlock) { NTSTATUS Status = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; HCRYPTKEY hCryptKey = NULL; HMAC_INFO hmacinfo; BYTE bKiHashData[MD5_HASH_BYTESIZE]; // Message integrity keys RFC 2831 sec 2.3 DWORD cbKiHashData = 0; // Size of Message integrity keys BYTE bHMACData[HMAC_MD5_HASH_BYTESIZE]; DWORD cbHMACData = 0; #if DBG char szTemp[TEMPSIZE]; ULONG iTempLen = 20; ZeroMemory(szTemp, TEMPSIZE); #endif DebugLog((DEB_TRACE_FUNC, "CalculateSASLHMAC: Entering\n")); DebugLog((DEB_TRACE, "CalculateSASLHMAC: Processing %d bytes in data block\n", cbData)); // Clear the output ZeroMemory(pMacBlock, sizeof(SASL_MAC_BLOCK)); // Initialize local variables ZeroMemory(bKiHashData, MD5_HASH_BYTESIZE); ZeroMemory(bHMACData, HMAC_MD5_HASH_BYTESIZE); ZeroMemory(&hmacinfo, sizeof(HMAC_INFO)); // Always do an integrety calculation on the input data // We should have clear text data at this stage if (!dwSeqNum) { if ( !CryptCreateHash( g_hCryptProv, CALG_MD5, 0, 0, &hHash ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } if ( !CryptHashData( hHash, (const unsigned char *)pContext->bSessionKey, MD5_HASH_BYTESIZE, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } if (pstrSignKeyConst->Length) { if ( !CryptHashData( hHash, (const unsigned char *)pstrSignKeyConst->Buffer, pstrSignKeyConst->Length, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } } cbKiHashData = MD5_HASH_BYTESIZE; if ( !CryptGetHashParam( hHash, HP_HASHVAL, bKiHashData, &cbKiHashData, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: CryptGetHashParam failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } CryptDestroyHash( hHash ); hHash = NULL; ASSERT(cbKiHashData == MD5_HASH_BYTESIZE); // save the key for later sign/verify use if (fSign == TRUE) { memcpy(pContext->bKiSignHashData, bKiHashData, MD5_HASH_BYTESIZE); } else { memcpy(pContext->bKiVerifyHashData, bKiHashData, MD5_HASH_BYTESIZE); } #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex(bKiHashData, MD5_HASH_BYTESIZE, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CalculateSASLHMAC: Calculated Ki hash is %s\n", szTemp)); } #endif } else { // retrieve it from the saved context info if (fSign == TRUE) { memcpy(bKiHashData, pContext->bKiSignHashData, MD5_HASH_BYTESIZE); } else { memcpy(bKiHashData, pContext->bKiVerifyHashData, MD5_HASH_BYTESIZE); } cbKiHashData = MD5_HASH_BYTESIZE; #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex(bKiHashData, MD5_HASH_BYTESIZE, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CalculateSASLHMAC: Ki retrieved from context is %s\n", szTemp)); } #endif } DebugLog((DEB_TRACE, "CalculateSASLHMAC: Ready to start the HMAC calculation\n")); // We now have Kic or Kis depending on if we are running as server or client // Now calculate the SASL_MAC_BLOCK structure to compare or set for message pMacBlock->wMsgType = htons(1); pMacBlock->dwSeqNumber = htonl(dwSeqNum); DebugLog((DEB_TRACE, "CalculateSASLHMAC: SeqNumber is %ld\n", dwSeqNum)); // Need to create the symmetric key from the cleartext shared secret // Specified CALC_RC4 since we need to provide a valid encrypt type for import key // not actually utilized when we do the HMAC which is simply a hash function Status = CreateSymmetricKey(CALG_RC4, MD5_HASH_BYTESIZE, bKiHashData, NULL, &hCryptKey); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: Error in CreateSymmetricKey Status 0x%x\n", Status)); goto CleanUp; } if ( !CryptCreateHash( g_hCryptProv, CALG_HMAC, hCryptKey, 0, &hHash ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } hmacinfo.HashAlgid = CALG_MD5; // Use MD5 as the hashing function for the HMAC hmacinfo.cbOuterString = 0; // use default 64 byte outerstring hmacinfo.cbInnerString = 0; // use default 64 byte innerstring if ( !CryptSetHashParam( hHash, HP_HMAC_INFO, (PBYTE)&hmacinfo, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC CryptSetHashParam failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } // Prepend SeqNum to the data stream to perform HMAC on // Need to form the network order version first #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)&pMacBlock->dwSeqNumber, sizeof(DWORD), szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CalculateSASLHMAC: HMAC component SeqNum %s\n", szTemp)); } #endif if ( !CryptHashData( hHash, (const unsigned char *)&pMacBlock->dwSeqNumber, sizeof(DWORD), 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } // Now HMAC the data to protect #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); if (cbData < iTempLen) { iTempLen = cbData; } BinToHex(pData, iTempLen, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CalculateSASLHMAC: HMAC component Data (%dof%d bytes) %s\n", iTempLen, cbData, szTemp)); } #endif if (cbData) { if ( !CryptHashData( hHash, pData, cbData, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } } cbHMACData = HMAC_MD5_HASH_BYTESIZE; if ( !CryptGetHashParam( hHash, HP_HASHVAL, bHMACData, &cbHMACData, 0 ) ) { DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC CryptGetHashParam failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } DebugLog((DEB_TRACE, "CalculateSASLHMAC: HMAC hash length %d bytes\n", cbHMACData)); ASSERT(cbHMACData == HMAC_MD5_HASH_BYTESIZE); CryptDestroyKey( hCryptKey ); hCryptKey = NULL; CryptDestroyHash( hHash ); hHash = NULL; // We now have the HMAC so form up the MAC block for SASL // Now convert the Hash to Hex - for TESTING ONLY if (cbHMACData != HMAC_MD5_HASH_BYTESIZE) { // This should never happen DebugLog((DEB_ERROR, "CalculateSASLHMAC: HMAC-MD5 result length incorrect\n")); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex(bHMACData, HMAC_MD5_HASH_BYTESIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: HMAC-MD5 is %s\n", szTemp)); #endif memcpy(pMacBlock->hmacMD5, bHMACData, SASL_MAC_HMAC_SIZE); #if DBG ZeroMemory(szTemp, TEMPSIZE); BinToHex((PUCHAR)pMacBlock, HMAC_MD5_HASH_BYTESIZE, szTemp); DebugLog((DEB_TRACE, "CalculateSASLHMAC: HMAC-MD5 block is %s\n", szTemp)); #endif CleanUp: // Release Key resources if (hCryptKey) { CryptDestroyKey( hCryptKey ); hCryptKey = NULL; } // Release Hash resources if (hHash) { CryptDestroyHash( hHash ); hHash = NULL; } DebugLog((DEB_TRACE_FUNC, "CalculateSASLHMAC: Leaving status 0x%x\n", Status)); return(Status); } NTSTATUS SEC_ENTRY CalculateKc( IN PBYTE pbSessionKey, IN USHORT cbHA1n, IN PSTRING pstrSealKeyConst, IN PBYTE pHashData) // MD5 hash for Kc { NTSTATUS Status = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; DWORD cbKcHashData = 0; // Size of Message integrity keys ASSERT(cbHA1n <= MD5_HASH_BYTESIZE); ASSERT(cbHA1n > 0); #if DBG char szTemp[TEMPSIZE]; ZeroMemory(szTemp, TEMPSIZE); BinToHex(pbSessionKey, MD5_HASH_BYTESIZE, szTemp); DebugLog((DEB_TRACE_FUNC, "CalculateKc: Entering\n")); DebugLog((DEB_TRACE_FUNC, "CalculateKc: Binary SessionKey %s\n", szTemp)); DebugLog((DEB_TRACE_FUNC, "CalculateKc: cbHA1n %d\n", cbHA1n)); DebugLog((DEB_TRACE_FUNC, "CalculateKc: SealKeyConst %Z\n", pstrSealKeyConst)); #endif // Clear the output ZeroMemory(pHashData, MD5_HASH_BYTESIZE); // Kc = MD5( {H(A1)[0...cbHA1n], ConstantString}) take only the first cbHA1n bytes of H(A1) if ( !CryptCreateHash( g_hCryptProv, CALG_MD5, 0, 0, &hHash ) ) { DebugLog((DEB_ERROR, "CalculateKc: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } if ( !CryptHashData( hHash, (const unsigned char *)pbSessionKey, cbHA1n, 0 ) ) { DebugLog((DEB_ERROR, "CalculateKc: CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } if (pstrSealKeyConst->Length) { if ( !CryptHashData( hHash, (const unsigned char *)pstrSealKeyConst->Buffer, pstrSealKeyConst->Length, 0 ) ) { DebugLog((DEB_ERROR, "CalculateKc: CryptHashData failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } } cbKcHashData = MD5_HASH_BYTESIZE; if ( !CryptGetHashParam( hHash, HP_HASHVAL, pHashData, &cbKcHashData, 0 ) ) { DebugLog((DEB_ERROR, "CalculateKc: CryptGetHashParam failed : 0x%lx\n", GetLastError())); Status = STATUS_ENCRYPTION_FAILED; goto CleanUp; } CryptDestroyHash( hHash ); hHash = NULL; DebugLog((DEB_TRACE, "CalculateKc: readback hash with %d bytes\n", cbKcHashData)); #if DBG // Now convert the Hash to Hex - for TESTING ONLY ZeroMemory(szTemp, TEMPSIZE); BinToHex(pHashData, MD5_HASH_BYTESIZE, szTemp); if (szTemp) { DebugLog((DEB_TRACE, "CalculateKc: Kc hash is %s\n", szTemp)); } #endif CleanUp: // Release Hash resources if (hHash) { CryptDestroyHash( hHash ); hHash = NULL; } DebugLog((DEB_TRACE_FUNC, "CalculateKc: Leaving status 0x%x\n", Status)); return(Status); } BYTE DESParityTable[] = {0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03, 0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04}; // // set the parity on the DES key - ODD parity // NOTE : must be called before deskey // key must be cbKey number of bytes // routine from RSA lib // void SetDESParity( PBYTE pbKey, DWORD cbKey ) { DWORD i; for (i=0;i>4] + DESParityTable[pbKey[i]&0x0F]) % 2)) pbKey[i] = pbKey[i] ^ 0x01; } } //+------------------------------------------------------------------------- // // Function: addDESParity // // Synopsis: This routine is called for DES plaintext keys to add in Odd parity bits // Input of 7 bytes will be expanded to 8bytes with parity // Input of 14 bytes will be expanded to 14 bytes // // Effects: no global effect. // // Arguments: // // IN pbSrckey -- buffer with key to expand // IN cbKey -- size of input non-parity expanded key // OUT pbOutputkey -- buffer with key to expand // // Requires: no global requirements // // Returns: STATUS_SUCCESS, or resource error // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS AddDESParity( IN PBYTE pbSrcKey, IN DWORD cbSrcKey, OUT PBYTE pbDstKey, OUT PDWORD pcbDstKey ) { NTSTATUS Status = STATUS_SUCCESS; BYTE bKiHashData[MD5_HASH_BYTESIZE]; // Message integrity keys RFC 2831 sec 2.3 ASSERT(pbSrcKey); ASSERT(pbDstKey); ASSERT(pcbDstKey); ZeroMemory(pbDstKey, MD5_HASH_BYTESIZE); if ((cbSrcKey != 7) && (cbSrcKey != 14)) { DebugLog((DEB_ERROR, "AddDESParity: wrong input size buffer\n")); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } pbDstKey[0] = pbSrcKey[0]; pbDstKey[1] = (pbSrcKey[1] >> 1) | ((pbSrcKey[0] & 0x01) << 7); pbDstKey[2] = (pbSrcKey[2] >> 2) | ((pbSrcKey[1] & 0x03) << 6); pbDstKey[3] = (pbSrcKey[3] >> 3) | ((pbSrcKey[2] & 0x07) << 5); pbDstKey[4] = (pbSrcKey[4] >> 4) | ((pbSrcKey[3] & 0x0F) << 4); pbDstKey[5] = (pbSrcKey[5] >> 5) | ((pbSrcKey[4] & 0x1F) << 3); pbDstKey[6] = (pbSrcKey[6] >> 6) | ((pbSrcKey[5] & 0x3F) << 2); pbDstKey[7] = (pbSrcKey[6] << 1); SetDESParity(pbDstKey, 8); *pcbDstKey = 8; // Now check if need to expand the 14 bytes into the full 16 byte buffer if (cbSrcKey == 14) { pbDstKey[0 + 8] = pbSrcKey[0 + 7]; pbDstKey[1 + 8] = (pbSrcKey[1 + 7] >> 1) | ((pbSrcKey[0 + 7] & 0x01) << 7); pbDstKey[2 + 8] = (pbSrcKey[2 + 7] >> 2) | ((pbSrcKey[1 + 7] & 0x03) << 6); pbDstKey[3 + 8] = (pbSrcKey[3 + 7] >> 3) | ((pbSrcKey[2 + 7] & 0x07) << 5); pbDstKey[4 + 8] = (pbSrcKey[4 + 7] >> 4) | ((pbSrcKey[3 + 7] & 0x0F) << 4); pbDstKey[5 + 8] = (pbSrcKey[5 + 7] >> 5) | ((pbSrcKey[4 + 7] & 0x1F) << 3); pbDstKey[6 + 8] = (pbSrcKey[6 + 7] >> 6) | ((pbSrcKey[5 + 7] & 0x3F) << 2); pbDstKey[7 + 8] = (pbSrcKey[6 + 7] << 1); SetDESParity(pbDstKey + 8, 8); *pcbDstKey = 16; } #if DBG char szTemp[TEMPSIZE]; ZeroMemory(szTemp, TEMPSIZE); BinToHex(pbSrcKey, (UINT)cbSrcKey, szTemp); DebugLog((DEB_TRACE, "AddDESParity: Key no-parity : %s\n", szTemp)); ZeroMemory(szTemp, TEMPSIZE); BinToHex(pbDstKey, (UINT)*pcbDstKey, szTemp); DebugLog((DEB_TRACE, "AddDESParity: Key expanded with parity : %s\n", szTemp)); #endif CleanUp: return Status; }