//+----------------------------------------------------------------------- // // 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 #include #endif // WIN32_CHICAGO #ifndef WIN32_CHICAGO extern "C" { #include "krbprgma.h" #include #include #include #include #include #include #include #include #include #include #include } #include #include #include #include #include #include #include "debug.h" #include "fileno.h" #else// WIN32_CHICAGO #include "tostring.hxx" #endif // WIN32_CHICAGO #include #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; }