|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1991 - 1992
//
// File: tickets.c
//
// Contents: Ticket bundling code
//
//
// History: 6 Dec 91, RichardW Created
// 04 Jun 92 RichardW NT-ized
// 08-Jun-93 WadeR Converted to C++, rewrote packing code
//
//------------------------------------------------------------------------
#ifdef WIN32_CHICAGO
#include<kerb.hxx>
#include<kerbp.h>
#endif // WIN32_CHICAGO
#ifndef WIN32_CHICAGO
extern "C" { #include "krbprgma.h"
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntlsa.h>
#include <samrpc.h>
#include <samisrv.h>
#include <lsarpc.h>
#include <lsaisrv.h>
#include <lsaitf.h>
#include <wincrypt.h>
} #include <kerbcomm.h>
#include <kerberr.h>
#include <kerbcon.h>
#include <midles.h>
#include <authen.hxx>
#include <kerberos.h>
#include "debug.h"
#include "fileno.h"
#else// WIN32_CHICAGO
#include "tostring.hxx"
#endif // WIN32_CHICAGO
#include <utils.hxx>
#define FILENO FILENO_TICKETS
//
// Debugging support.
//
#ifndef WIN32_CHICAGO
#ifdef DEBUG_SUPPORT
DEBUG_KEY KSuppDebugKeys[] = { {DEB_ERROR, "Error"}, {DEB_WARN, "Warning"}, {DEB_TRACE, "Trace"}, {DEB_T_SOCK, "Sock"}, {0, NULL } }; #endif
DEFINE_DEBUG_DEFER(KSupp, KSuppDebugKeys); #endif // WIN32_CHICAGO
RTL_CRITICAL_SECTION OssCriticalSection; BOOLEAN TicketsInitialized; BOOLEAN KerbUseFastDecodeAlloc = FALSE;
#define I_LsaIThreadAlloc MIDL_user_allocate
#define I_LsaIThreadFree MIDL_user_free
//+-------------------------------------------------------------------------
//
// Function: KerbConvertGeneralizedTimeToLargeInt
//
// Synopsis: Converts a generalized time (ASN.1 format) to a large integer
// (NT format)
//
// Effects:
//
// Arguments: TimeStamp - receives NT-style time
// ClientTime - client generalized time
// ClientUsec - client micro second count
//
// Requires: none
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbConvertGeneralizedTimeToLargeInt( OUT PTimeStamp TimeStamp, IN PKERB_TIME ClientTime, IN int ClientUsec ) { KERB_TIME ZeroTime; TIME_FIELDS TimeFields;
//
// Special case zero time
//
RtlZeroMemory( &ZeroTime, sizeof(KERB_TIME) );
ZeroTime.universal = TRUE;
//
// Skip this check after 3/1/97 - no clients should send this sort of
// zero time
//
if (RtlEqualMemory( &ZeroTime, ClientTime, sizeof(KERB_TIME) )) { #ifndef WIN32_CHICAGO
TimeStamp->QuadPart = 0; #else // WIN32_CHICAGO
*TimeStamp = 0; #endif // WIN32_CHICAGO
return; }
//
// Check for MIT zero time
//
ZeroTime.year = 1970; ZeroTime.month = 1; ZeroTime.day = 1;
if (RtlEqualMemory( &ZeroTime, ClientTime, sizeof(KERB_TIME) )) { #ifndef WIN32_CHICAGO
TimeStamp->QuadPart = 0; #else // WIN32_CHICAGO
*TimeStamp = 0; #endif // WIN32_CHICAGO
return; } else { TimeFields.Year = ClientTime->year; TimeFields.Month = ClientTime->month; TimeFields.Day = ClientTime->day; TimeFields.Hour = ClientTime->hour; TimeFields.Minute = ClientTime->minute; TimeFields.Second = ClientTime->second; TimeFields.Milliseconds = ClientTime->millisecond; // to convert from micro to milli
TimeFields.Weekday = 0;
#ifndef WIN32_CHICAGO
RtlTimeFieldsToTime( &TimeFields, TimeStamp ); #else // WIN32_CHICAGO
LARGE_INTEGER TempTimeStamp; RtlTimeFieldsToTime( &TimeFields, &TempTimeStamp ); *TimeStamp = TempTimeStamp.QuadPart; #endif // WIN32_CHICAGO
//
// add in any micro seconds
//
#ifndef WIN32_CHICAGO
TimeStamp->QuadPart += ClientUsec * 10; #else // WIN32_CHICAGO
*TimeStamp += ClientUsec * 10; #endif // WIN32_CHICAGO
} }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertLargeIntToGeneralizedTime
//
// Synopsis: Converts a large integer to ageneralized time
//
// Effects:
//
// Arguments: ClientTime - receives generalized time
// ClientUsec - receives micro second count
// TimeStamp - contains NT-style time
//
// Requires: none
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbConvertLargeIntToGeneralizedTime( OUT PKERB_TIME ClientTime, OUT OPTIONAL int * ClientUsec, IN PTimeStamp TimeStamp ) { TIME_FIELDS TimeFields;
//
// Special case zero time
//
#ifndef WIN32_CHICAGO
if (TimeStamp->QuadPart == 0) #else // WIN32_CHICAGO
if (*TimeStamp == 0) #endif // WIN32_CHICAGO
{ RtlZeroMemory( ClientTime, sizeof(KERB_TIME) );
//
// For MIT compatibility, time zero is 1/1/70
//
ClientTime->year = 1970; ClientTime->month = 1; ClientTime->day = 1;
if (ARGUMENT_PRESENT(ClientUsec))
{ *ClientUsec = 0; } ClientTime->universal = TRUE; } else {
#ifndef WIN32_CHICAGO
RtlTimeToTimeFields( TimeStamp, &TimeFields ); #else // WIN32_CHICAGO
RtlTimeToTimeFields( (LARGE_INTEGER*)TimeStamp, &TimeFields ); #endif // WIN32_CHICAGO
//
// Generalized times can only contains years up to four digits.
//
if (TimeFields.Year > 2037) { ClientTime->year = 2037; } else { ClientTime->year = TimeFields.Year; }
ClientTime->month = (ASN1uint8_t) TimeFields.Month; ClientTime->day = (ASN1uint8_t) TimeFields.Day; ClientTime->hour = (ASN1uint8_t) TimeFields.Hour; ClientTime->minute = (ASN1uint8_t) TimeFields.Minute; ClientTime->second = (ASN1uint8_t) TimeFields.Second;
// MIT kerberos does not support millseconds
//
ClientTime->millisecond = 0;
if (ARGUMENT_PRESENT(ClientUsec)) { //
// Since we don't include milliseconds above, use the whole
// thing here.
//
#ifndef WIN32_CHICAGO
*ClientUsec = (TimeStamp->LowPart / 10) % 1000000; #else // WIN32_CHICAGO
*ClientUsec = (int) ((*TimeStamp / 10) % 1000000); #endif // WIN32_CHICAGO
}
ClientTime->diff = 0; ClientTime->universal = TRUE; }
}
VOID KerbConvertLargeIntToGeneralizedTimeWrapper( OUT PKERB_TIME ClientTime, OUT OPTIONAL long * ClientUsec, IN PTimeStamp TimeStamp ) { int temp;
if (ClientUsec != NULL) { KerbConvertLargeIntToGeneralizedTime( ClientTime, &temp, TimeStamp );
*ClientUsec = temp; } else { KerbConvertLargeIntToGeneralizedTime( ClientTime, NULL, TimeStamp ); } }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeHostAddresses
//
// Synopsis: Frees a host address allocated with KerbBuildHostAddresses
//
// Effects:
//
// Arguments: Addresses - The name to free
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeHostAddresses( IN PKERB_HOST_ADDRESSES Addresses ) { PKERB_HOST_ADDRESSES Elem,NextElem;
Elem = Addresses; while (Elem != NULL) { if (Elem->value.address.value != NULL) { MIDL_user_free(Elem->value.address.value); } NextElem = Elem->next; MIDL_user_free(Elem); Elem = NextElem; } }
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbCheckTimeSkew
//
// Synopsis: Verifies the supplied time is within the skew of another
// supplied time
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN KerbCheckTimeSkew( IN PTimeStamp CurrentTime, IN PTimeStamp ClientTime, IN PTimeStamp AllowedSkew ) { TimeStamp TimePlus, TimeMinus;
TimePlus.QuadPart = CurrentTime->QuadPart + AllowedSkew->QuadPart; TimeMinus.QuadPart = CurrentTime->QuadPart - AllowedSkew->QuadPart;
if ((ClientTime->QuadPart > TimePlus.QuadPart) || (ClientTime->QuadPart < TimeMinus.QuadPart)) { return(FALSE); }
return(TRUE); }
//+-------------------------------------------------------------------------
//
// Function: KerbVerifyTicket
//
// Synopsis: Verifies that the specified ticket is valid by checking
// for valid times, flags, and server principal name. This is
// called by KerbCheckTicket to verify an AP request and by the
// KDC to verify additional tickets in TGS request
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbVerifyTicket( IN PKERB_TICKET PackedTicket, IN ULONG NameCount, IN OPTIONAL PUNICODE_STRING ServiceNames, IN PUNICODE_STRING ServiceRealm, IN PKERB_ENCRYPTION_KEY ServiceKey, IN OPTIONAL PTimeStamp SkewTime, OUT PKERB_ENCRYPTED_TICKET * DecryptedTicket ) { KERBERR KerbErr = KDC_ERR_NONE; UNICODE_STRING TicketRealm = {0}; PKERB_ENCRYPTED_TICKET EncryptPart = NULL; TimeStamp TimePlus, TimeMinus, TimeNow, StartTime,EndTime, Time2Plus; ULONG TicketFlags = 0;
#ifdef notedef
if ( ARGUMENT_PRESENT(ServiceNames) ) { ULONG Index;
KerbErr = KRB_AP_ERR_NOT_US;
//
// Loop through names looking for a match
//
for (Index = 0; Index < NameCount ; Index++ ) { if (KerbCompareStringToPrincipalName( &PackedTicket->server_name, &ServiceNames[Index] ) ) { KerbErr = KDC_ERR_NONE; break; }
}
if (!KERB_SUCCESS(KerbErr)) { DebugLog(( DEB_WARN, "KLIN(%x) Ticket (%s) not for this service (%wZ).\n", KLIN(FILENO, __LINE__), PackedTicket->server_name.name_string->value, &ServiceNames[0] )); goto Cleanup; } }
if (ARGUMENT_PRESENT(ServiceRealm)) { KerbErr = KerbConvertRealmToUnicodeString( &TicketRealm, &PackedTicket->realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
if (!KerbCompareUnicodeRealmNames( &TicketRealm, ServiceRealm )) { KerbErr = KRB_AP_ERR_NOT_US; DebugLog(( DEB_WARN, "KLIN(%x) Ticket (%wZ) not for this realm (%wZ).\n", KLIN(FILENO, __LINE__), &TicketRealm, ServiceRealm )); goto Cleanup;
} } #endif
//
// Unpack ticket.
//
KerbErr = KerbUnpackTicket( PackedTicket, ServiceKey, &EncryptPart );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KLIN(%x) KerbUnpackTicket failed: 0x%x", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
if (PackedTicket->ticket_version != KERBEROS_VERSION) { DebugLog(( DEB_WARN, "KLIN(%x) Ticket has bad version %d\n", KLIN(FILENO, __LINE__),PackedTicket->ticket_version )); KerbErr = KRB_AP_ERR_BADVERSION; goto Cleanup; }
//
// If the caller provided a skew time, check the times on the ticket.
// Otherwise it is up to the caller to check that the ticket times are
// correct
//
if (ARGUMENT_PRESENT(SkewTime)) { //
// Check the times on the ticket. We do this last because when the KDC
// wants to renew a ticket, the timestamps may be incorrect, but it will
// accept the ticket anyway. This way the KDC can be certain when the
// times are wrong that everything else is OK.
//
GetSystemTimeAsFileTime((PFILETIME) &TimeNow );
#ifndef WIN32_CHICAGO
TimePlus.QuadPart = TimeNow.QuadPart + SkewTime->QuadPart; Time2Plus.QuadPart = TimePlus.QuadPart + SkewTime->QuadPart; TimeMinus.QuadPart = TimeNow.QuadPart - SkewTime->QuadPart; #else // WIN32_CHICAGO
TimePlus = TimeNow + *SkewTime; Time2Plus = TimePlus + *SkewTime; TimeMinus = TimeNow - *SkewTime; #endif // WIN32_CHICAGO
KerbConvertGeneralizedTimeToLargeInt( &EndTime, &EncryptPart->endtime, 0 );
//
// Did the ticket expire already?
//
#ifndef WIN32_CHICAGO
if ( EndTime.QuadPart < TimeMinus.QuadPart ) #else // WIN32_CHICAGO
if ( EndTime < TimeMinus ) #endif // WIN32_CHICAGO
{ DebugLog(( DEB_WARN, "KLIN(%x) KerbCheckTicket: ticket is expired.\n", KLIN(FILENO, __LINE__)));
KerbErr = KRB_AP_ERR_TKT_EXPIRED; goto Cleanup; }
//
// Is the ticket valid yet?
//
if (EncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present) { KerbConvertGeneralizedTimeToLargeInt( &StartTime, &EncryptPart->KERB_ENCRYPTED_TICKET_starttime, 0 );
TicketFlags = KerbConvertFlagsToUlong( &EncryptPart->flags ); //
// BUG 403734: Look into this a bit more
// We don't check for tickets that aren't valid yet, as
// our KDC doesn't normally hand out post dated tickets. As long
// as the end time is valid, that is good enough for us.
//
//
// Does the ticket start in the future? Allow twice the skew in
// the reverse direction.
//
#ifndef WIN32_CHICAGO
if ( (StartTime.QuadPart > Time2Plus.QuadPart) || #else // WIN32_CHICAGO
if ( (StartTime > Time2Plus) || #endif // WIN32_CHICAGO
((TicketFlags & KERB_TICKET_FLAGS_invalid) != 0 )) { KerbErr = KRB_AP_ERR_TKT_NYV; goto Cleanup; } } }
*DecryptedTicket = EncryptPart; EncryptPart = NULL;
Cleanup:
if (EncryptPart != NULL) { KerbFreeTicket(EncryptPart); }
KerbFreeString(&TicketRealm); return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Function: KerbCheckTicket
//
// Synopsis: Decrypts a ticket and authenticator, verifies them.
//
// Effects: decrypts the ticket and authenticator (in place) allocates mem.
//
// Arguments: [PackedTicket] -- Encrypted ticket
// [PackedTicketSize] - Size of encrypted ticket
// [pedAuth] -- Encrypted authenticator
// [pkKey] -- Key to decrypt ticket with
// [alAuthenList] -- List of authenticators to check against
// [NameCount] -- Count of service names
// [pwzServiceName] -- Name of service (may be NULL).
// [CheckForTimeSKewReplay] -- If TRUE, check authenticator cache for timeskew and/or replay
// [KdcRequest] -- If TRUE, this is the ticket in a TGS req (allows for checking time skew only)
// [pkitTicket] -- Decrypted ticket
// [pkiaAuth] -- Decrypted authenticator
// [pkTicketKey] -- Session key from ticket
// [pkSessionKey] -- Session key to use
//
// Returns: KDC_ERR_NONE if everything is OK, else error.
//
// History: 4-04-93 WadeR Created
//
// Notes: The caller must call KerbFreeTicket and
// KerbFreeAuthenticator on pkitTicket and pkiaAuth,
// respectively.
//
// If pwzServiceName == NULL, it won't check the service name.
//
// See sections 3.2.3 and A.10 of the Kerberos V5 R5.2 spec
//
//----------------------------------------------------------------------------
KERBERR NTAPI KerbCheckTicket( IN PKERB_TICKET PackedTicket, IN PKERB_ENCRYPTED_DATA EncryptedAuthenticator, IN PKERB_ENCRYPTION_KEY pkKey, IN OUT CAuthenticatorList * AuthenticatorList, IN PTimeStamp SkewTime, IN ULONG NameCount, IN PUNICODE_STRING ServiceNames, IN PUNICODE_STRING ServiceRealm, IN BOOLEAN CheckForTimeSkewReplay, IN BOOLEAN KdcRequest, OUT PKERB_ENCRYPTED_TICKET * EncryptTicket, OUT PKERB_AUTHENTICATOR * Authenticator, OUT OPTIONAL PKERB_ENCRYPTION_KEY pkTicketKey, OUT PKERB_ENCRYPTION_KEY pkSessionKey, OUT PBOOLEAN UseSubKey ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET EncryptPart = NULL; LARGE_INTEGER AuthenticatorTime;
//
// The caller will free these, so we must make sure they are valid
// if we return before putting anything in them. This will zero out
// all of the pointers in them, so it's safe to free them later.
//
*EncryptTicket = NULL; *Authenticator = NULL; *UseSubKey = FALSE;
RtlZeroMemory( pkSessionKey, sizeof(KERB_ENCRYPTION_KEY) ); if (ARGUMENT_PRESENT(pkTicketKey)) { *pkTicketKey = *pkSessionKey; }
//
// Is the ticket for this service?
// ServerName in ticket is different length then ServerName passed in,
// or same length but contents don't match.
//
// If either of KerbUnpackTicket or KerbUnpackAuthenticator
// get bad data, they could access violate.
//
__try { KerbErr = KerbVerifyTicket( PackedTicket, NameCount, ServiceNames, ServiceRealm, pkKey, SkewTime, &EncryptPart );
if (!KERB_SUCCESS(KerbErr)) { __leave; }
//
// Unpack Authenticator.
//
KerbErr = KerbUnpackAuthenticator( &EncryptPart->key, EncryptedAuthenticator, KdcRequest, Authenticator );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN,"KerbUnpackAuthenticator failed: 0x%x\n", KerbErr) ); __leave; }
//
// Check the contents of the authenticator
//
if ((*Authenticator)->authenticator_version != KERBEROS_VERSION) { DebugLog(( DEB_WARN, "Authenticator has bad version %d\n", (*Authenticator)->authenticator_version )); KerbErr = KRB_AP_ERR_BADVERSION; __leave; }
if (!KerbComparePrincipalNames( &EncryptPart->client_name, &(*Authenticator)->client_name ) || !KerbCompareRealmNames( &EncryptPart->client_realm, &(*Authenticator)->client_realm ) ) { DebugLog(( DEB_WARN, "Authenticator principal != ticket principal\n")); KerbErr = KRB_AP_ERR_BADMATCH; __leave; }
//
// Make sure the authenticator isn't a repeat, or too old.
//
if (CheckForTimeSkewReplay) { BOOLEAN fCheckReplay = TRUE;
if (KdcRequest) { fCheckReplay = FALSE; // bug 38404 for KDC TGS check timeskew only
}
KerbConvertGeneralizedTimeToLargeInt( &AuthenticatorTime, &(*Authenticator)->client_time, (*Authenticator)->client_usec );
KerbErr = (KERBERR) AuthenticatorList->Check( EncryptedAuthenticator->cipher_text.value, EncryptedAuthenticator->cipher_text.length, NULL, 0, &AuthenticatorTime, TRUE, FALSE, fCheckReplay); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN,"Failed authenticator (replay/time_skew) check: 0x%x\n",KerbErr)); __leave; } } } __except(EXCEPTION_EXECUTE_HANDLER) { // Any exceptions are likely from bad ticket data being unmarshalled.
DebugLog(( DEB_WARN, "Exception 0x%X in KerbCheckTicket (likely bad ticket or auth.\n", GetExceptionCode() )); KerbErr = KRB_AP_ERR_BADVERSION; }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Extract the correct session key. If the Sub-session key in the
// Authenticator is present, use it. Otherwise, use the session key
// from the ticket.
//
if (((*Authenticator)->bit_mask & KERB_AUTHENTICATOR_subkey_present) != 0) { D_DebugLog(( DEB_TRACE, "KerbCheckTicket: Using sub session key from authenticator.\n" )); KerbErr = KerbDuplicateKey( pkSessionKey, &(*Authenticator)->KERB_AUTHENTICATOR_subkey ); *UseSubKey = TRUE; } else { KerbErr = KerbDuplicateKey( pkSessionKey, &EncryptPart->key ); }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// The reply has to be encrypted with the ticket key, not the new
// session key
//
if (ARGUMENT_PRESENT(pkTicketKey)) { KerbErr = KerbDuplicateKey( pkTicketKey, &EncryptPart->key ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
*EncryptTicket = EncryptPart; EncryptPart = NULL;
Cleanup: if (EncryptPart != NULL) { KerbFreeTicket(EncryptPart); } if (!KERB_SUCCESS(KerbErr)) { KerbFreeKey(pkSessionKey); if (ARGUMENT_PRESENT(pkTicketKey)) { KerbFreeKey(pkTicketKey); } }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbDuplicateSid
//
// Synopsis: Duplicates a SID
//
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
//
// Arguments: DestinationSid - Receives a copy of the SourceSid
// SourceSid - SID to copy
//
// Requires:
//
// Returns: STATUS_SUCCESS - the copy succeeded
// STATUS_INSUFFICIENT_RESOURCES - the call to allocate memory
// failed
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbDuplicateSid( OUT PSID * DestinationSid, IN PSID SourceSid ) { ULONG SidSize;
if (!RtlValidSid(SourceSid)) { return STATUS_INVALID_PARAMETER; }
DsysAssert(RtlValidSid(SourceSid));
SidSize = RtlLengthSid(SourceSid); *DestinationSid = (PSID) MIDL_user_allocate( SidSize ); if (*DestinationSid == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
RtlCopyMemory( *DestinationSid, SourceSid, SidSize );
return(STATUS_SUCCESS); }
#endif // WIN32_CHICAGO
//
// Ticket pack/unpack code.
//
//+---------------------------------------------------------------------------
//
// Function: KerbPackTicket
//
// Synopsis: Packs a KerbInternalTicket to a KerbTicket
//
// Effects: Allocates the KerbTicket via MIDL.
//
// Arguments: [InternalTicket] -- Internal ticket to pack. Those fields
// reused in the packed ticket are zeroed.
// [pkKey] -- Key to pack it with
// [KeyVersion] -- Key version of pkKey (KERB_NO_KEY_VERSION if none)
// [PackedTicket] -- (out) encrypted ticket. Only the encrypt_part
// is allocated.
//
// History: 08-Jun-93 WadeR Created
//
// Notes: The MES encoding needs to be changed to ASN1 encoding
//
//----------------------------------------------------------------------------
KERBERR NTAPI KerbPackTicket( IN PKERB_TICKET InternalTicket, IN PKERB_ENCRYPTION_KEY pkKey, IN ULONG KeyVersion, OUT PKERB_TICKET PackedTicket ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL; ULONG cbEncryptedPart; KERB_TICKET TemporaryTicket; PUCHAR MarshalledEncryptPart = NULL; BOOLEAN RestoreDataOnError = FALSE;
//
// Pack the data into the encrypted portion.
//
RtlZeroMemory( &TemporaryTicket, sizeof(KERB_TICKET) );
EncryptedTicket = (PKERB_ENCRYPTED_TICKET) InternalTicket->encrypted_part.cipher_text.value;
KerbErr = KerbPackData( EncryptedTicket, KERB_ENCRYPTED_TICKET_PDU, &cbEncryptedPart, &MarshalledEncryptPart );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to marshall ticket: 0x%x\n",KerbErr)); goto Cleanup; }
//
// And encrypt it.
//
TemporaryTicket = *InternalTicket;
RtlZeroMemory( &InternalTicket->realm, sizeof(KERB_REALM) );
RtlZeroMemory( &InternalTicket->server_name, sizeof(KERB_PRINCIPAL_NAME) );
InternalTicket->ticket_extensions = NULL;
RestoreDataOnError = TRUE;
KerbErr = KerbAllocateEncryptionBufferWrapper( pkKey->keytype, cbEncryptedPart, &TemporaryTicket.encrypted_part.cipher_text.length, &TemporaryTicket.encrypted_part.cipher_text.value );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
DebugLog((DEB_TRACE,"KerbPackTicket: Using KeyVersion 0x%x and Algorithm %d to encrypt ticket\n", KeyVersion, pkKey->keytype));
KerbErr = KerbEncryptDataEx( &TemporaryTicket.encrypted_part, cbEncryptedPart, MarshalledEncryptPart, KeyVersion, KERB_TICKET_SALT, pkKey );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to encrypt data: 0x%x\n",KerbErr)); goto Cleanup; }
*PackedTicket = TemporaryTicket;
Cleanup:
if (MarshalledEncryptPart != NULL) { MIDL_user_free(MarshalledEncryptPart); }
if (!KERB_SUCCESS(KerbErr)) { if (TemporaryTicket.encrypted_part.cipher_text.value != NULL) { MIDL_user_free(TemporaryTicket.encrypted_part.cipher_text.value); }
if (RestoreDataOnError) { InternalTicket->realm = TemporaryTicket.realm; InternalTicket->server_name = TemporaryTicket.server_name; InternalTicket->ticket_extensions = TemporaryTicket.ticket_extensions; } }
return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Function: KerbUnpackTicket
//
// Synopsis: Decrypts and unpacks the encyrpted part of aticket.
//
// Effects: Allocates memory, decrypts pktTicket in place
//
// Arguments: [PackedTicket] -- ticket to unpack
// [PackedTicketSize] -- length of packed ticket
// [pkKey] -- key to unpack it with
// [InternalTicket] -- (out) unpacked ticket
//
// Returns: KDC_ERR_NONE or error from decrypt
//
// Signals: Any exception the MIDL unpacking code throws.
//
// History: 09-Jun-93 WadeR Created
//
// Notes: Free InternalTicket with KerbFreeTicket, below.
//
//----------------------------------------------------------------------------
KERBERR NTAPI KerbUnpackTicket( IN PKERB_TICKET PackedTicket, IN PKERB_ENCRYPTION_KEY pkKey, OUT PKERB_ENCRYPTED_TICKET * InternalTicket ) { KERBERR KerbErr = KDC_ERR_NONE; PUCHAR EncryptedPart = NULL; ULONG EncryptSize; PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL;
//
// Now decrypt the encrypted part of the ticket
//
EncryptedPart = (PUCHAR) MIDL_user_allocate(PackedTicket->encrypted_part.cipher_text.length);
if (EncryptedPart == NULL) { return(KRB_ERR_GENERIC); }
EncryptSize = PackedTicket->encrypted_part.cipher_text.length;
KerbErr = KerbDecryptDataEx( &PackedTicket->encrypted_part, pkKey, KERB_TICKET_SALT, &EncryptSize, EncryptedPart );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to decrypt ticket: 0x%x\n",KerbErr));
#if DBG
//
// Who's this ticket *supposed* to be for?
//
KerbPrintPrincipalName(DEB_ERROR, &PackedTicket->server_name); KerbPrintKerbRealm(DEB_ERROR, &PackedTicket->realm); #endif
goto Cleanup; }
KerbErr = KerbUnpackData( EncryptedPart, EncryptSize, KERB_ENCRYPTED_TICKET_PDU, (PVOID *) &EncryptedTicket ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unmarshall ticket: 0x%x\n",KerbErr)); goto Cleanup; }
*InternalTicket = EncryptedTicket;
Cleanup:
if (EncryptedPart != NULL) { MIDL_user_free(EncryptedPart); }
return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Function: KerbCreateAuthenticator
//
// Synopsis: Creates an authenticator for a client to pass to a service
//
// Effects: Encrypts pedAuthenticator
//
// Arguments: [pkKey] -- (in) session key from the ticket this
// authenticator is for.
// [dwEncrType] -- (in) Desired encryption type
// [dwSeq] -- (in) nonce for authenticator
// [pTimeStamp] -- (out) Time for authenticator
// [ClientName] -- (in) name of principal
// [ClientRealm] -- (in) logon realm of principal
// [SkewTime] -- (in) Skew of server's time
// [pkSubKey] -- (in) desired sub key (may be NULL)
// [GssChecksum] -- (in) optional checksum message to put in authenticator
// [KdcRequest] -- (in) If TRUE, this is an authenticator for a KDC request
// and we use a different salt
// [Authenticator]-- (out) completed authenticator
// [pAuthenticatorTime] -- (out optional) timestamp placed on AP request
//
// History: 4-28-93 WadeR Created
//
// Notes: If pkKey is NULL, a null subkey is used.
//
//
//----------------------------------------------------------------------------
KERBERR NTAPI KerbCreateAuthenticator( IN PKERB_ENCRYPTION_KEY pkKey, IN ULONG SequenceNumber, OUT OPTIONAL PTimeStamp pAuthenticatorTime, IN PKERB_INTERNAL_NAME ClientName, IN PUNICODE_STRING ClientRealm, IN OPTIONAL PTimeStamp SkewTime, IN PKERB_ENCRYPTION_KEY pkSubKey, IN OPTIONAL PKERB_CHECKSUM GssChecksum, IN BOOLEAN KdcRequest, OUT PKERB_ENCRYPTED_DATA Authenticator ) { KERB_AUTHENTICATOR InternalAuthenticator; PKERB_AUTHENTICATOR AuthPointer = &InternalAuthenticator; ULONG cbAuthenticator; PUCHAR PackedAuthenticator = NULL; KERBERR KerbErr = KDC_ERR_NONE; TimeStamp TimeToUse;
Authenticator->cipher_text.value = NULL;
RtlZeroMemory( &InternalAuthenticator, sizeof(KERB_AUTHENTICATOR) );
// Build an authenticator
InternalAuthenticator.authenticator_version = KERBEROS_VERSION;
// Use "InitString" because we will marshall and then discard the
// InternalAthenticator. Therefore it's not a problem having the
// string point to memory we don't own.
KerbErr = KerbConvertUnicodeStringToRealm( &InternalAuthenticator.client_realm, ClientRealm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertKdcNameToPrincipalName( &InternalAuthenticator.client_name, ClientName ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Stick the correct time in the authenticator
//
GetSystemTimeAsFileTime((PFILETIME)&TimeToUse); if (ARGUMENT_PRESENT(SkewTime)) { #ifndef WIN32_CHICAGO
TimeToUse.QuadPart += SkewTime->QuadPart; #else // WIN32_CHICAGO
TimeToUse += *SkewTime; #endif // WIN32_CHICAGO
}
KerbConvertLargeIntToGeneralizedTimeWrapper( &InternalAuthenticator.client_time, &InternalAuthenticator.client_usec, &TimeToUse );
if (pAuthenticatorTime) { *pAuthenticatorTime = TimeToUse; // return the authenticator time
}
InternalAuthenticator.bit_mask |= KERB_AUTHENTICATOR_sequence_number_present;
ASN1intx_setuint32( &InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number, SequenceNumber );
if (InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if (ARGUMENT_PRESENT(pkSubKey)) { InternalAuthenticator.bit_mask |= KERB_AUTHENTICATOR_subkey_present; InternalAuthenticator.KERB_AUTHENTICATOR_subkey = *pkSubKey; }
//
// If the GSS checksum is present, include it and set it in the bitmask
//
if (ARGUMENT_PRESENT(GssChecksum)) { InternalAuthenticator.checksum = *GssChecksum; InternalAuthenticator.bit_mask |= checksum_present; }
KerbErr = KerbPackData( AuthPointer, KERB_AUTHENTICATOR_PDU, &cbAuthenticator, &PackedAuthenticator );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to marshall authenticator: 0x%x\n",KerbErr)); goto Cleanup; }
//
// Now we need to encrypt the buffer
//
KerbErr = KerbAllocateEncryptionBufferWrapper( pkKey->keytype, cbAuthenticator, &Authenticator->cipher_text.length, &Authenticator->cipher_text.value );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbEncryptDataEx( Authenticator, cbAuthenticator, PackedAuthenticator, KERB_NO_KEY_VERSION, KdcRequest ? KERB_TGS_REQ_AP_REQ_AUTH_SALT : KERB_AP_REQ_AUTH_SALT, pkKey );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to encrypt data: 0x%x\n",KerbErr)); goto Cleanup; }
Cleanup:
KerbFreePrincipalName(&InternalAuthenticator.client_name); KerbFreeRealm(&InternalAuthenticator.client_realm);
if (InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number.value != NULL) { ASN1intx_free(&InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number); }
if (PackedAuthenticator != NULL) { MIDL_user_free(PackedAuthenticator);
}
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbUnpackAuthenticator
//
// Synopsis: Unpacks and decrypts an authenticator
//
// Effects: allocates memory for output authenticator
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbUnpackAuthenticator( IN PKERB_ENCRYPTION_KEY Key, IN PKERB_ENCRYPTED_DATA EncryptedAuthenticator, IN BOOLEAN KdcRequest, OUT PKERB_AUTHENTICATOR * Authenticator ) { KERBERR KerbErr = KDC_ERR_NONE; PUCHAR EncryptedPart; ULONG EncryptedSize; ULONG Pdu = KERB_AUTHENTICATOR_PDU;
*Authenticator = NULL;
//
// Decrypt it
//
EncryptedPart = (PUCHAR) MIDL_user_allocate(EncryptedAuthenticator->cipher_text.length); if (EncryptedPart == NULL) { return(KRB_ERR_GENERIC); }
EncryptedSize = EncryptedAuthenticator->cipher_text.length; KerbErr = KerbDecryptDataEx( EncryptedAuthenticator, Key, KdcRequest ? KERB_TGS_REQ_AP_REQ_AUTH_SALT : KERB_AP_REQ_AUTH_SALT, &EncryptedSize, EncryptedPart );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to decrypt authenticator: 0x%x\n",KerbErr)); goto Cleanup; }
//
// Unpack it
//
KerbErr = KerbUnpackData( EncryptedPart, EncryptedSize, Pdu, (PVOID *) Authenticator ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unmarshall authenticator: 0x%x\n",KerbErr)); goto Cleanup; }
Cleanup:
if (EncryptedPart != NULL) { MIDL_user_free(EncryptedPart); }
if (!KERB_SUCCESS(KerbErr) && (*Authenticator != NULL)) { MIDL_user_free(*Authenticator); *Authenticator = NULL; }
return (KerbErr); }
//
// KDC Reply stuff
//
//+-------------------------------------------------------------------------
//
// Function: KerbPackKdcReplyBody
//
// Synopsis: Marshalls a the body of a KDC reply
//
// Effects: allocates value of encrypted reply
//
// Arguments: ReplyBody - The reply body to marshall
// Key - The key to encrypt the reply
// Pdu - Pdu to pack with, eith AS or TGS reply
// EncryptedReplyBody - receives the encrypted and marshalled reply
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbPackKdcReplyBody( IN PKERB_ENCRYPTED_KDC_REPLY ReplyBody, IN PKERB_ENCRYPTION_KEY Key, IN ULONG KeyVersion, IN ULONG KeySalt, IN ULONG Pdu, OUT PKERB_ENCRYPTED_DATA EncryptedReply ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG BodySize; ULONG EncryptionOverhead; PUCHAR MarshalledReply = NULL; ULONG TotalSize; ULONG BlockSize = 0;
EncryptedReply->cipher_text.value = NULL;
KerbErr = KerbPackData( ReplyBody, Pdu, &BodySize, &MarshalledReply );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Failed to marshall kdc reply body: 0x%x\n", KerbErr)); goto Cleanup; }
//
// Now we need to encrypt this into the encrypted data structure.
//
//
// First get the overhead size
//
KerbErr = KerbGetEncryptionOverhead( Key->keytype, &EncryptionOverhead, &BlockSize ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
DsysAssert(BlockSize <= 8);
TotalSize = ROUND_UP_COUNT(EncryptionOverhead + BodySize, BlockSize);
EncryptedReply->cipher_text.length = TotalSize; EncryptedReply->cipher_text.value = (PUCHAR) MIDL_user_allocate(TotalSize);
if (EncryptedReply->cipher_text.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Now encrypt the buffer see raid 502476 if adding in new etypes - must fix I_GetASTIcket()'s call to
// pass in correct value KERB_AS_REP_SALT rather than KERB_AS_REP_SALT but need to map for rc4 first
//
KerbErr = KerbEncryptDataEx( EncryptedReply, BodySize, MarshalledReply, KeyVersion, KeySalt, Key );
DebugLog((DEB_TRACE,"KerbPackKdcReplyBody: KeyVersion 0x%lx Algorithm %d KerbErr 0x%x\n", KeyVersion, Key->keytype, KerbErr));
Cleanup:
if (MarshalledReply != NULL) { MIDL_user_free(MarshalledReply); }
if (!KERB_SUCCESS(KerbErr) && (EncryptedReply->cipher_text.value != NULL)) { MIDL_user_free(EncryptedReply->cipher_text.value); EncryptedReply->cipher_text.value = NULL; }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbUnpackKdcReplyBody
//
// Synopsis: Unpacks a KDC reply body
//
// Effects:
//
// Arguments: EncryptedReplyBody - an encrypted marshalled reply body.
// Key - Key to decrypt the reply.
// Pdu - PDU of reply body (eithe AS or TGS)
// ReplyBody - receives the decrypted reply body, allocated with
// MIDL_user_allocate.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbUnpackKdcReplyBody( IN PKERB_ENCRYPTED_DATA EncryptedReplyBody, IN PKERB_ENCRYPTION_KEY Key, IN ULONG Pdu, OUT PKERB_ENCRYPTED_KDC_REPLY * ReplyBody ) { KERBERR KerbErr = KDC_ERR_NONE; PUCHAR MarshalledReply = NULL; ULONG ReplySize;
*ReplyBody = NULL; MarshalledReply = (PUCHAR) MIDL_user_allocate(EncryptedReplyBody->cipher_text.length);
if (MarshalledReply == NULL) { return(KRB_ERR_GENERIC); }
//
// First decrypt the buffer
// Note: The switch on KERB_AS_REPLY_PDU below is incorrect. See bug 502476
//
ReplySize = EncryptedReplyBody->cipher_text.length; KerbErr = KerbDecryptDataEx( EncryptedReplyBody, Key, (Pdu == KERB_AS_REPLY_PDU) ? KERB_AS_REP_SALT : KERB_TGS_REP_SALT, &ReplySize, MarshalledReply );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbUnpackData( MarshalledReply, ReplySize, Pdu, (PVOID *) ReplyBody );
if (!KERB_SUCCESS(KerbErr)) { //
// MIT KDCs send back TGS reply bodies instead of AS reply bodies
// so try TGS here
//
if (Pdu == KERB_ENCRYPTED_AS_REPLY_PDU) { KerbErr = KerbUnpackData( MarshalledReply, ReplySize, KERB_ENCRYPTED_TGS_REPLY_PDU, (PVOID *) ReplyBody ); }
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unmarshall kdc reply body: 0x%x\n",KerbErr)); goto Cleanup; } }
Cleanup:
if (MarshalledReply != NULL) { MIDL_user_free(MarshalledReply); }
if (!KERB_SUCCESS(KerbErr) && (*ReplyBody != NULL)) { MIDL_user_free(*ReplyBody); *ReplyBody = NULL; }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbFindAuthDataEntry
//
// Synopsis: Finds a specific entry in an authorization data structure
//
// Effects:
//
// Arguments: EntryId - ID of the entry to locate
// AuthData - the authorization data to search
//
// Requires:
//
// Returns: NULL if it wasn't found of the auth data entry
//
// Notes:
//
//
//--------------------------------------------------------------------------
PKERB_AUTHORIZATION_DATA KerbFindAuthDataEntry( IN ULONG EntryId, IN PKERB_AUTHORIZATION_DATA AuthData ) { PKERB_AUTHORIZATION_DATA TempData = AuthData;
while (TempData != NULL) { if (TempData->value.auth_data_type == (int) EntryId) { break; }
TempData = TempData->next; }
return(TempData); }
//+-------------------------------------------------------------------------
//
// Function: KerbFindPreAuthDataEntry
//
// Synopsis: Finds a specific entry in an authorization data structure
//
// Effects:
//
// Arguments: EntryId - ID of the entry to locate
// AuthData - the authorization data to search
//
// Requires:
//
// Returns: NULL if it wasn't found of the auth data entry
//
// Notes:
//
//
//--------------------------------------------------------------------------
PKERB_PA_DATA KerbFindPreAuthDataEntry( IN ULONG EntryId, IN PKERB_PA_DATA_LIST AuthData ) { PKERB_PA_DATA_LIST TempData = AuthData;
while (TempData != NULL) { if (TempData->value.preauth_data_type == (int) EntryId) { break; }
TempData = TempData->next; }
return(TempData != NULL ? &TempData->value : NULL); }
//+-------------------------------------------------------------------------
//
// Function: KerbFreePreAuthData
//
// Synopsis: Frees a pa-data list
//
// Effects:
//
// Arguments: PreAuthData - data to free
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreePreAuthData( IN OPTIONAL PKERB_PA_DATA_LIST PreAuthData ) { PKERB_PA_DATA_LIST Next,Last;
Next = PreAuthData;
while (Next != NULL) { Last = Next->next;
if (Next->value.preauth_data.value != NULL) { MIDL_user_free(Next->value.preauth_data.value); }
MIDL_user_free(Next); Next = Last; } }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeAuthData
//
// Synopsis: Frees and auth data structure that was allocated in
// pieces
//
// Effects: frees with MIDL_user_Free
//
// Arguments: AuthData - the auth data to free
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeAuthData( IN PKERB_AUTHORIZATION_DATA AuthData ) { PKERB_AUTHORIZATION_DATA TempData1,TempData2;
TempData1 = AuthData;
while (TempData1 != NULL) { TempData2 = TempData1->next;
if (TempData1->value.auth_data.value != NULL) { MIDL_user_free(TempData1->value.auth_data.value); }
MIDL_user_free(TempData1); TempData1 = TempData2; } }
//+-------------------------------------------------------------------------
//
// Function: KerbCopyAndAppendAuthData
//
// Synopsis: copies the elements from the input auth data and appends
// them to the end of the output auth data.
//
// Effects: allocates each auth data with MIDL_user_allocate
//
// Arguments: OutputAuthData - receives list of append auth data
// InputAuthData - optionally contains auth data to append
//
// Requires:
//
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC;
//
// Notes: on failure output auth data will be freed and set to NULL.
//
//
//--------------------------------------------------------------------------
KERBERR KerbCopyAndAppendAuthData( OUT PKERB_AUTHORIZATION_DATA * OutputAuthData, IN PKERB_AUTHORIZATION_DATA InputAuthData ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_AUTHORIZATION_DATA *LastEntry = OutputAuthData; PKERB_AUTHORIZATION_DATA TempEntry = NULL;
//
// Find the end of the list
//
while (*LastEntry != NULL) { LastEntry = &((*LastEntry)->next); }
while (InputAuthData != NULL) { //
// copy the existing entry
//
TempEntry = (PKERB_AUTHORIZATION_DATA) MIDL_user_allocate(sizeof(KERB_AUTHORIZATION_DATA));
if (TempEntry == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
TempEntry->value.auth_data.length = InputAuthData->value.auth_data.length; TempEntry->value.auth_data_type = InputAuthData->value.auth_data_type; TempEntry->next = NULL;
TempEntry->value.auth_data.value = (PUCHAR) MIDL_user_allocate(InputAuthData->value.auth_data.length);
if (TempEntry->value.auth_data.value == NULL) { MIDL_user_free(TempEntry); goto Cleanup; }
RtlCopyMemory( TempEntry->value.auth_data.value, InputAuthData->value.auth_data.value, InputAuthData->value.auth_data.length );
//
// add it to the end of the list
//
*LastEntry = TempEntry; LastEntry = &TempEntry->next; InputAuthData = InputAuthData->next; }
KerbErr = KDC_ERR_NONE;
Cleanup:
if (!KERB_SUCCESS(KerbErr)) { KerbFreeAuthData(*OutputAuthData); *OutputAuthData = NULL; }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertCryptListToArray
//
// Synopsis: Converts a linked-list crypt vector to an array of ULONGs
//
// Effects: allocates return with MIDL_user_allocate
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbConvertCryptListToArray( OUT PULONG * ETypeArray, OUT PULONG ETypeCount, IN PKERB_CRYPT_LIST CryptList ) { KERBERR Status = KDC_ERR_NONE; PKERB_CRYPT_LIST NextEType; ULONG ClientETypeCount; PULONG ClientETypes = NULL;
//
// Build a vector of the client encrypt types
//
NextEType = CryptList;
ClientETypeCount = 0; while (NextEType != NULL) { ClientETypeCount++; NextEType = NextEType->next; }
ClientETypes = (PULONG) MIDL_user_allocate(sizeof(ULONG) * ClientETypeCount); if (ClientETypes == NULL) { Status = KRB_ERR_GENERIC; goto Cleanup; }
NextEType = CryptList;
ClientETypeCount = 0;
while (NextEType != NULL) { ClientETypes[ClientETypeCount] = NextEType->value; ClientETypeCount++; NextEType = NextEType->next; }
*ETypeCount = ClientETypeCount; *ETypeArray = ClientETypes;
Cleanup:
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertArrayToCryptList
//
// Synopsis: Converts an array of encryption to types to a linked list
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbConvertArrayToCryptList( OUT PKERB_CRYPT_LIST * CryptList, IN PULONG ETypeArray, IN ULONG ETypeCount, IN BOOL bIncludeOldEtypes ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG Index; PKERB_CRYPT_LIST ListHead = NULL; PKERB_CRYPT_LIST ListTail = NULL; PKERB_CRYPT_LIST NewListEntry = NULL;
//
// If there no encryption types, bail out now.
//
if (ETypeCount == 0) { *CryptList = NULL; return(KDC_ERR_NONE); }
for (Index = 0; Index < ETypeCount ; Index++) { if ( !bIncludeOldEtypes && ((ETypeArray[Index] == KERB_ETYPE_RC4_MD4) || (ETypeArray[Index] == KERB_ETYPE_RC4_HMAC_OLD)) ) { continue; }
NewListEntry = (PKERB_CRYPT_LIST) MIDL_user_allocate(sizeof(KERB_CRYPT_LIST));
if (NewListEntry == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
NewListEntry->value = ETypeArray[Index]; NewListEntry->next = NULL;
if (ListTail != NULL) { ListTail->next = NewListEntry; } else { DsysAssert(ListHead == NULL); ListHead = NewListEntry; }
ListTail = NewListEntry; }
*CryptList = ListHead; ListHead = NULL;
Cleanup:
while (ListHead != NULL) { NewListEntry = ListHead->next; MIDL_user_free(ListHead); ListHead = NewListEntry; }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertKeysToCryptList
//
// Synopsis: Converts an array of keys to types to a linked list
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbConvertKeysToCryptList( OUT PKERB_CRYPT_LIST * CryptList, IN PKERB_STORED_CREDENTIAL Keys ) { KERBERR KerbErr = KDC_ERR_NONE; ULONG Index; PKERB_CRYPT_LIST ListHead = NULL; PKERB_CRYPT_LIST ListTail = NULL; PKERB_CRYPT_LIST NewListEntry = NULL;
//
// If there no encryption types, bail out now.
//
if (Keys->CredentialCount == 0) { *CryptList = NULL; return(KDC_ERR_NONE); }
for (Index = 0; Index < Keys->CredentialCount ; Index++ ) { NewListEntry = (PKERB_CRYPT_LIST) MIDL_user_allocate(sizeof(KERB_CRYPT_LIST));
if (NewListEntry == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
NewListEntry->value = Keys->Credentials[Index].Key.keytype; NewListEntry->next = NULL;
if (ListTail != NULL) { ListTail->next = NewListEntry; } else { DsysAssert(ListHead == NULL); ListHead = NewListEntry; }
ListTail = NewListEntry; }
*CryptList = ListHead; ListHead = NULL;
Cleanup:
while (ListHead != NULL) { NewListEntry = ListHead->next; MIDL_user_free(ListHead); ListHead = NewListEntry; }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeCryptList
//
// Synopsis: Frees a list of crypt types
//
// Effects:
//
// Arguments: CryptList - List to free
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeCryptList( IN PKERB_CRYPT_LIST CryptList ) { PKERB_CRYPT_LIST ListHead = CryptList; PKERB_CRYPT_LIST NewListEntry;
while (ListHead != NULL) { NewListEntry = ListHead->next; MIDL_user_free(ListHead); ListHead = NewListEntry; } }
//+-------------------------------------------------------------------------
//
// Function: KerbInitAsn
//
// Synopsis: Initializes asn1 marshalling code
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: KDC_ERR_NONE on success, KRB_ERR_GENERIC on failure
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOL fKRB5ModuleStarted = FALSE;
KERBERR KerbInitAsn( IN OUT ASN1encoding_t * pEnc, IN OUT ASN1decoding_t * pDec ) { KERBERR KerbErr = KRB_ERR_GENERIC; ASN1error_e Asn1Err;
if (!fKRB5ModuleStarted) { fKRB5ModuleStarted = TRUE; KRB5_Module_Startup(); }
if (pEnc != NULL) { Asn1Err = ASN1_CreateEncoder( KRB5_Module, pEnc, NULL, // pbBuf
0, // cbBufSize
NULL // pParent
); } else { Asn1Err = ASN1_CreateDecoder( KRB5_Module, pDec, NULL, // pbBuf
0, // cbBufSize
NULL // pParent
); }
if (ASN1_SUCCESS != Asn1Err) { DebugLog((DEB_ERROR, "Failed to init ASN1: 0x%x\n",Asn1Err)); goto Cleanup; }
KerbErr = KDC_ERR_NONE;
Cleanup:
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbTermAsn
//
// Synopsis: terminates an ASN world
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbTermAsn( IN ASN1encoding_t pEnc, IN ASN1decoding_t pDec ) { if (pEnc != NULL) { ASN1_CloseEncoder(pEnc); } else if (pDec != NULL) { ASN1_CloseDecoder(pDec); }
//KRB5_Module_Cleanup();
}
//+-------------------------------------------------------------------------
//
// Function: KerbPackData
//
// Synopsis: Packs a datatype using ASN.1 encoding
//
// Effects: allocates memory with MIDL_user_allocate
//
// Arguments: Data - The message to marshall/pack.
// PduValue - The PDU for the message type
// DataSize - receives the size of the marshalled message in
// bytes.
// MarshalledData - receives a pointer to the marshalled
// message buffer.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbPackData( IN PVOID Data, IN ULONG PduValue, OUT PULONG DataSize, OUT PUCHAR * MarshalledData ) { KERBERR KerbErr = KDC_ERR_NONE; ASN1encoding_t pEnc = NULL; ASN1error_e Asn1Err;
KerbErr = KerbInitAsn( &pEnc, // we are encoding
NULL );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Encode the data type.
//
// D_DebugLog((DEB_TRACE, "KerbPackData encoding pdu #%d\n", PduValue));
Asn1Err = ASN1_Encode( pEnc, Data, PduValue, ASN1ENCODE_ALLOCATEBUFFER, NULL, // pbBuf
0 // cbBufSize
);
if (!ASN1_SUCCEEDED(Asn1Err)) { DebugLog((DEB_ERROR,"Failed to encode data: %d\n",Asn1Err)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } else { //
// when the oss compiler was used the allocation routines were configurable.
// therefore, the encoded data could just be free'd using our
// deallocator. in the new model we cannot configure the allocation routines
// for encoding.
// so we do not have to go and change every place where a free
// of an encoded buffer is done, use our allocator to allocate a new buffer,
// then copy the encoded data to it, and free the buffer that was allocated by
// the encoding engine. THIS SHOULD BE CHANGED FOR BETTER PERFORMANCE
//
*MarshalledData = (PUCHAR) MIDL_user_allocate(pEnc->len);
if (*MarshalledData == NULL) { KerbErr = KRB_ERR_GENERIC; *DataSize = 0; } else { RtlCopyMemory(*MarshalledData, pEnc->buf, pEnc->len); *DataSize = pEnc->len;
}
ASN1_FreeEncoded(pEnc, pEnc->buf); }
Cleanup:
KerbTermAsn(pEnc, NULL);
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbUnpackData
//
// Synopsis: Unpacks an message from the ASN.1 encoding
//
// Effects:
//
// Arguments: Data - Buffer containing the reply message.
// DataSize - Size of the reply message in bytes
// Reply - receives a KERB_ENCRYPTED_DATA structure allocated with
// MIDL_user_allocate.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbUnpackData( IN PUCHAR Data, IN ULONG DataSize, IN ULONG PduValue, OUT PVOID * DecodedData ) { KERBERR KerbErr = KDC_ERR_NONE; ASN1decoding_t pDec = NULL; ASN1error_e Asn1Err;
if ((DataSize == 0) || (Data == NULL)) { DebugLog((DEB_ERROR, "KerbUnpackData Trying to unpack NULL data\n")); return(KRB_ERR_GENERIC); }
KerbErr = KerbInitAsn( NULL, &pDec // we are decoding
);
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KerbUnpackData failed to init Asn %#x\n", KerbErr)); return(KerbErr); }
*DecodedData = NULL;
Asn1Err = ASN1_Decode( pDec, DecodedData, PduValue, ASN1DECODE_SETBUFFER, (BYTE *) Data, DataSize );
if (!ASN1_SUCCEEDED(Asn1Err)) { D_DebugLog((DEB_TRACE, "KerbUnpackData Asn1Err %#x\n", Asn1Err));
if ((ASN1_ERR_BADARGS == Asn1Err) || (ASN1_ERR_EOD == Asn1Err)) { D_DebugLog((DEB_TRACE, "More input required to decode data %d.\n", PduValue)); KerbErr = KDC_ERR_MORE_DATA; } else { if (ASN1_ERR_BADTAG != Asn1Err) { DebugLog((DEB_ERROR, "Failed to decode data: %d\n", Asn1Err )); }
KerbErr = KRB_ERR_GENERIC; }
*DecodedData = NULL; }
KerbTermAsn(NULL, pDec);
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeData
//
// Synopsis: Frees a structure unpacked by the ASN1 decoder
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeData( IN ULONG PduValue, IN PVOID Data ) { ASN1decoding_t pDec = NULL;
if (ARGUMENT_PRESENT(Data)) { KERBERR KerbErr; KerbErr = KerbInitAsn( NULL, &pDec // this is a decoded structure
);
if (KERB_SUCCESS(KerbErr)) { ASN1_FreeDecoded(pDec, Data, PduValue);
KerbTermAsn(NULL, pDec); } } }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeTicketExtensions
//
// Synopsis: Frees a host address allocated with KerbDuplicateTicketExtensions
//
// Effects:
//
// Arguments: Addresses - The name to free
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeTicketExtensions( IN PKERB_TICKET_EXTENSIONS Extensions ) { PKERB_TICKET_EXTENSIONS Elem,NextElem;
Elem = Extensions; while (Elem != NULL) { if (Elem->value.te_data.value != NULL) { MIDL_user_free(Elem->value.te_data.value); }
NextElem = Elem->next; MIDL_user_free(Elem); Elem = NextElem; } }
//+-------------------------------------------------------------------------
//
// Function: KerbDuplicateTicketExtensions
//
// Synopsis: duplicates the ticket extensions field from a ticket
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbDuplicateTicketExtensions( OUT PKERB_TICKET_EXTENSIONS * Dest, IN PKERB_TICKET_EXTENSIONS Source ) { KERBERR Status = KDC_ERR_NONE; PKERB_TICKET_EXTENSIONS SourceElem; PKERB_TICKET_EXTENSIONS DestElem; PKERB_TICKET_EXTENSIONS * NextElem;
*Dest = NULL;
SourceElem = Source; NextElem = Dest;
while (SourceElem != NULL) { DestElem = (PKERB_TICKET_EXTENSIONS) MIDL_user_allocate(sizeof(KERB_TICKET_EXTENSIONS));
if (DestElem == NULL) { Status = KRB_ERR_GENERIC; goto Cleanup; }
*DestElem = *SourceElem; DestElem->value.te_data.value = (PUCHAR) MIDL_user_allocate(SourceElem->value.te_data.length);
if (DestElem->value.te_data.value == NULL) { MIDL_user_free(DestElem); Status = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( DestElem->value.te_data.value, SourceElem->value.te_data.value, SourceElem->value.te_data.length );
DestElem->next = NULL; *NextElem = DestElem; NextElem = &DestElem->next; SourceElem = SourceElem->next; }
Cleanup:
if (!KERB_SUCCESS(Status)) { KerbFreeTicketExtensions(*Dest); *Dest = NULL; }
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbDuplicateTicket
//
// Synopsis: Duplicates a ticket so the original may be freed
//
// Effects:
//
// Arguments: Dest - Destination, receives duplicate
// Source - Source ticket
//
// Requires:
//
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC;
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR NTAPI KerbDuplicateTicket( OUT PKERB_TICKET Dest, IN PKERB_TICKET Source ) { KERBERR KerbErr = KDC_ERR_NONE; RtlZeroMemory( Dest, sizeof(KERB_TICKET) );
Dest->ticket_version = Source->ticket_version;
KerbErr = KerbDuplicatePrincipalName( &Dest->server_name, &Source->server_name );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbDuplicateRealm( &Dest->realm, Source->realm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbDuplicateTicketExtensions( &Dest->ticket_extensions, Source->ticket_extensions );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Dest->encrypted_part = Source->encrypted_part; Dest->encrypted_part.cipher_text.value = (PUCHAR) MIDL_user_allocate(Dest->encrypted_part.cipher_text.length);
if (Dest->encrypted_part.cipher_text.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( Dest->encrypted_part.cipher_text.value, Source->encrypted_part.cipher_text.value, Dest->encrypted_part.cipher_text.length );
Cleanup:
if (!KERB_SUCCESS(KerbErr)) { KerbFreeDuplicatedTicket(Dest); }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeDuplicatedTicket
//
// Synopsis: Frees ticket duplicated with KerbDuplicateTicket
//
// Effects: frees memory
//
// Arguments: Ticket - ticket to free
//
// Requires:
//
// Returns: none
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeDuplicatedTicket( IN PKERB_TICKET Ticket ) { KerbFreePrincipalName( &Ticket->server_name );
KerbFreeRealm( &Ticket->realm );
if (Ticket->encrypted_part.cipher_text.value != NULL) { MIDL_user_free(Ticket->encrypted_part.cipher_text.value); }
KerbFreeTicketExtensions( Ticket->ticket_extensions ); }
//+-------------------------------------------------------------------------
//
// Function: KerbBuildErrorMessageEx
//
// Synopsis: Builds an error message
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: marshalled error message, to be freed with MIDL_user_free
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbBuildErrorMessageEx( IN KERBERR ErrorCode, IN OPTIONAL PKERB_EXT_ERROR pExtendedError, IN PUNICODE_STRING ServerRealm, IN PKERB_INTERNAL_NAME ServerName, IN OPTIONAL PUNICODE_STRING ClientRealm, IN PBYTE ErrorData, IN ULONG ErrorDataSize, OUT PULONG ErrorMessageSize, OUT PUCHAR* ErrorMessage ) { KERBERR KerbErr = KDC_ERR_NONE; KERB_ERROR Error; KERB_TYPED_DATA Data = {0}; TimeStamp TimeNow;
*ErrorMessageSize = 0; *ErrorMessage = NULL;
GetSystemTimeAsFileTime( (PFILETIME) &TimeNow );
RtlZeroMemory( &Error, sizeof(KERB_ERROR) );
DsysAssert(ErrorCode != KDC_ERR_MORE_DATA);
Error.version = KERBEROS_VERSION; Error.message_type = KRB_ERROR;
KerbConvertLargeIntToGeneralizedTimeWrapper( &Error.server_time, &Error.server_usec, &TimeNow );
//
// These errors must *never* hit the wire. They're for use in the KDC
// and kerb innards only.
//
switch (ErrorCode) { case KDC_ERR_MORE_DATA: case KDC_ERR_NOT_RUNNING: case KDC_ERR_NO_RESPONSE: case KDC_ERR_NO_TRUST_PATH: case KRB_ERR_NAME_TOO_LONG: ErrorCode = KRB_ERR_GENERIC; break; default: NOTHING; }
Error.error_code = ErrorCode;
//
// Ignore errors because this is already an error return
//
KerbConvertUnicodeStringToRealm( &Error.realm, ServerRealm );
if (ARGUMENT_PRESENT(ClientRealm) && (ClientRealm->Buffer != NULL)) { KerbConvertUnicodeStringToRealm( &Error.client_realm, ClientRealm );
Error.bit_mask |= client_realm_present; }
KerbConvertKdcNameToPrincipalName( &Error.server_name, ServerName );
//
// Small problem here. We may have preauth data that we want
// to return to the client, instead of extended errors. To
// avoid this, we just make sure that we only return extended
// errors if no ErrorData previously set.
//
if (ARGUMENT_PRESENT(ErrorData)) { Error.error_data.length = (int) ErrorDataSize; Error.error_data.value = ErrorData; Error.bit_mask |= error_data_present; } else if (ARGUMENT_PRESENT(pExtendedError) && !EXT_ERROR_SUCCESS((*pExtendedError))) { Data.data_type = TD_EXTENDED_ERROR;
KerbErr = KerbPackData( pExtendedError, KERB_EXT_ERROR_PDU, &Data.data_value.length, &Data.data_value.value );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KerbBuildErrorMessageEx failed To pack extended error %#x!\n", KerbErr)); goto Cleanup; }
Error.bit_mask |= error_data_present;
KerbErr = TypedDataListPushFront( NULL, &Data, &Error.error_data.length, &Error.error_data.value );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KerbBuildErrorMessageEx failed To pack typed data %#x!\n", KerbErr)); goto Cleanup; } }
KerbErr = KerbPackData( &Error, KERB_ERROR_PDU, ErrorMessageSize, ErrorMessage );
Cleanup:
KerbFreeRealm( &Error.realm );
KerbFreeRealm( &Error.client_realm );
KerbFreePrincipalName( &Error.server_name );
if (Data.data_value.value && Data.data_value.length) { MIDL_user_free(Data.data_value.value); }
if ((ErrorData != Error.error_data.value) && Error.error_data.value && Error.error_data.length) { MIDL_user_free(Error.error_data.value); }
return (KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetKeyFromList
//
// Synopsis: Gets the key of the appropriate encryption type off the list
//
// Effects:
//
// Arguments: Passwords - list of keys
// EncryptionType - Encryption type to use
//
// Requires:
//
// Returns: The found key, or NULL if one wasn't found
//
// Notes:
//
//
//--------------------------------------------------------------------------
PKERB_ENCRYPTION_KEY KerbGetKeyFromList( IN PKERB_STORED_CREDENTIAL Passwords, IN ULONG EncryptionType ) { ULONG Index;
if (!ARGUMENT_PRESENT(Passwords)) { return(NULL); }
for (Index = 0; Index < Passwords->CredentialCount ; Index++ ) { if (Passwords->Credentials[Index].Key.keytype == (int) EncryptionType) { return(&Passwords->Credentials[Index].Key); } }
return(NULL); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetKeyFromListByIndex
//
// Synopsis: Gets the key of the appropriate encryption type off the list
// starting from a provided index
//
// Effects:
//
// Arguments: Passwords - list of keys
// EncryptionType - Encryption type to use
// pIndex - provides starting location for key selection
//
// Requires:
//
// Returns: The found key, or NULL if one wasn't found
//
// Notes:
//
//
//--------------------------------------------------------------------------
PKERB_ENCRYPTION_KEY KerbGetKeyFromListByIndex( IN PKERB_STORED_CREDENTIAL Passwords, IN ULONG EncryptionType, OUT PULONG pIndex ) { ULONG Index;
if (!ARGUMENT_PRESENT(Passwords) || !ARGUMENT_PRESENT(pIndex)) { return(NULL); }
if (*pIndex >= Passwords->CredentialCount) { return(NULL); }
for (Index = *pIndex; Index < Passwords->CredentialCount ; Index++ ) { if (Passwords->Credentials[Index].Key.keytype == (int) EncryptionType) { *pIndex = Index + 1; // Start on next key if called again
return(&Passwords->Credentials[Index].Key); } }
return(NULL); }
//+-------------------------------------------------------------------------
//
// Function: KerbFindCommonCryptSystem
//
// Synopsis: Finds a common crypt system including availablity
// of passwords.
//
// Effects:
//
// Arguments: CryptList - List of client's crypto systems
// Passwords - List of passwords
// MorePassword - Optionally another list of passwords to consider
// Key - Receives key for common crypt system
//
// Requires:
//
// Returns: KDC_ERR_ETYPE_NOTSUPP if no common system can be found
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbFindCommonCryptSystem( IN PKERB_CRYPT_LIST CryptList, IN PKERB_STORED_CREDENTIAL Passwords, IN OPTIONAL PKERB_STORED_CREDENTIAL MorePasswords, OUT PKERB_ENCRYPTION_KEY * Key ) { ULONG PasswordTypes[KERB_MAX_CRYPTO_SYSTEMS] = {0};
PULONG pCryptoSystems = NULL; ULONG CryptoSystems[KERB_MAX_CRYPTO_SYSTEMS]; ULONG PasswordCount; ULONG CryptoCount; ULONG Index; PKERB_CRYPT_LIST NextEType; ULONG Index2; ULONG KeyCount; KERBERR KerbErr = KDC_ERR_ETYPE_NOTSUPP; ULONG CommonCryptSystem = KERB_ETYPE_DEFAULT;
DsysAssert(CryptList != NULL);
if ((Passwords == NULL ) || (CryptList == NULL)) { DebugLog((DEB_ERROR, "Null password or crypt list passed to KerbFindCommonCryptSystem\n")); DsysAssert(FALSE); return(KDC_ERR_ETYPE_NOTSUPP); }
PasswordCount = Passwords->CredentialCount;
if (PasswordCount >= KERB_MAX_CRYPTO_SYSTEMS) { D_DebugLog((DEB_ERROR, "Got more than 20 crypto systems in password list\n")); DsysAssert(PasswordCount < KERB_MAX_CRYPTO_SYSTEMS); return(KDC_ERR_ETYPE_NOTSUPP); }
KeyCount = 0;
for (Index = 0; Index < PasswordCount ; Index++ ) { if (ARGUMENT_PRESENT(MorePasswords)) { for (Index2 = 0; Index2 < MorePasswords->CredentialCount; Index2++ ) { if (Passwords->Credentials[Index].Key.keytype == MorePasswords->Credentials[Index2].Key.keytype) { PasswordTypes[KeyCount++] = (ULONG) Passwords->Credentials[Index].Key.keytype; break; } } } else { PasswordTypes[KeyCount++] = (ULONG) Passwords->Credentials[Index].Key.keytype; } }
CryptoCount = 0; NextEType = CryptList;
while (NextEType != NULL) { NextEType = NextEType->next; CryptoCount++;
// restrict to 100 crypt systems, even on a slowbuffer.
if (CryptoCount > KERB_MAX_CRYPTO_SYSTEMS_SLOWBUFF) { return(KDC_ERR_ETYPE_NOTSUPP); } }
if (CryptoCount >= KERB_MAX_CRYPTO_SYSTEMS) { pCryptoSystems = (PULONG) MIDL_user_allocate(CryptoCount * sizeof(ULONG)); if (NULL == pCryptoSystems) { return ( KRB_ERR_GENERIC ); } } else // fast buff
{ pCryptoSystems = CryptoSystems; }
// populate values
NextEType = CryptList; Index = 0;
while (NextEType != NULL) { pCryptoSystems[Index] = NextEType->value; NextEType = NextEType->next; Index++; }
DsysAssert(Index == CryptoCount);
KerbErr = KerbMapStatusToKerbError( CDFindCommonCSystemWithKey( CryptoCount, pCryptoSystems, PasswordCount, PasswordTypes, &CommonCryptSystem ));
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KLIN(%x) Missing common crypt system: %#x\n", KLIN(FILENO, __LINE__), KerbErr)); goto cleanup; }
//
// Now find the key to return.
//
for (Index = 0; Index < Passwords->CredentialCount ; Index++ ) { if (Passwords->Credentials[Index].Key.keytype == (int) CommonCryptSystem) { *Key = &Passwords->Credentials[Index].Key; KerbErr = KDC_ERR_NONE; break; } }
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Couldn't find password type after finding common csystem!\n", KLIN(FILENO, __LINE__))); }
cleanup:
if ((pCryptoSystems != NULL) && (pCryptoSystems != CryptoSystems)) { MIDL_user_free(pCryptoSystems); }
return (KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbFindCommonCryptSystemForSKey
//
// Synopsis: Finds a common crypt system including availablity
// for session keys.
//
// Effects:
//
// Arguments: CryptList - List of client's crypto systems
// CryptListSupported - List of crypto systems supported
// Etype - Receives the common crypt system
//
// Requires:
//
// Returns: KDC_ERR_ETYPE_NOTSUPP if no common system can be found
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbFindCommonCryptSystemForSKey( IN PKERB_CRYPT_LIST CryptList, IN PKERB_CRYPT_LIST CryptListSupported, OUT ULONG * Etype ) { *Etype = KERB_ETYPE_NULL;
for (PKERB_CRYPT_LIST NextEType1 = CryptList; NextEType1 != NULL; NextEType1 = NextEType1->next) { for (PKERB_CRYPT_LIST NextEType2 = CryptListSupported; NextEType2 != NULL; NextEType2 = NextEType2->next) { if (NextEType1->value == NextEType2->value) { *Etype = NextEType1->value; return KDC_ERR_NONE; } } }
return KDC_ERR_ETYPE_NOTSUPP; }
//+-------------------------------------------------------------------------
//
// Function: KerbMapKerbError
//
// Synopsis: Maps a kerb error to an NTSTATUS
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbMapKerbError( IN KERBERR KerbError ) { NTSTATUS Status; switch(KerbError) { case KDC_ERR_NONE: Status = STATUS_SUCCESS; break; case KDC_ERR_CLIENT_REVOKED: Status = STATUS_ACCOUNT_DISABLED; break; case KDC_ERR_KEY_EXPIRED: Status = STATUS_PASSWORD_EXPIRED; break; case KRB_ERR_GENERIC: Status = STATUS_INSUFFICIENT_RESOURCES; break; case KRB_AP_ERR_SKEW: case KRB_AP_ERR_TKT_NYV: // Note this was added because of the following scenario:
// Let's say the dc and the client have the correct time. And the
// server's time is off. We aren't going to get rid of the ticket for the
// server on the client because it hasn't expired yet. But, the server
// thinks it has. If event logging was turned on, then admins could look
// at the server's event log and potentially deduce that the server's
// time is off relative to the dc.
case KRB_AP_ERR_TKT_EXPIRED: Status = STATUS_TIME_DIFFERENCE_AT_DC; break; case KDC_ERR_POLICY: Status = STATUS_ACCOUNT_RESTRICTION; break; case KDC_ERR_C_PRINCIPAL_UNKNOWN: Status = STATUS_NO_SUCH_USER; break; case KDC_ERR_S_PRINCIPAL_UNKNOWN: Status = STATUS_NO_TRUST_SAM_ACCOUNT; break; case KRB_AP_ERR_MODIFIED: case KDC_ERR_PREAUTH_FAILED: case KDC_ERR_PREAUTH_REQUIRED: Status = STATUS_WRONG_PASSWORD; break; case KRB_ERR_RESPONSE_TOO_BIG: Status = STATUS_INVALID_BUFFER_SIZE; break; case KDC_ERR_PADATA_TYPE_NOSUPP: Status = STATUS_NOT_SUPPORTED; break; case KRB_AP_ERR_NOT_US: Status = SEC_E_WRONG_PRINCIPAL; break;
case KDC_ERR_SVC_UNAVAILABLE: Status = STATUS_NO_LOGON_SERVERS; break; case KDC_ERR_WRONG_REALM: Status = STATUS_NO_LOGON_SERVERS; break; case KDC_ERR_CANT_VERIFY_CERTIFICATE: Status = TRUST_E_SYSTEM_ERROR; break; case KDC_ERR_INVALID_CERTIFICATE: Status = STATUS_INVALID_PARAMETER; break; case KDC_ERR_REVOKED_CERTIFICATE: Status = CRYPT_E_REVOKED; break; case KDC_ERR_REVOCATION_STATUS_UNKNOWN: Status = CRYPT_E_NO_REVOCATION_CHECK; break; case KDC_ERR_REVOCATION_STATUS_UNAVAILABLE: Status = CRYPT_E_REVOCATION_OFFLINE; break; case KDC_ERR_CLIENT_NAME_MISMATCH: case KERB_PKINIT_CLIENT_NAME_MISMATCH: case KDC_ERR_KDC_NAME_MISMATCH: Status = STATUS_PKINIT_NAME_MISMATCH; break; case KDC_ERR_PATH_NOT_ACCEPTED: Status = STATUS_TRUST_FAILURE; break; case KDC_ERR_ETYPE_NOTSUPP: Status = STATUS_KDC_UNKNOWN_ETYPE; break; case KDC_ERR_MUST_USE_USER2USER: case KRB_AP_ERR_USER_TO_USER_REQUIRED: Status = STATUS_USER2USER_REQUIRED; break; case KRB_AP_ERR_NOKEY: Status = STATUS_NO_KERB_KEY; break; case KRB_ERR_NAME_TOO_LONG: Status = STATUS_NAME_TOO_LONG; break; default: Status = STATUS_LOGON_FAILURE; }
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbMakeDomainRelativeSid
//
// Synopsis: Given a domain Id and a relative ID create the corresponding
// SID allocated with MIDL_user_allocate.
//
// Effects:
//
// Arguments: DomainId - The template SID to use.
//
// RelativeId - The relative Id to append to the DomainId.
//
// Requires:
//
// Returns: Sid - Returns a pointer to a buffer allocated from
// MIDL_user_allocate containing the resultant Sid.
//
// Notes:
//
//
//--------------------------------------------------------------------------
PSID KerbMakeDomainRelativeSid( IN PSID DomainId, IN ULONG RelativeId ) { UCHAR DomainIdSubAuthorityCount; ULONG Size; PSID Sid;
//
// Allocate a Sid which has one more sub-authority than the domain ID.
//
DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId )); Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);
if ((Sid = MIDL_user_allocate( Size )) == NULL ) { return NULL; }
//
// Initialize the new SID to have the same inital value as the
// domain ID.
//
if ( !NT_SUCCESS( RtlCopySid( Size, Sid, DomainId ) ) ) { MIDL_user_free( Sid ); return NULL; }
//
// Adjust the sub-authority count and
// add the relative Id unique to the newly allocated SID
//
(*(RtlSubAuthorityCountSid( Sid ))) ++; *RtlSubAuthoritySid( Sid, DomainIdSubAuthorityCount ) = RelativeId;
return Sid; }
//+-------------------------------------------------------------------------
//
// Function: KerbFreeCertificateList
//
// Synopsis: Frees a list of certificates created by KerbCreateCertificateList
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFreeCertificateList( IN PKERB_CERTIFICATE_LIST Certificates ) { PKERB_CERTIFICATE_LIST Last,Next;
Last = NULL; Next = Certificates;
while (Next != NULL) { Last = Next; Next = Next->next;
if (Last->value.cert_data.value != NULL) { MIDL_user_free(Last->value.cert_data.value); }
MIDL_user_free(Last); } }
//+-------------------------------------------------------------------------
//
// Function: KerbCreateCertificateList
//
// Synopsis: Creates a list of certificates from a cert context
//
// Effects:
//
// Arguments: Certficates - receives list of certificates.
// CertContext - Context containing certificates
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbCreateCertificateList( OUT PKERB_CERTIFICATE_LIST * Certificates, IN PCCERT_CONTEXT CertContext ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_CERTIFICATE_LIST ListEntry = NULL;
if (!ARGUMENT_PRESENT(CertContext)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// Croft up a bogus certificate entry
//
ListEntry = (PKERB_CERTIFICATE_LIST) MIDL_user_allocate(sizeof(KERB_CERTIFICATE_LIST));
if (ListEntry == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
ListEntry->next = NULL; ListEntry->value.cert_type = KERB_CERTIFICATE_TYPE_X509; ListEntry->value.cert_data.length = CertContext->cbCertEncoded; ListEntry->value.cert_data.value = (PUCHAR) MIDL_user_allocate(ListEntry->value.cert_data.length);
if (ListEntry->value.cert_data.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( ListEntry->value.cert_data.value, CertContext->pbCertEncoded, CertContext->cbCertEncoded );
*Certificates = ListEntry; ListEntry = NULL;
Cleanup:
KerbFreeCertificateList(ListEntry); return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertFlagsToUlong
//
// Synopsis: Converts a bit-stream flags field into a ULONG
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG KerbConvertFlagsToUlong( IN PVOID Flags ) { ULONG Output = 0; PUCHAR OutputPointer = &((PUCHAR) &Output)[3]; ULONG Index = 0; PKERB_TICKET_FLAGS InternalFlags = (PKERB_TICKET_FLAGS) Flags; ULONG InternalLength;
if (InternalFlags->length > 32) { InternalLength = 32; } else { InternalLength = (ULONG) InternalFlags->length; }
while (InternalLength > 7) { *OutputPointer = InternalFlags->value[Index++]; OutputPointer--; InternalLength -= 8; }
//
// Copy the remaining bits, masking off what should be zero
//
if (InternalLength != 0) { *OutputPointer = (UCHAR) (InternalFlags->value[Index] & ~((1 << (8-InternalLength)) - 1)); }
return(Output); }
//+-------------------------------------------------------------------------
//
// Function: KerbConvertUlongToFlagUlong
//
// Synopsis: Converts the byte order of a ULONG into that used by flags
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG KerbConvertUlongToFlagUlong( IN ULONG Flag ) { ULONG ReturnFlag;
((PUCHAR) &ReturnFlag)[0] = ((PUCHAR) &Flag)[3]; ((PUCHAR) &ReturnFlag)[1] = ((PUCHAR) &Flag)[2]; ((PUCHAR) &ReturnFlag)[2] = ((PUCHAR) &Flag)[1]; ((PUCHAR) &ReturnFlag)[3] = ((PUCHAR) &Flag)[0];
return(ReturnFlag); }
//+-------------------------------------------------------------------------
//
// Function: KerbCompareObjectIds
//
// Synopsis: Compares two object IDs for equality
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN KerbCompareObjectIds( IN PKERB_OBJECT_ID Object1, IN PKERB_OBJECT_ID Object2 ) { while (Object1 != NULL) { if (Object2 == NULL) { return(FALSE); }
if (Object1->value != Object2->value) { return(FALSE); }
Object1 = Object1->next; Object2 = Object2->next; }
if (Object2 != NULL) { return(FALSE); } else { return(TRUE); } }
//+-------------------------------------------------------------------------
//
// Function: KdcGetClientNetbiosAddress
//
// Synopsis: Gets the client's netbios address from the list of
// addresses it sends.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbGetClientNetbiosAddress( OUT PUNICODE_STRING ClientNetbiosAddress, IN PKERB_HOST_ADDRESSES Addresses ) { PKERB_HOST_ADDRESSES TempAddress = Addresses; STRING TempString; KERBERR KerbErr;
RtlInitUnicodeString( ClientNetbiosAddress, NULL );
while (TempAddress != NULL) { //
// Check for netbios
//
if (TempAddress->value.address_type == KERB_ADDRTYPE_NETBIOS) { //
// Copy out the string
//
TempString.Buffer = (PCHAR) TempAddress->value.address.value; TempString.Length = TempString.MaximumLength = (USHORT) TempAddress->value.address.length;
KerbErr = KerbStringToUnicodeString( ClientNetbiosAddress, &TempString ); if (KERB_SUCCESS(KerbErr)) { //
// Strip trailing spaces
//
if (ClientNetbiosAddress->Length >= sizeof(WCHAR)) { while ((ClientNetbiosAddress->Length > 0) && (ClientNetbiosAddress->Buffer[(ClientNetbiosAddress->Length / sizeof(WCHAR))-1] == L' ')) { ClientNetbiosAddress->Length -= sizeof(WCHAR); } return(KDC_ERR_NONE); } } else { return(KerbErr); }
}
TempAddress = TempAddress->next; }
//
// It is o.k. to not have a netbios name
//
return(KDC_ERR_NONE); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetPacFromAuthData
//
// Synopsis: Gets the PAC from the auth data list
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbGetPacFromAuthData( IN PKERB_AUTHORIZATION_DATA AuthData, OUT PKERB_IF_RELEVANT_AUTH_DATA ** ReturnIfRelevantData, OUT PKERB_AUTHORIZATION_DATA * Pac ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_AUTHORIZATION_DATA PacAuthData = NULL; PKERB_AUTHORIZATION_DATA RelevantAuthData = NULL; PKERB_IF_RELEVANT_AUTH_DATA * IfRelevantData = NULL;
*ReturnIfRelevantData = NULL; *Pac = NULL;
//
// Look for the if-relevant data
//
RelevantAuthData = KerbFindAuthDataEntry( KERB_AUTH_DATA_IF_RELEVANT, AuthData );
if (RelevantAuthData != NULL) { //
// Unpack it
//
KerbErr = KerbUnpackData( RelevantAuthData->value.auth_data.value, RelevantAuthData->value.auth_data.length, PKERB_IF_RELEVANT_AUTH_DATA_PDU, (PVOID *) &IfRelevantData );
if (KERB_SUCCESS(KerbErr)) { //
// Look for the PAC in the if-relevant data
//
PacAuthData = KerbFindAuthDataEntry( KERB_AUTH_DATA_PAC, *IfRelevantData ); } else { //
// We don't mind if we couldn't unpack it.
// Tickets do not always have PAC information.
//
KerbErr = KDC_ERR_NONE; } }
//
// If we didn't find it in the if-relevant data, look outside
//
if (PacAuthData == NULL) { PacAuthData = KerbFindAuthDataEntry( KERB_AUTH_DATA_PAC, AuthData ); }
//
// Copy the PAC to return it
//
if (PacAuthData != NULL) { *Pac = PacAuthData; }
*ReturnIfRelevantData = IfRelevantData; IfRelevantData = NULL;
return(KerbErr); }
#if DBG
#define KERB_DEBUG_WARN_LEVEL 0x0002
//+-------------------------------------------------------------------------
//
// Function: DebugDisplayTime
//
// Synopsis: Displays a FILETIME
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
void DebugDisplayTime( IN ULONG DebugLevel, IN FILETIME *pFileTime ) { SYSTEMTIME SystemTime;
if (DebugLevel & KERB_DEBUG_WARN_LEVEL) { FileTimeToSystemTime(pFileTime, &SystemTime);
DebugLog((DEB_ERROR," %02d:%02d:%02d - %02d %02d %04d\n", SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond, SystemTime.wDay,SystemTime.wMonth,SystemTime.wYear)); } return; } #endif
//+-------------------------------------------------------------------------
//
// Function: VerifyClientAddress
//
// Synopsis: Verifies that the client address is present in the address list
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: TRUE if the address checks out OK, FALSE otherwise
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN KerbVerifyClientAddress( IN SOCKADDR * ClientAddress, IN PKERB_HOST_ADDRESSES Addresses ) { PKERB_HOST_ADDRESSES TempAddress = Addresses; BOOLEAN IpAddressesPresent = FALSE;
//
// ISSUE-2001/03/05-markpu
// This routine is inadequate in that it only deals with IPv4
// addresses. Address matching has to be more elaborate than that.
//
while (TempAddress != NULL) { if ( TempAddress->value.address_type == KERB_ADDRTYPE_INET && ClientAddress->sa_family == AF_INET ) { struct sockaddr_in * InetAddress = (struct sockaddr_in *) ClientAddress;
IpAddressesPresent = TRUE;
//
// Check that the addresses match
//
if (TempAddress->value.address.length == sizeof(ULONG)) { if (RtlEqualMemory( TempAddress->value.address.value, &InetAddress->sin_addr.S_un.S_addr, sizeof(ULONG))) { return TRUE; } } }
TempAddress = TempAddress->next; }
D_DebugLog((DEB_WARN,"Client address not in address list\n"));
//
// If there were no IP addresses in the ticket, return TRUE, since we
// probably are in a situation where only netbios addresses are in the
// ticket, so the socket address matched none of them
//
return !IpAddressesPresent; }
//+-------------------------------------------------------------------------
//
// Function: KerbCopyDomainRelativeSid
//
// Synopsis: Given a domain Id and a relative ID create the corresponding
// SID at the location indicated by TargetSid
//
// Effects:
//
// Arguments: TargetSid - target memory location
// DomainId - The template SID to use.
//
// RelativeId - The relative Id to append to the DomainId.
//
// Requires:
//
// Returns: Size - Size of the sid copied
//
// Notes:
//
//
//--------------------------------------------------------------------------
DWORD KerbCopyDomainRelativeSid( OUT PSID TargetSid, IN PSID DomainId, IN ULONG RelativeId ) { UCHAR DomainIdSubAuthorityCount; ULONG Size;
//
// Allocate a Sid which has one more sub-authority than the domain ID.
//
DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId )); Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);
//
// Initialize the new SID to have the same inital value as the
// domain ID.
//
if ( !NT_SUCCESS( RtlCopySid( Size, TargetSid, DomainId ) ) ) {
return 0; }
//
// Adjust the sub-authority count and
// add the relative Id unique to the newly allocated SID
//
(*(RtlSubAuthorityCountSid( TargetSid ))) ++; *RtlSubAuthoritySid( TargetSid, DomainIdSubAuthorityCount ) = RelativeId;
return Size; }
//+-------------------------------------------------------------------------
//
// Function: KerbHashS4UPreauth
//
// Synopsis: Given a domain Id and a relative ID create the corresponding
// SID at the location indicated by TargetSid
//
// Effects:
//
// Arguments: TargetSid - target memory location
// DomainId - The template SID to use.
//
// RelativeId - The relative Id to append to the DomainId.
//
// Requires:
//
// Returns: Size - Size of the sid copied
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbHashS4UPreauth( IN PKERB_PA_FOR_USER S4UPreauth, IN PKERB_ENCRYPTION_KEY Key, IN LONG ChecksumType, IN OUT PKERB_CHECKSUM CheckSum ) { NTSTATUS Status;
PCHECKSUM_FUNCTION MD5Check = NULL; PCHECKSUM_BUFFER MD5ScratchBuffer = NULL; PKERB_PRINCIPAL_NAME_name_string TmpName = S4UPreauth->userName.name_string;
//
// Locate the MD5 Hash Function
//
Status = CDLocateCheckSum(ChecksumType, &MD5Check);
if( !NT_SUCCESS(Status) ) { D_DebugLog( (DEB_ERROR, "Failure Locating MD5: 0x%x.\n", Status));
goto Cleanup; }
//
// Initialize the HMAC using the TGT session key
//
if (NULL != MD5Check->InitializeEx2) { Status = MD5Check->InitializeEx2( Key->keyvalue.value, Key->keyvalue.length, NULL, KERB_NON_KERB_CKSUM_SALT, &MD5ScratchBuffer ); } else { Status = MD5Check->InitializeEx( Key->keyvalue.value, Key->keyvalue.length, KERB_NON_KERB_CKSUM_SALT, &MD5ScratchBuffer ); }
if( !NT_SUCCESS(Status) ) { D_DebugLog((DEB_ERROR,"Failure initializing MD5HMAC: 0x%x.\n",Status)); goto Cleanup; }
//
// Build the MD5 hash
//
//
// Pull in client realm name.
//
Status = MD5Check->Sum( MD5ScratchBuffer, sizeof(DWORD), (PUCHAR) &S4UPreauth->userName.name_type );
if( !NT_SUCCESS(Status) ) { D_DebugLog((DEB_ERROR,"Failure building MD5: 0x%x.\n",Status)); goto Cleanup; }
if (TmpName->value == NULL) { DsysAssert(FALSE); goto Cleanup; }
do { Status = MD5Check->Sum( MD5ScratchBuffer, lstrlenA( TmpName->value ), (PUCHAR) TmpName->value );
if( !NT_SUCCESS(Status) ) { D_DebugLog((DEB_ERROR,"Failure building MD5: 0x%x.\n",Status)); goto Cleanup; }
TmpName = TmpName->next;
} while ( TmpName != NULL );
//
// Then the client realm
//
Status = MD5Check->Sum( MD5ScratchBuffer, lstrlenA( S4UPreauth->userRealm ), (PUCHAR) S4UPreauth->userRealm ); if( !NT_SUCCESS(Status) ) { D_DebugLog((DEB_ERROR,"Failure building MD5: 0x%x.\n",Status)); goto Cleanup; } //
// Authentication package.
//
Status = MD5Check->Sum( MD5ScratchBuffer, lstrlenA( S4UPreauth->authentication_package ), (PUCHAR) S4UPreauth->authentication_package ); if( !NT_SUCCESS(Status) ) { D_DebugLog( (DEB_ERROR,"Failure building MD5: 0x%x.\n",Status)); goto Cleanup; } if ( S4UPreauth->bit_mask & KERB_PA_FOR_USER_authorization_data_present ) { //
// Hash authorization data.
//
Status = MD5Check->Sum( MD5ScratchBuffer, S4UPreauth->authorization_data.length, (PUCHAR) S4UPreauth->authorization_data.value ); if( !NT_SUCCESS(Status) ) { D_DebugLog( (DEB_ERROR,"Failure building MD5: 0x%x.\n",Status)); goto Cleanup; } } //
// Copy the hash results into the checksum field
//
CheckSum->checksum_type = ChecksumType; CheckSum->checksum.length = MD5Check->CheckSumSize; Status = MD5Check->Finalize( MD5ScratchBuffer, CheckSum->checksum.value ); if ( !NT_SUCCESS(Status) ) { D_DebugLog( (DEB_ERROR,"Failure FINALIZING MD5: 0x%x.\n",Status)); goto Cleanup; } Cleanup: if ( MD5Check != NULL ) { MD5Check->Finish( &MD5ScratchBuffer ); } return Status; }
|