|
|
//+-----------------------------------------------------------------------
//
// 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 <kerb.hxx>
#define USERAPI_ALLOCATE
#include <kerbp.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif
#ifndef WIN32_CHICAGO
extern "C" { #include <cryptdll.h>
} #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
|