|
|
//+---------------------------------------------------------------------------
//
// 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 <tostring.hxx>
#include <userall.h>
#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); }
|