//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1996 // // File: userapi.cxx // // Contents: User-mode APIs to Kerberos package // // // History: 17-April-1996 Created MikeSw // //------------------------------------------------------------------------ #include extern "C" { #include } #include "krnlapi.h" #define DONT_SUPPORT_OLD_TYPES_USER 1 // // Make these extern "C" to allow them to be pageable. // extern "C" { KspInitPackageFn KerbInitKernelPackage; KspDeleteContextFn KerbDeleteKernelContext; KspInitContextFn KerbInitKernelContext; KspMapHandleFn KerbMapKernelHandle; KspMakeSignatureFn KerbMakeSignature; KspVerifySignatureFn KerbVerifySignature; KspSealMessageFn KerbSealMessage; KspUnsealMessageFn KerbUnsealMessage; KspGetTokenFn KerbGetContextToken; KspQueryAttributesFn KerbQueryContextAttributes; KspCompleteTokenFn KerbCompleteToken; SpExportSecurityContextFn KerbExportContext; SpImportSecurityContextFn KerbImportContext; KspSetPagingModeFn KerbSetPageMode ; NTSTATUS KerbMakeSignatureToken( IN PKERB_KERNEL_CONTEXT Context, IN ULONG QualityOfProtection, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Encrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG ChecksumType, OUT PULONG EncryptionType, OUT PULONG SequenceNumber ); NTSTATUS KerbVerifySignatureToken( IN PKERB_KERNEL_CONTEXT Context, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Decrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG QualityOfProtection, OUT PULONG ChecksumType, OUT PCRYPTO_SYSTEM * CryptSystem, OUT PULONG SequenceNumber ); NTSTATUS NTAPI KerbInitDefaults(); } #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, KerbInitKernelPackage) #pragma alloc_text(PAGE, KerbDeleteKernelContext) #pragma alloc_text(PAGE, KerbInitKernelContext) #pragma alloc_text(PAGE, KerbMapKernelHandle) #pragma alloc_text(PAGEMSG, KerbMakeSignature) #pragma alloc_text(PAGEMSG, KerbVerifySignature) #pragma alloc_text(PAGEMSG, KerbSealMessage) #pragma alloc_text(PAGEMSG, KerbUnsealMessage) #pragma alloc_text(PAGEMSG, KerbGetContextToken) #pragma alloc_text(PAGEMSG, KerbQueryContextAttributes) #pragma alloc_text(PAGEMSG, KerbMakeSignatureToken) #pragma alloc_text(PAGEMSG, KerbVerifySignatureToken) #pragma alloc_text(PAGE, KerbCompleteToken) #pragma alloc_text(PAGE, KerbExportContext) #pragma alloc_text(PAGE, KerbImportContext) #pragma alloc_text(PAGE, KerbInitDefaults) #endif SECPKG_KERNEL_FUNCTION_TABLE KerberosFunctionTable = { KerbInitKernelPackage, KerbDeleteKernelContext, KerbInitKernelContext, KerbMapKernelHandle, KerbMakeSignature, KerbVerifySignature, KerbSealMessage, KerbUnsealMessage, KerbGetContextToken, KerbQueryContextAttributes, KerbCompleteToken, KerbExportContext, KerbImportContext, KerbSetPageMode }; POOL_TYPE KerbPoolType ; #define MAYBE_PAGED_CODE() \ if ( KerbPoolType == PagedPool ) \ { \ PAGED_CODE(); \ } PVOID KerbPagedList ; PVOID KerbNonPagedList ; PVOID KerbActiveList ; ERESOURCE KerbGlobalResource; BOOLEAN KerbCryptInitialized; ULONG KerbMaxTokenSize = KERBEROS_MAX_TOKEN; extern "C" { int LibAttach(HANDLE, PVOID); } #define KerbWriteLockGlobals() \ { \ DebugLog((DEB_TRACE_LOCKS,"Write locking Globals\n")); \ KeEnterCriticalRegion(); \ ExAcquireResourceExclusiveLite(&KerbGlobalResource,TRUE); \ } #define KerbReadLockGlobals() \ { \ DebugLog((DEB_TRACE_LOCKS,"Read locking Globals\n")); \ KeEnterCriticalRegion(); \ ExAcquireSharedWaitForExclusive(&KerbGlobalResource, TRUE); \ } #define KerbUnlockGlobals() \ { \ DebugLog((DEB_TRACE_LOCKS,"Unlocking Globals\n")); \ ExReleaseResourceLite(&KerbGlobalResource); \ KeLeaveCriticalRegion(); \ } // // 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 }; 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; #define KERB_MAX_CHECKSUM_LENGTH 24 //+------------------------------------------------------------------------- // // Function: KerbInitDefaults // // Synopsis: Opens registry key, and gets custom defaults // // Effects: Changes MaxTokenSize // // Arguments: None // // Requires: // // Returns: STATUS_SUCCESS on success // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbInitDefaults() { UNICODE_STRING ParameterPath; UNICODE_STRING MaxTokenValue; OBJECT_ATTRIBUTES oa; ULONG BytesRead; NTSTATUS Status; HANDLE hParamKey = INVALID_HANDLE_VALUE; KEY_VALUE_PARTIAL_INFORMATION KeyPartialInformation; PAGED_CODE(); RtlInitUnicodeString(&ParameterPath, KERB_PARAMETER_PATH); RtlInitUnicodeString(&MaxTokenValue, KERB_PARAMETER_MAX_TOKEN_SIZE); InitializeObjectAttributes( &oa, &ParameterPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = ZwOpenKey( &hParamKey, KEY_READ, &oa ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "KerbInitDefault:OpenKey failed:0x%x\n",Status)); goto Cleanup; } Status = ZwQueryValueKey( hParamKey, &MaxTokenValue, KeyValuePartialInformation, (PVOID)&KeyPartialInformation, sizeof(KeyPartialInformation), &BytesRead ); if (!NT_SUCCESS(Status) || KeyPartialInformation.Type != REG_DWORD) { DebugLog((DEB_WARN, "KerbInitDefault:QueryValueKey failed:0x%x\n",Status)); goto Cleanup; } else { PULONG Value = (PULONG) &KeyPartialInformation.Data; KerbMaxTokenSize = *((PULONG)Value); } Cleanup: if (INVALID_HANDLE_VALUE != hParamKey) { ZwClose(hParamKey); } return Status; } //+------------------------------------------------------------------------- // // Function: KerbInitKernelPackage // // Synopsis: Initialize an instance of the Kerberos package in the kernel // // 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 KerbInitKernelPackage( PSECPKG_KERNEL_FUNCTIONS FunctionTable ) { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); KerbPoolType = PagedPool ; KerbPagedList = KSecCreateContextList( KSecPaged ); if ( !KerbPagedList ) { return STATUS_NO_MEMORY ; } KerbActiveList = KerbPagedList ; Status = ExInitializeResourceLite (&KerbGlobalResource); if (!NT_SUCCESS(Status)) { return Status; } // Get registry values, ignore failures KerbInitDefaults(); return STATUS_SUCCESS ; } //+------------------------------------------------------------------------- // // Function: KerbDeleteKernelContext // // Synopsis: Deletes a kernel mode context by unlinking it and then // dereferencing it. // // Effects: // // Arguments: ContextHandle - Kernel context handle of the context to delete // LsaContextHandle - Receives LSA context handle of the context // to delete // // Requires: // // Returns: STATUS_SUCCESS on success // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbDeleteKernelContext( IN LSA_SEC_HANDLE ContextHandle, OUT PLSA_SEC_HANDLE LsaContextHandle ) { PKERB_KERNEL_CONTEXT Context = NULL; NTSTATUS SaveStatus = STATUS_SUCCESS; DebugLog((DEB_TRACE,"KerbDeleteUserModeContext called\n")); Context = KerbReferenceContext( ContextHandle, TRUE // unlink it ); if (Context == NULL) { DebugLog((DEB_WARN,"Failed to reference context 0x%x by lsa handle\n", ContextHandle )); *LsaContextHandle = ContextHandle; return(STATUS_INVALID_HANDLE); } KerbReadLockContexts(); *LsaContextHandle = Context->LsaContextHandle; if ((Context->ContextAttributes & KERB_CONTEXT_EXPORTED) != 0) { SaveStatus = SEC_I_NO_LSA_CONTEXT; } KerbUnlockContexts(); KerbDereferenceContext( Context ); return((SaveStatus == SEC_I_NO_LSA_CONTEXT) ? SEC_I_NO_LSA_CONTEXT : STATUS_SUCCESS); } //+------------------------------------------------------------------------- // // Function: KerbInitKernelContext // // Synopsis: Creates a kernel-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. // NewContextHandle - Receives kernel mode context handle // // Requires: // // Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbInitKernelContext( IN LSA_SEC_HANDLE LsaContextHandle, IN PSecBuffer PackedContext, OUT PLSA_SEC_HANDLE NewContextHandle ) { NTSTATUS Status; PKERB_KERNEL_CONTEXT Context = NULL; PAGED_CODE(); DebugLog((DEB_TRACE,"KerbInitUserModeContex called\n")); Status = KerbCreateKernelModeContext( LsaContextHandle, PackedContext, &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create kernel mode context: 0x%x\n", Status)); goto Cleanup; } *NewContextHandle = KerbGetContextHandle(Context); Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } if (PackedContext->pvBuffer != NULL) { KspKernelFunctions.FreeHeap(PackedContext->pvBuffer); PackedContext->pvBuffer = NULL; } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbMapKernelHandle // // Synopsis: Maps a kernel handle into an LSA handle // // Effects: // // Arguments: ContextHandle - Kernel context handle of the context to map // LsaContextHandle - Receives LSA context handle of the context // to map // // Requires: // // Returns: STATUS_SUCCESS on success // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbMapKernelHandle( IN LSA_SEC_HANDLE ContextHandle, OUT PLSA_SEC_HANDLE LsaContextHandle ) { PKERB_KERNEL_CONTEXT Context = NULL; DebugLog((DEB_TRACE,"KerbMapKernelhandle called\n")); PAGED_CODE(); Context = KerbReferenceContext( ContextHandle, FALSE // don't it ); if (Context == NULL) { DebugLog((DEB_WARN,"Failed to reference context 0x%x by lsa handle\n", ContextHandle )); *LsaContextHandle = ContextHandle; } else { *LsaContextHandle = Context->LsaContextHandle; KerbDereferenceContext( Context ); // // If the lsa context handle is zero, this is an imported context // so there is no lsa context // if (*LsaContextHandle == 0) { return(SEC_E_UNSUPPORTED_FUNCTION); } } return(STATUS_SUCCESS); } //+------------------------------------------------------------------------- // // Function: KerbRandomFill // // Synopsis: Generates random data in the buffer. // // Arguments: [pbBuffer] -- // [cbBuffer] -- // // History: 5-20-93 RichardW Created // // Notes: // //-------------------------------------------------------------------------- VOID KerbRandomFill( PUCHAR pbBuffer, ULONG cbBuffer) { if (!CDGenerateRandomBits(pbBuffer, cbBuffer)) { return; } return; } //+------------------------------------------------------------------------- // // 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 // 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 KerbMakeSignatureToken( IN PKERB_KERNEL_CONTEXT Context, IN ULONG QualityOfProtection, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Encrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG ChecksumType, OUT PULONG EncryptionType, OUT PULONG SequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_GSS_SIGNATURE Signature; PKERB_GSS_SEAL_SIGNATURE SealSignature; ULONG MessageSize; ULONG SignatureSize = 0; PULONG Nonce; gss_OID MechUsed; BOOLEAN GssCompatible = TRUE; // // Make sure that cryptdll stuff is initialized. // MAYBE_PAGED_CODE(); if (!KerbCryptInitialized) { KerbWriteLockGlobals(); if ( !KerbCryptInitialized ) { if (LibAttach(NULL, NULL)) { KerbCryptInitialized = TRUE; } } KerbUnlockGlobals(); } // // 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 (QualityOfProtection == GSS_KRB5_INTEG_C_QOP_DEFAULT) { 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 (!Encrypt || !GssCompatible) { TotalBufferSize = 0; } if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { 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) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_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) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_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) { *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN; Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4; } } else { ASSERT (Context->SessionKey.keytype == KERB_ETYPE_RC4_HMAC_NT_EXP); *ChecksumType = KERB_CHECKSUM_HMAC_MD5; *EncryptionType = KERB_ETYPE_RC4_PLAIN_EXP; Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_HMAC; if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_RC4; } } } else { if (Encrypt) { Signature->SealAlgorithm[1] = KERB_GSS_SIG_SECOND; Signature->SealAlgorithm[0] = KERB_GSS_SEAL_DES_CBC; } // // Use the exportable version if necessasry // *EncryptionType = KERB_ETYPE_DES_PLAIN; switch(QualityOfProtection) { case GSS_KRB5_INTEG_C_QOP_MD5: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_MD25; *ChecksumType = KERB_CHECKSUM_MD25; break; case GSS_KRB5_INTEG_C_QOP_DEFAULT: case GSS_KRB5_INTEG_C_QOP_DES_MD5: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC_MD5; *ChecksumType = KERB_CHECKSUM_DES_MAC_MD5; break; case GSS_KRB5_INTEG_C_QOP_DES_MAC: Signature->SignatureAlgorithm[0] = KERB_GSS_SIG_DES_MAC; *ChecksumType = KERB_CHECKSUM_DES_MAC; break; default: DebugLog((DEB_ERROR,"Invalid quality of protection sent to MakeSignature: %d.\n", QualityOfProtection )); 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. // if (Context->ContextAttributes & KERB_CONTEXT_INBOUND) { *(PULONG)(&Signature->SequenceNumber[4]) = 0xffffffff; Nonce = &Context->ReceiveNonce; } else { ASSERT((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0); *(PULONG)(&Signature->SequenceNumber[4]) = 0x00000000; Nonce = &Context->Nonce; } // // 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; } 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); (*Nonce)++; *SequenceNumber = *(PULONG)Signature->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_KERNEL_CONTEXT Context, IN PSecBuffer SignatureBuffer, IN ULONG TotalBufferSize, IN BOOLEAN Decrypt, IN ULONG SuppliedNonce, OUT PKERB_GSS_SIGNATURE * OutputSignature, OUT PULONG QualityOfProtection, OUT PULONG 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; ULONG EncryptionType; PCRYPTO_SYSTEM LocalCryptSystem = NULL ; PKERB_GSS_SIGNATURE Signature; PULONG ContextNonce; gss_OID MechUsed; // // Make sure that cryptdll stuff is initialized. // MAYBE_PAGED_CODE(); if (!KerbCryptInitialized) { KerbWriteLockGlobals(); if ( !KerbCryptInitialized ) { if (LibAttach(NULL, NULL)) { KerbCryptInitialized = TRUE; } } KerbUnlockGlobals(); } // // 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 // if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { 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; } // // 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; } else { 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; } else { 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; } else { Status = STATUS_SUCCESS; } } } // // Protection from bad Signature Size // if (SignatureSize == 0) { 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; goto Cleanup; } // // Verify the sequence number // if (Signature->SignatureAlgorithm[1] != KERB_GSS_SIG_SECOND) { 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\n", Context->SessionKey.keytype )); 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\n", Signature->SignatureAlgorithm[0])); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } if (Decrypt) { if (Signature->SealAlgorithm[1] != KERB_GSS_SIG_SECOND) { 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\n" )); 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\n")); 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\n")); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } break; default: DebugLog((DEB_ERROR,"Invalid seal type to VerifySignature: %d, %d\n", Signature->SealAlgorithm[0], EncryptionType)); Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } } // // Check the filler // if ((Decrypt && (*(PUSHORT) Signature->SealFiller != 0xffff)) || (!Decrypt && (*(PULONG) Signature->SignFiller != 0xffffffff))) { 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\n", EncryptionType,Status)); 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 { if ((Context->ContextAttributes & KERB_CONTEXT_OUTBOUND) != 0) { ContextNonce = &Context->ReceiveNonce; } else { ContextNonce = &Context->Nonce; } } Nonce[0] = (UCHAR) ((*ContextNonce & 0xff000000) >> 24); Nonce[1] = (UCHAR) ((*ContextNonce & 0x00ff0000) >> 16); Nonce[2] = (UCHAR) ((*ContextNonce & 0x0000ff00) >> 8); Nonce[3] = (UCHAR) (*ContextNonce & 0x000000ff); *SequenceNumber = *(PULONG) Nonce; 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 (*(PULONG)(&Signature->SequenceNumber[4]) != 0xffffffff) { Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } } else { ASSERT((Context->ContextAttributes & KERB_CONTEXT_INBOUND) != 0); if (*(PULONG)(&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); } //+------------------------------------------------------------------------- // // Function: KerbMakeSignature // // 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: Cluster folks need to run this at dpc level (non paged) // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbMakeSignature( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_KERNEL_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]; BOOLEAN ContextsLocked = FALSE; ULONG ChecksumType = 0; ULONG EncryptType; ULONG TotalBufferSize = 0; ULONG OutputSize; ULONG SequenceNumber; MAYBE_PAGED_CODE(); DebugLog((DEB_TRACE,"KerbMakeSignature Called\n")); Context = KerbReferenceContext( ContextHandle, FALSE // don't unlink ); if (Context == NULL) { DebugLog((DEB_ERROR, "Invalid handle supplied for MakeSignature(0x%x)\n", ContextHandle)); Status = STATUS_INVALID_HANDLE; goto Cleanup; } // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { 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\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } KerbWriteLockContexts(); ContextsLocked = TRUE; // // 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 = KerbMakeSignatureToken( Context, QualityOfProtection, SignatureBuffer, TotalBufferSize, FALSE, // don't encrypt MessageSequenceNumber, &Signature, &ChecksumType, &EncryptType, &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.\n ",ChecksumType,Status )); goto Cleanup; } ASSERT(Check->CheckSumSize <= sizeof(LocalChecksum)); Status = CDLocateCSystem(EncryptType, &CryptSystem); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",EncryptType,Status )); 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; } KerbUnlockContexts(); ContextsLocked = FALSE; // // 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 (ContextsLocked) { KerbUnlockContexts(); } if (Context != NULL) { KerbDereferenceContext(Context); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbVerifySignature // // 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: Cluster folks need to run this at dpc level (non paged) // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbVerifySignature( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_KERNEL_CONTEXT Context = NULL; PCHECKSUM_FUNCTION Check; PSecBuffer SignatureBuffer = NULL; ULONG Index; PCHECKSUM_BUFFER CheckBuffer = NULL; PKERB_GSS_SIGNATURE Signature; ULONG ChecksumType; BOOLEAN ContextsLocked = FALSE; UCHAR LocalChecksum[KERB_MAX_CHECKSUM_LENGTH]; ULONG Protection; ULONG TotalBufferSize = 0; ULONG SequenceNumber; MAYBE_PAGED_CODE(); DebugLog((DEB_TRACE,"KerbVerifySignature Called\n")); Context = KerbReferenceContext( ContextHandle, FALSE // don't unlink ); if (Context == NULL) { DebugLog((DEB_ERROR, "Invalid handle supplied for VerifySignature(0x%x)\n", ContextHandle)); Status = STATUS_INVALID_HANDLE; goto Cleanup; } // // Find the body and signature SecBuffers from pMessage // for (Index = 0; Index < MessageBuffers->cBuffers ; Index++ ) { 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\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } KerbWriteLockContexts(); ContextsLocked = TRUE; // // 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 { 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\n", Status)); 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\n", Status)); goto Cleanup; } ASSERT(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; } KerbUnlockContexts(); ContextsLocked = FALSE; // // 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)) { Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } if (ARGUMENT_PRESENT(QualityOfProtection)) { *QualityOfProtection = Protection; } Cleanup: if (ContextsLocked) { KerbUnlockContexts(); } if (Context != NULL) { KerbDereferenceContext(Context); } DebugLog((DEB_TRACE, "SpVerifySignature returned 0x%x\n", Status)); return(Status); } NTSTATUS NTAPI KerbSealMessage( IN LSA_SEC_HANDLE ContextHandle, IN ULONG QualityOfProtection, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber ) { MAYBE_PAGED_CODE(); return(STATUS_NOT_SUPPORTED); } NTSTATUS NTAPI KerbUnsealMessage( IN LSA_SEC_HANDLE ContextHandle, IN PSecBufferDesc MessageBuffers, IN ULONG MessageSequenceNumber, OUT PULONG QualityOfProtection ) { MAYBE_PAGED_CODE(); return(STATUS_NOT_SUPPORTED); } //+------------------------------------------------------------------------- // // Function: KerbGetContextToken // // Synopsis: returns a pointer to the token for a server-side context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbGetContextToken( IN LSA_SEC_HANDLE ContextHandle, OUT OPTIONAL PHANDLE ImpersonationToken, OUT OPTIONAL PACCESS_TOKEN * RawToken ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_KERNEL_CONTEXT Context = NULL; PAGED_CODE(); DebugLog((DEB_TRACE,"KerbGetContextToken Called\n")); Context = KerbReferenceContext( ContextHandle, FALSE // don't unlink ); if (Context == NULL) { DebugLog((DEB_ERROR, "Invalid handle supplied for GetContextToken(0x%x)\n", ContextHandle)); return(STATUS_INVALID_HANDLE); } KerbReadLockContexts(); if (Context->TokenHandle == NULL) { Status = SEC_E_NO_IMPERSONATION; KerbUnlockContexts(); goto Cleanup; } if (ARGUMENT_PRESENT(ImpersonationToken)) { *ImpersonationToken = Context->TokenHandle; } if (ARGUMENT_PRESENT(RawToken)) { if (Context->TokenHandle != NULL) { if (Context->AccessToken == NULL) { Status = ObReferenceObjectByHandle( Context->TokenHandle, TOKEN_IMPERSONATE, NULL, ExGetPreviousMode(), (PVOID *) &Context->AccessToken, NULL // no handle information ); } } if (NT_SUCCESS(Status)) { *RawToken = Context->AccessToken; } } KerbUnlockContexts(); Cleanup: 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); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbQueryContextAttributes // // Synopsis: Querys attributes of the specified context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbQueryContextAttributes( IN LSA_SEC_HANDLE ContextHandle, IN ULONG ContextAttribute, IN OUT PVOID Buffer ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_KERNEL_CONTEXT Context = NULL; PSecPkgContext_Sizes SizeInfo; PSecPkgContext_Names NameInfo; PSecPkgContext_Lifespan LifespanInfo; PSecPkgContext_Flags FlagsInfo; PSecPkgContext_SessionKey SessionKeyInfo; PSecPkgContext_UserFlags UserFlagsInfo ; PSecPkgContext_PackageInfo PackageInfo = NULL; PSecPkgContext_TargetInformation TargetInformation = NULL; UNICODE_STRING FullName; PAGED_CODE(); DebugLog((DEB_TRACE,"SpQueryContextAttributes Called\n")); Context = KerbReferenceContext( ContextHandle, FALSE // don't unlink ); // // allow PACKAGE_INFO or NEGOTIATION_INFO to be queried against // incomplete contexts. // if( (Context == NULL) && (ContextAttribute != SECPKG_ATTR_PACKAGE_INFO) && (ContextAttribute != SECPKG_ATTR_NEGOTIATION_INFO) ) { DebugLog((DEB_ERROR, "Invalid handle supplied for QueryContextAttributes(0x%x)\n", ContextHandle)); return(STATUS_INVALID_HANDLE); } // // Return the appropriate information // switch(ContextAttribute) { case SECPKG_ATTR_SIZES: // // 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 = KerbMaxTokenSize; // if ((Context->ContextFlags & (ISC_RET_CONFIDENTIALITY | ISC_RET_SEQUENCE_DETECT)) != 0) // { SizeInfo->cbMaxSignature = KERB_SIGNATURE_SIZE; // } // else // { // SizeInfo->cbMaxSignature = 0; // } if ((Context->ContextFlags & ISC_RET_CONFIDENTIALITY) != 0) { SizeInfo->cbBlockSize = 1; SizeInfo->cbSecurityTrailer = KERB_SIGNATURE_SIZE; } else { SizeInfo->cbBlockSize = 0; SizeInfo->cbSecurityTrailer = 0; } break; case SECPKG_ATTR_NAMES: NameInfo = (PSecPkgContext_Names) Buffer; NameInfo->sUserName = (LPWSTR) KspKernelFunctions.AllocateHeap(Context->FullName.Length + sizeof(WCHAR)); if (NameInfo->sUserName != NULL) { RtlCopyMemory( NameInfo->sUserName, Context->FullName.Buffer, Context->FullName.Length ); NameInfo->sUserName[Context->FullName.Length/sizeof(WCHAR)] = L'\0'; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } 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) { Status = STATUS_NOT_FOUND; break; } TargetInformation->MarshalledTargetInfo = (PUCHAR) KspKernelFunctions.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; // // BUG 454552: set start time properly. // LifespanInfo->tsStart.QuadPart = 0; LifespanInfo->tsExpiry = Context->Lifetime; break; case SECPKG_ATTR_FLAGS: FlagsInfo = (PSecPkgContext_Flags) Buffer; FlagsInfo->Flags = Context->ContextFlags; break; case SECPKG_ATTR_SESSION_KEY: SessionKeyInfo = (PSecPkgContext_SessionKey) Buffer; SessionKeyInfo->SessionKeyLength = Context->SessionKey.keyvalue.length; if (SessionKeyInfo->SessionKeyLength != 0) { SessionKeyInfo->SessionKey = (PUCHAR) KspKernelFunctions.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) KspKernelFunctions.AllocateHeap(1); if (SessionKeyInfo->SessionKey != NULL) { *(PUCHAR) SessionKeyInfo->SessionKey = 0; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } break; case SECPKG_ATTR_USER_FLAGS: UserFlagsInfo = (PSecPkgContext_UserFlags) Buffer ; UserFlagsInfo->UserFlags = 0 ; Status = STATUS_SUCCESS ; break; case SECPKG_ATTR_PACKAGE_INFO: case SECPKG_ATTR_NEGOTIATION_INFO: // // Return the information about this package. This is useful for // callers who used SPNEGO and don't know what package they got. // PackageInfo = (PSecPkgContext_PackageInfo) Buffer; PackageInfo->PackageInfo = (PSecPkgInfo) KspKernelFunctions.AllocateHeap( sizeof(SecPkgInfo) + sizeof(KERBEROS_PACKAGE_NAME) + sizeof(KERBEROS_PACKAGE_COMMENT) ); if (PackageInfo->PackageInfo == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } PackageInfo->PackageInfo->Name = (LPTSTR) (PackageInfo->PackageInfo + 1); PackageInfo->PackageInfo->Comment = (LPTSTR) (((PBYTE) PackageInfo->PackageInfo->Name) + sizeof(KERBEROS_PACKAGE_NAME)); wcscpy( PackageInfo->PackageInfo->Name, KERBEROS_PACKAGE_NAME ); wcscpy( PackageInfo->PackageInfo->Comment, KERBEROS_PACKAGE_COMMENT ); PackageInfo->PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; PackageInfo->PackageInfo->wRPCID = KERBEROS_RPCID; PackageInfo->PackageInfo->fCapabilities = KERBEROS_CAPABILITIES; PackageInfo->PackageInfo->cbMaxToken = KerbMaxTokenSize; if ( ContextAttribute == SECPKG_ATTR_NEGOTIATION_INFO ) { PSecPkgContext_NegotiationInfo NegInfo ; NegInfo = (PSecPkgContext_NegotiationInfo) PackageInfo ; if( Context != NULL ) { NegInfo->NegotiationState = SECPKG_NEGOTIATION_COMPLETE ; } else { NegInfo->NegotiationState = 0; } } break; default: Status = STATUS_NOT_SUPPORTED; break; } if (Context != NULL) { KerbDereferenceContext(Context); } return(Status); } //+------------------------------------------------------------------------- // // Function: KerbCompleteToken // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbCompleteToken( IN LSA_SEC_HANDLE ContextId, IN PSecBufferDesc Token ) { PAGED_CODE(); return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbExportContext( IN LSA_SEC_HANDLE Context, IN ULONG Flags, OUT PSecBuffer PackedContext, IN OUT PHANDLE TokenHandle ) { return(SEC_E_UNSUPPORTED_FUNCTION); } //+------------------------------------------------------------------------- // // Function: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KerbImportContext( IN PSecBuffer PackedContext, IN OPTIONAL HANDLE TokenHandle, OUT PLSA_SEC_HANDLE ContextHandle ) { NTSTATUS Status; PKERB_KERNEL_CONTEXT Context = NULL; PAGED_CODE(); DebugLog((DEB_TRACE,"KerbInitUserModeContext called\n")); Status = KerbCreateKernelModeContext( 0, // LsaContextHandle not present PackedContext, &Context ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create kernel mode context: 0x%x\n", Status)); goto Cleanup; } if (!KerbCryptInitialized) { KerbWriteLockGlobals(); if ( !KerbCryptInitialized ) { if (LibAttach(NULL, NULL)) { KerbCryptInitialized = TRUE; } } KerbUnlockGlobals(); } KerbWriteLockContexts(); Context->TokenHandle = TokenHandle; *ContextHandle = KerbGetContextHandle(Context); Context->ContextAttributes |= KERB_CONTEXT_IMPORTED; KerbUnlockContexts(); Cleanup: if (Context != NULL) { KerbDereferenceContext(Context); } return(Status); } NTSTATUS KerbSetPageMode( BOOLEAN Pagable ) { if ( Pagable ) { KerbPoolType = PagedPool ; KerbActiveList = KerbPagedList ; } else { if ( KerbNonPagedList == NULL ) { KerbNonPagedList = KSecCreateContextList( KSecNonPaged ); if ( KerbNonPagedList == NULL ) { return STATUS_NO_MEMORY ; } } KerbActiveList = KerbNonPagedList ; KerbPoolType = NonPagedPool ; } return STATUS_SUCCESS ; }