//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: gettgs.cxx // // Contents: GetTGSTicket and support functions // // Classes: // // Functions: // // History: 04-Mar-94 wader Created // //---------------------------------------------------------------------------- #include "kdcsvr.hxx" #include "kdctrace.h" #include #include #include "fileno.h" #define FILENO FILENO_GETTGS extern LARGE_INTEGER tsInfinity; extern LONG lInfinity; UNICODE_STRING KdcNullString = {0,0,NULL}; //-------------------------------------------------------------------- // // Name: KdcGetS4UPac // // Synopsis: Track down the user acct for PAC info. // // Effects: Get the PAC // // Arguments: S4UClientName - ClientName from S4U PA Data // PAC - Resultant PAC (signed w/? key) // // Requires: // // Returns: KDC_ERR_ or KRB_AP_ERR errors only // // Notes: Free client name and realm w/ // // //-------------------------------------------------------------------------- KERBERR KdcGetS4UTicketInfo( IN PKERB_INTERNAL_NAME S4UClientName, IN OUT PUSER_INTERNAL6_INFORMATION * UserInfo, IN OUT PSID_AND_ATTRIBUTES_LIST GroupMembership, IN OUT PKERB_EXT_ERROR ExtendedError ) { KERBERR KerbErr; UNICODE_STRING S4UClient = {0}; KDC_TICKET_INFO S4UClientInfo; KerbErr = KerbConvertKdcNameToString( &S4UClient, S4UClientName, NULL ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Get the user information... // KerbErr = KdcGetTicketInfo( &S4UClient, SAM_OPEN_BY_UPN_OR_ACCOUNTNAME, // extra flags? S4UClientName, NULL, &S4UClientInfo, ExtendedError, NULL, USER_ALL_KDC_GET_PAC_AUTH_DATA, 0L, UserInfo, GroupMembership ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Getting the ticket info for S4U req failed - %x\n", KerbErr)); DsysAssert(FALSE); goto Cleanup; } Cleanup: KerbFreeString(&S4UClient); FreeTicketInfo( &S4UClientInfo ); return KerbErr; } //+------------------------------------------------------------------------- // // Function: KdcAuditAccountMapping // // Synopsis: Generates, if necessary, a success/failure audit for name // mapping. The names are converted to a string before // being passed to the LSA. // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAuditAccountMapping( IN PKERB_INTERNAL_NAME ClientName, IN KERB_REALM ClientRealm, IN OPTIONAL PKDC_TICKET_INFO MappedTicketInfo ) { UNICODE_STRING ClientString = {0}; PUNICODE_STRING MappedName = NULL; UNICODE_STRING UnicodeRealm = {0}; UNICODE_STRING NullString = {0}; KERBERR KerbErr; BOOLEAN Successful; if (ARGUMENT_PRESENT(MappedTicketInfo)) { if (!SecData.AuditKdcEvent(KDC_AUDIT_MAP_SUCCESS)) { return; } Successful = TRUE; MappedName = &MappedTicketInfo->AccountName; } else { if (!SecData.AuditKdcEvent(KDC_AUDIT_MAP_FAILURE)) { return; } MappedName = &NullString; Successful = FALSE; } KerbErr = KerbConvertRealmToUnicodeString( &UnicodeRealm, &ClientRealm ); if (!KERB_SUCCESS(KerbErr)) { return; } if (KERB_SUCCESS(KerbConvertKdcNameToString( &ClientString, ClientName, &UnicodeRealm ))) { LsaIAuditAccountLogon( SE_AUDITID_ACCOUNT_MAPPED, Successful, &GlobalKdcName, &ClientString, MappedName, 0 // no status ); KerbFreeString( &ClientString ); } KerbFreeString( &UnicodeRealm ); } //---------------------------------------------------------------- // // Name: KdcInsertAuthorizationData // // Synopsis: Inserts auth data into a newly created ticket. // // Arguments: FinalTicket - Ticket to insert Auth data into // EncryptedAuthData - Auth data (optional) // SourceTicket - Source ticket // // Notes: This copies the authorization data from the source ticket // to the destiation ticket, and adds the authorization data // passed in. It is called by GetTGSTicket. // // This assumes that pedAuthData is an encrypted // KERB_AUTHORIZATION_DATA. // It will copy all the elements of that list to the new ticket. // If pedAuthData is not supplied (or is empty), and there is // auth data in the source ticket, it is copied to the new // ticket. If no source ticket, and no auth data is passed // in, nothing is done. // //---------------------------------------------------------------- KERBERR KdcInsertAuthorizationData( OUT PKERB_ENCRYPTED_TICKET FinalTicket, OUT PKERB_EXT_ERROR pExtendedError, IN OPTIONAL PKERB_ENCRYPTED_DATA EncryptedAuthData, IN OPTIONAL PKERB_INTERNAL_NAME S4UClientName, IN BOOLEAN DoingS4U, IN PKERB_ENCRYPTED_TICKET SourceTicket, IN BOOLEAN AddResourceGroups, IN OPTIONAL PKDC_TICKET_INFO OriginalServerInfo, IN OPTIONAL PKERB_ENCRYPTION_KEY OriginalServerKey, IN OPTIONAL PKERB_ENCRYPTION_KEY TargetServerKey ) { PKERB_AUTHORIZATION_DATA SourceAuthData = NULL; PKERB_AUTHORIZATION_DATA FinalAuthData = NULL; PKERB_AUTHORIZATION_DATA PacAuthData = NULL; PKERB_AUTHORIZATION_DATA NewPacAuthData = NULL; KERBERR KerbErr = KDC_ERR_NONE; PKERB_AUTHORIZATION_DATA_LIST * TempAuthData = NULL; PKERB_AUTHORIZATION_DATA NextAuthData; PKERB_INTERNAL_NAME ClientName = NULL; PKERB_IF_RELEVANT_AUTH_DATA * IfRelevantData = NULL; PKERB_AUTHORIZATION_DATA SuppliedAuthData = NULL; UNICODE_STRING DummyName = {0}; SID_AND_ATTRIBUTES_LIST S4UGroupMembership = {0}; PUSER_INTERNAL6_INFORMATION S4UUserInfo = NULL; TRACE(KDC, InsertAuthorizationData, DEB_FUNCTION); D_DebugLog(( DEB_T_TICKETS, "Inserting authorization data into ticket.\n" )); // // First try to decrypt the supplied authorization data // if (!DoingS4U) { if (ARGUMENT_PRESENT(EncryptedAuthData)) { KerbErr = KerbDecryptDataEx( EncryptedAuthData, &SourceTicket->key, KERB_NON_KERB_SALT, // WAS BUG: wrong salt, removed per MikeSw &EncryptedAuthData->cipher_text.length, EncryptedAuthData->cipher_text.value ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KLIN(%x) Failed to decrypt encrypted auth data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } // // Now decode it // KerbErr = KerbUnpackData( EncryptedAuthData->cipher_text.value, EncryptedAuthData->cipher_text.length, PKERB_AUTHORIZATION_DATA_LIST_PDU, (PVOID *) &TempAuthData ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } if (TempAuthData != NULL) { SuppliedAuthData = *TempAuthData; } } if (SourceTicket->bit_mask & KERB_ENCRYPTED_TICKET_authorization_data_present) { DsysAssert(SourceTicket->KERB_ENCRYPTED_TICKET_authorization_data != NULL); SourceAuthData = SourceTicket->KERB_ENCRYPTED_TICKET_authorization_data; // // Get the AuthData from the source ticket // KerbErr = KerbGetPacFromAuthData( SourceAuthData, &IfRelevantData, &PacAuthData ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to get pac from auth data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } } // // The new auth data is the original auth data appended to the // supplied auth data. The new auth data goes first, followed by the // auth data from the original ticket. // // // Update the PAC, if it is present. // if (ARGUMENT_PRESENT(OriginalServerKey) && (PacAuthData != NULL)) { KerbErr = KdcVerifyAndResignPac( OriginalServerKey, TargetServerKey, OriginalServerInfo, AddResourceGroups, PacAuthData ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to verify & resign pac: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } // // Copy the old auth data & insert the PAC // KerbErr = KdcInsertPacIntoAuthData( SourceAuthData, (IfRelevantData != NULL) ? *IfRelevantData : NULL, PacAuthData, &FinalAuthData ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to insert pac into auth data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } } } if (DoingS4U) { // // Use the PAC from the S4U data to return in the TGT / Service Ticket // KerbErr = KdcGetS4UTicketInfo( S4UClientName, &S4UUserInfo, &S4UGroupMembership, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KdcGetPacAuthData( S4UUserInfo, &S4UGroupMembership, TargetServerKey, NULL, // no credential key AddResourceGroups, FinalTicket, S4UClientName, &NewPacAuthData, pExtendedError ); SamIFreeSidAndAttributesList(&S4UGroupMembership); SamIFree_UserInternal6Information( S4UUserInfo ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Failed to get S4UPacAUthData\n")); DsysAssert(FALSE); goto Cleanup; } FinalAuthData = NewPacAuthData; NewPacAuthData = NULL; } // // If there was no original PAC, try to insert one here. If the ticket // was issued from this realm we don't add a pac. // else if ((PacAuthData == NULL) && !SecData.IsOurRealm(&SourceTicket->client_realm)) { KDC_TICKET_INFO ClientTicketInfo = {0}; SID_AND_ATTRIBUTES_LIST GroupMembership = {0}; PUSER_INTERNAL6_INFORMATION UserInfo = NULL; // // Try mapping the name to get a PAC // KerbErr = KerbConvertPrincipalNameToKdcName( &ClientName, &SourceTicket->client_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KdcGetTicketInfo( &DummyName, SAM_OPEN_BY_ALTERNATE_ID, ClientName, &SourceTicket->client_realm, &ClientTicketInfo, pExtendedError, NULL, // no handle USER_ALL_KDC_GET_PAC_AUTH_DATA, 0L, // no extended fields &UserInfo, &GroupMembership ); if (KERB_SUCCESS(KerbErr)) { KdcAuditAccountMapping( ClientName, SourceTicket->client_realm, &ClientTicketInfo ); FreeTicketInfo(&ClientTicketInfo); KerbFreeKdcName(&ClientName); KerbErr = KdcGetPacAuthData( UserInfo, &GroupMembership, TargetServerKey, NULL, // no credential key AddResourceGroups, FinalTicket, NULL, // no S4U client &NewPacAuthData, pExtendedError ); SamIFreeSidAndAttributesList(&GroupMembership); SamIFree_UserInternal6Information( UserInfo ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } else if (KerbErr == KDC_ERR_C_PRINCIPAL_UNKNOWN) { KdcAuditAccountMapping( ClientName, SourceTicket->client_realm, NULL ); KerbFreeKdcName(&ClientName); KerbErr = KDC_ERR_NONE; } if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // If we got a PAC, stick it in the list // if (NewPacAuthData != NULL) { // // Copy the old auth data & insert the PAC // KerbErr = KerbCopyAndAppendAuthData( &NewPacAuthData, SourceAuthData ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to insert pac into auth data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } FinalAuthData = NewPacAuthData; NewPacAuthData = NULL; } } // // if there was any auth data and we didn't copy it transfering the // PAC, do so now // if ((SourceAuthData != NULL) && (FinalAuthData == NULL)) { KerbErr = KerbCopyAndAppendAuthData( &FinalAuthData, SourceAuthData ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } if (SuppliedAuthData != NULL) { KerbErr = KerbCopyAndAppendAuthData( &FinalAuthData, SuppliedAuthData ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } if (FinalAuthData != NULL) { FinalTicket->bit_mask |= KERB_ENCRYPTED_TICKET_authorization_data_present; FinalTicket->KERB_ENCRYPTED_TICKET_authorization_data = FinalAuthData; FinalAuthData = NULL; } KerbErr = KDC_ERR_NONE; Cleanup: KerbFreeAuthData( FinalAuthData ); if (TempAuthData != NULL) { KerbFreeData( PKERB_AUTHORIZATION_DATA_LIST_PDU, TempAuthData ); } KerbFreeAuthData(NewPacAuthData); if (IfRelevantData != NULL) { KerbFreeData( PKERB_IF_RELEVANT_AUTH_DATA_PDU, IfRelevantData ); } return(KerbErr); } //+--------------------------------------------------------------------------- // // Function: BuildTicketTGS // // Synopsis: Builds (most of) a TGS ticket // // Arguments: ServiceTicketInfo - Ticket info for the requested service // ReferralRealm - Realm to build referral to // RequestBody - The request causing this ticket to be built // SourceTicket - The TGT used to make this request // Referral - TRUE if this is an inter-realm referral ticke // CommonEType - Contains the common encryption type between // client and server // NewTicket - The new ticket built here. // // // History: 24-May-93 WadeR Created // // Notes: see 3.3.3, A.6 of the Kerberos V5 R5.2 spec // //---------------------------------------------------------------------------- KERBERR BuildTicketTGS( IN PKDC_TICKET_INFO ServiceTicketInfo, IN PKERB_KDC_REQUEST_BODY RequestBody, IN PKERB_TICKET SourceTicket, IN BOOLEAN Referral, IN OPTIONAL PKERB_INTERNAL_NAME S4UClientName, IN OPTIONAL PUNICODE_STRING S4UClientRealm, IN ULONG CommonEType, OUT PKERB_TICKET NewTicket, IN OUT PKERB_EXT_ERROR ExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; KERB_TICKET OutputTicket; PKERB_ENCRYPTED_TICKET EncryptedTicket; PKERB_ENCRYPTED_TICKET SourceEncryptPart; LARGE_INTEGER SourceRenewUntil; LARGE_INTEGER SourceEndTime; LARGE_INTEGER SourceStartTime; LARGE_INTEGER TicketLifespan; LARGE_INTEGER TicketRenewspan; UNICODE_STRING NewTransitedInfo = {0,0,NULL}; UNICODE_STRING ClientRealm = {0,0,NULL}; UNICODE_STRING TransitedRealm = {0,0,NULL}; UNICODE_STRING OldTransitedInfo = {0,0,NULL}; STRING OldTransitedString; ULONG KdcOptions = 0; BOOLEAN fKpasswd = FALSE; ULONG TicketFlags = 0; ULONG SourceTicketFlags = 0; PKERB_HOST_ADDRESSES Addresses = NULL; TRACE(KDC, BuildTicketTGS, DEB_FUNCTION); D_DebugLog(( DEB_T_TICKETS, "Building a TGS ticket\n" )); SourceEncryptPart = (PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value; OutputTicket = *NewTicket; EncryptedTicket = (PKERB_ENCRYPTED_TICKET) OutputTicket.encrypted_part.cipher_text.value; KdcOptions = KerbConvertFlagsToUlong( &RequestBody->kdc_options ); // // Get the times from the source ticket into a usable form // if (SourceEncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_renew_until_present) { KerbConvertGeneralizedTimeToLargeInt( &SourceRenewUntil, &SourceEncryptPart->KERB_ENCRYPTED_TICKET_renew_until, 0 ); } else { SourceRenewUntil.QuadPart = 0; } KerbConvertGeneralizedTimeToLargeInt( &SourceEndTime, &SourceEncryptPart->endtime, 0 ); if (SourceEncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present) { KerbConvertGeneralizedTimeToLargeInt( &SourceStartTime, &SourceEncryptPart->KERB_ENCRYPTED_TICKET_starttime, 0 ); } else { SourceStartTime.QuadPart = 0; } // // Check to see if the request is for the kpasswd service, in // which case, we only want the ticket to be good for 2 minutes. // KerbErr = KerbCompareKdcNameToPrincipalName( &RequestBody->server_name, GlobalKpasswdName, &fKpasswd ); if (!fKpasswd || !KERB_SUCCESS(KerbErr)) { TicketLifespan = SecData.KdcTgsTicketLifespan(); TicketRenewspan = SecData.KdcTicketRenewSpan(); } else { TicketLifespan.QuadPart = (LONGLONG) 10000000 * 60 * 2; TicketRenewspan.QuadPart = (LONGLONG) 10000000 * 60 * 2; } // // TBD: We need to make the ticket 10 minutes if we're doing s4U // KerbErr = KdcBuildTicketTimesAndFlags( 0, // no client policy ServiceTicketInfo->fTicketOpts, &TicketLifespan, &TicketRenewspan, NULL, // no logoff time NULL, // no acct expiry. RequestBody, SourceEncryptPart, EncryptedTicket, ExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Failed to build ticket times and flags: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } TicketFlags = KerbConvertFlagsToUlong( &EncryptedTicket->flags ); SourceTicketFlags = KerbConvertFlagsToUlong( &SourceEncryptPart->flags ); KerbErr = KerbMakeKey( CommonEType, &EncryptedTicket->key ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } OldTransitedString.Buffer = (PCHAR) SourceEncryptPart->transited.contents.value; OldTransitedString.Length = OldTransitedString.MaximumLength = (USHORT) SourceEncryptPart->transited.contents.length; // // Fill in the service names // if (Referral) { PKERB_INTERNAL_NAME TempServiceName; // // For referral tickets we put a the name "krbtgt/remoterealm@localrealm" // // // We should only be doing this when we didn't get a non-ms principal // KerbErr = KerbBuildFullServiceKdcName( &ServiceTicketInfo->AccountName, SecData.KdcServiceName(), KRB_NT_SRV_INST, &TempServiceName ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertKdcNameToPrincipalName( &OutputTicket.server_name, TempServiceName ); KerbFreeKdcName(&TempServiceName); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // If we got here on a referral ticket and are generating one // and the referral ticket we received was not from the client's // realm, add in the transited information. // if (!KerbCompareRealmNames( &SourceEncryptPart->client_realm, &SourceTicket->realm)) { KerbErr = KerbStringToUnicodeString( &OldTransitedInfo, &OldTransitedString ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertRealmToUnicodeString( &TransitedRealm, &SourceTicket->realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &SourceEncryptPart->client_realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KdcInsertTransitedRealm( &NewTransitedInfo, &OldTransitedInfo, &ClientRealm, &TransitedRealm, SecData.KdcDnsRealmName() ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } } else { UNICODE_STRING TempServiceName; // // If the client didn't request name canonicalization, use the // name supplied by the client // if (((KdcOptions & KERB_KDC_OPTIONS_name_canonicalize) != 0) && ((ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY) == 0)) { if (ServiceTicketInfo->UserId == DOMAIN_USER_RID_KRBTGT) { PKERB_INTERNAL_NAME TempServiceName = NULL; KerbErr = KerbBuildFullServiceKdcName( SecData.KdcDnsRealmName(), SecData.KdcServiceName(), KRB_NT_SRV_INST, &TempServiceName ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertKdcNameToPrincipalName( &OutputTicket.server_name, TempServiceName ); KerbFreeKdcName(&TempServiceName); } else // // We no longer use the NC bit to change the server name, so just // duplicate the non-NC case, and return the server name from // the TGS_REQ. NC is still used for building PA DATA for referral // however. and we should keep it for TGT renewal. TS 2001-4-03 // { KerbErr = KerbDuplicatePrincipalName( &OutputTicket.server_name, &RequestBody->KERB_KDC_REQUEST_BODY_server_name ); } } else { KerbErr = KerbDuplicatePrincipalName( &OutputTicket.server_name, &RequestBody->KERB_KDC_REQUEST_BODY_server_name ); } if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } // // Copy all the other strings over // EncryptedTicket->client_realm = SourceEncryptPart->client_realm; // // S4U dance... Get the client name and realm from // if (ARGUMENT_PRESENT(S4UClientName) && ARGUMENT_PRESENT(S4UClientRealm) && !Referral) { DebugLog((DEB_ERROR, "Swapping real client name for S4U CLient name\n")); KerbErr = KerbConvertKdcNameToPrincipalName( &EncryptedTicket->client_name, S4UClientName ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertUnicodeStringToRealm( &EncryptedTicket->client_realm, S4UClientRealm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } else { KerbErr = KerbDuplicatePrincipalName( &EncryptedTicket->client_name, &SourceEncryptPart->client_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } EncryptedTicket->client_realm = SourceEncryptPart->client_realm; } // // If the client did not request canonicalization, return the same // realm as it sent. Otherwise, send our DNS realm name // // if (((KdcOptions & KERB_KDC_OPTIONS_name_canonicalize) != 0) || // ((ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY) == 0)) // // { OutputTicket.realm = SecData.KdcKerbDnsRealmName(); // } // else // { // OutputTicket.realm = RequestBody->realm; // } // // Insert transited realms, if present // if (NewTransitedInfo.Length != 0) { STRING TempString; KerbErr = KerbUnicodeStringToKerbString( &TempString, &NewTransitedInfo ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } EncryptedTicket->transited.transited_type = DOMAIN_X500_COMPRESS; EncryptedTicket->transited.contents.value = (PUCHAR) TempString.Buffer; EncryptedTicket->transited.contents.length = (int) TempString.Length; } else { EncryptedTicket->transited.transited_type = DOMAIN_X500_COMPRESS; EncryptedTicket->transited.contents.value = (PUCHAR) MIDL_user_allocate(OldTransitedString.Length); if (EncryptedTicket->transited.contents.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } EncryptedTicket->transited.contents.length = (int) OldTransitedString.Length; RtlCopyMemory( EncryptedTicket->transited.contents.value, OldTransitedString.Buffer, OldTransitedString.Length ); } // // Insert the client addresses. We only update them if the new ticket // is forwarded of proxied and the source ticket was forwardable or proxiable // - else we copy the old ones // if ((((TicketFlags & KERB_TICKET_FLAGS_forwarded) != 0) && ((SourceTicketFlags & KERB_TICKET_FLAGS_forwardable) != 0)) || (((TicketFlags & KERB_TICKET_FLAGS_proxy) != 0) && ((SourceTicketFlags & KERB_TICKET_FLAGS_proxiable) != 0))) { if ((RequestBody->bit_mask & addresses_present) != 0) { Addresses = RequestBody->addresses; } } else { if ((SourceEncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_client_addresses_present) != 0) { Addresses = SourceEncryptPart->KERB_ENCRYPTED_TICKET_client_addresses; } } if (Addresses != NULL) { EncryptedTicket->KERB_ENCRYPTED_TICKET_client_addresses = Addresses; EncryptedTicket->bit_mask |= KERB_ENCRYPTED_TICKET_client_addresses_present; } // // The authorization data will be added by the caller, so set it // to NULL here. // EncryptedTicket->KERB_ENCRYPTED_TICKET_authorization_data = NULL; OutputTicket.ticket_version = KERBEROS_VERSION; *NewTicket = OutputTicket; Cleanup: if (!KERB_SUCCESS(KerbErr)) { KdcFreeInternalTicket(&OutputTicket); } KerbFreeString(&NewTransitedInfo); KerbFreeString(&OldTransitedInfo); KerbFreeString(&ClientRealm); KerbFreeString(&TransitedRealm); return(KerbErr); } //+------------------------------------------------------------------------- // // Function: KdcVerifyTgsLogonRestrictions // // Synopsis: Verifies that a client is allowed to request a TGS ticket // by checking logon restrictions. // // Effects: // // Arguments: ClientName - Name of client to check // // Requires: // // Returns: KDC_ERR_NONE or a logon restriction error // // Notes: // // //-------------------------------------------------------------------------- KERBERR KdcCheckTgsLogonRestrictions( IN PKERB_INTERNAL_NAME ClientName, IN PUNICODE_STRING ClientRealm, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR Status; UNICODE_STRING MappedClientRealm = {0}; BOOLEAN ClientReferral; KDC_TICKET_INFO ClientInfo = {0}; SAMPR_HANDLE UserHandle = NULL; PUSER_INTERNAL6_INFORMATION UserInfo = NULL; LARGE_INTEGER LogoffTime; NTSTATUS LogonStatus = STATUS_SUCCESS; // // If the client is from a different realm, don't bother looking // it up - the account won't be here. // if (!SecData.IsOurRealm( ClientRealm )) { return(KDC_ERR_NONE); } // // Normalize the client name // Status = KdcNormalize( ClientName, NULL, ClientRealm, KDC_NAME_CLIENT, &ClientReferral, &MappedClientRealm, &ClientInfo, pExtendedError, &UserHandle, USER_ALL_KERB_CHECK_LOGON_RESTRICTIONS, 0L, &UserInfo, NULL // no group memberships ); if (!KERB_SUCCESS(Status)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to normalize name ", KLIN(FILENO, __LINE__))); KerbPrintKdcName(DEB_ERROR,ClientName); goto Cleanup; } Status = KerbCheckLogonRestrictions( UserHandle, NULL, // No client address is available &UserInfo->I1, KDC_RESTRICT_PKINIT_USED | KDC_RESTRICT_IGNORE_PW_EXPIRATION, // Don't bother checking for password expiration &LogoffTime, &LogonStatus ); if (!KERB_SUCCESS(Status)) { DebugLog((DEB_WARN,"KLIN (%x) Logon restriction check failed: 0x%x\n", KLIN(FILENO, __LINE__),Status)); // // This is a *very* important error to trickle back. See 23456 in bug DB // FILL_EXT_ERROR(pExtendedError, LogonStatus, FILENO, __LINE__); goto Cleanup; } Cleanup: KerbFreeString( &MappedClientRealm ); FreeTicketInfo( &ClientInfo ); SamIFree_UserInternal6Information( UserInfo ); if (UserHandle != NULL) { SamrCloseHandle(&UserHandle); } return Status; } //+------------------------------------------------------------------------- // // Function: KdcBuildReferralInfo // // Synopsis: Builds the referral information to return to the client. // We only return the realm name and no server name // // Effects: // // Arguments: ReferralRealm - realm to refer client to // ReferralInfo - recevies encoded referral info // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- KERBERR KdcBuildReferralInfo( IN PUNICODE_STRING ReferralRealm, OUT PKERB_PA_DATA_LIST *ReferralInfo ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_PA_DATA_LIST ListElem = NULL; KERB_PA_SERV_REFERRAL ReferralData = {0}; // // Fill in the unencoded structure. // KerbErr = KerbConvertUnicodeStringToRealm( &ReferralData.referred_server_realm, ReferralRealm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } ListElem = (PKERB_PA_DATA_LIST) MIDL_user_allocate(sizeof(KERB_PA_DATA_LIST)); if (ListElem == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } RtlZeroMemory(ListElem, sizeof(KERB_PA_DATA_LIST)); ListElem->value.preauth_data_type = KRB5_PADATA_REFERRAL_INFO; KerbErr = KerbPackData( &ReferralData, KERB_PA_SERV_REFERRAL_PDU, (PULONG) &ListElem->value.preauth_data.length, &ListElem->value.preauth_data.value ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } *ReferralInfo = ListElem; ListElem = NULL; Cleanup: if (ListElem != NULL) { if (ListElem->value.preauth_data.value != NULL) { KdcFreeEncodedData(ListElem->value.preauth_data.value); } MIDL_user_free(ListElem); } KerbFreeRealm(&ReferralData.referred_server_realm); return(KerbErr); } //-------------------------------------------------------------------- // // Name: I_RenewTicket // // Synopsis: Renews an internal ticket. // // Arguments: SourceTicket - Source ticket for this request // ServiceName - Name of service for ticket // ClientRealm - Realm of client // ServiceTicketInfo - Ticket info from service account // RequestBody - Body of ticket request // NewTicket - Receives new ticket // CommonEType - Receives common encryption type for service ticket // TicketKey - Receives key used to encrypt the ticket // // Notes: Validates the ticket, gets the service's current key, // and builds the reply. // // //-------------------------------------------------------------------- KERBERR I_RenewTicket( IN PKERB_TICKET SourceTicket, IN PKERB_INTERNAL_NAME ServiceName, IN PKDC_TICKET_INFO ServiceTicketInfo, IN PKERB_KDC_REQUEST_BODY RequestBody, IN PULONG CommonEType, OUT PKERB_TICKET NewTicket, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET SourceEncryptPart = (PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value; PKERB_ENCRYPTED_TICKET NewEncryptPart = (PKERB_ENCRYPTED_TICKET) NewTicket->encrypted_part.cipher_text.value; PKERB_ENCRYPTION_KEY ServerKey; BOOLEAN NamesEqual = FALSE; TRACE(KDC, I_RenewTicket, DEB_FUNCTION); D_DebugLog(( DEB_TRACE, "Trying to renew a ticket to ")); D_KerbPrintKdcName(DEB_TRACE, ServiceName ); // // Make sure the original is renewable. // if ((KerbConvertFlagsToUlong(&SourceEncryptPart->flags) & KERB_TICKET_FLAGS_renewable) == 0) { D_DebugLog((DEB_WARN, "KLIN(%x) Attempt made to renew non-renewable ticket\n", KLIN(FILENO, __LINE__))); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } // // Make sure the source ticket service equals the service from the ticket info // KerbErr = KerbCompareKdcNameToPrincipalName( &SourceTicket->server_name, ServiceName, &NamesEqual ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } if (!NamesEqual) { // // Make sure we the renewed ticket is for the same service as the original. // FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } // // Find the common crypt system. // KerbErr = KerbFindCommonCryptSystem( RequestBody->encryption_type, ServiceTicketInfo->Passwords, NULL, CommonEType, &ServerKey ); if (!KERB_SUCCESS(KerbErr)) { KdcReportKeyError( &ServiceTicketInfo->AccountName, &ServiceTicketInfo->AccountName, KDCEVENT_NO_KEY_UNION_TGS, RequestBody->encryption_type, ServiceTicketInfo->Passwords ); goto Cleanup; } // // Build the renewal ticket // KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, FALSE, // not referral NULL, // not doing s4u NULL, // not doing s4u *CommonEType, NewTicket, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR, "KLIN(%x) Failed to build TGS ticket for renewal: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } // // BuildTicket puts a random session key in the ticket, // so replace it with the one from the source ticket. // KerbFreeKey( &NewEncryptPart->key ); KerbErr = KerbDuplicateKey( &NewEncryptPart->key, &SourceEncryptPart->key ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Insert the auth data into the new ticket. // // // BUG 455049: if the service password changes, this will cause problems // because we don't resign the pac. // KerbErr = KdcInsertAuthorizationData( NewEncryptPart, pExtendedError, (RequestBody->bit_mask & enc_authorization_data_present) ? &RequestBody->enc_authorization_data : NULL, NULL, FALSE, SourceEncryptPart, FALSE, // don't add local groups NULL, NULL, NULL ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to insert authorization data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } Cleanup: if (!KERB_SUCCESS(KerbErr)) { KdcFreeInternalTicket( NewTicket ); } return(KerbErr); } //-------------------------------------------------------------------- // // Name: I_Validate // // Synopsis: Validates a post-dated ticket so that it can be used. // This is not implemented. // // Arguments: pkitSourceTicket - (in) ticket to be validated // pkiaAuthenticator - // pService - (in) service ticket is for // pRealm - (in) realm service exists in // pktrRequest - (in) holds nonce for new ticket // pkdPAData - (in) // pkitTicket - (out) new ticket // // Notes: See 3.3 of the Kerberos V5 R5.2 spec // //-------------------------------------------------------------------- KERBERR I_Validate( IN PKERB_TICKET SourceTicket, IN PKERB_INTERNAL_NAME ServiceName, IN PUNICODE_STRING ClientRealm, IN PKERB_KDC_REQUEST_BODY RequestBody, OUT PULONG CommonEType, OUT PKERB_TICKET NewTicket, OUT PKERB_EXT_ERROR pExtendedError ) { return(KRB_ERR_GENERIC); #ifdef notdef TRACE(KDC, I_Validate, DEB_FUNCTION); HRESULT hr; D_DebugLog(( DEB_TRACE, "Trying to validate a ticket to '%ws' for '%ws'...\n", pkitSourceTicket->ServerName.accsid.pwszDisplayName, pkitSourceTicket->kitEncryptPart.Principal.accsid.pwszDisplayName )); PrintRequest( DEB_T_TICKETS, pktrRequest ); PrintTicket( DEB_T_TICKETS, "Ticket to validate:", pkitSourceTicket ); if ( (pkitSourceTicket->kitEncryptPart.fTicketFlags & (KERBFLAG_POSTDATED | KERBFLAG_INVALID)) != (KERBFLAG_POSTDATED | KERBFLAG_INVALID) ) { hr = KDC_E_BADOPTION; } else if (_wcsicmp(pkitSourceTicket->ServerName.accsid.pwszDisplayName, pasService->pwszDisplayName) != 0) { hr = KDC_E_BADOPTION; } else { TimeStamp tsNow, tsMinus, tsPlus; GetCurrentTimeStamp( &tsNow ); tsMinus = tsNow - SkewTime; tsPlus = tsNow + SkewTime; PrintTime(DEB_TRACE, "Current time: ", tsNow ); PrintTime(DEB_TRACE, "Past time: ", tsMinus ); PrintTime(DEB_TRACE, "Future time: ", tsPlus ); if (pkitSourceTicket->kitEncryptPart.tsStartTime > tsPlus ) hr = KRB_E_TKT_NYV; else if (pkitSourceTicket->kitEncryptPart.tsEndTime < tsMinus ) hr = KRB_E_TKT_EXPIRED; else { *pkitTicket = *pkitSourceTicket; pkitTicket->kitEncryptPart.fTicketFlags &= (~KERBFLAG_INVALID); hr = S_OK; } } return(hr); #endif // notdef } //-------------------------------------------------------------------- // // Name: I_GetTGSTicket // // Synopsis: Gets an internal ticket using a KDC ticket (TGT). // // Arguments: SourceTicket - TGT for the client // ServiceName - Service to get a ticket to // RequestBody - Body of KDC request message // ServiceTicketInfo - Ticket info for the service of the // source ticket // TicketEncryptionKey - If present, then this is a // enc_tkt_in_skey request and the PAC should be // encrypted with this key. // CommonEType - Receives common encrytion type // NewTicket - Receives newly created ticket // ReplyPaData - Contains any PA data to put in the reply // // Notes: See GetTGSTicket. // // //-------------------------------------------------------------------- KERBERR I_GetTGSTicket( IN PKERB_TICKET SourceTicket, IN PKERB_INTERNAL_NAME ServiceName, IN PUNICODE_STRING RequestRealm, IN PKERB_KDC_REQUEST_BODY RequestBody, IN PKDC_TICKET_INFO ServiceTicketInfo, IN OPTIONAL PKERB_ENCRYPTION_KEY TicketEncryptionKey, IN OPTIONAL PKERB_INTERNAL_NAME S4UClientName, IN OPTIONAL PUNICODE_STRING S4UClientRealm, OUT PULONG CommonEType, OUT PKERB_TICKET Ticket, OUT PKERB_PA_DATA_LIST * ReplyPaData, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; UNICODE_STRING LocalServiceName; UNICODE_STRING ServicePrincipal; UNICODE_STRING ServiceRealm = {0}; UNICODE_STRING ClientRealm = {0}; BOOLEAN Referral = FALSE; KERB_ENCRYPTED_TICKET EncryptedTicket = {0}; PKERB_ENCRYPTED_TICKET OutputEncryptedTicket = NULL; PKERB_ENCRYPTED_TICKET SourceEncryptPart = NULL; PKERB_INTERNAL_NAME TargetPrincipal = ServiceName; KERB_TICKET NewTicket = {0}; PKERB_ENCRYPTION_KEY ServerKey; PKERB_ENCRYPTION_KEY OldServerKey; KDC_TICKET_INFO OldServiceTicketInfo = {0}; ULONG NameFlags = 0; ULONG KdcOptions = 0; BOOLEAN GetS4UPac = FALSE; TRACE(KDC, I_GetTGSTicket, DEB_FUNCTION); // // Store away the encrypted ticket from the output ticket to // assign it at the end. // SourceEncryptPart = (PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value; OutputEncryptedTicket = (PKERB_ENCRYPTED_TICKET) Ticket->encrypted_part.cipher_text.value; NewTicket.encrypted_part.cipher_text.value = (PUCHAR) &EncryptedTicket; // // Copy the space for flags from the real destination. // EncryptedTicket.flags = OutputEncryptedTicket->flags; LocalServiceName.Buffer = NULL; D_DebugLog(( DEB_TRACE, "Trying to build a new ticket to ")); D_KerbPrintKdcName( DEB_TRACE, ServiceName ); KdcOptions = KerbConvertFlagsToUlong( &RequestBody->kdc_options ); if (KdcOptions & (KERB_KDC_OPTIONS_unused7 | KERB_KDC_OPTIONS_reserved | KERB_KDC_OPTIONS_unused9) ) { DebugLog(( DEB_ERROR,"KLIN(%x) Bad options in TGS request: 0x%x\n", KLIN(FILENO, __LINE__), KdcOptions )); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } // // Check if the client said to canonicalize the name // // if ((KdcOptions & KERB_KDC_OPTIONS_name_canonicalize) != 0) { NameFlags |= KDC_NAME_CHECK_GC; } // // Verify this account is allowed to issue tickets. // if ((ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && ((ServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0)) { D_DebugLog((DEB_ERROR,"Trying to make a TGS request with a ticket to %wZ\n", &ServiceTicketInfo->AccountName )); KerbErr = KRB_AP_ERR_NOT_US; goto Cleanup; } // // Copy the ticket info into the old structure. It will be replaced with // new info from Normalize. // OldServiceTicketInfo = *ServiceTicketInfo; RtlZeroMemory( ServiceTicketInfo, sizeof(KDC_TICKET_INFO) ); // // If the client name is in our realm, verify client // identity and build the PAC for the client. // if ( ARGUMENT_PRESENT(S4UClientRealm) && SecData.IsOurRealm(S4UClientRealm) ) { // Fester: DebugLog((DEB_ERROR, "Doing S4U client lookup!\n")); GetS4UPac = TRUE; } // // If we have to refer, Normalize will put the credentials of the target // realm in ServiceTicketInfo. Otherwise, it will be NULL. // KerbErr = KdcNormalize( TargetPrincipal, NULL, RequestRealm, NameFlags | KDC_NAME_SERVER | KDC_NAME_FOLLOW_REFERRALS, &Referral, &ServiceRealm, ServiceTicketInfo, pExtendedError, NULL, // no user handle 0L, // no fields to fetch 0L, // no extended fields NULL, // no user all information NULL // no group membership ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN,"KLIN(%x) Failed to normalize ", KLIN(FILENO, __LINE__))); KerbPrintKdcName(DEB_WARN,ServiceName); DebugLog((DEB_WARN,"\t 0x%x\n",KerbErr)); goto Cleanup; } if (ServiceTicketInfo != NULL) { if ((ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && (ServiceTicketInfo->UserAccountControl & USER_ACCOUNT_DISABLED) != 0) { KerbErr = KDC_ERR_CLIENT_REVOKED; D_DebugLog((DEB_WARN,"KLIN(%x) Failed to normalize, account is disabled ", KLIN(FILENO, __LINE__))); KerbPrintKdcName(DEB_WARN,ServiceName); D_DebugLog((DEB_WARN,"\t 0x%x\n",KerbErr)); FILL_EXT_ERROR(pExtendedError, STATUS_ACCOUNT_DISABLED, FILENO, __LINE__); goto Cleanup; } } // // If this isn't an interdomain trust account, go ahead and issue a normal // ticket. // if ((ServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0) { // // Find the common crypt system. // KerbErr = KerbFindCommonCryptSystem( RequestBody->encryption_type, ServiceTicketInfo->Passwords, NULL, CommonEType, &ServerKey ); if (!KERB_SUCCESS(KerbErr)) { KdcReportKeyError( &ServiceTicketInfo->AccountName, &ServiceTicketInfo->AccountName, KDCEVENT_NO_KEY_UNION_TGS, RequestBody->encryption_type, ServiceTicketInfo->Passwords ); goto Cleanup; } // // Check whether service is interactive, 'cause you can't // get a ticket to an interactive service. // KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, Referral, S4UClientName, S4UClientRealm, *CommonEType, &NewTicket, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Failed to build TGS ticket for %wZ : 0x%x\n", KLIN(FILENO, __LINE__), &LocalServiceName, KerbErr )); goto Cleanup; } } else { // // Need to build a referal ticket. // D_DebugLog(( DEB_T_KDC, "GetTGSTicket: referring to domain '%wZ'\n", &ServiceTicketInfo->AccountName )); // // Verify that if the trust is not transitive, the client is from // this realm. // SourceEncryptPart =(PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value; if (((ServiceTicketInfo->fTicketOpts & AUTH_REQ_TRANSITIVE_TRUST) == 0) && (ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT)) { if (!SecData.IsOurRealm(&SourceEncryptPart->client_realm)) { D_DebugLog((DEB_WARN,"Client from realm %s attempted to access non transitve trust to %wZ : illegal\n", SourceEncryptPart->client_realm, &ServiceTicketInfo->AccountName )); KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; } } // // Verify that the trust for the client is transitive as well, if it isn't // from this domain. This means that if the source ticket trust isn't // transitive, then this ticket can't be used to get further // tgt's, in any realm. // // e.g. the TGT from client comes from a domain w/ which we don't // have transitive trust. // if (((OldServiceTicketInfo.fTicketOpts & AUTH_REQ_TRANSITIVE_TRUST) == 0) && (OldServiceTicketInfo.UserId != DOMAIN_USER_RID_KRBTGT)) { KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; } // // This is probably not a common error, but could // indicate a configuration problem, so log an explicit // error. See bug 87879. // if (KerbErr == KDC_ERR_PATH_NOT_ACCEPTED) { KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &SourceEncryptPart->client_realm ); if (KERB_SUCCESS(KerbErr)) { ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_FAILED_TRANSITIVE_TRUST, 0, // no raw data NULL, // no raw data 2, // number of strings ClientRealm.Buffer, ServiceTicketInfo->AccountName.Buffer ); } KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; goto Cleanup; } // // Find the common crypt system. // KerbErr = KerbFindCommonCryptSystem( RequestBody->encryption_type, ServiceTicketInfo->Passwords, NULL, CommonEType, &ServerKey ); if (!KERB_SUCCESS(KerbErr)) { KdcReportKeyError( &ServiceTicketInfo->AccountName, &ServiceTicketInfo->AccountName, KDCEVENT_NO_KEY_UNION_TGS, RequestBody->encryption_type, ServiceTicketInfo->Passwords ); goto Cleanup; } KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, TRUE, NULL, // not doing s4u NULL, // not doing s4u *CommonEType, &NewTicket, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Failed to build TGS ticket for %wZ : 0x%x\n", KLIN(FILENO, __LINE__), &ServiceTicketInfo->AccountName, KerbErr )); goto Cleanup; } // // If this is a referral/canonicaliztion, return the target realm // if (Referral && ((KdcOptions & KERB_KDC_OPTIONS_name_canonicalize) != 0)) { D_DebugLog((DEB_TRACE,"Building referral info for realm %wZ\n", &ServiceRealm )); KerbErr = KdcBuildReferralInfo( &ServiceRealm, ReplyPaData ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } } OldServerKey = KerbGetKeyFromList( OldServiceTicketInfo.Passwords, SourceTicket->encrypted_part.encryption_type ); DsysAssert(OldServerKey != NULL); if (OldServerKey == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } // // Insert the auth data into the new ticket. // KerbErr = KdcInsertAuthorizationData( &EncryptedTicket, pExtendedError, (RequestBody->bit_mask & enc_authorization_data_present) ? &RequestBody->enc_authorization_data : NULL, S4UClientName, GetS4UPac, (PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value, ((ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && ((ServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0)), // only insert for non-referrals &OldServiceTicketInfo, OldServerKey, ARGUMENT_PRESENT(TicketEncryptionKey) ? TicketEncryptionKey : ServerKey ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to insert authorization data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } *Ticket = NewTicket; *OutputEncryptedTicket = EncryptedTicket; Ticket->encrypted_part.cipher_text.value = (PUCHAR) OutputEncryptedTicket; Cleanup: // // Now free the original service ticket info (which was for the KDC) so // we can get it for the real service // FreeTicketInfo( &OldServiceTicketInfo ); if (!KERB_SUCCESS(KerbErr)) { KdcFreeInternalTicket( &NewTicket ); } KerbFreeString( &ServiceRealm ); KerbFreeString( &ClientRealm ); return(KerbErr); } //+------------------------------------------------------------------------- // // Function: KerbUnpackAdditionalTickets // // Synopsis: Unpacks the AdditionalTickets field of a KDC request // and (a) verifies that the ticket is TGT for this realm // and (b) the ticket is encrypted with the corret key and // (c) the ticket is valid // // Effects: allocate output ticket // // Arguments: // // Requires: // // Returns: // // Notes: there can only be one additional ticket // // //-------------------------------------------------------------------------- KERBERR KdcUnpackAdditionalTickets( IN PKERB_TICKET_LIST TicketList, OUT PKERB_ENCRYPTED_TICKET * AdditionalTicket ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL; UNICODE_STRING ServerNames[3]; PKERB_ENCRYPTION_KEY EncryptionKey = NULL; PKERB_TICKET Ticket; KERB_REALM LocalRealm; KDC_TICKET_INFO KrbtgtTicketInfo = {0}; // // Verify that there is a ticket & that there is only one ticket // // // TBD: Make this work w/ S4U && U2U as there will be more than 1 ticket // at that point. Evaluate options by lowest id first... // S4UToSelf (12) // if ((TicketList == NULL) || (TicketList->next != NULL)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Trying to unpack null ticket or more than one ticket\n", KLIN(FILENO, __LINE__))); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } KerbErr = SecData.GetKrbtgtTicketInfo(&KrbtgtTicketInfo); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } Ticket = &TicketList->value; // // Verify the ticket, first with the normal password list // ServerNames[0] = *SecData.KdcFullServiceKdcName(); ServerNames[1] = *SecData.KdcFullServiceDnsName(); ServerNames[2] = *SecData.KdcFullServiceName(); EncryptionKey = KerbGetKeyFromList( KrbtgtTicketInfo.Passwords, Ticket->encrypted_part.encryption_type ); if (EncryptionKey == NULL) { KerbErr = KDC_ERR_ETYPE_NOTSUPP; goto Cleanup; } // // NOTE: we only allow additional tickets from our realm. This // means cross-realm TGTs can't be used as additional tickets. // KerbErr = KerbVerifyTicket( Ticket, 3, // 3 names ServerNames, SecData.KdcDnsRealmName(), EncryptionKey, &SkewTime, &EncryptedTicket ); // // if it failed due to wrong password, try again with older password // if ((KerbErr == KRB_AP_ERR_MODIFIED) && (KrbtgtTicketInfo.OldPasswords != NULL)) { EncryptionKey = KerbGetKeyFromList( KrbtgtTicketInfo.OldPasswords, Ticket->encrypted_part.encryption_type ); if (EncryptionKey == NULL) { KerbErr = KDC_ERR_ETYPE_NOTSUPP; goto Cleanup; } KerbErr = KerbVerifyTicket( &TicketList->value, 2, // 2 names ServerNames, SecData.KdcDnsRealmName(), EncryptionKey, &SkewTime, &EncryptedTicket ); } if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to verify additional ticket: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; } LocalRealm = SecData.KdcKerbDnsRealmName(); // // Verify the realm of the ticket // if (!KerbCompareRealmNames( &LocalRealm, &Ticket->realm )) { D_DebugLog((DEB_ERROR,"KLIN(%x) Additional ticket realm is wrong: %s instead of %s\n", KLIN(FILENO, __LINE__), Ticket->realm, LocalRealm)); KerbErr = KDC_ERR_POLICY; goto Cleanup; } // // Verify the realm of the client is the same as our realm // if (!KerbCompareRealmNames( &LocalRealm, &EncryptedTicket->client_realm )) { D_DebugLog((DEB_ERROR,"KLIN(%x) Additional ticket client realm is wrong: %s instead of %s\n", KLIN(FILENO, __LINE__),EncryptedTicket->client_realm, LocalRealm)); KerbErr = KDC_ERR_POLICY; goto Cleanup; } *AdditionalTicket = EncryptedTicket; EncryptedTicket = NULL; Cleanup: if (EncryptedTicket != NULL) { KerbFreeTicket(EncryptedTicket); } FreeTicketInfo(&KrbtgtTicketInfo); return(KerbErr); } //-------------------------------------------------------------------- // // Name: KdcFindS4UClientAndRealm // // Synopsis: Decodes PA DATA to find PA_DATA_FOR_USER entry. // // Effects: Get a client name and realm for processing S4U request // // Arguments: PAList - Preauth data list from TGS_REQ // ServerKey - Key in authenticator, used to sign PA_DATA. // ClientRealm - Target for client realm // ClientName - Principal to get S4U ticket for // // Requires: // // Returns: KDC_ERR_ or KRB_AP_ERR errors only // // Notes: Free client name and realm w/ // // //-------------------------------------------------------------------------- KERBERR KdcFindS4UClientAndRealm( IN PKERB_PA_DATA_LIST PaList, IN OUT PUNICODE_STRING ClientRealm, IN OUT PKERB_INTERNAL_NAME * ClientName ) { KERBERR Kerberr = KRB_ERR_GENERIC; PKERB_PA_DATA PaData = NULL; PKERB_PA_FOR_USER S4URequest = NULL; *ClientName = NULL; RtlInitUnicodeString( ClientRealm, NULL ); PaData = KerbFindPreAuthDataEntry( KRB5_PADATA_S4U, PaList ); if (NULL == PaData) { DebugLog((DEB_ERROR, "No S4U pa data \n")); Kerberr = KDC_ERR_BADOPTION; goto Cleanup; } Kerberr = KerbUnpackData( PaData->preauth_data.value, PaData->preauth_data.length, KERB_PA_FOR_USER_PDU, (PVOID* ) &S4URequest ); if (!KERB_SUCCESS(Kerberr)) { DebugLog((DEB_ERROR, "Failed to unpack PA_FOR_USER\n")); goto Cleanup; } Kerberr = KerbConvertRealmToUnicodeString( ClientRealm, &S4URequest->client_realm ); if (!KERB_SUCCESS(Kerberr)) { goto Cleanup; } Kerberr = KerbConvertPrincipalNameToKdcName( ClientName, &S4URequest->client_name ); if (!KERB_SUCCESS(Kerberr)) { goto Cleanup; } Cleanup: if (S4URequest != NULL) { KerbFreeData( KERB_PA_FOR_USER_PDU, S4URequest ); } return Kerberr; } //-------------------------------------------------------------------- // // Name: HandleTGSRequest // // Synopsis: Gets a ticket using a KDC ticket (TGT). // // Effects: Allocates and encrypts a KDC reply // // Arguments: ClientAddress - Optionally contains client IP address // RequestMessage - contains the TGS request message // RequestRealm - The realm of the request, from the request // message // OutputMessage - Contains the buffer to send back to the client // Requires: // // Returns: KDC_ERR_ or KRB_AP_ERR errors only // // Notes: // // //-------------------------------------------------------------------------- KERBERR HandleTGSRequest( IN OPTIONAL SOCKADDR * ClientAddress, IN PKERB_TGS_REQUEST RequestMessage, IN PUNICODE_STRING RequestRealm, OUT PKERB_MESSAGE_BUFFER OutputMessage, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; KDC_TICKET_INFO ServerTicketInfo = {0}; KDC_TICKET_INFO TgtTicketInfo = {0}; KERB_TICKET SourceTicket = {0}; KERB_TICKET NewTicket = {0}; KERB_ENCRYPTED_TICKET EncryptedTicket = {0}; PKERB_ENCRYPTED_TICKET SourceEncryptPart = NULL; PKERB_ENCRYPTED_TICKET AdditionalTicket = NULL; PKERB_KDC_REQUEST_BODY RequestBody = &RequestMessage->request_body; KERB_TGS_REPLY Reply = {0}; KERB_ENCRYPTED_KDC_REPLY ReplyBody = {0}; PKERB_AP_REQUEST UnmarshalledApRequest = NULL; PKERB_AUTHENTICATOR UnmarshalledAuthenticator = NULL; PKERB_PA_DATA ApRequest = NULL; PKERB_PA_DATA_LIST ReplyPaData = NULL; KERB_ENCRYPTION_KEY ReplyKey = {0}; PKERB_ENCRYPTION_KEY ServerKey; PKERB_INTERNAL_NAME ServerName = NULL; PKERB_INTERNAL_NAME ClientName = NULL; PKERB_INTERNAL_NAME S4UClientName = NULL; UNICODE_STRING ClientRealm = {0}; UNICODE_STRING ClientStringName = {0}; UNICODE_STRING ServerStringName = {0}; UNICODE_STRING S4UClientRealm = {0}; PUNICODE_STRING S4URealm = NULL; ULONG CommonEType; ULONG KdcOptions = 0; ULONG TicketFlags = 0; ULONG ReplyTicketFlags = 0; BOOLEAN Validating = FALSE; BOOLEAN UseSubKey = FALSE; BOOLEAN Renew = FALSE; BOOLEAN CheckAdditionalTicketMatch = FALSE; KDC_TGS_EVENT_INFO TGSEventTraceInfo = {0}; TRACE(KDC, HandleTGSRequest, DEB_FUNCTION); // // Initialize [out] structures, so if we terminate early, they can // be correctly marshalled by the stub // NewTicket.encrypted_part.cipher_text.value = (PUCHAR) &EncryptedTicket; EncryptedTicket.flags.value = (PUCHAR) &TicketFlags; EncryptedTicket.flags.length = sizeof(ULONG) * 8; ReplyBody.flags.value = (PUCHAR) &ReplyTicketFlags; ReplyBody.flags.length = sizeof(ULONG) * 8; KdcOptions = KerbConvertFlagsToUlong( &RequestBody->kdc_options ); // // Start event tracing // if (KdcEventTraceFlag){ TGSEventTraceInfo.EventTrace.Guid = KdcHandleTGSRequestGuid; TGSEventTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START; TGSEventTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID; TGSEventTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER) + sizeof (ULONG); TGSEventTraceInfo.KdcOptions = KdcOptions; TraceEvent( KdcTraceLoggerHandle, (PEVENT_TRACE_HEADER)&TGSEventTraceInfo ); } // // Check for additional tickets // if ((RequestBody->bit_mask & additional_tickets_present) != 0) { // // The ticket must be unpacked with the krbtgt key // KerbErr = KdcUnpackAdditionalTickets( RequestBody->additional_tickets, &AdditionalTicket ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Failed to unpack additional tickets: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; } } // // Make sure that if there is a ticket, then enc_tkt_in_skey is set and // if not, then it isn't set // // // TBD: S4UToProxy (E) Ok to have additional ticket if // CNAME-IN-ADDL-TKT set. // if ((((KdcOptions & KERB_KDC_OPTIONS_enc_tkt_in_skey) != 0) ^ (AdditionalTicket != NULL))) { // tbd: more logic for determining if multiple addl tickets are "bad option" D_DebugLog((DEB_ERROR,"KLIN(%x) Client didn't match enc_tkt_in_skey with additional tickts : %d vs %d\n", KLIN(FILENO, __LINE__),((KdcOptions & KERB_KDC_OPTIONS_enc_tkt_in_skey) != 0), (AdditionalTicket != NULL))); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } // // Pass data to next level, w/ flag // // TBD: S4UToProxy (D) // Forwardable flag must be set in the service ticket // Check our options, that the additional ticket has bit set. // This additional service ticket must have the fwdable flag set, // and must validate correctly. // // We'll need to crack the server key, and the ticket, as validation of // identity. // // // The server name is optional. // if ((RequestBody->bit_mask & KERB_KDC_REQUEST_BODY_server_name_present) != 0) { KerbErr = KerbConvertPrincipalNameToKdcName( &ServerName, &RequestBody->KERB_KDC_REQUEST_BODY_server_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Verify the at the server name is the same as the client name // from the addional ticket by getting the ticket info for supplied // TGT for the server. Later on we will compare it against the real // ticket info. // if (AdditionalTicket != NULL) { PKERB_INTERNAL_NAME TgtClientName = NULL; UNICODE_STRING TgtRealmName = {0}; UNICODE_STRING CrackedRealm = {0}; BOOLEAN Referral = FALSE; KerbErr = KerbConvertPrincipalNameToKdcName( &TgtClientName, &AdditionalTicket->client_name ); if (KERB_SUCCESS(KerbErr)) { KerbErr = KerbConvertRealmToUnicodeString( &TgtRealmName, &AdditionalTicket->client_realm ); if (KERB_SUCCESS(KerbErr)) { // TBD: make sure normalize takes into account // deleg restrictions, but not here, for god' sake. // . KerbErr = KdcNormalize( TgtClientName, &TgtRealmName, &TgtRealmName, KDC_NAME_CLIENT | KDC_NAME_INBOUND, &Referral, &CrackedRealm, &TgtTicketInfo, pExtendedError, NULL, // no user handle 0L, // no fields to fetch 0L, // no extended fields NULL, // no user all NULL // no group membership ); if (KERB_SUCCESS(KerbErr)) { KerbFreeString(&CrackedRealm); if (Referral) { KerbErr = KRB_AP_ERR_BADMATCH; } else { CheckAdditionalTicketMatch = FALSE; } } KerbFreeString(&TgtRealmName); } KerbFreeKdcName(&TgtClientName); } if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to normalize client name from supplied ticket\n", KLIN(FILENO, __LINE__))); goto Cleanup; } } } else { // // There must be an additional ticket. // if (AdditionalTicket == NULL) { KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; goto Cleanup; } KerbErr = KerbConvertPrincipalNameToKdcName( &ServerName, &AdditionalTicket->client_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } // // Convert the server name to a string for auditing. // KerbErr = KerbConvertKdcNameToString( &ServerStringName, ServerName, NULL ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } D_DebugLog((DEB_TRACE, "GetTGSTicket called. Service=" )); D_KerbPrintKdcName(DEB_TRACE, ServerName ); // // The TGS and authenticator are in an AP request in the pre-auth data. // Find it and decode the AP request now. // if ((RequestMessage->bit_mask & KERB_KDC_REQUEST_preauth_data_present) == 0) { D_DebugLog((DEB_ERROR, "KLIN(%x) No pre-auth data in TGS request - not allowed.\n", KLIN(FILENO, __LINE__))); KerbErr = KDC_ERR_PADATA_TYPE_NOSUPP; goto Cleanup; } // // Get the TGT from the PA data. // ApRequest = KerbFindPreAuthDataEntry( KRB5_PADATA_TGS_REQ, RequestMessage->KERB_KDC_REQUEST_preauth_data ); if (ApRequest == NULL) { D_DebugLog((DEB_ERROR,"KLIN(%x) No pre-auth data in TGS request - not allowed.\n", KLIN(FILENO, __LINE__))); FILL_EXT_ERROR(pExtendedError, STATUS_NO_PA_DATA, FILENO, __LINE__); KerbErr = KDC_ERR_PADATA_TYPE_NOSUPP; goto Cleanup; } // // Verify the request. This includes decoding the AP request, // finding the appropriate key to decrypt the ticket, and checking // the ticket. // KerbErr = KdcVerifyKdcRequest( ApRequest->preauth_data.value, ApRequest->preauth_data.length, ClientAddress, TRUE, // this is a kdc request &UnmarshalledApRequest, &UnmarshalledAuthenticator, &SourceEncryptPart, &ReplyKey, NULL, // no ticket key &ServerTicketInfo, &UseSubKey, pExtendedError ); // // If you want to validate a ticket, then it's OK if it isn't // currently valid. // if (KerbErr == KRB_AP_ERR_TKT_NYV && (KdcOptions & KERB_KDC_OPTIONS_validate)) { D_DebugLog((DEB_TRACE,"Validating a not-yet-valid ticket\n")); KerbErr = KDC_ERR_NONE; } else if (KerbErr == KRB_AP_ERR_MODIFIED) { // // Bug 276943: When the authenticator is encrypted with something other // than the session key, KRB_AP_ERR_BAD_INTEGRITY must be // returned per RFC 1510 // D_DebugLog((DEB_TRACE,"Could not decrypt the ticket\n")); KerbErr = KRB_AP_ERR_BAD_INTEGRITY; } // // Verify the checksum on the ticket, if present // if ( KERB_SUCCESS(KerbErr) && (UnmarshalledAuthenticator != NULL) && (UnmarshalledAuthenticator->bit_mask & checksum_present) != 0) { KerbErr = KdcVerifyTgsChecksum( &RequestMessage->request_body, &ReplyKey, &UnmarshalledAuthenticator->checksum ); } if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to verify TGS request: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); FILL_EXT_ERROR(pExtendedError, STATUS_KDC_INVALID_REQUEST, FILENO, __LINE__); goto Cleanup; } // // Now that we've validated the request, // Check to see if the cname in padata bit is set, and // we have the KDC option set. // if ((KdcOptions & KERB_KDC_OPTIONS_cname_in_pa_data) != 0) { KerbErr = KdcFindS4UClientAndRealm( RequestMessage->KERB_KDC_REQUEST_preauth_data, &S4UClientRealm, &S4UClientName ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcFindS4UClientAndRealm failed\n")); FILL_EXT_ERROR(pExtendedError, STATUS_NO_PA_DATA, FILENO, __LINE__); goto Cleanup; } S4URealm = &S4UClientRealm; } KerbErr = KerbConvertPrincipalNameToKdcName( &ClientName, &SourceEncryptPart->client_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } KerbErr = KerbConvertKdcNameToString( &ClientStringName, ClientName, NULL ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // And the realm // KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &SourceEncryptPart->client_realm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // If the client is in this domain and if we are supposed to // verify the client's account is still good, // do it now. // if ((SecData.KdcFlags() & AUTH_REQ_VALIDATE_CLIENT) != 0) { LARGE_INTEGER AuthTime; LARGE_INTEGER CurrentTime; NtQuerySystemTime(&CurrentTime); KerbConvertGeneralizedTimeToLargeInt( &AuthTime, &SourceEncryptPart->authtime, 0 ); // // Only check if we haven't checked recently // if ((CurrentTime.QuadPart > AuthTime.QuadPart) && ((CurrentTime.QuadPart - AuthTime.QuadPart) > SecData.KdcRestrictionLifetime().QuadPart)) { KerbErr = KdcCheckTgsLogonRestrictions( ClientName, &ClientRealm, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_WARN, "KLIN(%x) Client failed TGS logon restrictions: 0x%x : ", KLIN(FILENO, __LINE__),KerbErr)); KerbPrintKdcName(DEB_WARN, ClientName); goto Cleanup; } } } // // Build a ticket struture to pass to the worker functions // SourceTicket = UnmarshalledApRequest->ticket; SourceTicket.encrypted_part.cipher_text.value = (PUCHAR) SourceEncryptPart; // // Build the new ticket // D_DebugLog((DEB_TRACE, "Handle TGS request: Client = %wZ,\n ",&ClientRealm)); D_KerbPrintKdcName(DEB_TRACE, ClientName); D_DebugLog((DEB_TRACE, "\t ServerName = \n")); D_KerbPrintKdcName(DEB_TRACE, ServerName); // // Pass off the work to the worker routines // if (KdcOptions & KERB_KDC_OPTIONS_renew) { D_DebugLog((DEB_T_KDC,"Renewing ticket ticket\n")); Renew = TRUE; KerbErr = I_RenewTicket( &SourceTicket, ServerName, &ServerTicketInfo, RequestBody, &CommonEType, &NewTicket, pExtendedError ); } else if (KdcOptions & KERB_KDC_OPTIONS_validate) { D_DebugLog((DEB_T_KDC,"Validating ticket\n")); KerbErr = I_Validate( &SourceTicket, ServerName, &ClientRealm, RequestBody, &CommonEType, &NewTicket, pExtendedError ); Validating = TRUE; } else { D_DebugLog((DEB_T_KDC,"Getting TGS ticket\n")); KerbErr = I_GetTGSTicket( &SourceTicket, ServerName, RequestRealm, RequestBody, &ServerTicketInfo, AdditionalTicket != NULL ? &AdditionalTicket->key : NULL, S4UClientName, S4URealm, &CommonEType, &NewTicket, &ReplyPaData, pExtendedError ); } if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN,"KLIN(%x) TGS ticket worker failed: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; } DsysAssert(ServerTicketInfo.Passwords != NULL); // // Check to see if the additional ticket supplied is the one for this // server, if necessary // if (CheckAdditionalTicketMatch ) { if (ServerTicketInfo.UserId != TgtTicketInfo.UserId) { D_DebugLog((DEB_ERROR,"KLIN(%x) Supplied ticket is not for server: %wZ vs. %wZ\n", KLIN(FILENO, __LINE__),&TgtTicketInfo.AccountName, &ServerTicketInfo.AccountName )); KerbErr = KRB_AP_ERR_BADMATCH; goto Cleanup; } } // // Determine the keys to encrypt the ticket with. (The key to encrypt the // reply with was determined by CheckTicket.) if ((KdcOptions & KERB_KDC_OPTIONS_enc_tkt_in_skey) && (AdditionalTicket != NULL)) { // // Use the session key from the tgt // ServerKey = &AdditionalTicket->key; } else { ServerKey = KerbGetKeyFromList( ServerTicketInfo.Passwords, CommonEType ); DsysAssert(ServerKey != NULL); if (ServerKey == NULL) { D_DebugLog((DEB_ERROR, "KLIN(%x) BADERROR: cannot find server key while validating\n", KLIN(FILENO, __LINE__))); KerbErr = KDC_ERR_ETYPE_NOTSUPP; goto Cleanup; } } KerbErr = BuildReply( NULL, RequestBody->nonce, &NewTicket.server_name, NewTicket.realm, ((EncryptedTicket.bit_mask & KERB_ENCRYPTED_TICKET_client_addresses_present) != 0) ? EncryptedTicket.KERB_ENCRYPTED_TICKET_client_addresses : NULL, &NewTicket, &ReplyBody ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Put in any PA data for the reply // if (ReplyPaData != NULL) { ReplyBody.encrypted_pa_data = (struct KERB_ENCRYPTED_KDC_REPLY_encrypted_pa_data_s *) ReplyPaData; ReplyBody.bit_mask |= encrypted_pa_data_present; } // // Now build the real reply and return it. // Reply.version = KERBEROS_VERSION; Reply.message_type = KRB_TGS_REP; Reply.KERB_KDC_REPLY_preauth_data = NULL; Reply.bit_mask = 0; Reply.client_realm = SourceEncryptPart->client_realm; Reply.client_name = SourceEncryptPart->client_name; // // Copy in the ticket // KerbErr = KerbPackTicket( &NewTicket, ServerKey, CommonEType, &Reply.ticket ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x) Failed to pack ticket: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; } // // Copy in the encrypted part // KerbErr = KerbPackKdcReplyBody( &ReplyBody, &ReplyKey, ReplyKey.keytype, KERB_ENCRYPTED_TGS_REPLY_PDU, &Reply.encrypted_part ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR,"KLIN(%x)Failed to pack KDC reply body: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; } // // Now build the real reply message // KerbErr = KerbPackData( &Reply, KERB_TGS_REPLY_PDU, &OutputMessage->BufferSize, &OutputMessage->Buffer ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } // // Audit the successful ticket generation // if (SecData.AuditKdcEvent(KDC_AUDIT_TGS_SUCCESS)) { BYTE ServerSid[MAX_SID_LEN]; GUID LogonGuid; NTSTATUS Status = STATUS_SUCCESS; PKERB_TIME pStartTime; pStartTime = &(((PKERB_ENCRYPTED_TICKET) NewTicket.encrypted_part.cipher_text.value)->KERB_ENCRYPTED_TICKET_starttime); Status = LsaIGetLogonGuid( &ClientStringName, &ClientRealm, (PBYTE) pStartTime, sizeof(KERB_TIME), &LogonGuid ); ASSERT(NT_SUCCESS( Status )); KdcMakeAccountSid(ServerSid, ServerTicketInfo.UserId); KdcLsaIAuditKdcEvent( Renew ? SE_AUDITID_TICKET_RENEW_SUCCESS : SE_AUDITID_TGS_TICKET_REQUEST, &ClientStringName, &ClientRealm, NULL, // no client SID &ServerTicketInfo.AccountName, ServerSid, (PULONG) &KdcOptions, NULL, // success &CommonEType, NULL, // no preauth type GET_CLIENT_ADDRESS(ClientAddress), &LogonGuid ); } Cleanup: // // Complete the event // if (KdcEventTraceFlag){ //These variables point to either a unicode string struct containing //the corresponding string or a pointer to KdcNullString PUNICODE_STRING pStringToCopy; WCHAR UnicodeNullChar = 0; UNICODE_STRING UnicodeEmptyString = {sizeof(WCHAR),sizeof(WCHAR),&UnicodeNullChar}; TGSEventTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END; TGSEventTraceInfo.EventTrace.Flags = WNODE_FLAG_USE_MOF_PTR | WNODE_FLAG_TRACED_GUID; // Always output error code. KdcOptions was captured on the start event TGSEventTraceInfo.eventInfo[0].DataPtr = (ULONGLONG) &KerbErr; TGSEventTraceInfo.eventInfo[0].Length = sizeof(ULONG); TGSEventTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER) + sizeof(MOF_FIELD); // Build counted MOF strings from the unicode strings. // If data is unavailable then output a NULL string if (ClientStringName.Buffer != NULL && ClientStringName.Length > 0) { pStringToCopy = &ClientStringName; } else { pStringToCopy = &UnicodeEmptyString; } TGSEventTraceInfo.eventInfo[1].DataPtr = (ULONGLONG) &pStringToCopy->Length; TGSEventTraceInfo.eventInfo[1].Length = sizeof(pStringToCopy->Length); TGSEventTraceInfo.eventInfo[2].DataPtr = (ULONGLONG) pStringToCopy->Buffer; TGSEventTraceInfo.eventInfo[2].Length = pStringToCopy->Length; TGSEventTraceInfo.EventTrace.Size += sizeof(MOF_FIELD)*2; if (ServerStringName.Buffer != NULL && ServerStringName.Length > 0) { pStringToCopy = &ServerStringName; } else { pStringToCopy = &UnicodeEmptyString; } TGSEventTraceInfo.eventInfo[3].DataPtr = (ULONGLONG) &pStringToCopy->Length; TGSEventTraceInfo.eventInfo[3].Length = sizeof(pStringToCopy->Length); TGSEventTraceInfo.eventInfo[4].DataPtr = (ULONGLONG) pStringToCopy->Buffer; TGSEventTraceInfo.eventInfo[4].Length = pStringToCopy->Length; TGSEventTraceInfo.EventTrace.Size += sizeof(MOF_FIELD)*2; if (ClientRealm.Buffer != NULL && ClientRealm.Length > 0) { pStringToCopy = &ClientRealm; } else { pStringToCopy = &UnicodeEmptyString; } TGSEventTraceInfo.eventInfo[5].DataPtr = (ULONGLONG) &pStringToCopy->Length; TGSEventTraceInfo.eventInfo[5].Length = sizeof(pStringToCopy->Length); TGSEventTraceInfo.eventInfo[6].DataPtr = (ULONGLONG) pStringToCopy->Buffer; TGSEventTraceInfo.eventInfo[6].Length = pStringToCopy->Length; TGSEventTraceInfo.EventTrace.Size += sizeof(MOF_FIELD)*2; TraceEvent( KdcTraceLoggerHandle, (PEVENT_TRACE_HEADER)&TGSEventTraceInfo ); } // // Audit *most* failures (see bug 37126) // if (!KERB_SUCCESS(KerbErr) && SecData.AuditKdcEvent(KDC_AUDIT_TGS_FAILURE)) { // it is not uncommon to hit this error when // clients attempt to get a ticket outside of their // realm... if (KerbErr != KDC_ERR_S_PRINCIPAL_UNKNOWN && SecData.IsOurRealm(RequestRealm)) { KdcLsaIAuditKdcEvent( SE_AUDITID_TGS_TICKET_REQUEST, (ClientStringName.Buffer != NULL) ? &ClientStringName : &KdcNullString, &ClientRealm, // no domain name NULL, &ServerStringName, NULL, &KdcOptions, (PULONG) &KerbErr, NULL, // no etype NULL, // no preauth type GET_CLIENT_ADDRESS(ClientAddress), NULL // no logon guid ); } } KerbFreeKdcName( &ClientName ); KerbFreeString( &ClientRealm ); KerbFreeKdcName( &S4UClientName ); KerbFreeString( &S4UClientRealm ); KerbFreeKdcName( &ServerName ); KerbFreeKey( &ReplyKey ); KdcFreeKdcReplyBody( &ReplyBody ); KerbFreeString( &ClientStringName ); KerbFreeString( &ServerStringName ); // // If we are validating the ticket key is in the serverticketinfo // if (AdditionalTicket != NULL) { KerbFreeTicket(AdditionalTicket); } if (ReplyPaData != NULL) { KerbFreePreAuthData(ReplyPaData); } KerbFreeApRequest(UnmarshalledApRequest); KerbFreeAuthenticator(UnmarshalledAuthenticator); KerbFreeTicket(SourceEncryptPart); KdcFreeInternalTicket(&NewTicket); FreeTicketInfo(&ServerTicketInfo); FreeTicketInfo(&TgtTicketInfo); KdcFreeKdcReply( &Reply ); return(KerbErr); }