//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1996 // // File: userapi.cxx // // Contents: User-mode APIs to Kerberos package // // // History: 17-April-1996 Created MikeSw // 26-Sep-1998 ChandanS // Added more debugging support etc. // //------------------------------------------------------------------------ /* * Copyright 1993 by OpenVision Technologies, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of OpenVision not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. OpenVision makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #define USERAPI_ALLOCATE #include #ifdef RETAIL_LOG_SUPPORT static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif #ifndef WIN32_CHICAGO extern "C" { #include } #endif // WIN32_CHICAGO #include "userapi.h" #define DONT_SUPPORT_OLD_TYPES_USER 1 #define ALIGN_SIZE sizeof(PVOID) #define ALIGN_SHIFT (ALIGN_SIZE - 0x00000001) // 0x00000007 #define IS_POINTER_ALIGNED(ptr) (((UINT_PTR)(ptr) & ALIGN_SHIFT) == 0x00000000) // can't sign or seal messages greater than this #define KERB_MAX_MESSAGE_SIZE 0x40000000 // // Common GSS object IDs, taken from MIT kerberos distribution. // gss_OID_desc oids[] = { {5, "\053\005\001\005\002"}, // original mech id {9, "\052\206\110\206\367\022\001\002\002"}, // standard mech id {10, "\052\206\110\206\367\022\001\002\002\001"}, // krb5_name type {10, "\052\206\110\206\367\022\001\002\002\002"}, // krb5_principal type {10, "\052\206\110\206\367\022\001\002\002\003"}, // user2user mech id {9, "\052\206\110\202\367\022\001\002\002"}, // bogus mangled OID from spnego }; gss_OID_desc * gss_mech_krb5 = oids; gss_OID_desc * gss_mech_krb5_new = oids+1; gss_OID_desc * gss_mech_krb5_u2u = oids+4; gss_OID_desc * gss_mech_krb5_spnego = oids+5; #ifndef WIN32_CHICAGO //+------------------------------------------------------------------------- // // Function: SpUserModeInitialize // // Synopsis: Returns table of usermode functions to caller // // Effects: // // Arguments: // // Requires: // // Returns: SUCCESS if version is correct // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SEC_ENTRY SpUserModeInitialize( IN ULONG LsaVersion, OUT PULONG PackageVersion, OUT PSECPKG_USER_FUNCTION_TABLE * UserFunctionTable, OUT PULONG pcTables) { if (LsaVersion != SECPKG_INTERFACE_VERSION) { DebugLog((DEB_ERROR,"Invalid LSA version: %d. %ws, line %d\n", LsaVersion, THIS_FILE, __LINE__)); return(STATUS_INVALID_PARAMETER); } *PackageVersion = SECPKG_INTERFACE_VERSION ; KerberosUserFunctionTable.InstanceInit = SpInstanceInit; KerberosUserFunctionTable.MakeSignature = SpMakeSignature; KerberosUserFunctionTable.VerifySignature = SpVerifySignature; KerberosUserFunctionTable.SealMessage = SpSealMessage; KerberosUserFunctionTable.UnsealMessage = SpUnsealMessage; KerberosUserFunctionTable.GetContextToken = SpGetContextToken; KerberosUserFunctionTable.QueryContextAttributes = SpQueryContextAttributes; KerberosUserFunctionTable.CompleteAuthToken = SpCompleteAuthToken; KerberosUserFunctionTable.InitUserModeContext = SpInitUserModeContext; KerberosUserFunctionTable.DeleteUserModeContext = SpDeleteUserModeContext; KerberosUserFunctionTable.FormatCredentials = SpFormatCredentials; KerberosUserFunctionTable.MarshallSupplementalCreds = SpMarshallSupplementalCreds; KerberosUserFunctionTable.ExportContext = SpExportSecurityContext; KerberosUserFunctionTable.ImportContext = SpImportSecurityContext; *pcTables = 1; *UserFunctionTable = &KerberosUserFunctionTable; if (KerberosState != KerberosLsaMode) { // // SafeAllocaInitialize was already called in SpLsaModeInitialize // SafeAllocaInitialize(SAFEALLOCA_USE_DEFAULT, SAFEALLOCA_USE_DEFAULT, KerbAllocate, KerbFree); } return( STATUS_SUCCESS ); } #endif // WIN32_CHICAGO //+------------------------------------------------------------------------- // // Function: SpInstanceInit // // Synopsis: Initialize an instance of the Kerberos package in a client's // address space // // Effects: // // Arguments: Version - Version of the security dll loading the package // FunctionTable - Contains helper routines for use by Kerberos // UserFunctions - Receives a copy of Kerberos's user mode // function table // // Requires: // // Returns: STATUS_SUCCESS // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpInstanceInit( IN ULONG Version, IN PSECPKG_DLL_FUNCTIONS DllFunctionTable, OUT PVOID * UserFunctionTable ) { NTSTATUS Status = STATUS_SUCCESS; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; if (!KerbGlobalInitialized) { KerberosState = KerberosUserMode; Status = KerbInitGlobalVariables(); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to initialize global variables: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; } Status = KerbInitContextList(); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to initialize context list: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__ )); goto Cleanup; } } else { D_DebugLog((DEB_TRACE,"Re-initializing kerberos from LSA mode to User Mode\n")); } UserFunctions = DllFunctionTable; #ifndef WIN32_CHICAGO // // Build the two well known sids we need. // if( KerbGlobalLocalSystemSid == NULL ) { Status = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0,0,0,0,0,0,0, &KerbGlobalLocalSystemSid ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } if( KerbGlobalAliasAdminsSid == NULL ) { Status = RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &KerbGlobalAliasAdminsSid ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } #endif // WIN32_CHICAGO KerbGlobalInitialized = TRUE; Cleanup: if( !KerbGlobalInitialized && !NT_SUCCESS(Status) ) { if( KerbGlobalLocalSystemSid != NULL ) { RtlFreeSid( KerbGlobalLocalSystemSid ); KerbGlobalLocalSystemSid = NULL; } if( KerbGlobalAliasAdminsSid != NULL ) { RtlFreeSid( KerbGlobalAliasAdminsSid ); KerbGlobalAliasAdminsSid = NULL; } } 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, SEC_I_NO_LSA_CONTEXT if this was // created from an exported context // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpDeleteUserModeContext( IN LSA_SEC_HANDLE ContextHandle ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; D_DebugLog((DEB_TRACE_API,"SpDeleteUserModeContext called\n")); Status = KerbReferenceContextByLsaHandle( ContextHandle, TRUE, &Context // unlink it ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_TRACE,"Failed to reference context 0x%x by lsa handle\n", ContextHandle)); return(STATUS_SUCCESS); // no error code should be returned in this case } // // Make sure we don't try to call the LSA to delete imported contexts // KerbReadLockContexts(); if ((Context->ContextAttributes & KERB_CONTEXT_IMPORTED) != 0) { Status = SEC_I_NO_LSA_CONTEXT; } KerbUnlockContexts(); KerbDereferenceContext( Context ); D_DebugLog((DEB_TRACE_API, "SpDeleteUserModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(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 LSA_SEC_HANDLE ContextHandle, IN PSecBuffer PackedContext ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; D_DebugLog((DEB_TRACE_API,"SpInitUserModeContext called\n")); Status = KerbCreateUserModeContext( ContextHandle, PackedContext, &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create user mode context: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } if (PackedContext->pvBuffer != NULL) { FreeContextBuffer(PackedContext->pvBuffer); PackedContext->pvBuffer = NULL; } D_DebugLog((DEB_TRACE_API, "SpInitUserModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // 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 LSA_SEC_HANDLE ContextHandle, IN ULONG Flags, OUT PSecBuffer PackedContext, OUT PHANDLE TokenHandle ) { PKERB_CONTEXT Context = NULL; NTSTATUS Status = STATUS_SUCCESS; BOOLEAN MappedContext = FALSE; D_DebugLog((DEB_TRACE_API,"SpExportContext Called\n")); D_DebugLog((DEB_TRACE_USER,"Exporting context 0x%p, flags 0x%x\n",ContextHandle, Flags)); // // We don't support reseting the context // if ((Flags & SECPKG_CONTEXT_EXPORT_RESET_NEW) != 0) { return(SEC_E_UNSUPPORTED_FUNCTION); } if (ARGUMENT_PRESENT(TokenHandle)) { *TokenHandle = NULL; } PackedContext->pvBuffer = NULL; PackedContext->cbBuffer = 0; PackedContext->BufferType = 0; Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for ExportSecurityContext(%p) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } Status = KerbMapContext( Context, &MappedContext, PackedContext ); if (!NT_SUCCESS(Status)) { goto Cleanup; } DsysAssert(MappedContext); // // We need to figure out if this was exported // ((PKERB_CONTEXT)PackedContext->pvBuffer)->ContextAttributes |= KERB_CONTEXT_EXPORTED; // // Now either duplicate the token or copy it. // if (ARGUMENT_PRESENT(TokenHandle)) { KerbWriteLockContexts(); if ((Flags & SECPKG_CONTEXT_EXPORT_DELETE_OLD) != 0) { *TokenHandle = Context->TokenHandle; Context->TokenHandle = NULL; } else { Status = NtDuplicateObject( NtCurrentProcess(), Context->TokenHandle, NULL, TokenHandle, 0, // no new access 0, // no handle attributes DUPLICATE_SAME_ACCESS ); } KerbUnlockContexts(); if (!NT_SUCCESS(Status)) { goto Cleanup; } } Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } D_DebugLog((DEB_TRACE_API, "SpExportSecurityContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: SpImportSecurityContext // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SpImportSecurityContext( IN PSecBuffer PackedContext, IN HANDLE Token, OUT PLSA_SEC_HANDLE ContextHandle ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; D_DebugLog((DEB_TRACE_API,"SpImportSecurityContext called\n")); SecBuffer TmpSecBuffer = (*PackedContext); #if _WIN64 PBYTE TmpBuffer = NULL; // // See if this is an aligned buffer - SAP has some IPC mechanisms // which don't gaurantee that the pSecBuffer is actually 8 bit aligned, // so we should. Blech! // if (!IS_POINTER_ALIGNED(PackedContext->pvBuffer)) { TmpBuffer = (PBYTE) UserFunctions->AllocateHeap(((UNALIGNED SecBuffer *)PackedContext)->cbBuffer); if ( NULL == TmpBuffer ) { Status = STATUS_NO_MEMORY; goto Cleanup; } // // Align it. Note that the members inside of this buffer were already // aligned on export, so this should be all we need to do // RtlCopyMemory( TmpBuffer, PackedContext->pvBuffer, PackedContext->cbBuffer ); TmpSecBuffer.pvBuffer = TmpBuffer; } #endif // _WIN64 Status = KerbCreateUserModeContext( 0, // no lsa context &TmpSecBuffer, &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create user mode context: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; } KerbWriteLockContexts(); Context->TokenHandle = Token; Context->ContextAttributes |= KERB_CONTEXT_IMPORTED; *ContextHandle = KerbGetContextHandle(Context); //Context->LsaContextHandle = *ContextHandle; KerbUnlockContexts(); Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } #if _WIN64 if ( TmpBuffer ) { UserFunctions->FreeHeap(TmpBuffer); } #endif // _WIN64 D_DebugLog((DEB_TRACE_API, "SpImportSecurityContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); D_DebugLog((DEB_TRACE_USER," Imported Context handle = 0x%x\n",*ContextHandle)); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: KerbGetChecksumAndEncryptionType // // Synopsis: Gets the ChecksumType and the EncryptionType // // Effects: // // Arguments: Context - Context to use for signing // QualityOfProtection - flags indicating what kind of checksum // to use // ChecksumType - Receives the type of checksum to use // EncryptionType - Receives the type of encryption to use // // Requires: The context must be write locked // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbGetChecksumAndEncryptionType( IN PKERB_CONTEXT Context, IN ULONG QualityOfProtection, OUT PLONG ChecksumType, OUT PLONG EncryptionType ) { NTSTATUS Status = STATUS_SUCCESS; // // If the keytype is an MS keytype, we need to use an MS encryption // scheme. // if (!KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype)) { #ifndef DONT_SUPPORT_OLD_TYPES_USER if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD; } else if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD_EXP) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD_EXP; } else #endif if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN; } else { DsysAssert (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT_EXP); *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_EXP; } } else { // // Use the exportable version if necessasry // *EncryptionType = KERB_ETYPE_DES_PLAIN; switch(QualityOfProtection) { case GSS_KRB5_INTEG_C_QOP_MD5: *ChecksumType = KERB_CHECKSUM_MD25; break; case KERB_WRAP_NO_ENCRYPT: case GSS_KRB5_INTEG_C_QOP_DEFAULT: case GSS_KRB5_INTEG_C_QOP_DES_MD5: *ChecksumType = KERB_CHECKSUM_DES_MAC_MD5; break; case GSS_KRB5_INTEG_C_QOP_DES_MAC: *ChecksumType = KERB_CHECKSUM_DES_MAC; break; default: DebugLog((DEB_ERROR,"Invalid quality of protection sent to MakeSignature: %d. %ws, line %d\n", QualityOfProtection, THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } } Cleanup: return(Status); } //+------------------------------------------------------------------------- // // Function: KerbMakeSignatureToken // // Synopsis: Makes the signature token for a signed or sealed message // // Effects: // // Arguments: Context - Context to use for signing // QualityOfProtection - flags indicating what kind of checksum // to use // SignatureBuffer - Buffer in which to place signature // TotalBufferSize - Total size of all buffers to be signed // Encrypt - if TRUE, then prepare a header for an encrypted buffer // SuppliedNonce - Nonce supplied by caller, used for datagram // // Requires: The context must be write locked // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbMakeSignatureToken( IN PKERB_CONTEXT Context, IN ULONG QualityOfProtection, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Encrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG SequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_GSS_SIGNATURE Signature; PKERB_GSS_SEAL_SIGNATURE SealSignature; ULONG MessageSize; ULONG SignatureSize; PULONG Nonce; gss_OID MechUsed; BOOLEAN GssCompatible = TRUE; // // Compute the size of the header. For encryption headers, we need // to round up the size of the data & add 8 bytes for a confounder. // if ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0 || (Context->ContextFlags & ISC_RET_DATAGRAM) != 0) { GssCompatible = FALSE; } // // Since RPC doesn't carry around the size of the size of the // signature bufer, we use it in the header. This break rfc1964 compat. // if (!GssCompatible || !Encrypt) { TotalBufferSize = 0; } // D_DebugLog((DEB_TRACE, "KerbMakeSignatureToken ContextAttributes %#x\n", Context->ContextAttributes)); if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { D_DebugLog((DEB_TRACE_U2U, "KerbMakeSignatureToken u2u oid used\n")); MechUsed = gss_mech_krb5_u2u; } else { MechUsed = gss_mech_krb5_new; } if (Encrypt) { // // NOTE: according to rfc1964, buffers that are an even multiple of // 8 bytes have 8 bytes of zeros appended. Because we cannot modify // the input buffers, the caller will have to do this for us. // MessageSize = TotalBufferSize + sizeof(KERB_GSS_SEAL_SIGNATURE); } else { MessageSize = TotalBufferSize + sizeof(KERB_GSS_SIGNATURE); } SignatureSize = g_token_size(MechUsed, MessageSize) - TotalBufferSize; // // Make Dave happy (verify that the supplied signature buffer is large // enough for a signature): // if (SignatureBuffer->cbBuffer < SignatureSize) { Status = STATUS_BUFFER_TOO_SMALL; goto Cleanup; } // // create the header with the GSS oid // Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer; g_make_token_header( MechUsed, MessageSize, (PUCHAR *) &Signature, (Encrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG) ); // // Fill in the header information according to RFC1964 // Signature->SignatureAlgorithm[1] = KERB_GSS_SIG_SECOND; // // If the keytype is an MS keytype, we need to use an MS encryption // scheme. // if (!KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype)) { #ifndef DONT_SUPPORT_OLD_TYPES_USER if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD) { Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4_OLD; } } else if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_OLD_EXP) { Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4_OLD; } } else #endif if (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT) { Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4; } } else { DsysAssert (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT_EXP); Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4; } } // // if we aren't actually encrypting, reset the encryption alg // if (QualityOfProtection == KERB_WRAP_NO_ENCRYPT) { if (!Encrypt) { DebugLog((DEB_ERROR,"KERB_WRAP_NO_ENCRYPT flag passed to MakeSignature!\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // In this case use the default, but we will not encrypt // Signature->SealAlgorithm[1] = KERB_GSS_NO_SEAL_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_NO_SEAL; } } else { if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_DES_CBC; } // // Use the exportable version if necessasry // switch(QualityOfProtection) { case GSS_KRB5_INTEG_C_QOP_MD5: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_MD25; break; case KERB_WRAP_NO_ENCRYPT: if (!Encrypt) { DebugLog((DEB_ERROR,"KERB_WRAP_NO_ENCRYPT flag passed to MakeSignature!\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // In this case use the default, but we will not encrypt // Signature->SealAlgorithm[1] = KERB_GSS_NO_SEAL_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_NO_SEAL; case GSS_KRB5_INTEG_C_QOP_DEFAULT: case GSS_KRB5_INTEG_C_QOP_DES_MD5: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC_MD5; break; case GSS_KRB5_INTEG_C_QOP_DES_MAC: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC; break; default: DebugLog((DEB_ERROR,"Invalid quality of protection sent to MakeSignature: %d. %ws, line %d\n", QualityOfProtection, THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } } // // Put in the filler - it is different for signing & sealing // if (Encrypt) { memset(Signature->SealFiller,0xff,2); } else { memset(Signature->SignFiller,0xff,4); } // // Inbound contexts get a high dword of 0xffffffff, outbound gets // 0x00000000. // Nonce = &Context->Nonce; if (Context->ContextAttributes & KERB_CONTEXT_INBOUND) { *(ULONG UNALIGNED *)(&Signature->SequenceNumber[4]) = 0xffffffff; } else { DsysAssert((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0); *(ULONG UNALIGNED *)(&Signature->SequenceNumber[4]) = 0x00000000; } // // If this is datagram, or integrity without replay & sequence detection, // use the nonce from the caller // if (((Context->ContextFlags & ISC_RET_DATAGRAM) != 0) || ((Context->ContextFlags & (ISC_RET_INTEGRITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT)) == ISC_RET_INTEGRITY)) { Nonce = &SuppliedNonce; } if (!KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype)) { Signature->SequenceNumber[0] = (UCHAR) ((*Nonce & 0xff000000) >> 24); Signature->SequenceNumber[1] = (UCHAR) ((*Nonce & 0x00ff0000) >> 16); Signature->SequenceNumber[2] = (UCHAR) ((*Nonce & 0x0000ff00) >> 8); Signature->SequenceNumber[3] = (UCHAR) (*Nonce & 0x000000ff); } else { Signature->SequenceNumber[3] = (UCHAR) ((*Nonce & 0xff000000) >> 24); Signature->SequenceNumber[2] = (UCHAR) ((*Nonce & 0x00ff0000) >> 16); Signature->SequenceNumber[1] = (UCHAR) ((*Nonce & 0x0000ff00) >> 8); Signature->SequenceNumber[0] = (UCHAR) (*Nonce & 0x000000ff); } (*Nonce)++; *SequenceNumber = *(ULONG UNALIGNED *)Signature->SequenceNumber; D_DebugLog((DEB_TRACE_USER,"Makign signature buffer (encrypt = %d) with nonce 0x%x\n", Encrypt, *SequenceNumber )); // // If we are encrypting, add the confounder to the end of the signature // if (Encrypt) { SealSignature = (PKERB_GSS_SEAL_SIGNATURE) Signature; KerbRandomFill( SealSignature->Confounder, KERB_GSS_SIG_CONFOUNDER_SIZE ); } // // Set the size of the signature // SignatureBuffer->cbBuffer = SignatureSize; *OutputSignature = Signature; Cleanup: return (Status); } //+------------------------------------------------------------------------- // // Function: KerbVerifySignatureToken // // Synopsis: Verifies the header on a signed or sealed message // // Effects: // // Arguments: Context - context to use for verification // SignatureBuffer - Buffer containing signature // TotalBufferSize - Size of all buffers signed/encrypted // Decrypt - TRUE if we are unsealing // SuppliedNonce - Nonce supplied by caller, used for datagram // QualityOfProtection - returns GSS quality of protection flags // ChecksumType - Type of checksum used in this signature // EncryptionType - Type of encryption used in this signature // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbVerifySignatureToken( IN PKERB_CONTEXT Context, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Decrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG QualityOfProtection, OUT PLONG ChecksumType, OUT PCRYPTO_SYSTEM * CryptSystem, OUT PULONG SequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; ULONG SignatureSize = 0; UCHAR Nonce[8]; PCRYPT_STATE_BUFFER CryptBuffer = NULL; ULONG OutputSize; LONG EncryptionType = 0; PCRYPTO_SYSTEM LocalCryptSystem = NULL ; PKERB_GSS_SIGNATURE Signature; PULONG ContextNonce; gss_OID MechUsed; // // Since RPC doesn't carry around the size of the size of the // signature bufer, we use it in the header. This break rfc1964 compat. // if (!Decrypt || ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) || ((Context->ContextFlags & ISC_RET_DATAGRAM) != 0)) { TotalBufferSize = 0; } // // Verify the signature header // D_DebugLog((DEB_TRACE, "KerbVerifySignatureToken ContextAttributes %#x\n", Context->ContextAttributes)); if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { D_DebugLog((DEB_TRACE_U2U, "KerbVerifySignatureToken MechUsed = gss_mech_krb5_u2u\n")); MechUsed = gss_mech_krb5_u2u; } else { MechUsed = gss_mech_krb5_new; } Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer; if (!g_verify_token_header( MechUsed, (INT *) &SignatureSize, (PUCHAR *) &Signature, (Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG), SignatureBuffer->cbBuffer + TotalBufferSize)) { //Status = SEC_E_MESSAGE_ALTERED; bug 28448 Status = SEC_E_INVALID_TOKEN; } // // If that didn't work, try with the old mech. Need this is for DCE clients // for whom we can't tell what mech they use. // if (!NT_SUCCESS(Status) && ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)) { Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer; if (!g_verify_token_header( gss_mech_krb5, (INT *) &SignatureSize, (PUCHAR *) &Signature, (Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG), SignatureBuffer->cbBuffer + TotalBufferSize)) { //Status = SEC_E_MESSAGE_ALTERED; bug 28448 Status = SEC_E_INVALID_TOKEN; } else { D_DebugLog((DEB_TRACE, "KerbVerifySignatureToken Signature gss_mech_krb5\n")); Status = STATUS_SUCCESS; } } // // MS RPC clients don't send the size properly, so set the total size // to zero and try again. // if (Decrypt && !NT_SUCCESS(Status)) { TotalBufferSize = 0; Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer; if (!g_verify_token_header( MechUsed, (INT *) &SignatureSize, (PUCHAR *) &Signature, (Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG), SignatureBuffer->cbBuffer + TotalBufferSize)) { //Status = SEC_E_MESSAGE_ALTERED; bug 28448 Status = SEC_E_INVALID_TOKEN; } else { D_DebugLog((DEB_TRACE, "KerbVerifySignatureToken Signature MechUsed\n")); Status = STATUS_SUCCESS; } // // If that didn't work, try with the old mech. Need this is for DCE clients // for whom we can't tell what mech they use. // if (!NT_SUCCESS(Status) && ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)) { Signature = (PKERB_GSS_SIGNATURE) SignatureBuffer->pvBuffer; if (!g_verify_token_header( gss_mech_krb5, (INT *) &SignatureSize, (PUCHAR *) &Signature, (Decrypt ? KG_TOK_WRAP_MSG : KG_TOK_MIC_MSG), SignatureBuffer->cbBuffer + TotalBufferSize)) { //Status = SEC_E_MESSAGE_ALTERED; bug 28448 Status = SEC_E_INVALID_TOKEN; } else { D_DebugLog((DEB_TRACE, "KerbVerifySignatureToken Signature gss_mech_krb5\n")); Status = STATUS_SUCCESS; } } } // // Protection from bad Signature Size // if (SignatureSize == 0) { D_DebugLog((DEB_ERROR, "Bad Signature %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } // // Subtract the total buffer size from Signature size to get the real // size of the signature. // SignatureSize -= TotalBufferSize; // // Make sure the signature is big enough. We can't enforce a strict // size because RPC will transmit the maximum number of bytes instead // of the actual number. // if ((Decrypt && (SignatureSize < sizeof(KERB_GSS_SEAL_SIGNATURE))) || (!Decrypt && (SignatureSize < sizeof(KERB_GSS_SIGNATURE)))) { //Status = SEC_E_MESSAGE_ALTERED; bug 28448 Status = SEC_E_INVALID_TOKEN; goto Cleanup; } // // Verify the sequence number // if (Signature->SignatureAlgorithm[1] != KERB_GSS_SIG_SECOND) { D_DebugLog((DEB_ERROR, "Not KERB_GSS_SIG_SECOND %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } // // Figure out the algorithm // switch(Context->SessionKey.keytype) { case KERB_ETYPE_DES_CBC_MD5: case KERB_ETYPE_DES_CBC_CRC: EncryptionType = KERB_ETYPE_DES_PLAIN; break; case KERB_ETYPE_RC4_HMAC_OLD_EXP: EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD_EXP; break; case KERB_ETYPE_RC4_HMAC_OLD: EncryptionType = KERB_ETYPE_RC4_PLAIN_OLD; break; case KERB_ETYPE_RC4_HMAC_NT_EXP: EncryptionType = KERB_ETYPE_RC4_PLAIN_EXP; break; case KERB_ETYPE_RC4_HMAC_NT: EncryptionType = KERB_ETYPE_RC4_PLAIN; break; default: DebugLog((DEB_ERROR,"Unknown key type: %d. %ws, %d\n", Context->SessionKey.keytype, THIS_FILE, __LINE__ )); Status = STATUS_INTERNAL_ERROR; goto Cleanup; } // // if the key is exportable, make sure to use the exportable plain // version. // switch(Signature->SignatureAlgorithm[0]) { case KERB_GSS_SIG_MD25: *QualityOfProtection = GSS_KRB5_INTEG_C_QOP_MD5; *ChecksumType = KERB_CHECKSUM_MD25; break; case KERB_GSS_SIG_DES_MAC_MD5: *QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DES_MD5; *ChecksumType = KERB_CHECKSUM_DES_MAC_MD5; break; case KERB_GSS_SIG_DES_MAC: *QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DES_MAC; *ChecksumType = KERB_CHECKSUM_DES_MAC; break; case KERB_GSS_SIG_HMAC: *QualityOfProtection = GSS_KRB5_INTEG_C_QOP_DEFAULT; *ChecksumType = KERB_CHECKSUM_HMAC_MD5; break; default: DebugLog((DEB_ERROR,"Invalid signature type to VerifySignature: %d. %ws, line %d\n", Signature->SignatureAlgorithm[0], THIS_FILE, __LINE__ )); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } if (Decrypt) { if ((Signature->SealAlgorithm[1] == KERB_GSS_NO_SEAL_SECOND) && (Signature->SealAlgorithm[0] == KERB_GSS_NO_SEAL)) { *QualityOfProtection = KERB_WRAP_NO_ENCRYPT; } else { if (Signature->SealAlgorithm[1] != KERB_GSS_SIG_SECOND) { D_DebugLog((DEB_ERROR, "Not KERB_GSS_SIG_SECOND %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } // // Verify the seal algorithm // switch(EncryptionType) { case KERB_ETYPE_DES_PLAIN: if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_DES_CBC) { DebugLog((DEB_ERROR,"Trying to mix encryption types. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } break; case KERB_ETYPE_RC4_PLAIN_OLD_EXP: case KERB_ETYPE_RC4_PLAIN_OLD: if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_RC4_OLD) { DebugLog((DEB_ERROR,"Trying to mix encryption types. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } break; case KERB_ETYPE_RC4_PLAIN_EXP: case KERB_ETYPE_RC4_PLAIN: if (Signature->SealAlgorithm[0] != KERB_GSS_SEAL_RC4) { DebugLog((DEB_ERROR, "Trying to mix encryption types. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } break; default: DebugLog((DEB_ERROR,"Invalid seal type to VerifySignature: %d, %d. %ws, line %d\n", Signature->SealAlgorithm[0], EncryptionType, THIS_FILE, __LINE__ )); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } } } // // Check the filler // if ((Decrypt && (*(USHORT UNALIGNED *) Signature->SealFiller != 0xffff)) || (!Decrypt && (*(ULONG UNALIGNED *) Signature->SignFiller != 0xffffffff))) { D_DebugLog((DEB_ERROR, "Bad filler %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } // // Verify the sequence number. To do this we need to decrypt it with // the session key with the checksum as the IV. // Status = CDLocateCSystem(EncryptionType, &LocalCryptSystem); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x. %ws, line %d\n",EncryptionType,Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Now we need to Decrypt the sequence number, using the checksum as the // IV // Status = LocalCryptSystem->Initialize( Context->SessionKey.keyvalue.value, Context->SessionKey.keyvalue.length, 0, // no flags &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Set the initial vector // Status = LocalCryptSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, Signature->Checksum, 8 ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Now encrypt the sequence number // OutputSize = 8; Status = LocalCryptSystem->Decrypt( CryptBuffer, Signature->SequenceNumber, 8, Signature->SequenceNumber, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // For datagram or integrity only, we use just the supplied nonce. // if (((Context->ContextFlags & ISC_RET_DATAGRAM) != 0) || ((Context->ContextFlags & (ISC_RET_INTEGRITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT)) == ISC_RET_INTEGRITY)) { ContextNonce = &SuppliedNonce; } else { ContextNonce = &Context->ReceiveNonce; } if (!KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype)) { Nonce[0] = (UCHAR) ((*ContextNonce & 0xff000000) >> 24); Nonce[1] = (UCHAR) ((*ContextNonce & 0x00ff0000) >> 16); Nonce[2] = (UCHAR) ((*ContextNonce & 0x0000ff00) >> 8); Nonce[3] = (UCHAR) (*ContextNonce & 0x000000ff); } else { Nonce[3] = (UCHAR) ((*ContextNonce & 0xff000000) >> 24); Nonce[2] = (UCHAR) ((*ContextNonce & 0x00ff0000) >> 16); Nonce[1] = (UCHAR) ((*ContextNonce & 0x0000ff00) >> 8); Nonce[0] = (UCHAR) (*ContextNonce & 0x000000ff); } *SequenceNumber = *(ULONG UNALIGNED *) Nonce; D_DebugLog((DEB_TRACE_USER,"Verifying signature buffer (decrypt = %d) with nonce 0x%x, message seq 0x%x\n", Decrypt, *(ULONG UNALIGNED *) Nonce, *(ULONG UNALIGNED *) Signature->SequenceNumber )); if (!RtlEqualMemory( Nonce, Signature->SequenceNumber, 4)) { Status = SEC_E_OUT_OF_SEQUENCE; goto Cleanup; } (*ContextNonce)++; // // Inbound contexts send a high dword of 0xffffffff, outbound gets // 0x00000000. // if (Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) { if (*(ULONG UNALIGNED *)(&Signature->SequenceNumber[4]) != 0xffffffff) { D_DebugLog((DEB_ERROR, "Bad sequence number %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } } else { DsysAssert((Context->ContextAttributes & KERB_CONTEXT_INBOUND) != 0); if (*(ULONG UNALIGNED *)(&Signature->SequenceNumber[4]) != 0) { Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } } if (ARGUMENT_PRESENT(CryptSystem)) { *CryptSystem = LocalCryptSystem; } *OutputSignature = Signature; Cleanup: if ( ( CryptBuffer != NULL ) && ( LocalCryptSystem != NULL ) ) { LocalCryptSystem->Discard(&CryptBuffer); } return(Status); } #define KERB_MAX_CHECKSUM_LENGTH 24 #define KERB_MAX_KEY_LENGTH 24 #define KERB_MAX_BLOCK_LENGTH 24 //+------------------------------------------------------------------------- // // Function: SpMakeSignature // // Synopsis: Signs a message buffer by calculatinga 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: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpMakeSignature( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; PCHECKSUM_FUNCTION Check; PCRYPTO_SYSTEM CryptSystem = NULL ; PSecBuffer SignatureBuffer = NULL; ULONG Index; PCHECKSUM_BUFFER CheckBuffer = NULL; PCRYPT_STATE_BUFFER CryptBuffer = NULL; PKERB_GSS_SIGNATURE Signature; UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH]; LONG ChecksumType = 0; LONG EncryptType; ULONG TotalBufferSize = 0; ULONG OutputSize; ULONG SequenceNumber; D_DebugLog((DEB_TRACE_API,"SpMakeSignature Called\n")); D_DebugLog((DEB_TRACE_USER, "Make Signature handle = 0x%x\n",ContextHandle)); Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for MakeSignature(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { // // We can't allow a combination of the two readonly buffer types, or you'll just // get READONLY behavior. // if (( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY ) && ( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM )) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "Can't have readonly & readonly_w_checksum\n")); goto Cleanup; } if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { SignatureBuffer = &MessageBuffers->pBuffers[Index]; } else if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY))) { TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer; } } if (SignatureBuffer == NULL) { DebugLog((DEB_ERROR, "No signature buffer found. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Verify that the context was created with the integrity bit // if ((Context->ContextFlags & KERB_SIGN_FLAGS) == 0) { if (SignatureBuffer->cbBuffer < sizeof(KERB_NULL_SIGNATURE)) { Status = SEC_E_BUFFER_TOO_SMALL; goto Cleanup; } SignatureBuffer->cbBuffer = sizeof(KERB_NULL_SIGNATURE); *(PKERB_NULL_SIGNATURE) SignatureBuffer->pvBuffer = 0; Status = STATUS_SUCCESS; goto Cleanup; } Status = KerbGetChecksumAndEncryptionType( Context, QualityOfProtection, &ChecksumType, &EncryptType ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = KerbMakeSignatureToken( Context, QualityOfProtection, SignatureBuffer, TotalBufferSize, FALSE, // don't encrypt MessageSequenceNumber, &Signature, &SequenceNumber ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Locate the checksum for the context, loading it if necessary from the // the crypto support DLL // Status = CDLocateCheckSum(ChecksumType, &Check); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d checksum: 0x%x. %ws, line %d\n",ChecksumType,Status, THIS_FILE, __LINE__)); goto Cleanup; } DsysAssert(Check->CheckSumSize <= sizeof(LocalChecksum)); Status = CDLocateCSystem(EncryptType, &CryptSystem); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x. %ws, line %d\n",EncryptType,Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Generate a check sum of the message, and store it into the signature // buffer. // if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, NULL, KERB_SAFE_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, KERB_SAFE_SALT, &CheckBuffer ); } if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Sum in 8 bytes of the signature // Check->Sum( CheckBuffer, 8, ((PUCHAR) Signature) -2 ); for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) { if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) && (MessageBuffers->pBuffers[Index].cbBuffer != 0)) { Check->Sum( CheckBuffer, MessageBuffers->pBuffers[Index].cbBuffer, (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer ); } } (void) Check->Finalize(CheckBuffer, LocalChecksum); Status = Check->Finish(&CheckBuffer); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Copy in the first 8 bytes of the checksum // RtlCopyMemory( Signature->Checksum, LocalChecksum, 8 ); // // Now we need to encrypt the sequence number, using the checksum as the // IV // Status = CryptSystem->Initialize( Context->SessionKey.keyvalue.value, Context->SessionKey.keyvalue.length, 0, // no options &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Set the initial vector // Status = CryptSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, LocalChecksum, 8 ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Now encrypt the sequence number // Status = CryptSystem->Encrypt( CryptBuffer, Signature->SequenceNumber, 8, Signature->SequenceNumber, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Cleanup: if ( ( CryptBuffer != NULL) && ( CryptSystem != NULL ) ) { CryptSystem->Discard(&CryptBuffer); } if (Context != NULL) { KerbDereferenceContext(Context); } D_DebugLog((DEB_TRACE_API, "SpMakeSignature returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: SpVerifySignature // // 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: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpVerifySignature( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; PCHECKSUM_FUNCTION Check; PSecBuffer SignatureBuffer = NULL; ULONG Index; PCHECKSUM_BUFFER CheckBuffer = NULL; PKERB_GSS_SIGNATURE Signature; LONG ChecksumType; UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH]; ULONG Protection; ULONG TotalBufferSize = 0; ULONG SequenceNumber; D_DebugLog((DEB_TRACE_API,"SpVerifySignature Called\n")); D_DebugLog((DEB_TRACE_USER, "Verify Signature handle = 0x%x\n",ContextHandle)); Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for VerifySignature(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { // // We can't allow a combination of the two readonly buffer types, or you'll just // get READONLY behavior. // if (( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY ) && ( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM )) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "Can't have readonly & readonly_w_checksum\n")); goto Cleanup; } if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { SignatureBuffer = &MessageBuffers->pBuffers[Index]; } else if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY))) { TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer; } } if (SignatureBuffer == NULL) { DebugLog((DEB_ERROR, "No signature buffer found. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Also, verify that the context was created with the integrity bit // if ((Context->ContextFlags & KERB_SIGN_FLAGS) == 0) { PKERB_NULL_SIGNATURE NullSignature = (PKERB_NULL_SIGNATURE) SignatureBuffer->pvBuffer; if (SignatureBuffer->cbBuffer >= sizeof(KERB_NULL_SIGNATURE) && (*NullSignature == 0)) { Status = STATUS_SUCCESS; } else { D_DebugLog((DEB_ERROR, "Bad signature %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; } goto Cleanup; } // // Verify the signature header // Status = KerbVerifySignatureToken( Context, SignatureBuffer, TotalBufferSize, FALSE, // don't decrypt MessageSequenceNumber, &Signature, &Protection, &ChecksumType, NULL, // don't need crypt system &SequenceNumber ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to verify signature token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Now compute the checksum and verify it // Status = CDLocateCheckSum(ChecksumType, &Check); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load MD5 checksum: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } DsysAssert(Check->CheckSumSize <= sizeof(LocalChecksum)); // // Generate a check sum of the message, and store it into the signature // buffer. // // // if available use the Ex2 version for keyed checksums where checksum // must be passed in on verification // if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, Signature->Checksum, KERB_SAFE_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, KERB_SAFE_SALT, &CheckBuffer ); } if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Sum in 8 bytes of the signature // Check->Sum( CheckBuffer, 8, ((PUCHAR) Signature) -2 ); for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) { if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY)) && (MessageBuffers->pBuffers[Index].cbBuffer != 0)) { Check->Sum( CheckBuffer, MessageBuffers->pBuffers[Index].cbBuffer, (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer ); } } (void) Check->Finalize(CheckBuffer, LocalChecksum); Status = Check->Finish(&CheckBuffer); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (!RtlEqualMemory( LocalChecksum, Signature->Checksum, 8)) { D_DebugLog((DEB_ERROR, "Bad checksum %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } if (ARGUMENT_PRESENT(QualityOfProtection)) { *QualityOfProtection = Protection; } Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } D_DebugLog((DEB_TRACE_API, "SpVerifySignature returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } #define STREAM_CIPHER_BLOCKLEN 1 //+------------------------------------------------------------------------- // // Function: SpSealMessage // // Synopsis: Seals a message buffer by calculating a checksum over all // the non-read only data buffers and encrypting the data, checksum // and a sequence number. // // 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: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpSealMessage( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; PCHECKSUM_FUNCTION Check = NULL ; PCRYPTO_SYSTEM CryptSystem = NULL ; PSecBuffer SignatureBuffer = NULL; ULONG Index; PCHECKSUM_BUFFER CheckBuffer = NULL; PCRYPT_STATE_BUFFER CryptBuffer = NULL; PKERB_GSS_SEAL_SIGNATURE SealSignature; UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH]; UCHAR LocalKey[KERB_MAX_KEY_LENGTH]; UCHAR LocalBlockBuffer[KERB_MAX_BLOCK_LENGTH]; ULONG BeginBlockSize = 0; PBYTE BeginBlockPointer = NULL; ULONG EndBlockSize = 0; ULONG EncryptBufferSize = 0; PBYTE EncryptBuffer = NULL; BOOLEAN DoEncryption = TRUE; ULONG BlockSize = 1; LONG ChecksumType = 0; LONG EncryptType; ULONG TotalBufferSize = 0; ULONG OutputSize; ULONG ContextAttributes; ULONG SequenceNumber; D_DebugLog((DEB_TRACE_API,"SpSealMessage Called\n")); D_DebugLog((DEB_TRACE_USER, "SealMessage handle = 0x%x\n",ContextHandle)); Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for SpSealMessage(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // get the encryption type for the context // Status = KerbGetChecksumAndEncryptionType( Context, QualityOfProtection, &ChecksumType, &EncryptType ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Locate the cryptsystem for the context, loading it if necessary from the // the crypto support DLL // Status = CDLocateCSystem(EncryptType, &CryptSystem); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x. %ws, line %d\n",EncryptType,Status, THIS_FILE, __LINE__)); goto Cleanup; } BlockSize = CryptSystem->BlockSize; // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { // // We can't allow a combination of the two readonly buffer types, or you'll just // get READONLY behavior. // if (( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY ) && ( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM )) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "Can't have readonly & readonly_w_checksum\n")); goto Cleanup; } if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { SignatureBuffer = &MessageBuffers->pBuffers[Index]; } else if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY ))) { // // use real block size from crypt type // if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_PADDING) { if (STREAM_CIPHER_BLOCKLEN != BlockSize) { TotalBufferSize = ROUND_UP_COUNT(TotalBufferSize+1,BlockSize); } else { // // For stream encryption, only 1 byte of padding // TotalBufferSize += BlockSize; } } else { TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer; } } } if (SignatureBuffer == NULL) { DebugLog((DEB_ERROR, "No signature buffer found. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } ContextAttributes = Context->ContextAttributes; // // If we are not encrypting, just wrapping, remember that // if (QualityOfProtection == KERB_WRAP_NO_ENCRYPT) { DoEncryption = FALSE; // // Reset the block size because we are not really encrypting // } // // Verify that the context was created with the integrity bit // if (DoEncryption && ((Context->ContextFlags & ISC_RET_CONFIDENTIALITY) == 0)) { DebugLog((DEB_ERROR,"Trying to seal without asking for confidentiality. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } Status = KerbMakeSignatureToken( Context, QualityOfProtection, SignatureBuffer, TotalBufferSize, TRUE, // do encrypt MessageSequenceNumber, (PKERB_GSS_SIGNATURE *) &SealSignature, &SequenceNumber ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Locate the checksum for the context, loading it if necessary from the // the crypto support DLL // Status = CDLocateCheckSum(ChecksumType, &Check); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d checksum: 0x%x. %ws, line %d\n",ChecksumType,Status, THIS_FILE, __LINE__)); goto Cleanup; } DsysAssert(Check->CheckSumSize <= sizeof(LocalChecksum)); // // Generate a check sum of the message, and store it into the signature // buffer. // Status = Check->InitializeEx( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, KERB_PRIV_SALT, &CheckBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (DoEncryption) { // // Create the encryption key by xoring with 0xf0f0f0f0 // DsysAssert(Context->SessionKey.keyvalue.length <= sizeof(LocalKey)); if (Context->SessionKey.keyvalue.length > sizeof(LocalKey)) { Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } for (Index = 0; Index < Context->SessionKey.keyvalue.length ; Index++ ) { LocalKey[Index] = Context->SessionKey.keyvalue.value[Index] ^ 0xf0; } Status = CryptSystem->Initialize( LocalKey, Context->SessionKey.keyvalue.length, 0, // no options &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Sum in 8 bytes of the signature // Check->Sum( CheckBuffer, 8, ((PUCHAR) SealSignature) -2 ); // // Sum the confounder // Check->Sum( CheckBuffer, KERB_GSS_SIG_CONFOUNDER_SIZE, SealSignature->Confounder ); if (DoEncryption) { if ((EncryptType == KERB_ETYPE_RC4_PLAIN) || (EncryptType == KERB_ETYPE_RC4_PLAIN_EXP)) { Status = CryptSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, (PUCHAR) &SequenceNumber, sizeof(ULONG) ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Encrypt the 8 confounder bytes // Status = CryptSystem->Encrypt( CryptBuffer, SealSignature->Confounder, KERB_GSS_SIG_CONFOUNDER_SIZE, SealSignature->Confounder, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) { if ((BUFFERTYPE(MessageBuffers->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY )) && (MessageBuffers->pBuffers[Index].cbBuffer != 0)) { // // If the SECBUFFER_READONLY_WITH_CHECKSUM is set, then don't encrypt the buffer, but be sure // to checksum it. This is primarily for consistencies sake... // if (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM)) { // // Take into account that the input buffers may not all be aligned // properly // DsysAssert(BeginBlockSize < BlockSize); if (BeginBlockSize != 0) { // // We have a fragment we still need to encrypt // EncryptBuffer = (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer + (BlockSize - BeginBlockSize); EncryptBufferSize = MessageBuffers->pBuffers[Index].cbBuffer - (BlockSize - BeginBlockSize); } else { // // There is no fragment to encrypt, so try to do the whole // buffer // EncryptBuffer = (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer; EncryptBufferSize = MessageBuffers->pBuffers[Index].cbBuffer; } EndBlockSize = EncryptBufferSize - ROUND_DOWN_COUNT(EncryptBufferSize,BlockSize); DsysAssert(EndBlockSize < BlockSize); EncryptBufferSize = EncryptBufferSize - EndBlockSize; // // If this is padding, fill it in with the appropriate data & // length // if (MessageBuffers->pBuffers[Index].BufferType == SECBUFFER_PADDING) { if (MessageBuffers->pBuffers[Index].cbBuffer < BlockSize) { DebugLog((DEB_ERROR, "Pad buffer is too small: %d instead of %d. %ws, %d\n", MessageBuffers->pBuffers[Index].cbBuffer, BlockSize, THIS_FILE, __LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } memset( MessageBuffers->pBuffers[Index].pvBuffer, BlockSize - BeginBlockSize, BlockSize - BeginBlockSize ); MessageBuffers->pBuffers[Index].cbBuffer = BlockSize - BeginBlockSize; // // If there is a fragment, we will encrypt the padding with the fragment. // Otherwise we will do just a padding buffer. // if (BeginBlockSize != 0) { EncryptBufferSize = 0; } // // The padding fixes up the end block. // EndBlockSize = 0; } } // // Checksum the whole buffer. We do this now to get the right amount of // padding. // Check->Sum( CheckBuffer, MessageBuffers->pBuffers[Index].cbBuffer, (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer ); if (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM)) { if (BeginBlockSize != 0) { RtlCopyMemory( LocalBlockBuffer+BeginBlockSize, MessageBuffers->pBuffers[Index].pvBuffer, BlockSize - BeginBlockSize ); if (DoEncryption) { // // Now encrypt the buffer // Status = CryptSystem->Encrypt( CryptBuffer, LocalBlockBuffer, BlockSize, LocalBlockBuffer, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Copy the pieces back // RtlCopyMemory( BeginBlockPointer, LocalBlockBuffer, BeginBlockSize ); RtlCopyMemory( MessageBuffers->pBuffers[Index].pvBuffer, LocalBlockBuffer + BeginBlockSize, BlockSize - BeginBlockSize ); } if (DoEncryption && (EncryptBufferSize != 0)) { // // Now encrypt the buffer // Status = CryptSystem->Encrypt( CryptBuffer, EncryptBuffer, EncryptBufferSize, EncryptBuffer, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } DsysAssert(OutputSize == EncryptBufferSize); } // // Prepare for the next go-round // RtlCopyMemory( LocalBlockBuffer, EncryptBuffer+EncryptBufferSize, EndBlockSize ); BeginBlockSize = EndBlockSize; BeginBlockPointer = (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer + MessageBuffers->pBuffers[Index].cbBuffer - EndBlockSize; } } } // // Make sure there are no left-over bits // if (BeginBlockSize != 0) { DebugLog((DEB_ERROR,"Non-aligned buffer size to SealMessage: %d extra bytes\n", BeginBlockSize )); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } (void) Check->Finalize(CheckBuffer, LocalChecksum); Status = Check->Finish(&CheckBuffer); if (!NT_SUCCESS(Status)) { goto Cleanup; } CheckBuffer = NULL; // // Copy in the first 8 bytes of the checksum // RtlCopyMemory( SealSignature->Signature.Checksum, LocalChecksum, 8 ); if (DoEncryption) { CryptSystem->Discard( &CryptBuffer ); } // // Now we need to encrypt the sequence number, using the checksum as the // IV // Status = CryptSystem->Initialize( Context->SessionKey.keyvalue.value, Context->SessionKey.keyvalue.length, 0, // no options &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Set the initial vector // Status = CryptSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, LocalChecksum, 8 ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Now encrypt the sequence number // Status = CryptSystem->Encrypt( CryptBuffer, SealSignature->Signature.SequenceNumber, 8, SealSignature->Signature.SequenceNumber, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Cleanup: if ( ( CryptBuffer != NULL ) && ( CryptSystem != NULL ) ) { CryptSystem->Discard(&CryptBuffer); } if ( ( CheckBuffer != NULL ) && ( Check != NULL ) ) { Check->Finish(&CheckBuffer); } if (Context != NULL) { KerbDereferenceContext(Context); } D_DebugLog((DEB_TRACE_API, "SpSealMessage returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: KerbGetSealMessageBodySize // // Synopsis: From a input encrypted message, figures out where the // body starts // // Effects: // // Arguments: // // Requires: // // Returns: 0 on failure, # of bytes of data on success // // Notes: // // //-------------------------------------------------------------------------- ULONG KerbGetSealMessageBodySize( IN OUT PVOID * InputBuffer, IN ULONG InputBufferSize ) { INT BufferSize = (INT) InputBufferSize; PBYTE Buffer = (PBYTE) *InputBuffer; INT DerBufferSize; INT OidLength; if ((BufferSize-=1) < 0) return(0); if (*(Buffer++) != 0x60) return(0); if ((DerBufferSize = der_read_length(&Buffer, &BufferSize)) < 0) return(0); if (DerBufferSize != BufferSize) return(0); if ((BufferSize-=1) < 0) return(0); if (*(Buffer++) != 0x06) return(0); if ((BufferSize-=1) < 0) return(0); OidLength = *(Buffer++); if ((OidLength & 0x7fffffff) != OidLength) /* Overflow??? */ return(0); if ((BufferSize-= (int) OidLength) < 0) return(0); Buffer+=OidLength; if ((BufferSize-=2) < 0) return(0); Buffer += 2; // // take off size of header // if ((BufferSize -= sizeof(KERB_GSS_SEAL_SIGNATURE)) < 0) { return(0); } Buffer += sizeof(KERB_GSS_SEAL_SIGNATURE); *InputBuffer = Buffer; return((ULONG) BufferSize); } //+------------------------------------------------------------------------- // // Function: SpUnsealMessage // // Synopsis: Decrypts & Verifies an encrypted message according to // RFC 1964 Unwrap() API description // // 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: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpUnsealMessage( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; PCHECKSUM_FUNCTION Check = NULL ; PCRYPTO_SYSTEM CryptSystem = NULL ; PSecBuffer SignatureBuffer = NULL; PSecBuffer StreamBuffer = NULL; SecBuffer LocalSignatureBuffer = {0}; SecBuffer LocalDataBuffer = {0}; SecBufferDesc LocalBufferDesc = {0}; PSecBufferDesc BufferList = NULL; ULONG Index; PCHECKSUM_BUFFER CheckBuffer = NULL; PCRYPT_STATE_BUFFER CryptBuffer = NULL; PKERB_GSS_SEAL_SIGNATURE SealSignature; LONG ChecksumType; UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH]; UCHAR LocalKey[KERB_MAX_KEY_LENGTH]; UCHAR LocalBlockBuffer[KERB_MAX_BLOCK_LENGTH]; ULONG BeginBlockSize = 0; PBYTE BeginBlockPointer = NULL; ULONG EndBlockSize = 0; ULONG EncryptBufferSize; PBYTE EncryptBuffer; BOOLEAN DoDecryption = TRUE; ULONG BlockSize = 1; ULONG Protection = 0; ULONG TotalBufferSize = 0; ULONG OutputSize; ULONG ContextAttributes; ULONG SequenceNumber; D_DebugLog((DEB_TRACE_API,"SpUnsealSignature Called\n")); D_DebugLog((DEB_TRACE_USER, "SealMessage handle = 0x%x\n",ContextHandle)); Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for SpUnsealMessage (0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { // // We can't allow a combination of the two readonly buffer types, or you'll just // get READONLY behavior. // if (( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY ) && ( MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM )) { Status = SEC_E_INVALID_TOKEN; DebugLog((DEB_ERROR, "Can't have readonly & readonly_w_checksum\n")); goto Cleanup; } if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_TOKEN) { SignatureBuffer = &MessageBuffers->pBuffers[Index]; } else if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_STREAM) { StreamBuffer = &MessageBuffers->pBuffers[Index]; // // The total buffer size is everything in the stream buffer // TotalBufferSize = MessageBuffers->pBuffers[Index].cbBuffer; } else if ((MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY) == 0) { TotalBufferSize += MessageBuffers->pBuffers[Index].cbBuffer; } } // // Check for a stream buffer. If it is present, it contains the whole // message // if (StreamBuffer != NULL) { if (SignatureBuffer != NULL) { DebugLog((DEB_ERROR,"Both stream and signature buffer present. %ws, line %d\n",THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Parse the stream to distinguish the header from the body // LocalSignatureBuffer = *StreamBuffer; LocalSignatureBuffer.BufferType = SECBUFFER_TOKEN; LocalDataBuffer = *StreamBuffer; LocalDataBuffer.BufferType = SECBUFFER_DATA; LocalDataBuffer.cbBuffer = KerbGetSealMessageBodySize( &LocalDataBuffer.pvBuffer, LocalDataBuffer.cbBuffer ); if (LocalDataBuffer.cbBuffer == 0) { DebugLog((DEB_ERROR,"Failed to find header on stream buffer. %ws %d\n", THIS_FILE,__LINE__ )); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } LocalSignatureBuffer.cbBuffer = StreamBuffer->cbBuffer - LocalDataBuffer.cbBuffer; SignatureBuffer = &LocalSignatureBuffer; LocalBufferDesc.cBuffers = 1; LocalBufferDesc.pBuffers = &LocalDataBuffer; BufferList = &LocalBufferDesc; // // Adjust the total buffer size to remove the signature // TotalBufferSize -= LocalSignatureBuffer.cbBuffer; } else if (SignatureBuffer == NULL) { DebugLog((DEB_ERROR, "No signature buffer found. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } else { BufferList = MessageBuffers; } ContextAttributes = Context->ContextAttributes; // // Verify the signature header // Status = KerbVerifySignatureToken( Context, SignatureBuffer, TotalBufferSize, TRUE, // do decrypt MessageSequenceNumber, (PKERB_GSS_SIGNATURE *) &SealSignature, &Protection, &ChecksumType, &CryptSystem, &SequenceNumber ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to verify signature token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // If the protection level is no encryption, remember not to do the // decryption // if (Protection == KERB_WRAP_NO_ENCRYPT) { DoDecryption = FALSE; } // // Also, verify that the context was created with the Confidentiality bit // if ((DoDecryption && (Context->ContextFlags & ISC_RET_CONFIDENTIALITY) == 0)) { DebugLog((DEB_ERROR,"Tried to decrypt using non-confidential context. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } BlockSize = CryptSystem->BlockSize; // // Now compute the checksum and verify it // Status = CDLocateCheckSum(ChecksumType, &Check); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load MD5 checksum: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Create the encryption key by xoring with 0xf0f0f0f0 // DsysAssert(Context->SessionKey.keyvalue.length <= sizeof(LocalKey)); if (Context->SessionKey.keyvalue.length > sizeof(LocalKey)) { Status = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } // // Generate a check sum of the message, and store it into the signature // buffer. // if (NULL != Check->InitializeEx2) { Status = Check->InitializeEx2( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, SealSignature->Signature.Checksum, KERB_PRIV_SALT, &CheckBuffer ); } else { Status = Check->InitializeEx( Context->SessionKey.keyvalue.value, (ULONG) Context->SessionKey.keyvalue.length, KERB_PRIV_SALT, &CheckBuffer ); } if (!NT_SUCCESS(Status)) { goto Cleanup; } for (Index = 0; Index < Context->SessionKey.keyvalue.length ; Index++ ) { LocalKey[Index] = Context->SessionKey.keyvalue.value[Index] ^ 0xf0; } // // Sum in 8 bytes of the signature // Check->Sum( CheckBuffer, 8, ((PUCHAR) SealSignature) -2 ); if (DoDecryption) { Status = CryptSystem->Initialize( LocalKey, Context->SessionKey.keyvalue.length, 0, // no options &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Decrypt the confounder // if ((CryptSystem->EncryptionType == KERB_ETYPE_RC4_PLAIN) || (CryptSystem->EncryptionType == KERB_ETYPE_RC4_PLAIN_EXP)) { Status = CryptSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, (PUCHAR) &SequenceNumber, sizeof(ULONG) ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } OutputSize = KERB_GSS_SIG_CONFOUNDER_SIZE; Status = CryptSystem->Decrypt( CryptBuffer, SealSignature->Confounder, KERB_GSS_SIG_CONFOUNDER_SIZE, SealSignature->Confounder, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Sum the confounder // Check->Sum( CheckBuffer, KERB_GSS_SIG_CONFOUNDER_SIZE, SealSignature->Confounder ); for (Index = 0; Index < BufferList->cBuffers; Index++ ) { if ((BUFFERTYPE(BufferList->pBuffers[Index]) != SECBUFFER_TOKEN) && (!(BufferList->pBuffers[Index].BufferType & SECBUFFER_READONLY )) && (BufferList->pBuffers[Index].cbBuffer != 0)) { if (!(MessageBuffers->pBuffers[Index].BufferType & SECBUFFER_READONLY_WITH_CHECKSUM)) { // // Take into account that the input buffers may not all be aligned // properly // // // If there is a fragment to decrypt, convert it to a block // size fragment // if (BeginBlockSize != 0) { EncryptBuffer = (PBYTE) BufferList->pBuffers[Index].pvBuffer + (BlockSize - BeginBlockSize); EncryptBufferSize = BufferList->pBuffers[Index].cbBuffer - (BlockSize - BeginBlockSize); } else { EncryptBuffer = (PBYTE) BufferList->pBuffers[Index].pvBuffer; EncryptBufferSize = BufferList->pBuffers[Index].cbBuffer; } EndBlockSize = EncryptBufferSize - ROUND_DOWN_COUNT(EncryptBufferSize,BlockSize); DsysAssert(EndBlockSize < BlockSize); EncryptBufferSize = EncryptBufferSize - EndBlockSize; if (BeginBlockSize != 0) { RtlCopyMemory( LocalBlockBuffer+BeginBlockSize, BufferList->pBuffers[Index].pvBuffer, BlockSize - BeginBlockSize ); // // Now decrypt the buffer // if (DoDecryption) { Status = CryptSystem->Decrypt( CryptBuffer, LocalBlockBuffer, BlockSize, LocalBlockBuffer, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Then checksum the buffer // Check->Sum( CheckBuffer, BlockSize, LocalBlockBuffer ); // // Copy the pieces back // RtlCopyMemory( BeginBlockPointer, LocalBlockBuffer, BeginBlockSize ); RtlCopyMemory( BufferList->pBuffers[Index].pvBuffer, LocalBlockBuffer + BeginBlockSize, BlockSize - BeginBlockSize ); } // // Decrypt the buffer first // if (DoDecryption) { OutputSize = BufferList->pBuffers[Index].cbBuffer; Status = CryptSystem->Decrypt( CryptBuffer, EncryptBuffer, EncryptBufferSize, EncryptBuffer, &OutputSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // DsysAssert(OutputSize == BufferList->pBuffers[Index].cbBuffer); } // // Prepare for the next go-round // RtlCopyMemory( LocalBlockBuffer, EncryptBuffer+EncryptBufferSize, EndBlockSize ); BeginBlockSize = EndBlockSize; BeginBlockPointer = (PBYTE) MessageBuffers->pBuffers[Index].pvBuffer + MessageBuffers->pBuffers[Index].cbBuffer - EndBlockSize; } else { // // Just include the checksum of this buffer, its READONLY_WITH_CHECKSUM. // EncryptBuffer = (PBYTE) BufferList->pBuffers[Index].pvBuffer; EncryptBufferSize = BufferList->pBuffers[Index].cbBuffer; } // // Then checksum the buffer // Check->Sum( CheckBuffer, EncryptBufferSize, EncryptBuffer ); } } (void) Check->Finalize(CheckBuffer, LocalChecksum); Status = Check->Finish(&CheckBuffer); CheckBuffer = NULL; if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Make sure there are no left-over bits // if (BeginBlockSize != 0) { DebugLog((DEB_ERROR,"Non-aligned buffer size to SealMessage: %d extra bytes\n", BeginBlockSize )); Status = SEC_E_INVALID_TOKEN; goto Cleanup; } if (!RtlEqualMemory( LocalChecksum, SealSignature->Signature.Checksum, 8)) { D_DebugLog((DEB_ERROR, "Bad checksum %ws, %d\n", THIS_FILE, __LINE__)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } if (ARGUMENT_PRESENT(QualityOfProtection)) { *QualityOfProtection = Protection; } // // If this was a stream input, return the data in the data buffer // if (StreamBuffer != NULL) { BYTE PaddingBytes; // // Pull the padding off the data buffer // if (LocalDataBuffer.cbBuffer < 1) { DebugLog((DEB_ERROR,"Data buffer is zero length!\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } PaddingBytes = *(((PBYTE)LocalDataBuffer.pvBuffer) + LocalDataBuffer.cbBuffer - 1 ); // // Verify the padding: // if ((BlockSize >= PaddingBytes) && (LocalDataBuffer.cbBuffer >= PaddingBytes)) { LocalDataBuffer.cbBuffer -= PaddingBytes; for (Index = 0; Index < MessageBuffers->cBuffers; Index++ ) { if (BUFFERTYPE(MessageBuffers->pBuffers[Index]) == SECBUFFER_DATA) { MessageBuffers->pBuffers[Index] = LocalDataBuffer; break; } } } else { DebugLog((DEB_ERROR,"Bad padding: %d bytes\n", PaddingBytes)); Status = STATUS_INVALID_PARAMETER; } } Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } if ( ( CheckBuffer != NULL ) && ( Check != NULL ) ) { Check->Finish(&CheckBuffer); } if ( ( CryptBuffer != NULL ) && ( CryptSystem != NULL ) ) { CryptSystem->Discard(&CryptBuffer); } D_DebugLog((DEB_TRACE_API, "SpUnsealMessage returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); } #ifndef WIN32_CHICAGO //+------------------------------------------------------------------------- // // Function: SpGetContextToken // // Synopsis: returns a pointer to the token for a server-side context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpGetContextToken( IN LSA_SEC_HANDLE ContextHandle, OUT PHANDLE ImpersonationToken ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; LARGE_INTEGER CurrentTime; LARGE_INTEGER ContextExpires; D_DebugLog((DEB_TRACE_API,"SpGetContextToken called pid:0x%x, ctxt:0x%x\n", GetCurrentProcessId(), ContextHandle)); if (ImpersonationToken == NULL) { Status = STATUS_INVALID_PARAMETER; DebugLog((DEB_ERROR, "Null token handle supplied for GetContextToken. %ws, line %d\n", THIS_FILE, __LINE__)); goto Cleanup; } Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for GetContextToken(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); } GetSystemTimeAsFileTime((PFILETIME) &CurrentTime); KerbReadLockContexts(); *ImpersonationToken = Context->TokenHandle; ContextExpires = Context->Lifetime; KerbUnlockContexts(); if (KerbGlobalEnforceTime && ContextExpires.QuadPart < CurrentTime.QuadPart) { DebugLog((DEB_ERROR, "GetContextToken: Context 0x%x expired. %ws, line %d\n", ContextHandle, THIS_FILE, __LINE__)); Status = SEC_E_CONTEXT_EXPIRED; *ImpersonationToken = NULL; } else if (*ImpersonationToken == NULL) { Status = SEC_E_NO_IMPERSONATION; } if (Context != NULL) { // // Note: once we dereference the context the handle we return // may go away or be re-used. That is the price we have to pay // to avoid duplicating it. // KerbDereferenceContext(Context); } Cleanup: D_DebugLog((DEB_TRACE_API,"SpGetContextToken returned 0x%x, pid:0x%x, ctxt:0x%x\n", KerbMapKerbNtStatusToNtStatus(Status), GetCurrentProcessId(), ContextHandle)); return(KerbMapKerbNtStatusToNtStatus(Status)); } #endif // WIN32_CHICAGO //+------------------------------------------------------------------------- // // Function: SpQueryContextAttributes // // Synopsis: Querys attributes of the specified context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpQueryContextAttributes( IN LSA_SEC_HANDLE ContextHandle, IN ULONG ContextAttribute, IN OUT PVOID Buffer ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; PSecPkgContext_Sizes SizeInfo; PSecPkgContext_Names NameInfo; PSecPkgContext_DceInfo DceInfo; PSecPkgContext_Lifespan LifespanInfo; PSecPkgContext_Flags FlagsInfo; PSecPkgContext_PackageInfo PackageInfo; PSecPkgContext_NegotiationInfo NegInfo ; PSecPkgContext_SessionKey SessionKeyInfo; PSecPkgContext_KeyInfo KeyInfo; PSecPkgContext_AccessToken AccessToken; PSecPkgContext_TargetInformation TargetInformation; ULONG PackageInfoSize = 0; UNICODE_STRING FullName; LONG ChecksumType; LONG EncryptType; PCRYPTO_SYSTEM CryptSystem = NULL ; TimeStamp CurrentTime; D_DebugLog((DEB_TRACE_API,"SpQueryContextAttributes called pid:0x%x, ctxt:0x%x, Attr:0x%x\n", GetCurrentProcessId(), ContextHandle, ContextAttribute)); Status = KerbReferenceContextByLsaHandle( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for QueryContextAttributes(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Return the appropriate information // switch(ContextAttribute) { case SECPKG_ATTR_SIZES: gss_OID_desc * MechId; UINT MessageSize; if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { MechId = gss_mech_krb5_u2u; } else { MechId = gss_mech_krb5_new; } // // The sizes returned are used by RPC to determine whether to call // MakeSignature or SealMessage. The signature size should be zero // if neither is to be called, and the block size and trailer size // should be zero if SignMessage is not to be called. // SizeInfo = (PSecPkgContext_Sizes) Buffer; SizeInfo->cbMaxToken = KerbGlobalMaxTokenSize; // If we need to be Gss Compatible, then the Signature buffer size is // dependent on the message size. So, we'll set it to be largest pad // for the largest message size, say 1G. But, don't tax dce style // callers with extra bytes. if (((Context->ContextFlags & ISC_RET_DATAGRAM) != 0) || ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)) { MessageSize = 0; } else { MessageSize = KERB_MAX_MESSAGE_SIZE; } if ((Context->ContextFlags & (KERB_SIGN_FLAGS | ISC_RET_CONFIDENTIALITY)) != 0) { SizeInfo->cbMaxSignature = g_token_size(MechId, sizeof(KERB_GSS_SIGNATURE)); } else { SizeInfo->cbMaxSignature = sizeof(KERB_NULL_SIGNATURE); } // // get the encryption type for the context // Status = KerbGetChecksumAndEncryptionType( Context, KERB_WRAP_NO_ENCRYPT, // checksum not needed so use hardcoded QOP &ChecksumType, // checksum not needed here &EncryptType ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Locate the cryptsystem for the context, loading it if necessary from the // the crypto support DLL // Status = CDLocateCSystem(EncryptType, &CryptSystem); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x. %ws, line %d\n",EncryptType,Status, THIS_FILE, __LINE__)); goto Cleanup; } // // RPC keys off the trailer size to tell whether or not // to encrypt, not the flags from isc/asc. So, for dce style, // say the blocksize & trailersize are zero. // if (((Context->ContextFlags & ISC_RET_CONFIDENTIALITY) != 0) || ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) == 0)) { // // Use block size from crypto system // SizeInfo->cbBlockSize = CryptSystem->BlockSize; SizeInfo->cbSecurityTrailer = g_token_size(MechId, sizeof(KERB_GSS_SEAL_SIGNATURE) + MessageSize) - MessageSize; } else { SizeInfo->cbBlockSize = 0; SizeInfo->cbSecurityTrailer = 0; } break; case SECPKG_ATTR_SESSION_KEY: SessionKeyInfo = (PSecPkgContext_SessionKey) Buffer; SessionKeyInfo->SessionKeyLength = Context->SessionKey.keyvalue.length; if (SessionKeyInfo->SessionKeyLength != 0) { SessionKeyInfo->SessionKey = (PUCHAR) UserFunctions->AllocateHeap( SessionKeyInfo->SessionKeyLength); if (SessionKeyInfo->SessionKey!=NULL) { RtlCopyMemory( SessionKeyInfo->SessionKey, Context->SessionKey.keyvalue.value, Context->SessionKey.keyvalue.length ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } else { SessionKeyInfo->SessionKey = (PUCHAR) UserFunctions->AllocateHeap(1); if (SessionKeyInfo->SessionKey!=NULL) { *(PUCHAR) SessionKeyInfo->SessionKey = 0; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } break; case SECPKG_ATTR_NAMES: NameInfo = (PSecPkgContext_Names) Buffer; if (!KERB_SUCCESS(KerbBuildFullServiceName( &Context->ClientRealm, &Context->ClientName, &FullName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } #ifndef WIN32_CHICAGO NameInfo->sUserName = (LPWSTR) UserFunctions->AllocateHeap(FullName.Length + sizeof(WCHAR)); if (NameInfo->sUserName != NULL) { RtlCopyMemory( NameInfo->sUserName, FullName.Buffer, FullName.Length ); NameInfo->sUserName[FullName.Length/sizeof(WCHAR)] = L'\0'; } #else // WIN32_CHICAGO ANSI_STRING AnsiString; RtlUnicodeStringToAnsiString( &AnsiString, &FullName, TRUE); NameInfo->sUserName = (LPTSTR) UserFunctions->AllocateHeap(AnsiString.Length + sizeof(CHAR)); if (NameInfo->sUserName != NULL) { RtlCopyMemory( NameInfo->sUserName, AnsiString.Buffer, AnsiString.Length ); NameInfo->sUserName[AnsiString.Length] = '\0'; RtlFreeAnsiString(&AnsiString); } #endif // WIN32_CHICAGO else { Status = STATUS_INSUFFICIENT_RESOURCES; } KerbFreeString(&FullName); break; case SECPKG_ATTR_DCE_INFO: DceInfo = (PSecPkgContext_DceInfo) Buffer; if (!KERB_SUCCESS(KerbBuildFullServiceName( &Context->ClientRealm, &Context->ClientName, &FullName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } DceInfo->AuthzSvc = RPC_C_AUTHZ_NAME; #ifndef WIN32_CHICAGO DceInfo->pPac = UserFunctions->AllocateHeap(FullName.Length + sizeof(WCHAR)); if (DceInfo->pPac != NULL) { RtlCopyMemory( DceInfo->pPac, FullName.Buffer, FullName.Length ); ((LPWSTR)DceInfo->pPac)[FullName.Length/sizeof(WCHAR)] = L'\0'; } #else // WIN32_CHICAGO RtlUnicodeStringToAnsiString( &AnsiString, &FullName, TRUE); DceInfo->pPac = UserFunctions->AllocateHeap(AnsiString.Length + sizeof(CHAR)); if (DceInfo->pPac != NULL) { RtlCopyMemory( DceInfo->pPac, AnsiString.Buffer, AnsiString.Length ); ((LPTSTR) DceInfo->pPac)[AnsiString.Length] = '\0'; RtlFreeAnsiString(&AnsiString); } #endif // WIN32_CHICAGO else { Status = STATUS_INSUFFICIENT_RESOURCES; } KerbFreeString(&FullName); break; case SECPKG_ATTR_TARGET_INFORMATION: { TargetInformation = (PSecPkgContext_TargetInformation) Buffer; if (TargetInformation == NULL) { Status = STATUS_INVALID_PARAMETER; break; } TargetInformation->MarshalledTargetInfo = NULL; if (Context->pbMarshalledTargetInfo == NULL) { TargetInformation->MarshalledTargetInfo = NULL; TargetInformation->MarshalledTargetInfoLength = 0; break; } TargetInformation->MarshalledTargetInfo = (PUCHAR) UserFunctions->AllocateHeap( Context->cbMarshalledTargetInfo ); if (TargetInformation->MarshalledTargetInfo != NULL) { RtlCopyMemory( TargetInformation->MarshalledTargetInfo, Context->pbMarshalledTargetInfo, Context->cbMarshalledTargetInfo ); TargetInformation->MarshalledTargetInfoLength = Context->cbMarshalledTargetInfo; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } break; } case SECPKG_ATTR_LIFESPAN: LifespanInfo = (PSecPkgContext_Lifespan) Buffer; if (KerbGetTime(Context->StartTime) != KerbGetTime(KerbGlobalHasNeverTime)) { KerbUtcTimeToLocalTime( &LifespanInfo->tsStart, &(Context->StartTime) ); D_DebugLog((DEB_TRACE, "Used context start time \n")); } else if (NULL != Context->TicketCacheEntry) { KerbUtcTimeToLocalTime( &LifespanInfo->tsStart, &(Context->TicketCacheEntry->StartTime) ); KerbWriteLockContexts(); Context->StartTime = Context->TicketCacheEntry->StartTime; KerbUnlockContexts(); DebugLog((DEB_ERROR, "Used tkt cache entry start time \n")); } else // set it to current time { // The context is not in a state where we've got a // tkt cache entry, so let's use current time. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime ); KerbUtcTimeToLocalTime( &LifespanInfo->tsStart, &CurrentTime ); DebugLog((DEB_ERROR, "NO START TIME PRESENT IN CONTEXT, or CACHE ENTRY!\n")); } KerbUtcTimeToLocalTime( &LifespanInfo->tsExpiry, &Context->Lifetime ); break; case SECPKG_ATTR_FLAGS: FlagsInfo = (PSecPkgContext_Flags) Buffer; if ((Context->ContextAttributes & KERB_CONTEXT_INBOUND) != 0) { FlagsInfo->Flags = KerbMapContextFlags( Context->ContextFlags ); } else { FlagsInfo->Flags = Context->ContextFlags; } break; #ifndef WIN32_CHICAGO case SECPKG_ATTR_KEY_INFO: PCRYPTO_SYSTEM CryptoSystem; KeyInfo = (PSecPkgContext_KeyInfo) Buffer; KeyInfo->KeySize = KerbIsKeyExportable(&Context->SessionKey) ? 56 : 128; KeyInfo->EncryptAlgorithm = Context->SessionKey.keytype; KeyInfo->SignatureAlgorithm = KERB_IS_DES_ENCRYPTION(Context->SessionKey.keytype) ? KERB_CHECKSUM_MD25 : KERB_CHECKSUM_HMAC_MD5; KeyInfo->sSignatureAlgorithmName = NULL; KeyInfo->sEncryptAlgorithmName = NULL; // // The checksum doesn't include a name, so don't fill it in - leave // it as an empty string, so callers don't die when they // try to manipulate it. // Status = CDLocateCSystem(KeyInfo->EncryptAlgorithm, &CryptoSystem); if (NT_SUCCESS(Status)) { KeyInfo->sEncryptAlgorithmName = (LPWSTR) UserFunctions->AllocateHeap(sizeof(WCHAR) * ((ULONG) wcslen(CryptoSystem->Name) + 1)); if (KeyInfo->sEncryptAlgorithmName != NULL) { wcscpy( KeyInfo->sEncryptAlgorithmName, CryptoSystem->Name ); KeyInfo->sSignatureAlgorithmName = (LPWSTR) UserFunctions->AllocateHeap(sizeof(WCHAR)); if (KeyInfo->sSignatureAlgorithmName != NULL) { *KeyInfo->sSignatureAlgorithmName = L'\0'; } else { Status = STATUS_INSUFFICIENT_RESOURCES; UserFunctions->FreeHeap(KeyInfo->sEncryptAlgorithmName); KeyInfo->sEncryptAlgorithmName = NULL; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } break; #endif // WIN32_CHICAGO 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. // PackageInfo = (PSecPkgContext_PackageInfo) Buffer; PackageInfoSize = sizeof(SecPkgInfo) + sizeof(KERBEROS_PACKAGE_NAME) + sizeof(KERBEROS_PACKAGE_COMMENT); PackageInfo->PackageInfo = (PSecPkgInfo) UserFunctions->AllocateHeap(PackageInfoSize); if (PackageInfo->PackageInfo == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } PackageInfo->PackageInfo->Name = (LPTSTR) (PackageInfo->PackageInfo + 1); PackageInfo->PackageInfo->Comment = (LPTSTR) (((PBYTE) PackageInfo->PackageInfo->Name) + sizeof(KERBEROS_PACKAGE_NAME)); lstrcpy( PackageInfo->PackageInfo->Name, KERBEROS_PACKAGE_NAME ); lstrcpy( PackageInfo->PackageInfo->Comment, KERBEROS_PACKAGE_COMMENT ); PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; PackageInfo->PackageInfo->wRPCID = RPC_C_AUTHN_GSS_KERBEROS; PackageInfo->PackageInfo->fCapabilities = KERBEROS_CAPABILITIES; PackageInfo->PackageInfo->cbMaxToken = KerbGlobalMaxTokenSize; if ( ContextAttribute == SECPKG_ATTR_NEGOTIATION_INFO ) { NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ; NegInfo->NegotiationState = Context->NegotiationInfo ; } break; case SECPKG_ATTR_ACCESS_TOKEN: { AccessToken = (PSecPkgContext_AccessToken) Buffer; // // ClientTokenHandle can be NULL, for instance: // 1. client side context. // 2. incomplete server context. // AccessToken->AccessToken = (void*)Context->TokenHandle; break; } default: Status = STATUS_NOT_SUPPORTED; break; } Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } D_DebugLog((DEB_TRACE_API,"SpQueryContextAttributes returned 0x%x, pid:0x%x, ctxt:0x%x, Attr:0x%x\n", KerbMapKerbNtStatusToNtStatus(Status), GetCurrentProcessId(), ContextHandle, ContextAttribute)); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: SpQueryLsaModeContextAttributes // // Synopsis: Querys attributes of the specified context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpQueryLsaModeContextAttributes( IN LSA_SEC_HANDLE ContextHandle, IN ULONG ContextAttribute, IN OUT PVOID Buffer ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CONTEXT Context = NULL; SecPkgContext_NativeNames NameInfo = {0}; BOOLEAN ContextsLocked = FALSE; UNICODE_STRING ServerName = {0}; UNICODE_STRING ClientName = {0}; BOOLEAN IsClientContext = FALSE; BOOLEAN TicketCacheLocked = FALSE; SecPkgContext_NegotiationInfo NegInfo = {0}; ULONG PackageInfoSize; D_DebugLog((DEB_TRACE_API,"SpQueryLsaModeContextAttributes called ctxt:0x%x, Attr:0x%x\n", ContextHandle, ContextAttribute)); Status = KerbReferenceContext( ContextHandle, FALSE, // don't unlink &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Invalid handle supplied for QueryContextAttributes(0x%x) Status = 0x%x. %ws, line %d\n", ContextHandle, Status, THIS_FILE, __LINE__)); goto Cleanup; } // // Ticket cache lock must be acquired prior to context lock according // to the documented locking order; it is going to be needed for SECPKG_ATTR_NATIVE_NAMES, // so grab it here to avoid a deadlock // if ( ContextAttribute == SECPKG_ATTR_NATIVE_NAMES ) { KerbReadLockTicketCache(); TicketCacheLocked = TRUE; } KerbReadLockContexts(); ContextsLocked = TRUE; // // Return the appropriate information // switch(ContextAttribute) { case SECPKG_ATTR_NATIVE_NAMES: // // Get outbound names from the ticket // if (Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) { IsClientContext = TRUE; if (Context->TicketCacheEntry != NULL) { KERBERR KerbErr = KDC_ERR_NONE; KerbErr = KerbConvertKdcNameToString( &ServerName, Context->TicketCacheEntry->ServiceName, &Context->TicketCacheEntry->DomainName ); if (KERB_SUCCESS(KerbErr)) { KerbErr = KerbConvertKdcNameToString( &ClientName, Context->TicketCacheEntry->ClientName, &Context->TicketCacheEntry->ClientDomainName ); } if (!KERB_SUCCESS(KerbErr)) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } } else { // // We couldn't find the names, so return an error // Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Cleanup; } } else { // // We have a server context // ClientName = Context->ClientPrincipalName; ServerName = Context->ServerPrincipalName; } if (ServerName.Length != 0) { Status = LsaFunctions->AllocateClientBuffer( NULL, ServerName.Length + sizeof(WCHAR), (PVOID *) &NameInfo.sServerName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaFunctions->CopyToClientBuffer( NULL, ServerName.Length + sizeof(WCHAR), NameInfo.sServerName, ServerName.Buffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } if (ClientName.Length != 0) { Status = LsaFunctions->AllocateClientBuffer( NULL, ClientName.Length + sizeof(WCHAR), (PVOID *) &NameInfo.sClientName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaFunctions->CopyToClientBuffer( NULL, ClientName.Length + sizeof(WCHAR), NameInfo.sClientName, ClientName.Buffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Copy the whole structure // #if _WIN64 SECPKG_CALL_INFO CallInfo; if (!LsaFunctions->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; } if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { SecPkgContext_NativeNamesW_WOW64 NameInfoWOW64; NameInfoWOW64.sServerName = PtrToUlong(NameInfo.sServerName); NameInfoWOW64.sClientName = PtrToUlong(NameInfo.sClientName); Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(SecPkgContext_NativeNamesW_WOW64), Buffer, &NameInfoWOW64 ); } else { #endif // _WIN64 Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(SecPkgContext_NativeNames), Buffer, &NameInfo ); #if _WIN64 } #endif // _WIN64 if (!NT_SUCCESS(Status)) { goto Cleanup; } break; case SECPKG_ATTR_NEGOTIATION_INFO: { PSecPkgInfo PackageInfo = NULL; SecPkgInfo TmpInfo = {0}; ULONG Offset = sizeof(SecPkgInfo); PackageInfoSize = sizeof(SecPkgInfo) + ROUND_UP_COUNT(sizeof(KERBEROS_PACKAGE_NAME), ALIGN_LPTSTR) + ROUND_UP_COUNT(sizeof(KERBEROS_PACKAGE_COMMENT), ALIGN_LPTSTR); Status = LsaFunctions->AllocateClientBuffer( NULL, PackageInfoSize, (PVOID *) &PackageInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } TmpInfo.Name = (SEC_WCHAR*) RtlOffsetToPointer( PackageInfo, Offset ); Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(KERBEROS_PACKAGE_NAME), TmpInfo.Name, KERBEROS_PACKAGE_NAME ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Offset += ROUND_UP_COUNT(sizeof(KERBEROS_PACKAGE_NAME), ALIGN_LPTSTR); TmpInfo.Comment = (SEC_WCHAR*) RtlOffsetToPointer( PackageInfo, Offset ); Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(KERBEROS_PACKAGE_COMMENT), TmpInfo.Comment, KERBEROS_PACKAGE_COMMENT ); if (!NT_SUCCESS(Status)) { goto Cleanup; } TmpInfo.wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; TmpInfo.wRPCID = RPC_C_AUTHN_GSS_KERBEROS; TmpInfo.fCapabilities = KERBEROS_CAPABILITIES; TmpInfo.cbMaxToken = KerbGlobalMaxTokenSize; Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(SecPkgInfo), PackageInfo, &TmpInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } NegInfo.PackageInfo = PackageInfo; NegInfo.NegotiationState = Context->NegotiationInfo; Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(SecPkgContext_NegotiationInfo), Buffer, &NegInfo ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } break; default: Status = STATUS_NOT_SUPPORTED; break; } Cleanup: if (TicketCacheLocked) { KerbUnlockTicketCache(); } if (ContextsLocked) { KerbUnlockContexts(); } if (Context != NULL) { KerbDereferenceContext(Context); } if (IsClientContext) { KerbFreeString( &ClientName ); KerbFreeString( &ServerName ); } if (!NT_SUCCESS(Status)) { if (NameInfo.sServerName != NULL) { LsaFunctions->FreeClientBuffer( NULL, NameInfo.sServerName ); } if (NameInfo.sClientName != NULL) { LsaFunctions->FreeClientBuffer( NULL, NameInfo.sClientName ); } } D_DebugLog((DEB_TRACE_API,"SpQueryLsaModeContextAttributes returned 0x%x, pid:0x%x, ctxt:0x%x, Attr:0x%x\n", KerbMapKerbNtStatusToNtStatus(Status), GetCurrentProcessId(), ContextHandle, ContextAttribute)); return(KerbMapKerbNtStatusToNtStatus(Status)); } //+------------------------------------------------------------------------- // // Function: SpCompleteAuthToken // // Synopsis: Completes a context (in Kerberos case, does nothing) // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI SpCompleteAuthToken( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc InputBuffer ) { return(STATUS_SUCCESS); } #ifndef WIN32_CHICAGO NTSTATUS NTAPI SpFormatCredentials( IN PSecBuffer Credentials, OUT PSecBuffer FormattedCredentials ) { return(STATUS_NOT_SUPPORTED); } NTSTATUS NTAPI SpMarshallSupplementalCreds( IN ULONG CredentialSize, IN PUCHAR Credentials, OUT PULONG MarshalledCredSize, OUT PVOID * MarshalledCreds ) { return(STATUS_NOT_SUPPORTED); } #endif // WIN32_CHICAGO