|
|
//+---------------------------------------------------------------------------
//
// 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 <userall.h>
#include <ntdef.h>
extern "C" { #include <md5.h>
}
#include "fileno.h"
#define FILENO FILENO_GETTGS
extern LARGE_INTEGER tsInfinity; extern LONG lInfinity;
UNICODE_STRING KdcNullString = {0,0,NULL};
GUID GUID_A_TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL = {0x46a9b11d,0x60ae,0x405a,0xb7,0xe8,0xff,0x8a,0x58,0xd4,0x56,0xd2}; GUID GUID_A_SECURED_FOR_CROSS_ORGANIZATION = {0x68B1D179,0x0D15,0x4d4f,0xAB,0x71,0x46,0x15,0x2E,0x79,0xA7,0xBC};
//
// If defined, all trusts, not just MIT, will be namespace filtered
//
#define NAMESPACE_FILTER_EVERY_TRUST
//--------------------------------------------------------------------
//
// Name: KdcCheckGroupExpansionAccess
//
// Synopsis: Validate that S4U caller has access to expand groups
//
// Effects: Use Authz to check client context.
//
// Arguments: S4UClientName - ClientName from S4U PA Data
// TgtAccountInfo - Information from the inbound tgt account,
// derived from KdcVerfiyKdcRequest.
// SD - Security descriptor from the user object.
//
// Requires:
//
// Returns: KDC_ERR_ or KRB_AP_ERR errors only
//
// Notes: Free client name and realm w/
//
//
//---
GUID GUID_PS_REMOTE_ACCESS_INFO = {0x037088f8,0x0ae1,0x11d2,0xb4,0x22,0x00,0xa0,0xc9,0x68,0xf9,0x39}; GUID GUID_C_COMPUTER = {0xbf967a86,0x0de6,0x11d0,0xa2,0x85,0x00,0xaa,0x00,0x30,0x49,0xe2}; GUID GUID_C_USER = {0xbf967aba,0x0de6,0x11d0,0xa2,0x85,0x00,0xaa,0x00,0x30,0x49,0xe2};
NTSTATUS KdcCheckGroupExpansionAccess( IN PKDC_S4U_TICKET_INFO S4UTicketInfo, IN PKDC_TICKET_INFO TgtAccountInfo, IN PUSER_INTERNAL6_INFORMATION UserInfo ) { NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr;
LUID ZeroLuid = {0,0}; DWORD AccessMask[3]; DWORD Error[3]; AUTHZ_CLIENT_CONTEXT_HANDLE hClientContext = NULL; AUTHZ_ACCESS_REQUEST Request = {0}; AUTHZ_ACCESS_REPLY Reply = {0}; KDC_AUTHZ_INFO AuthzInfo = {0}; KDC_AUTHZ_GROUP_BUFFERS InfoToFree = {0}; ULONG i = 0; OBJECT_TYPE_LIST TypeList[3] = {0}; BOOLEAN ComputerAccount = (( UserInfo->I1.UserAccountControl & ( USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT )) != 0);
PSECURITY_DESCRIPTOR Sd = UserInfo->I1.SecurityDescriptor.SecurityDescriptor; //
// Extract the netlogon validation info from
// the TGT used in the S4UToSelf request.
//
KerbErr = KdcGetSidsFromTgt( S4UTicketInfo->EvidenceTicket, &S4UTicketInfo->EvidenceTicketKey, 0, // no etype needed if we've got the evidence ticket key.
TgtAccountInfo, &AuthzInfo, &InfoToFree, &Status );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
if (!AuthzInitializeContextFromSid( AUTHZ_SKIP_TOKEN_GROUPS, AuthzInfo.SidAndAttributes[0].Sid, // userid is first element in array
KdcAuthzRM, NULL, ZeroLuid, &AuthzInfo, &hClientContext )) { DebugLog((DEB_ERROR, "AuthzInitializeContextFromSid() failed %x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
//
// Do access check.
//
TypeList[0].Level = ACCESS_OBJECT_GUID; TypeList[0].ObjectType = (ComputerAccount ? &GUID_C_COMPUTER : &GUID_C_USER ); TypeList[0].Sbz = 0;
TypeList[1].Level = ACCESS_PROPERTY_SET_GUID; TypeList[1].ObjectType = &GUID_PS_REMOTE_ACCESS_INFO; TypeList[1].Sbz = 0;
TypeList[2].Level = ACCESS_PROPERTY_GUID; TypeList[2].ObjectType = &GUID_A_TOKEN_GROUPS_GLOBAL_AND_UNIVERSAL; TypeList[2].Sbz = 0;
Request.DesiredAccess = ACTRL_DS_READ_PROP; Request.ObjectTypeList = TypeList; Request.ObjectTypeListLength = 3; Request.OptionalArguments = NULL; Request.PrincipalSelfSid = NULL;
Reply.ResultListLength = 3; Reply.GrantedAccessMask = AccessMask; Reply.Error = Error; if (!AuthzAccessCheck( 0, hClientContext, &Request, NULL, // TBD: add audit
Sd, NULL, NULL, &Reply, NULL // don't cache result? Check to see if optimal.
)) { Error[0] = GetLastError(); DebugLog((DEB_ERROR, "AuthzAccessCheck() failed %x\n", Error[0])); Status = STATUS_ACCESS_DENIED; goto Cleanup; }
for ( i = 0; i < Reply.ResultListLength; i++) { if ( Error[i] != ERROR_SUCCESS ) { DebugLog((DEB_ERROR, "GroupExpansion AuthZAC failed %x, lvl %x",Error[i],i)); Status = STATUS_ACCESS_DENIED; goto Cleanup; } }
Status = STATUS_SUCCESS;
Cleanup:
if ( Status == STATUS_ACCESS_DENIED ) { // catch unknown errors here.
DsysAssert(Error[i] == ERROR_ACCESS_DENIED);
KdcReportS4UGroupExpansionError( UserInfo, S4UTicketInfo, Error[i] // use whatever status was returned to us that caused failure.
); } if ( hClientContext != NULL ) { AuthzFreeContext(hClientContext); }
KdcFreeAuthzInfo( &InfoToFree );
return Status; }
//--------------------------------------------------------------------
//
// Name: KdcGetS4UTicketInfo
//
// Synopsis: Track down the user acct for PAC info.
//
// Effects: Get the PAC
//
// Arguments: S4UTicketInfo - Information used in processing S4U
// TgtAccountInfo - Info on TGT used in request.
// UserInfo - Internal 6 info for S4USelf client
// GroupMembership - Group membership of S4USelf client
//
// Requires:
//
// Returns: KDC_ERR_ or KRB_AP_ERR errors only
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcGetS4UTicketInfo( IN PKDC_S4U_TICKET_INFO S4UTicketInfo, IN PKDC_TICKET_INFO TgtAccountInfo, IN OUT PUSER_INTERNAL6_INFORMATION * UserInfo, IN OUT PSID_AND_ATTRIBUTES_LIST GroupMembership, IN OUT PKERB_EXT_ERROR ExtendedError ) { KERBERR KerbErr; UNICODE_STRING ReferralRealm = {0}; BOOLEAN Referral = FALSE; NTSTATUS Status = STATUS_SUCCESS; LARGE_INTEGER LogoffTime;
*UserInfo = NULL; RtlZeroMemory( GroupMembership, sizeof(SID_AND_ATTRIBUTES_LIST) );
PUSER_INTERNAL6_INFORMATION LocalUserInfo = NULL; KDC_TICKET_INFO LocalTicketInfo = {0}; SID_AND_ATTRIBUTES_LIST LocalGroupMembership ={0};
KerbErr = KdcNormalize( S4UTicketInfo->PACCName, NULL, NULL, NULL, KDC_NAME_CLIENT | KDC_NAME_S4U_CLIENT | KDC_NAME_FOLLOW_REFERRALS | KDC_NAME_CHECK_GC, FALSE, // do not restrict user accounts (user2user)
&Referral, &ReferralRealm, &LocalTicketInfo, ExtendedError, NULL, USER_ALL_KDC_GET_PAC_AUTH_DATA | USER_ALL_SECURITYDESCRIPTOR, 0L, &LocalUserInfo, &LocalGroupMembership );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcGetS4UTicketInfo normalize in KdcGetS4uTicketInfo failed %x\n", KerbErr)); goto cleanup; } else if (Referral) { DebugLog((DEB_ERROR, "KdcGetS4UTicketInfo normalize returned referral for S4U client\n")); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto cleanup; }
//
// Some account restrictions apply to S4u
// 1. Disabled accounts.
// 2. Expired accounts.
//
KerbErr = KerbCheckLogonRestrictions( NULL, // no user handle, since we're not doing sam check.
NULL, // No client address is available
&LocalUserInfo->I1, KDC_RESTRICT_S4U_CHECKS, // Don't bother checking for password expiration, wkstation , account hours
&LogoffTime, &Status );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "S4USelf client restricted %x\n", Status)); FILL_EXT_ERROR_EX2( ExtendedError, Status, FILENO, __LINE__); goto cleanup; }
//
// Make sure S4U to self caller has access rights to user object.
//
Status = KdcCheckGroupExpansionAccess( S4UTicketInfo, TgtAccountInfo, LocalUserInfo );
if (!NT_SUCCESS( Status )) { DebugLog((DEB_ERROR, "Failed Authz check \n")); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto cleanup; }
if (( LocalTicketInfo.fTicketOpts & AUTH_REQ_ALLOW_FORWARDABLE ) == 0) { S4UTicketInfo->Flags |= TI_SENSITIVE_CLIENT_ACCOUNT; }
*UserInfo = LocalUserInfo; LocalUserInfo = NULL;
*GroupMembership = LocalGroupMembership; RtlZeroMemory( &LocalGroupMembership, sizeof(SID_AND_ATTRIBUTES_LIST) );
cleanup:
KerbFreeString(&ReferralRealm); FreeTicketInfo(&LocalTicketInfo); SamIFreeSidAndAttributesList(&LocalGroupMembership); if (LocalUserInfo != NULL) { SamIFree_UserInternal6Information( LocalUserInfo ); }
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; UCHAR ClientSidBuffer[256]; // rtl functions also use hard-coded 256
PSID ClientSid = (PSID) ClientSidBuffer;
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 ))) { if ( ARGUMENT_PRESENT( MappedTicketInfo )) { KdcMakeAccountSid(ClientSid, MappedTicketInfo->UserId); } else { ClientSid = NULL; } LsaIAuditAccountLogonEx( SE_AUDITID_ACCOUNT_MAPPED, Successful, &GlobalKdcName, &ClientString, MappedName, 0, // no status
ClientSid );
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 KdcInsertInitialS4UAuthorizationData( OUT PKERB_ENCRYPTED_TICKET FinalTicket, OUT PKERB_EXT_ERROR pExtendedError, IN PKDC_S4U_TICKET_INFO S4UTicketInfo, IN PUSER_INTERNAL6_INFORMATION S4UClientInternalInfo, IN PSID_AND_ATTRIBUTES_LIST S4UGroupMembership, IN BOOLEAN AddResourceGroups, IN PKERB_ENCRYPTION_KEY TargetServerKey ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_AUTHORIZATION_DATA FinalAuthData = NULL;
TRACE(KDC, InsertAuthorizationData, DEB_FUNCTION);
D_DebugLog(( DEB_T_TICKETS, "Inserting S4U authorization data into ticket.\n" ));
//
// Use the PAC from the S4U data to return in the TGT / Service Ticket
//
KerbErr = KdcGetPacAuthData( S4UClientInternalInfo, S4UGroupMembership, TargetServerKey, NULL, // no credential key
AddResourceGroups, FinalTicket, S4UTicketInfo, &FinalAuthData, pExtendedError );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Failed to get S4UPacAUthData\n")); 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);
return(KerbErr); }
//----------------------------------------------------------------
//
// 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 PKERB_ENCRYPTED_TICKET SourceTicket, IN OPTIONAL PKDC_TICKET_INFO TargetServiceTicketInfo, IN OPTIONAL PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OPTIONAL PKDC_TICKET_INFO OriginalServerInfo, IN OPTIONAL PKERB_ENCRYPTION_KEY OriginalServerKey, IN OPTIONAL PKERB_ENCRYPTION_KEY TargetServerKey, IN OPTIONAL PKERB_ENCRYPTION_KEY Subkey, OUT OPTIONAL PS4U_DELEGATION_INFO* S4UDelegationInfo ) { 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_INTERNAL_NAME ClientName = NULL; PKERB_IF_RELEVANT_AUTH_DATA * IfRelevantData = NULL; PKERB_AUTHORIZATION_DATA SuppliedAuthData = NULL; UNICODE_STRING DummyName = {0}; NTSTATUS LogonStatus = STATUS_SUCCESS; TimeStamp LogoffTime; SAMPR_HANDLE UserHandle = NULL; BOOLEAN AddResourceGroups = FALSE;
PKERB_ENCRYPTED_TICKET EvidenceTicket = NULL;
TRACE(KDC, InsertAuthorizationData, DEB_FUNCTION);
if (ARGUMENT_PRESENT(TargetServiceTicketInfo)) { //
// Only add resource groups for non-referals.
//
AddResourceGroups = ((TargetServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && ((TargetServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0));
}
if (( ARGUMENT_PRESENT(S4UTicketInfo) ) && (( S4UTicketInfo->Flags & TI_S4UPROXY_INFO ) != 0)) { D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData EvidenceTicket is S4UTicketInfo\n")); D_DebugLog((DEB_T_PAC, "S4UTicketInfo (%p)\n", S4UTicketInfo)); D_DebugLog((DEB_T_PAC, "PAC CName ")); D_KerbPrintKdcName((DEB_T_PAC, S4UTicketInfo->PACCName)); D_DebugLog((DEB_T_PAC, "PAC CRealm %wZ\n", &S4UTicketInfo->PACCRealm)); D_DebugLog((DEB_T_PAC, "Requestor : ")); D_KerbPrintKdcName((DEB_T_PAC, S4UTicketInfo->RequestorServiceName )); D_DebugLog((DEB_T_PAC, "Realm %wZ\n", &S4UTicketInfo->RequestorServiceRealm ));
//
// 2 choices here - either we're grabbing the PAC from the accompanying
// evidence ticket (if the S4Uprxy request is coming from our realm), or
// we'll grab it out of the xrealm tgt.
//
// If we change the xrealm behavior, we'll need to modify this.
//
if ( S4UTicketInfo->Flags & TI_PRXY_REQUESTOR_THIS_REALM ) { EvidenceTicket = S4UTicketInfo->EvidenceTicket; DebugLog((DEB_T_PAC, "Using evidence ticket PAC\n")); } else { EvidenceTicket = SourceTicket; DebugLog((DEB_T_PAC, "Using xrealm Tgt PAC\n")); } } else // traditional TGS
{ D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData EvidenceTicket is SourceTicket (TGT in TGS AP request)\n"));
EvidenceTicket = SourceTicket; }
//
// First try to decrypt the supplied authorization data
//
if (ARGUMENT_PRESENT(EncryptedAuthData)) { //
// The enc_authorization_data must be decrypted with the sub session
// key from the authenticator if it is present (per the RFC)
//
PKERB_ENCRYPTION_KEY EncryptionKey; ULONG SaltType = KERB_NON_KERB_SALT;
if ( ARGUMENT_PRESENT( Subkey )) { EncryptionKey = Subkey; SaltType = KERB_TGS_REQ_SUBKEY_SALT; D_DebugLog((DEB_TRACE, "KdcInsertAuthorizationData: using SUBsession key & salt\n"));
} else {
EncryptionKey = &EvidenceTicket->key; SaltType = KERB_TGS_REQ_SESSKEY_SALT; D_DebugLog((DEB_TRACE, "KdcInsertAuthorizationData: using Session key & salt\n")); }
KerbErr = KerbDecryptDataEx( EncryptedAuthData, EncryptionKey, SaltType, // was KERB_NON_KERB_SALT
&EncryptedAuthData->cipher_text.length, EncryptedAuthData->cipher_text.value );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "KdcInsertAuthorizationData 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)) { DebugLog((DEB_ERROR, "KdcInsertAuthorizationData KLIN(%x) Failed to unpack data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
if (TempAuthData != NULL) { SuppliedAuthData = *TempAuthData; }
D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData using supplied authorization data\n")); }
if (EvidenceTicket->bit_mask & KERB_ENCRYPTED_TICKET_authorization_data_present) { DsysAssert(EvidenceTicket->KERB_ENCRYPTED_TICKET_authorization_data != NULL); SourceAuthData = EvidenceTicket->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; }
D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData extracted PAC from evidence ticket\n")); }
#ifdef NAMESPACE_FILTER_EVERY_TRUST // Enable once we decide that namespace filtering should be performed over all trusts
//
// Namespace filtering can not be done for S4U2self requests since the fundamental
// premise of namespace filtering is not satisfied there by design
//
if ( !S4UTicketInfo || !(S4UTicketInfo->Flags & TI_S4USELF_INFO)) { //
// Verify that the namespace presented via crealm is valid across this trust link
//
KerbErr = KdcFilterNamespace( OriginalServerInfo, SourceTicket->client_realm, pExtendedError );
if ( !KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "Failed filtering namespaces\n")); goto Cleanup; } }
#endif
//
// 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)) { D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData OriginalServerInfo %wZ\\%wZ\n", &OriginalServerInfo->TrustedForest, &OriginalServerInfo->AccountName));
KerbErr = KdcVerifyAndResignPac( OriginalServerKey, TargetServerKey, OriginalServerInfo, TargetServiceTicketInfo, S4UTicketInfo, FinalTicket, AddResourceGroups, pExtendedError, PacAuthData, S4UDelegationInfo );
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 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;
D_DebugLog((DEB_T_PAC, "KdcInsertAuthorizationData getting new PAC\n"));
KerbErr = KerbConvertPrincipalNameToKdcName( &ClientName, &EvidenceTicket->client_name );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KdcInsertAuthorizationData KLIN(%x) Convertname to kdc name failed: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
//
// S4UProxy
// Our evidence ticket *must* have auth data.
//
if (( ARGUMENT_PRESENT( S4UTicketInfo )) && (( S4UTicketInfo->Flags & TI_S4UPROXY_INFO ) != 0)) { DebugLog((DEB_ERROR, "Trying S4UProxy w/ no PAC\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
#ifndef NAMESPACE_FILTER_EVERY_TRUST
//
// This will perform an AltSecId mapping so first we must verify that
// the namespace presented via crealm is valid across this trust link
//
KerbErr = KdcFilterNamespace( OriginalServerInfo, SourceTicket->client_realm, pExtendedError );
if ( !KERB_SUCCESS( KerbErr )) { // TODO: add logging/auditing
goto Cleanup; }
#endif
KerbErr = KdcGetTicketInfo( &DummyName, SAM_OPEN_BY_ALTERNATE_ID, FALSE, // do not restrict user accounts (user2user)
ClientName, &SourceTicket->client_realm, &ClientTicketInfo, pExtendedError, &UserHandle, // no handle
USER_ALL_KDC_GET_PAC_AUTH_DATA | USER_ALL_KERB_CHECK_LOGON_RESTRICTIONS, 0L, // no extended fields
&UserInfo, &GroupMembership );
if (KERB_SUCCESS(KerbErr)) { KdcAuditAccountMapping( ClientName, SourceTicket->client_realm, &ClientTicketInfo );
//
// Check for any interesting account restrictions.
//
KerbErr = KerbCheckLogonRestrictions( UserHandle, NULL, // No client address is available
&UserInfo->I1, KDC_RESTRICT_PKINIT_USED | KDC_RESTRICT_IGNORE_PW_EXPIRATION, &LogoffTime, &LogonStatus );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "MIT PAC Client account restriction %x\n", LogonStatus)); FILL_EXT_ERROR_EX2(pExtendedError, LogonStatus, FILENO,__LINE__); goto Cleanup; }
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);
DebugLog((DEB_WARN, "GetTicketInfo Client Principal Unknown\n"));
KerbErr = KDC_ERR_NONE; }
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KdcInsertAuthorizationData KLIN(%x) Failed to GetTicketInfo: 0x%x\n", KLIN(FILENO, __LINE__), 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)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to appened auth data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; } }
if (SuppliedAuthData != NULL) { KerbErr = KerbCopyAndAppendAuthData( &FinalAuthData, SuppliedAuthData );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"KLIN(%x) Failed to appened auth data with supplied authdata: 0x%x\n", KLIN(FILENO, __LINE__), 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 (UserHandle != NULL) { SamrCloseHandle(&UserHandle); }
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 PKDC_S4U_TICKET_INFO S4UTicketInfo, 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 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; ULONG TicketFlags = 0; ULONG SourceTicketFlags = 0; PKERB_HOST_ADDRESSES Addresses = NULL; BOOLEAN fKpasswd = FALSE;
TRACE(KDC, BuildTicketTGS, DEB_FUNCTION);
D_DebugLog((DEB_T_KEY, "BuildTicketTGS building a TGS ticket Referral ? %s, CommonEType %#x\n", Referral ? "true" : "false", CommonEType));
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 );
//
// 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) { TicketLifespan.QuadPart = (LONGLONG) 10000000 * 60 * 2; TicketRenewspan.QuadPart = (LONGLONG) 10000000 * 60 * 2; } else { TicketLifespan = SecData.KdcTgsTicketLifespan(); TicketRenewspan = SecData.KdcTicketRenewSpan(); }
//
// TBD: We need to make the ticket 10 minutes if we're doing s4U
//
KerbErr = KdcBuildTicketTimesAndFlags( 0, ServiceTicketInfo->fTicketOpts, &TicketLifespan, &TicketRenewspan, S4UTicketInfo, 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 { //
// 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; } }
//
// Get cname for service ticket ...
//
// S4UToSelf / S4UProxy - We're in our realm, so take the "client" name
// (supplied as PA DATA, or, alternately, the name in a additional ticket),
// and make that the server name.
//
// Otherwise, normal TGS_REQ - use source ticket (tgt)
//
if (( ARGUMENT_PRESENT( S4UTicketInfo ) ) && ( (S4UTicketInfo->Flags & ( TI_S4UPROXY_INFO | TI_S4USELF_INFO ) ) != 0) && ( !Referral )) { KerbErr = KerbConvertKdcNameToPrincipalName( &EncryptedTicket->client_name, S4UTicketInfo->PACCName );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertUnicodeStringToRealm( &EncryptedTicket->client_realm, &S4UTicketInfo->PACCRealm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } else { KerbErr = KerbDuplicatePrincipalName( &EncryptedTicket->client_name, &SourceEncryptPart->client_name );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbDuplicateRealm( &EncryptedTicket->client_realm, SourceEncryptPart->client_realm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
//
// If the client did not request canonicalization, return the same
// realm as it sent. Otherwise, send our DNS realm name
//
OutputTicket.realm = SecData.KdcKerbDnsRealmName();
//
// 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; } else { EncryptedTicket->KERB_ENCRYPTED_TICKET_client_addresses = NULL; 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; SECPKG_CALL_INFO CallInfo ;
//
// 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
//
//
// Note: In some cases, attempts to normalize our own client name, e.g. the
// KDC's name someRealm\FooDc$, will result in recursion as follows :
//
// 1. We may need to talk to a GC to normalize the DC's name.
// 2. We try to get a ticket to a GC from this DC (aka the ClientName below)
// 3. We check logon restrictions
// 4. We try to normalize, recurse...
//
// We *should* in most cases be able to satisfy the DC lookup internally,
// unless the DS is just starting up, and replication is firing up. Assert,
// so we can determine why the client name is causing recursion in the normalize
// code path.
//
if ( LsaIGetCallInfo( &CallInfo ) ) { if (( CallInfo.Attributes & SECPKG_CALL_RECURSIVE ) && ( ClientName->NameCount == 1) && ( SecData.IsOurMachineName(&ClientName->Names[0]))) { D_DebugLog((DEB_ERROR, "Recursion detected during TGS logon restrictions\n")); return (KDC_ERR_NONE); } }
Status = KdcNormalize( ClientName, NULL, ClientRealm, NULL, KDC_NAME_CLIENT, FALSE, // do not restrict user accounts (user2user)
&ClientReferral, &MappedClientRealm, &ClientInfo, pExtendedError, &UserHandle, USER_ALL_KERB_CHECK_LOGON_RESTRICTIONS, 0L, &UserInfo, NULL // no group memberships
);
if (!KERB_SUCCESS(Status)) { DebugLog((DEB_ERROR, "KdcCheckTgsLogonRestrictions KLIN(%x) failed to normalize name %#x: ", KLIN(FILENO, __LINE__), Status)); 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
// ClientStringName - Client string name
// ServerStringName - Service string name
// 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
// pServerKey - the key to encrypt 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 PUNICODE_STRING ClientStringName, IN PUNICODE_STRING ServerStringName, IN PKERB_INTERNAL_NAME ServiceName, IN PKDC_TICKET_INFO ServiceTicketInfo, IN PKERB_KDC_REQUEST_BODY RequestBody, IN OPTIONAL PKERB_ENCRYPTION_KEY Subkey, OUT PKERB_TICKET NewTicket, OUT PKERB_ENCRYPTION_KEY pServerKey, 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; BOOLEAN NamesEqual = FALSE; ULONG CommonEType = KERB_ETYPE_DEFAULT; PKERB_ENCRYPTION_KEY pLocalServerKey = NULL;
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 supported crypt system
//
KerbErr = KerbFindCommonCryptSystemForSKey( RequestBody->encryption_type, ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, &CommonEType );
if (!KERB_SUCCESS(KerbErr)) { if (KDC_ERR_ETYPE_NOTSUPP == KerbErr) { KdcReportKeyError( ClientStringName, ServerStringName, KDC_KEY_ID_RENEWAL_SKEY, KDCEVENT_NO_KEY_INTERSECTION_TGS, RequestBody->encryption_type, ServiceTicketInfo ); }
DebugLog((DEB_ERROR, "KLIN(%x) failed to find common ETYPE: %#x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
//
// Find the common crypt system.
//
KerbErr = KerbFindCommonCryptSystem( ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo->Passwords, NULL, &pLocalServerKey );
if (!KERB_SUCCESS(KerbErr)) { if (KDC_ERR_ETYPE_NOTSUPP == KerbErr) { KdcReportKeyError( ClientStringName, ServerStringName, KDC_KEY_ID_RENEWAL_TICKET, KDCEVENT_NO_KEY_INTERSECTION_TGS, ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo ); }
DebugLog((DEB_ERROR, "KLIN(%x) failed to find common ETYPE: %#x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
//
// Build the renewal ticket
//
KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, FALSE, // not referral
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, SourceEncryptPart, NULL, // no server info
NULL, NULL, NULL, NULL, Subkey, NULL );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KLIN(%x) Failed to insert authorization data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
KerbErr = KerbDuplicateKey(pServerKey, pLocalServerKey);
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 PKERB_ENCRYPTION_KEY pServerKey, OUT PKERB_TICKET NewTicket, OUT PKERB_EXT_ERROR pExtendedError ) { return(KDC_ERR_BADOPTION); #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: KerbPerformTgsAccessCheck
//
// Synopsis: Access-checks the given ticket against the principal
// the ticket is for. Part of "this organization" vs.
// "other organization" logic.
//
// Arguments: TicketInfo ticket
// SdCount number of entries in the SecurityDescriptors
// array
// SecurityDescriptors security descriptors to check against
// EncryptedTicket ticket
// EncryptionType encryption type associated with ticket
//
// Notes: A successful check against _any_ of the security descriptors
// passed in causes the routine to succeed
//
// Returns:
//
// KDC_ERR_NONE - access check succeeded
// KDC_ERR_POLICY - access check failed
//
//--------------------------------------------------------------------
KERBERR KerbPerformTgsAccessCheck( IN PKDC_TICKET_INFO TicketInfo, IN PUCHAR SecurityDescriptor, IN PKERB_ENCRYPTED_TICKET EncryptedTicket, IN ULONG EncryptionType, OUT NTSTATUS * pStatus ) { KERBERR KerbErr; KDC_AUTHZ_INFO AuthzInfo = {0}; KDC_AUTHZ_GROUP_BUFFERS InfoToFree = {0}; AUTHZ_ACCESS_REPLY Reply = {0}; OBJECT_TYPE_LIST TypeList ={0}; AUTHZ_CLIENT_CONTEXT_HANDLE hClientContext = NULL; AUTHZ_ACCESS_REQUEST Request = {0}; DWORD AccessMask = 0; LUID ZeroLuid = {0,0}; DWORD Error = ERROR_ACCESS_DENIED; BOOLEAN OtherOrgSidFound = FALSE;
*pStatus = STATUS_SUCCESS;
KerbErr = KdcGetSidsFromTgt( EncryptedTicket, NULL, // no key for encrypted tgt.
EncryptionType, TicketInfo, &AuthzInfo, &InfoToFree, pStatus );
if ( !KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "KdcGetSidsFromTgt in KerbPerformTgsAccessCheck failed %x\n", KerbErr)); goto Cleanup; }
//
// Per the specification, the access check is only performed if the
// "other org" SID is in the list
//
for ( ULONG i = 0 ; i < AuthzInfo.SidCount ; i++ ) { if ( RtlEqualSid( AuthzInfo.SidAndAttributes[i].Sid, GlobalOtherOrganizationSid )) { OtherOrgSidFound = TRUE; break; } }
if ( !OtherOrgSidFound ) { KerbErr = KDC_ERR_NONE; goto Cleanup; }
if (!AuthzInitializeContextFromSid( AUTHZ_SKIP_TOKEN_GROUPS, // take the SIDs as they are
AuthzInfo.SidAndAttributes[0].Sid, // userid is first element in array
KdcAuthzRM, NULL, ZeroLuid, &AuthzInfo, &hClientContext )) { DebugLog((DEB_ERROR, "AuthzInitializeContextFromSid() failed in KerbPerformTgsAccessCheck%x\n", GetLastError())); KerbErr = KDC_ERR_POLICY; *pStatus = STATUS_AUTHENTICATION_FIREWALL_FAILED; goto Cleanup; }
//
// Perform the access check
//
TypeList.Level = ACCESS_OBJECT_GUID; TypeList.ObjectType = &GUID_A_SECURED_FOR_CROSS_ORGANIZATION; TypeList.Sbz = 0;
Request.DesiredAccess = ACTRL_DS_CONTROL_ACCESS; Request.ObjectTypeList = &TypeList; Request.ObjectTypeListLength = 1; Request.OptionalArguments = NULL; Request.PrincipalSelfSid = NULL;
Reply.ResultListLength = 1; // all or nothing w.r.t. access check.
Reply.GrantedAccessMask = &AccessMask; Reply.Error = &Error;
if (!AuthzAccessCheck( 0, hClientContext, &Request, NULL, // TBD: add audit
SecurityDescriptor, NULL, NULL, &Reply, NULL // don't cache result? Check to see if optimal.
)) { DebugLog((DEB_ERROR, "AuthzAccessCheck() failed in KerbPerformTgsAccessCheck%x\n", GetLastError())); KerbErr = KDC_ERR_POLICY; *pStatus = STATUS_AUTHENTICATION_FIREWALL_FAILED; } else if ( (*Reply.Error) != ERROR_SUCCESS ) { DebugLog((DEB_ERROR, "CrossOrg authz AC failed %x \n",(*Reply.Error))); KerbErr = KDC_ERR_POLICY; *pStatus = STATUS_AUTHENTICATION_FIREWALL_FAILED; } else { KerbErr = KDC_ERR_NONE; }
Cleanup:
if ( hClientContext != NULL ) { AuthzFreeContext(hClientContext); }
KdcFreeAuthzInfo( &InfoToFree );
return KerbErr; }
#ifdef ROGUE_DC
KERBERR KdcInstrumentRogueCrealm( IN OUT PKERB_ENCRYPTED_TICKET EncryptedTicket ) { KERBERR KerbErr; DWORD dwType; DWORD cbData = 0; PCHAR Buffer; PCHAR Value = NULL; ULONG NameType;
UNICODE_STRING ClientName = {0}; UNICODE_STRING DomainName = {0}; UNICODE_STRING EmailNameU = {0};
PCHAR EmailName = NULL; PCHAR NewCrealm = NULL;
//
// Optimization: no "rogue" key in registry - nothing for us to do
//
if ( hKdcRogueKey == NULL ) { return KDC_ERR_NONE; }
//
// Build an e-mail name of the client to look up mapping for in the registry
//
KerbErr = KerbConvertPrincipalNameToString( &ClientName, &NameType, &EncryptedTicket->client_name );
if ( !KERB_SUCCESS( KerbErr )) {
DebugLog((DEB_ERROR, "ROGUE: KerbConvertPrincipalNameToString failed\n")); goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &DomainName, &EncryptedTicket->client_realm );
if ( !KERB_SUCCESS( KerbErr )) {
DebugLog((DEB_ERROR, "ROGUE: KerbConvertRealmToUnicodeString failed\n")); goto Cleanup; }
KerbErr = KerbBuildEmailName( &DomainName, &ClientName, &EmailNameU );
if ( !KERB_SUCCESS( KerbErr )) {
DebugLog((DEB_ERROR, "ROGUE: KerbBuildEmailName failed\n")); goto Cleanup; }
EmailName = KerbAllocUtf8StrFromUnicodeString( &EmailNameU );
if ( EmailName == NULL ) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// See if this realm is in the substitute list in the registry
//
if ( ERROR_SUCCESS != RegQueryValueExA( hKdcRogueKey, EmailName, NULL, &dwType, (PBYTE)Value, &cbData ) || dwType != REG_MULTI_SZ || cbData <= 1 ) { DebugLog((DEB_ERROR, "ROGUE: Error reading from registry\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
SafeAllocaAllocate( Value, cbData );
if ( Value == NULL ) { DebugLog((DEB_ERROR, "ROGUE: Out of memory allocating substitution buffer\n" )); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if ( ERROR_SUCCESS != RegQueryValueExA( hKdcRogueKey, EmailName, NULL, &dwType, (PBYTE)Value, &cbData ) || dwType != REG_MULTI_SZ || cbData <= 1 ) { DebugLog((DEB_ERROR, "ROGUE: Error reading from registry\n")); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
DebugLog((DEB_ERROR, "ROGUE: Substituting the crealm for %wZ\n", &EmailName));
Buffer = Value;
while ( *Buffer != '\0' ) { if ( 0 == _strnicmp( Buffer, "crealm:", sizeof( "crealm" ) - 1 )) { Buffer += strlen( "crealm:" );
if ( *Buffer != '\0' ) { DebugLog((DEB_ERROR, "ROGUE: Replacing crealm for %wZ with %s\n", &EmailName, Buffer )); NewCrealm = Buffer; } }
//
// Move to the next line
//
while (*Buffer++ != '\0'); }
if ( NewCrealm != NULL ) { KERB_REALM NewCrealmCopy;
KerbErr = KerbDuplicateRealm( &NewCrealmCopy, NewCrealm );
if ( !KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "ROGUE: Out of memory inside KdcInstrumentRogueCrealm\n" )); goto Cleanup; }
KerbFreeRealm( &EncryptedTicket->client_realm ); EncryptedTicket->client_realm = NewCrealmCopy; }
Cleanup:
MIDL_user_free( EmailName ); KerbFreeString( &ClientName ); KerbFreeString( &DomainName ); KerbFreeString( &EmailNameU ); SafeAllocaFree( Value );
return KerbErr; } #endif
//--------------------------------------------------------------------
//
// Name: I_GetTGSTicket
//
// Synopsis: Gets an internal ticket using a KDC ticket (TGT).
//
// Arguments: SourceTicket - TGT for the client
// ClientStringName - Client string name
// ServerStringName - Server string name
// 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.
// NewTicket - Receives newly created ticket
// pServerKey - Key to encrypt ticket
// ReplyPaData - Contains any PA data to put in the reply
//
// Notes: See GetTGSTicket.
//
//
//--------------------------------------------------------------------
KERBERR I_GetTGSTicket( IN PKERB_TICKET SourceTicket, IN PUNICODE_STRING ClientStringName, IN PUNICODE_STRING ServerStringName, 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 PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OPTIONAL PKERB_ENCRYPTION_KEY Subkey, OUT PKERB_TICKET Ticket, OUT PKERB_ENCRYPTION_KEY pServerKey, OUT PKERB_PA_DATA_LIST * ReplyPaData, OUT PKERB_EXT_ERROR pExtendedError, OUT OPTIONAL PS4U_DELEGATION_INFO* S4UDelegationInfo ) { KERBERR KerbErr = KDC_ERR_NONE; UNICODE_STRING LocalServiceName; 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 pKeyToUse = NULL; PKERB_ENCRYPTION_KEY OldServerKey; KDC_TICKET_INFO OldServiceTicketInfo = {0}; SID_AND_ATTRIBUTES_LIST S4UClientGroupMembership = {0}; PUSER_INTERNAL6_INFORMATION S4UClientUserInfo = NULL; BOOLEAN bRestrictUserAccounts = LsaINoMoreWin2KDomain() && (TicketEncryptionKey ? FALSE : TRUE); PUSER_INTERNAL6_INFORMATION LocalUserInfo = NULL; ULONG NameFlags = KDC_NAME_FOLLOW_REFERRALS | KDC_NAME_CHECK_GC;
ULONG KdcOptions = 0; BOOLEAN GetInitialS4UPac = FALSE; PKERB_ENCRYPTION_KEY pServerKeyLocal = NULL; ULONG CommonEType = KERB_ETYPE_DEFAULT;
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;
KerbErr = KerbConvertRealmToUnicodeString( &ClientRealm, &SourceEncryptPart->client_realm );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
//
// Copy the space for flags from the real destination.
//
EncryptedTicket.flags = OutputEncryptedTicket->flags;
LocalServiceName.Buffer = NULL;
D_DebugLog((DEB_T_PAPI, "I_GetTGSTicket [entering] trying to build a new ticket\n")); D_KerbPrintKdcName((DEB_T_PAPI, 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; }
//
// See if we're getting a service ticket to a UPN, and alter name flags accordingly.
//
//
// KRB_NT_SRV_HST - This is a host/server name -
//
// KRB_NT_ENTERPRISE_PRINCIPAL - UPN, a user principal is calling s4uself, or using a UPN as a target.
//
NameFlags |= ((ServiceName->NameType == KRB_NT_ENTERPRISE_PRINCIPAL) ? KDC_NAME_UPN_TARGET : KDC_NAME_SERVER);
//
// 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) );
D_DebugLog((DEB_T_U2U, "I_GetTGSTicket requesting u2u? %s\n", KdcOptions & KERB_KDC_OPTIONS_enc_tkt_in_skey ? "TRUE" : "FALSE"));
KerbErr = KdcNormalize( TargetPrincipal, NULL, RequestRealm, &ClientRealm, NameFlags, bRestrictUserAccounts, // restrict user accounts (user2user)
&Referral, &ServiceRealm, ServiceTicketInfo, pExtendedError, NULL, // no user handle
USER_ALL_SECURITYDESCRIPTOR, // for KerbPerformTgsAccessCheck
bRestrictUserAccounts ? USER_EXTENDED_FIELD_SPN : 0, &LocalUserInfo, NULL );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "KLIN(%x) failed to normalize %#x, ServiceName ", KLIN(FILENO, __LINE__), KerbErr)); KerbPrintKdcName(DEB_WARN, ServiceName); goto Cleanup; }
if (ServiceTicketInfo != NULL) { //
// If a service account is marked as disabled, try NTLM. This
// prevents a large number of problems where the presense of a disabled
// computer account in our domain ends up hosing authentication to
// the "moved" or identical computer account in another forest.
//
if ((ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && (ServiceTicketInfo->UserAccountControl & USER_ACCOUNT_DISABLED) != 0) { KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; D_DebugLog((DEB_WARN, "KLIN(%x) Failed to normalize, account is disabled ", KLIN(FILENO, __LINE__))); FILL_EXT_ERROR_EX(pExtendedError, STATUS_ACCOUNT_DISABLED, FILENO, __LINE__); goto Cleanup; } }
//
// S4UToSelf
//
// If the client name is in our realm, verify client
// identity and build the PAC for the client. Also make sure
// the requestor is actually asking for a service ticket to himself,
// and not some other arbitrary service.
//
if (( S4UTicketInfo->Flags & TI_S4USELF_INFO ) != 0) { if (Referral) { if ((S4UTicketInfo->Flags & TI_REQUESTOR_THIS_REALM) != 0) { //
// The TGT's client is from this realm, but we're referring.
// The target's not in the same realm as the requesting service
// indicating an attempt to bypass the protocol.
//
DebugLog((DEB_ERROR, "S4USelf requestor realm != service realm\n")); DebugLog((DEB_ERROR, "TGT - %wZ\n", S4UTicketInfo->RequestorServiceRealm)); DebugLog((DEB_ERROR, "Service Realm - %wZ\n", &ServiceRealm)); DsysAssert(FALSE); KerbErr = KRB_AP_ERR_BADMATCH; goto Cleanup; } } else { S4UTicketInfo->Flags |= TI_TARGET_OUR_REALM;
//
// The client of the TGT and the SPN are in the same realm. Make
// sure they're the same account.
//
if ((S4UTicketInfo->Flags & TI_REQUESTOR_THIS_REALM) != 0) { if (S4UTicketInfo->RequestorTicketInfo.UserId != ServiceTicketInfo->UserId) { DebugLog((DEB_ERROR, "S4U requestor != target SPN\n")); DebugLog((DEB_ERROR, "Requestor %p, Target %p\n", &S4UTicketInfo->RequestorTicketInfo, &ServiceTicketInfo)); DsysAssert(FALSE); KerbErr = KRB_AP_ERR_BADMATCH; goto Cleanup; } } else { //
// The TGTs client is from another realm, but the SPN's in this one
// fail - someone's tinkering w/ the protocol.
//
DebugLog((DEB_ERROR, "S4USelf requestor realm != service realm\n")); DebugLog((DEB_ERROR, "TGT - %wZ\n", S4UTicketInfo->RequestorServiceRealm)); DebugLog((DEB_ERROR, "Service Realm - %wZ\n", &ServiceRealm)); DsysAssert(FALSE); KerbErr = KRB_AP_ERR_BADMATCH; goto Cleanup; } }
//
// If we're requesting S4USelf at the client realm, then
// we'll need to generate a PAC for that client.
//
if (SecData.IsOurRealm(&S4UTicketInfo->PACCRealm)) { //
// Normalize the client name, and get the info we need to
// generate a PAC.
//
KerbErr = KdcGetS4UTicketInfo( S4UTicketInfo, &OldServiceTicketInfo, // tgt's account info.
&S4UClientUserInfo, &S4UClientGroupMembership, pExtendedError );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcGetS4UTicketINfo failed - %x\n", KerbErr)); goto Cleanup; }
GetInitialS4UPac = TRUE; } }
//
// We're doing S4UProxy. Make sure the target service account is in
// our realm. This is for .Net only, and we expect a xrealm sol'n
// in the Longhorn release.
//
if ((S4UTicketInfo->Flags & TI_S4UPROXY_INFO ) != 0) { if ( Referral ) { DebugLog((DEB_ERROR, "Trying to do S4UProxy to another realm %wZ\n", &ServiceRealm )); FILL_EXT_ERROR_EX2(pExtendedError, STATUS_CROSSREALM_DELEGATION_FAILURE, FILENO, __LINE__); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
S4UTicketInfo->Flags |= TI_TARGET_OUR_REALM; } else if ( !Referral && LocalUserInfo && LocalUserInfo->I1.SecurityDescriptor.SecurityDescriptor && LsaINoMoreWin2KDomain() && ( ServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT ) == 0 ) { KERB_ENCRYPTED_TICKET * ETicket = (PKERB_ENCRYPTED_TICKET)SourceTicket->encrypted_part.cipher_text.value;
if ( ETicket->bit_mask & KERB_ENCRYPTED_TICKET_authorization_data_present ) { NTSTATUS Status = STATUS_SUCCESS;
//
// If there is no authorization data, no access check is possible
//
KerbErr = KerbPerformTgsAccessCheck( &OldServiceTicketInfo, LocalUserInfo->I1.SecurityDescriptor.SecurityDescriptor, ETicket, SourceTicket->encrypted_part.encryption_type, &Status );
if ( !KERB_SUCCESS( KerbErr )) { if ( KerbErr == KDC_ERR_POLICY && ( Status == STATUS_AUTHENTICATION_FIREWALL_FAILED || Status == STATUS_DOMAIN_TRUST_INCONSISTENT )) { FILL_EXT_ERROR_EX2( pExtendedError, Status, FILENO, __LINE__ ); }
goto Cleanup; } } }
//
// Find the supported crypt system.
//
KerbErr = KerbFindCommonCryptSystemForSKey( RequestBody->encryption_type, ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, &CommonEType );
if (!KERB_SUCCESS(KerbErr)) { if (KDC_ERR_ETYPE_NOTSUPP == KerbErr) { KdcReportKeyError( ClientStringName, ServerStringName, KDC_KEY_ID_TGS_SKEY, KDCEVENT_NO_KEY_INTERSECTION_TGS, RequestBody->encryption_type, ServiceTicketInfo ); }
DebugLog((DEB_ERROR, "KLIN(%x) failed to find common ETYPE: %#x\n", KLIN(FILENO, __LINE__), KerbErr));
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( ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo->Passwords, NULL, &pServerKeyLocal );
if (!KERB_SUCCESS(KerbErr)) { if (KDC_ERR_ETYPE_NOTSUPP == KerbErr) { KdcReportKeyError( ClientStringName, ServerStringName, KDC_KEY_ID_TGS_TICKET, KDCEVENT_NO_KEY_INTERSECTION_TGS, ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo ); }
DebugLog((DEB_ERROR, "KLIN(%x) failed to find common ETYPE: %#x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
//
// Check whether service is interactive, 'cause you can't
// get a ticket to an interactive service.
//
KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, Referral, S4UTicketInfo, 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_TICKETS, "GetTGSTicket: referring to domain '%wZ'\n", &ServiceTicketInfo->AccountName ));
//
// Verify that if the trust is not transitive, the client is from
// this realm.
// Also verify that if we came in through a non - transitive trust,
// we don't allow a referral.
//
SourceEncryptPart =(PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value;
if (( ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT ) && (( ServiceTicketInfo->fTicketOpts & AUTH_REQ_TRANSITIVE_TRUST ) == 0)) { if (!SecData.IsOurRealm(&SourceEncryptPart->client_realm)) { //
// One special case here, for S4U. Allow the hop back to our
// trusting realm.
//
if ((S4UTicketInfo->Flags & TI_S4USELF_INFO) != 0) { if (!RtlEqualUnicodeString(&ClientRealm, &ServiceTicketInfo->AccountName, TRUE )) { DebugLog((DEB_WARN,"S4U 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; } } else { 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)) { if ((S4UTicketInfo->Flags & TI_S4USELF_INFO) != 0) { if (!RtlEqualUnicodeString(&ClientRealm, &OldServiceTicketInfo.AccountName, TRUE )) { DebugLog((DEB_WARN,"TGT S4U Client from realm %s attempted to access non transitve trust to %wZ : illegal\n", SourceEncryptPart->client_realm, &OldServiceTicketInfo.AccountName ));
KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; } } else { DebugLog((DEB_WARN,"TGT Client from realm %s attempted to access non transitve trust to %wZ : illegal\n", SourceEncryptPart->client_realm, &OldServiceTicketInfo.AccountName ));
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) {
if (ClientRealm.Buffer != NULL ) { 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 preferred crypt system
//
KerbErr = KerbFindCommonCryptSystem( ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo->Passwords, NULL, &pServerKeyLocal );
if (!KERB_SUCCESS(KerbErr)) { if (KDC_ERR_ETYPE_NOTSUPP == KerbErr) { KdcReportKeyError( ClientStringName, ServerStringName, KDC_KEY_ID_TGS_REFERAL_TICKET, KDCEVENT_NO_KEY_INTERSECTION_TGS, ServiceTicketInfo->UserAccountControl & USER_USE_DES_KEY_ONLY ? kdc_pMitPrincipalPreferredCryptList : kdc_pPreferredCryptList, ServiceTicketInfo ); }
DebugLog((DEB_ERROR, "KLIN(%x) failed to find common ETYPE: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
KerbErr = BuildTicketTGS( ServiceTicketInfo, RequestBody, SourceTicket, TRUE, S4UTicketInfo, 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; } } }
//
// Here we're selecting a key with which to validate the PAC checksum
// before we copy it from the TGT (or evidence ticket) into the new
// service ticket or cross realm tgt.
//
//
// 3 choices -
//
// 1. U2U uses the session key in the accompanying addt'l ticket.
//
// 2. S4UProxy uses the requestor's key if we're the requestor's kdc (e.g.
// the first request of s4uproxy). Otherwise, it's just the xrealm
// krbtgt key.
//
// 3. Everything elses uses the krbtgt key, as the tgt's PAC is only
// signed w/ that key.
//
if (( S4UTicketInfo->Flags & TI_PRXY_REQUESTOR_THIS_REALM ) != 0) { D_DebugLog((DEB_T_KEY, "I_GetTGSTicket s4u prxy OldServerKey\n"));
OldServerKey = &S4UTicketInfo->EvidenceTicketKey; } else { D_DebugLog((DEB_T_KEY, "I_GetTGSTicket OldServiceTicketInfo OldServerKey\n"));
OldServerKey = KerbGetKeyFromList( OldServiceTicketInfo.Passwords, SourceTicket->encrypted_part.encryption_type ); }
DsysAssert(OldServerKey != NULL);
if (OldServerKey == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if (ARGUMENT_PRESENT(TicketEncryptionKey)) { pKeyToUse = TicketEncryptionKey; DebugLog((DEB_T_U2U, "I_GetTGSTicket signing PAC with u2u session key\n")); } else { DebugLog((DEB_T_KEY, "I_GetTGSTicket signing PAC with server key\n")); pKeyToUse = pServerKeyLocal; }
//
// Insert the auth data into the new ticket.
//
if ( GetInitialS4UPac ) { KerbErr = KdcInsertInitialS4UAuthorizationData( &EncryptedTicket, pExtendedError, S4UTicketInfo, S4UClientUserInfo, &S4UClientGroupMembership, ((ServiceTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT) && ((ServiceTicketInfo->UserAccountControl & USER_INTERDOMAIN_TRUST_ACCOUNT) == 0)), pKeyToUse ); } else { KerbErr = KdcInsertAuthorizationData( &EncryptedTicket, pExtendedError, (RequestBody->bit_mask & enc_authorization_data_present) ? &RequestBody->enc_authorization_data : NULL, (PKERB_ENCRYPTED_TICKET) SourceTicket->encrypted_part.cipher_text.value, ServiceTicketInfo, // Ticket info for the target service
S4UTicketInfo, &OldServiceTicketInfo, // krbtgt account used in TGS_REQ (could be cross realm).
OldServerKey, pKeyToUse, Subkey, S4UDelegationInfo ); }
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KLIN(%x) Failed to insert authorization data: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
KerbErr = KerbDuplicateKey(pServerKey, pKeyToUse); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KLIN(%x) Failed to insert duplicate key: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); goto Cleanup; }
#ifdef ROGUE_DC
KerbErr = KdcInstrumentRogueCrealm( &EncryptedTicket ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KLIN(%x) Failed to instrument a rogue crealm: 0x%x\n", KLIN(FILENO, __LINE__), KerbErr)); KerbErr = KDC_ERR_NONE; } #endif
*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 );
SamIFreeSidAndAttributesList(&S4UClientGroupMembership); SamIFree_UserInternal6Information( S4UClientUserInfo ); SamIFree_UserInternal6Information( LocalUserInfo );
if (!KERB_SUCCESS(KerbErr)) { KdcFreeInternalTicket( &NewTicket ); } KerbFreeString( &ServiceRealm );
KerbFreeString( &ClientRealm );
D_DebugLog((DEB_T_PAPI, "I_GetTGSTicket returning %#x\n", KerbErr));
return (KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbCheckA2D2Attribute
//
// Synopsis: Helper to validate the info in the A2D2 list given
// our target.
//
// Effects: allocate output ticket
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: there can only be one additional ticket
//
//
//--------------------------------------------------------------------------
KERBERR KerbCheckA2D2Attribute( IN PUSER_INTERNAL6_INFORMATION UserInternal6, IN PUNICODE_STRING TargetToMatch, IN PKERB_EXT_ERROR ExtendedError ) { KERBERR Kerberr = KDC_ERR_BADOPTION;
if (( UserInternal6->A2D2List == NULL ) || ( UserInternal6->A2D2List->NumSPNs == 0 )) { FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NOT_SUPPORTED, FILENO, __LINE__ ); return Kerberr; }
for (ULONG i = 0; i < UserInternal6->A2D2List->NumSPNs; i++) { if (RtlEqualUnicodeString( &(UserInternal6->A2D2List->SPNList[i]), TargetToMatch, TRUE )) { Kerberr = KDC_ERR_NONE; break; } }
//
// We have a list, but this target name isn't in there.
// Return error to indicate such, but server is still allowed S4U,
// just not for this target.
//
if (!KERB_SUCCESS(Kerberr)) { FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); }
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
//
// Also checks for s4uproxy requests, and validates them.
//
// Effects:
//
// Arguments: TicketList - from addt'l tickets field of tgs_req
// KdcOptions - request options.
// TargetServer - spn being asked for.
// SourceCName - client name from tgt
// SourceCRealm - client realm from tgt
// SourceKey - session key from tgt.
// u2uticketinfo - out param for u2u options.
// s4uticketinfo - out param for s4uproxy option.
// ExtendedError - out param for returning e_data.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcUnpackAdditionalTickets( IN PKERB_TICKET_LIST TicketList, IN ULONG KdcOptions, IN PUNICODE_STRING TargetName, IN PKERB_INTERNAL_NAME SourceCName, IN PUNICODE_STRING SourceCRealm, IN PKERB_ENCRYPTION_KEY SourceKey, OUT PKDC_U2U_TICKET_INFO U2UTicketInfo, OUT PKDC_S4U_TICKET_INFO S4UTicketInfo, OUT PKERB_EXT_ERROR ExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL; PKERB_ENCRYPTION_KEY EncryptionKey = NULL; PKERB_TICKET Ticket = NULL; UNICODE_STRING CrackedRealm = {0}; BOOLEAN Referral = FALSE; KDC_TICKET_INFO KrbtgtTicketInfo = {0}; KDC_TICKET_INFO RequestorTicketInfo = {0}; KERB_REALM LocalRealm; ULONG OptionCount = 0; ULONG TicketCount = 1; ULONG TicketFlags;
PUSER_INTERNAL6_INFORMATION RequesterUserInfo = NULL;
if ( TicketList == NULL ) { D_DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets KLIN(%x) Trying to unpack null ticket or more than one ticket\n", KLIN(FILENO, __LINE__))); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } else if ((KdcOptions & (KERB_KDC_OPTIONS_cname_in_addl_tkt | KERB_KDC_OPTIONS_enc_tkt_in_skey)) == 0) { D_DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets additional tickets present, but no options\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
Ticket = &TicketList->value; LocalRealm = SecData.KdcKerbDnsRealmName();
//
// U2U request - additional ticket contains TGT w/ session
// key we want to encrypt the final ticket with.
//
if ((KdcOptions & KERB_KDC_OPTIONS_enc_tkt_in_skey) != 0) { D_DebugLog((DEB_T_U2U, "KdcUnpackAdditionalTickets unpacking u2u tgt ticket\n"));
UNICODE_STRING ServerNames[3]; OptionCount++;
KerbErr = SecData.GetKrbtgtTicketInfo(&KrbtgtTicketInfo); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// 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; }
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) { DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets no encryption key found in krbtgt's OldPassword\n")); 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, "KdcUnpackAdditionalTickets KLIN(%x) Failed to verify additional ticket: 0x%x\n", KLIN(FILENO, __LINE__),KerbErr)); goto Cleanup; }
//
// Verify the realm of the ticket
//
if (!KerbCompareRealmNames( &LocalRealm, &Ticket->realm )) { D_DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets 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, "KdcUnpackAdditionalTickets 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; }
//
// Crack the name in the supplied TGT.
//
KerbErr = KerbConvertPrincipalNameToKdcName( &U2UTicketInfo->TgtCName, &EncryptedTicket->client_name );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &U2UTicketInfo->TgtCRealm, &EncryptedTicket->client_realm );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
KerbErr = KdcNormalize( U2UTicketInfo->TgtCName, &U2UTicketInfo->TgtCRealm, &U2UTicketInfo->TgtCRealm, NULL, KDC_NAME_CLIENT | KDC_NAME_INBOUND, FALSE, // do not restrict user accounts (user2user)
&Referral, &CrackedRealm, &U2UTicketInfo->TgtTicketInfo, ExtendedError, NULL, // no user handle
0L, // no fields to fetch
0L, // no extended fields
NULL, // no user all
NULL // no group membership
);
KerbFreeString( &CrackedRealm );
if ( Referral ) { KerbErr = KRB_AP_ERR_BADMATCH; }
if (KERB_SUCCESS(KerbErr)) { U2UTicketInfo->Flags |= TI_CHECK_RID; } else { DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets KLIN(%x) Failed to normalize client name from supplied ticket %#x\n", KLIN(FILENO, __LINE__), KerbErr));
KdcFreeU2UTicketInfo(U2UTicketInfo); RtlZeroMemory( U2UTicketInfo, sizeof(KDC_U2U_TICKET_INFO) );
goto Cleanup; }
U2UTicketInfo->Tgt = EncryptedTicket; EncryptedTicket = NULL;
if (TicketList->next != NULL) { TicketCount++; Ticket = &TicketList->next->value; }
U2UTicketInfo->Flags |= ( TI_INITIALIZED | TI_FREETICKET ); }
//
// S4UProxy request
// Now we've got to crack the additional ticket, find the server name,
// and crack the account.
//
// NOTE: The server must be in our realm to do this. This implies that
// other KDCs trust us to "do the right thing".
//
if ((KdcOptions & KERB_KDC_OPTIONS_cname_in_addl_tkt) != 0) { D_DebugLog((DEB_TRACE, "KdcUnpackAdditionalTickets unpacking evidence ticket\n"));
if ( Ticket == NULL ) { D_DebugLog((DEB_ERROR, "s4u set, but no ticket\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
//
// Can't do this w/o target name..
//
if ( TargetName->Buffer == NULL ) { KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
OptionCount++;
KerbErr = KerbConvertPrincipalNameToKdcName( &S4UTicketInfo->RequestorServiceName, &Ticket->server_name );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &S4UTicketInfo->RequestorServiceRealm, &Ticket->realm );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
//
// If the client of this S4UProxy request (e.g. a server running with
// an impersonated client context, asking for S4U) has an account in this
// realm (e.g. the crealm of the TGT == our realm), the accompanying
// evidence ticket must:
//
// 1. Be encrypted in the S4UClient's account key
// 2. Match the identity in the evidence ticket
//
if (SecData.IsOurRealm(SourceCRealm)) { D_DebugLog((DEB_T_TICKETS, "S4U Request -- OUR REALM\n"));
//
// First get the TGT holder's userinfo. This is used for checking
// the A2D2 attribute.
//
KerbErr = KdcNormalize( SourceCName, SourceCRealm, SourceCRealm, NULL, KDC_NAME_CLIENT, FALSE, // do not restrict user accounts (user2user)
&Referral, &CrackedRealm, &RequestorTicketInfo, ExtendedError, 0, NULL, USER_EXTENDED_FIELD_A2D2, &RequesterUserInfo, NULL );
KerbFreeString( &CrackedRealm ); if (!KERB_SUCCESS(KerbErr) || Referral ) { D_DebugLog((DEB_ERROR, "Unknown client dt PKERB_INTERNAL_NAME(%p)\n", SourceCName));
if (KERB_SUCCESS(KerbErr) && Referral) { KerbErr = KDC_ERR_BADOPTION; }
FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); goto Cleanup; }
//
// Now get the information associated with the evidence ticket.
//
KerbErr = KdcNormalize( S4UTicketInfo->RequestorServiceName, &S4UTicketInfo->RequestorServiceRealm, &S4UTicketInfo->RequestorServiceRealm, NULL, KDC_NAME_SERVER | KDC_NAME_CHECK_GC | KDC_NAME_FOLLOW_REFERRALS, FALSE, // do not restrict user accounts (user2user)
&Referral, &CrackedRealm, &S4UTicketInfo->RequestorTicketInfo, ExtendedError, 0, NULL, 0, NULL, NULL );
KerbFreeString( &CrackedRealm ); if ( !KERB_SUCCESS(KerbErr) || Referral ) { D_DebugLog((DEB_ERROR, "Unknown evidence ticket sname (%p)\n", S4UTicketInfo)); KerbErr = KDC_ERR_BADOPTION; FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); goto Cleanup; }
//
// The userids *must* match for the evidence ticket && the
// TGT. If not, log an event???
//
if ( S4UTicketInfo->RequestorTicketInfo.UserId != RequestorTicketInfo.UserId ) { KerbErr = KDC_ERR_BADOPTION; D_DebugLog((DEB_ERROR, "User id's don't match (%p) (%p)!\n", &S4UTicketInfo->RequestorTicketInfo, RequestorTicketInfo)); FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); goto Cleanup; }
//
// Validate the evidence ticket was encrypted w/ the proper key.
//
EncryptionKey = KerbGetKeyFromList( RequestorTicketInfo.Passwords, Ticket->encrypted_part.encryption_type );
if (EncryptionKey == NULL) { KerbErr = KDC_ERR_ETYPE_NOTSUPP; goto Cleanup; }
KerbErr = KerbVerifyTicket( Ticket, 0, NULL, SecData.KdcDnsRealmName(), EncryptionKey, &SkewTime, &EncryptedTicket );
if ((KerbErr == KRB_AP_ERR_MODIFIED) && (RequestorTicketInfo.OldPasswords != NULL)) { EncryptionKey = KerbGetKeyFromList( RequestorTicketInfo.OldPasswords, Ticket->encrypted_part.encryption_type );
if (EncryptionKey == NULL) { KerbErr = KDC_ERR_ETYPE_NOTSUPP; goto Cleanup; }
KerbErr = KerbVerifyTicket( Ticket, 0, NULL, SecData.KdcDnsRealmName(), EncryptionKey, &SkewTime, &EncryptedTicket ); }
if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_ERROR, "Couldn't decrypt evidence ticket %x\n",KerbErr)); KerbErr = KDC_ERR_BADOPTION; FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); goto Cleanup; }
//
// Check the A2D2 attribute for our name. Also return hints to
// the server w.r.t. whether they even support S4UProxy protocol
// using extended errors.
//
KerbErr = KerbCheckA2D2Attribute( RequesterUserInfo, TargetName, ExtendedError );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets failed to match %wZ to user %wZ id %#x\n", TargetName, &RequesterUserInfo->I1.UserName, RequesterUserInfo->I1.UserId)); goto Cleanup; }
KerbErr = KerbDuplicateKey( &S4UTicketInfo->EvidenceTicketKey, EncryptionKey );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
S4UTicketInfo->Flags |= TI_PRXY_REQUESTOR_THIS_REALM;
} else { //
// NOT OUR REALM!
//
// cname will == the info in the pac, as will crealm.
//
// The evidence ticket and the PAC signature will be generated using
// the session key in the cross-realm tgt.
//
DebugLog((DEB_T_TICKETS, "S4U Request !! NOT OUR REALM/n")); FILL_EXT_ERROR_EX2( ExtendedError, STATUS_CROSSREALM_DELEGATION_FAILURE, FILENO, __LINE__ ); KerbErr = KDC_ERR_POLICY; goto Cleanup; }
if (!NT_SUCCESS( KerbDuplicateStringEx( &S4UTicketInfo->TargetName, TargetName, FALSE ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// The ticket *must* be fwdable to be used in S4UProxy.
//
TicketFlags = KerbConvertFlagsToUlong( &EncryptedTicket->flags );
if (( TicketFlags & KERB_TICKET_FLAGS_forwardable ) == 0) { D_DebugLog((DEB_ERROR, "Non fwdble service ticket presented as S4UPrxy evidence\n")); FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
//
// Everything's looking good here. Grab the client name
// from the ticket, and the client realm. These will be put in
// the TGS_REP, when we need to.
//
KerbErr = KerbConvertPrincipalNameToKdcName( &S4UTicketInfo->PACCName, &EncryptedTicket->client_name );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &S4UTicketInfo->PACCRealm, &EncryptedTicket->client_realm );
if (!KERB_SUCCESS( KerbErr )) { goto Cleanup; }
//
// Keep a copy of the ticket, so we can copy over the pac later.
//
S4UTicketInfo->EvidenceTicket = EncryptedTicket; EncryptedTicket = NULL;
S4UTicketInfo->Flags |= ( TI_INITIALIZED | TI_S4UPROXY_INFO | TI_FREETICKET ); }
//
// Validate that our options were matched w/ tickets.
//
if ( TicketCount != OptionCount ) { D_DebugLog((DEB_ERROR, "Ticket count (%x) != option count (%x)\n", TicketCount, OptionCount)); FILL_EXT_ERROR_EX2( ExtendedError, STATUS_NO_MATCH, FILENO, __LINE__ ); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
Cleanup:
if (!KERB_SUCCESS( KerbErr )) { KdcFreeS4UTicketInfo( S4UTicketInfo ); RtlZeroMemory( S4UTicketInfo, sizeof(KDC_S4U_TICKET_INFO) );
KdcFreeU2UTicketInfo( U2UTicketInfo ); RtlZeroMemory( U2UTicketInfo, sizeof(KDC_U2U_TICKET_INFO) ); }
SamIFree_UserInternal6Information( RequesterUserInfo );
if (EncryptedTicket != NULL) { KerbFreeTicket( EncryptedTicket ); }
FreeTicketInfo( &KrbtgtTicketInfo ); FreeTicketInfo( &RequestorTicketInfo );
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 PKERB_INTERNAL_NAME TargetName, IN PKERB_ENCRYPTED_TICKET SourceTicket, IN PKERB_ENCRYPTION_KEY SourceTicketKey, IN PKERB_INTERNAL_NAME SourceCName, IN PUNICODE_STRING SourceCRealm, IN OUT PKDC_S4U_TICKET_INFO S4UTicketInfo, IN OUT PKERB_EXT_ERROR ExtendedError ) { KERBERR Kerberr = KDC_ERR_NONE; PKERB_PA_DATA PaData = NULL; PKERB_PA_FOR_USER S4URequest = NULL; NTSTATUS Status = STATUS_SUCCESS; unsigned char Hash[MD5DIGESTLEN]; KERB_CHECKSUM Checksum; BOOLEAN Referral; UNICODE_STRING FakeRealm = {0};
Checksum.checksum.value = Hash;
PaData = KerbFindPreAuthDataEntry( KRB5_PADATA_FOR_USER, PaList );
if (NULL == PaData) { goto Cleanup; }
if (( S4UTicketInfo->Flags & TI_S4UPROXY_INFO ) != 0) { D_DebugLog((DEB_ERROR, "Trying to mix S4U proxy and self requests\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)) { D_DebugLog((DEB_ERROR, "Failed to unpack PA_FOR_USER\n")); goto Cleanup; }
Status = KerbHashS4UPreauth( S4URequest, &SourceTicket->key, S4URequest->cksum.checksum_type, &Checksum );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to create S4U checksum\n")); Kerberr = KRB_AP_ERR_MODIFIED; goto Cleanup; }
if (!RtlEqualMemory( Checksum.checksum.value, S4URequest->cksum.checksum.value, Checksum.checksum.length )) { DebugLog((DEB_ERROR, "S4U PA checksum doesn't match!\n")); Kerberr = KRB_AP_ERR_MODIFIED; goto Cleanup; }
//
// Get the information on the requestor of the S4UToSelf
// if its from our realm.
//
if (SecData.IsOurRealm(SourceCRealm)) {
Kerberr = KdcNormalize( SourceCName, SourceCRealm, SourceCRealm, NULL, KDC_NAME_CLIENT, FALSE, &Referral, &FakeRealm, &S4UTicketInfo->RequestorTicketInfo, ExtendedError, NULL, NULL, NULL, NULL, NULL );
if ( !KERB_SUCCESS( Kerberr) || Referral ) { //
// Something's wrong here - we don't know about the client
// of this request, even though the TGTs from our realm.
//
D_DebugLog((DEB_ERROR, "Couldn't normalize S4UToSelf requestor\n")); Kerberr = KDC_ERR_WRONG_REALM; goto Cleanup; }
S4UTicketInfo->Flags |= (TI_REQUESTOR_THIS_REALM | TI_CHECK_RID);
}
Kerberr = KerbConvertRealmToUnicodeString( &S4UTicketInfo->PACCRealm, &S4URequest->userRealm );
if (!KERB_SUCCESS(Kerberr)) { goto Cleanup; }
Kerberr = KerbConvertPrincipalNameToKdcName( &S4UTicketInfo->PACCName, &S4URequest->userName );
if (!KERB_SUCCESS(Kerberr)) { goto Cleanup; }
Status = KerbDuplicateString( &S4UTicketInfo->RequestorServiceRealm, SourceCRealm );
if (!NT_SUCCESS(Status)) { Kerberr = KRB_ERR_GENERIC; goto Cleanup; }
Status = KerbDuplicateKdcName( &S4UTicketInfo->RequestorServiceName, SourceCName );
if (!NT_SUCCESS(Status)) { Kerberr = KRB_ERR_GENERIC; goto Cleanup; }
//
// This is the requestor's tgt - save it off for
// later authz checks.
//
S4UTicketInfo->EvidenceTicket = SourceTicket; Kerberr = KerbDuplicateKey( &S4UTicketInfo->EvidenceTicketKey, SourceTicketKey );
if (!KERB_SUCCESS( Kerberr )) { goto Cleanup; }
S4UTicketInfo->Flags |= ( TI_INITIALIZED | TI_S4USELF_INFO );
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, OUT PUNICODE_STRING ClientStringName, OUT PUNICODE_STRING ServerStringName ) { KERBERR KerbErr = KDC_ERR_NONE;
KDC_TICKET_INFO ServerTicketInfo = {0}; KDC_U2U_TICKET_INFO U2UTicketInfo = {0}; KDC_S4U_TICKET_INFO S4UTicketInfo = {0}; KERB_TICKET SourceTicket = {0}; KERB_TICKET NewTicket = {0}; KERB_ENCRYPTED_TICKET EncryptedTicket = {0}; PKERB_ENCRYPTED_TICKET SourceEncryptPart = 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}; KERB_ENCRYPTION_KEY ServerKey = {0}; KERB_ENCRYPTION_KEY SourceTicketKey ={0};
PKERB_INTERNAL_NAME ServerName = NULL; PKERB_INTERNAL_NAME SourceClientName = NULL; UNICODE_STRING SourceClientRealm = {0};
ULONG CommonEType = 0; ULONG KdcOptions = 0; ULONG TicketFlags = 0; ULONG ReplyTicketFlags = 0; ULONG TraceFlags = 0; ULONG KeySalt = KERB_TGS_REP_SALT;
KERB_REALM TmpRealm = NULL; KERB_PRINCIPAL_NAME TmpName = {0};
BOOLEAN Validating = FALSE; BOOLEAN UseSubKey = FALSE; BOOLEAN Renew = FALSE; PS4U_DELEGATION_INFO S4UDelegationInfo = NULL; KDC_TGS_EVENT_INFO TGSEventTraceInfo = {0}; PLSA_ADT_STRING_LIST TransittedServices = NULL;
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){
TraceFlags = GetTraceEnableFlags(KdcTraceLoggerHandle);
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 ); }
//
// 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; }
//
// PA Data processing for TGS_REQ
//
//
// 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, &SourceTicketKey, &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, "HandleTGSRequest 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, "HandleTGSRequest 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, "HandleTGSRequest 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; }
//
// Check for additional tickets
//
//
// The server name is optional, but only if you have enc_tkt_in_skey set..
//
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; }
//
// Convert the server name to a string for auditing.
//
KerbErr = KerbConvertKdcNameToString( ServerStringName, ServerName, NULL );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
//
// Grab the cname and crealm from the TGT (aka the source ticket)
//
KerbErr = KerbConvertPrincipalNameToKdcName( &SourceClientName, &SourceEncryptPart->client_name );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &SourceClientRealm, &SourceEncryptPart->client_realm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Validate
//
if ((RequestBody->bit_mask & additional_tickets_present) != 0) { KerbErr = KdcUnpackAdditionalTickets( RequestBody->additional_tickets, KdcOptions, ServerStringName, SourceClientName, &SourceClientRealm, &ReplyKey, &U2UTicketInfo, &S4UTicketInfo, pExtendedError );
if (!KERB_SUCCESS( KerbErr )) { DebugLog((DEB_ERROR, "KdcUnpackAdditionalTickets failed - %x\n", KerbErr)); goto Cleanup; } }
//
// Gather & convert names for auditing.
//
if ((RequestBody->bit_mask & KERB_KDC_REQUEST_BODY_server_name_present) == 0) { //
// There must be an additional ticket, and this *must* be U2U, right?.
//
if (( U2UTicketInfo.Flags & TI_INITIALIZED ) == 0) { KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; DsysAssert(FALSE); goto Cleanup; }
KerbErr = KerbDuplicateKdcName( &ServerName, U2UTicketInfo.TgtCName );
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; } }
KerbErr = KerbConvertKdcNameToString( ClientStringName, SourceClientName, &SourceClientRealm ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// S4UToSelf PA data
// Note: We can't do both S4U and S4UProxy requests at the same time..
//
KerbErr = KdcFindS4UClientAndRealm( RequestMessage->KERB_KDC_REQUEST_preauth_data, ServerName, SourceEncryptPart, &SourceTicketKey, SourceClientName, &SourceClientRealm, &S4UTicketInfo, pExtendedError );
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( SourceClientName, &SourceClientRealm, pExtendedError ); if (!KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_WARN, "HandleTGSRequest KLIN(%x) Client failed TGS logon restrictions: 0x%x : ", KLIN(FILENO, __LINE__),KerbErr)); D_KerbPrintKdcName((DEB_WARN, SourceClientName)); 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
//
#if DBG
if ( (S4UTicketInfo.Flags & TI_S4USELF_INFO) != 0) { D_DebugLog((DEB_T_S4U, "S4USelf request: requestor = %wZ, ", &SourceClientRealm)); D_KerbPrintKdcName((DEB_T_S4U, SourceClientName)); D_DebugLog((DEB_T_S4U, "S4USelf client realm %wZ\n", &S4UTicketInfo.PACCRealm)); D_KerbPrintKdcName((DEB_T_S4U, S4UTicketInfo.PACCName)); D_DebugLog((DEB_T_S4U, "HandleTGSRequest S4UTarget = %wZ, ", &S4UTicketInfo.TargetName)); D_KerbPrintKdcName((DEB_T_S4U, ServerName)); } else { D_DebugLog((DEB_TRACE, "HandleTGSRequest Service = ")); D_KerbPrintKdcName((DEB_TRACE, ServerName)); D_DebugLog((DEB_TRACE, "HandleTGSRequest: Client = %wZ, ", &SourceClientRealm)); D_KerbPrintKdcName((DEB_TRACE, SourceClientName)); }
#endif
//
// 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, ClientStringName, // needed for auditing
ServerStringName, // needed for auditing
ServerName, &ServerTicketInfo, RequestBody, ( UnmarshalledAuthenticator && ( UnmarshalledAuthenticator->bit_mask & KERB_AUTHENTICATOR_subkey_present )) ? &UnmarshalledAuthenticator->subkey : NULL, &NewTicket, &ServerKey, pExtendedError ); } else if (KdcOptions & KERB_KDC_OPTIONS_validate) { D_DebugLog((DEB_T_KDC, "HandleTGSRequest validating ticket\n"));
KerbErr = I_Validate( &SourceTicket, ServerName, &SourceClientRealm, RequestBody, &ServerKey, &NewTicket, pExtendedError );
Validating = TRUE; } else { D_DebugLog((DEB_T_KDC, "HandleTGSRequest getting new TGS ticket\n"));
KerbErr = I_GetTGSTicket( &SourceTicket, ClientStringName, // needed for auditing
ServerStringName, // needed for auditing
ServerName, RequestRealm, RequestBody, &ServerTicketInfo, (( U2UTicketInfo.Flags & TI_INITIALIZED ) ? &U2UTicketInfo.Tgt->key : NULL ), &S4UTicketInfo, ( UnmarshalledAuthenticator && ( UnmarshalledAuthenticator->bit_mask & KERB_AUTHENTICATOR_subkey_present )) ? &UnmarshalledAuthenticator->subkey : NULL, &NewTicket, &ServerKey, &ReplyPaData, pExtendedError, &S4UDelegationInfo ); }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
DsysAssert(ServerTicketInfo.Passwords != NULL);
//
// Check to see if the additional ticket supplied is the one for this
// server, if necessary
//
if ( (U2UTicketInfo.Flags & TI_CHECK_RID) && (ServerTicketInfo.UserId != U2UTicketInfo.TgtTicketInfo.UserId) ) { DebugLog((DEB_ERROR, "KLIN(%x) Supplied U2U ticket is not for server: %wZ (%#x) vs. %wZ (%#x)\n", KLIN(FILENO, __LINE__), &(U2UTicketInfo.TgtTicketInfo.AccountName), ServerTicketInfo.UserId, &(ServerTicketInfo.AccountName), U2UTicketInfo.TgtTicketInfo.UserId)); KerbErr = KRB_AP_ERR_BADMATCH; goto Cleanup; }
//
// S4UToSelf check - the requestor's userid must == that of the final service.
//
if ((( S4UTicketInfo.Flags & ( TI_S4USELF_INFO | TI_CHECK_RID )) == ( TI_S4USELF_INFO | TI_CHECK_RID )) && ( S4UTicketInfo.RequestorTicketInfo.UserId != ServerTicketInfo.UserId )) { DebugLog((DEB_ERROR, "KLIN(%x) Supplied S4U ticket is not for server: %wZ (%#x) vs. %wZ (%#x)\n", KLIN(FILENO, __LINE__), &(S4UTicketInfo.RequestorTicketInfo.AccountName), ServerTicketInfo.UserId, &(ServerTicketInfo.AccountName), S4UTicketInfo.RequestorTicketInfo.UserId)); KerbErr = KRB_AP_ERR_BADMATCH; 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;
//
// S4UToSelf.
// The client name in the reply is the client name from the
// PA Data.
//
if ((( S4UTicketInfo.Flags & ( TI_S4USELF_INFO | TI_S4UPROXY_INFO ) ) != 0) && (( S4UTicketInfo.Flags & TI_TARGET_OUR_REALM) != 0 )) { KerbErr = KerbConvertKdcNameToPrincipalName( &TmpName, S4UTicketInfo.PACCName );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertUnicodeStringToRealm( &TmpRealm, &S4UTicketInfo.PACCRealm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
D_DebugLog((DEB_T_TICKETS, "S4U Reply client name\n")); D_KerbPrintKdcName((DEB_T_TICKETS, S4UTicketInfo.PACCName)); D_DebugLog((DEB_T_TICKETS, "Realm %wZ\n", &S4UTicketInfo.PACCRealm));
Reply.client_realm = TmpRealm; Reply.client_name = TmpName; } else { //
// Default to information in the TGT
//
Reply.client_realm = SourceEncryptPart->client_realm; Reply.client_name = SourceEncryptPart->client_name; }
//
// Copy in the ticket
//
KerbErr = KerbPackTicket( &NewTicket, &ServerKey, ( U2UTicketInfo.Flags & TI_INITIALIZED ) ? KERB_NO_KEY_VERSION : ServerTicketInfo.PasswordVersion, &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 (will encrypt the data first)
//
if (UseSubKey == TRUE) { KeySalt = KERB_TGS_REP_SUBKEY_SALT; // otherwise keep KERB_TGS_REP_SALT
}
KerbErr = KerbPackKdcReplyBody( &ReplyBody, &ReplyKey, KERB_NO_KEY_VERSION, KeySalt, 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
//
CommonEType = ServerKey.keytype;
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, &SourceClientRealm, (PBYTE) pStartTime, sizeof(KERB_TIME), &LogonGuid ); ASSERT(NT_SUCCESS( Status ));
if (S4UDelegationInfo) { TransittedServices = (PLSA_ADT_STRING_LIST)MIDL_user_allocate( sizeof(LSA_ADT_STRING_LIST) + sizeof(LSA_ADT_STRING_LIST_ENTRY) * S4UDelegationInfo->TransitedListSize);
if (TransittedServices) { PLSA_ADT_STRING_LIST_ENTRY Entry;
Entry = (PLSA_ADT_STRING_LIST_ENTRY)&TransittedServices[1];
TransittedServices->cStrings = S4UDelegationInfo->TransitedListSize; TransittedServices->Strings = Entry;
for (ULONG i = 0; i < S4UDelegationInfo->TransitedListSize; i++) { Entry->Flags = 0; Entry->String.Length = S4UDelegationInfo->S4UTransitedServices[i].Length; Entry->String.MaximumLength = S4UDelegationInfo->S4UTransitedServices[i].MaximumLength; Entry->String.Buffer = S4UDelegationInfo->S4UTransitedServices[i].Buffer; Entry++; } } }
KdcMakeAccountSid(ServerSid, ServerTicketInfo.UserId); KdcLsaIAuditTgsEvent( Renew ? SE_AUDITID_TICKET_RENEW_SUCCESS : SE_AUDITID_TGS_TICKET_REQUEST, ClientStringName, &SourceClientRealm, NULL, // no client SID
&ServerTicketInfo.AccountName, ServerSid, (PULONG) &KdcOptions, NULL, // success
&CommonEType, NULL, // no preauth type
GET_CLIENT_ADDRESS(ClientAddress), &LogonGuid, TransittedServices ); }
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 (SourceClientRealm.Buffer != NULL && SourceClientRealm.Length > 0) { pStringToCopy = &SourceClientRealm; } 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 ); }
if (!KERB_SUCCESS(KerbErr) && SecData.AuditKdcEvent(KDC_AUDIT_TGS_FAILURE)) { if (( KerbErr != KDC_ERR_S_PRINCIPAL_UNKNOWN ) || (( KdcExtraLogLevel & LOG_SPN_UNKNOWN) != 0)) { KdcLsaIAuditTgsEvent( SE_AUDITID_TGS_TICKET_REQUEST, (ClientStringName->Buffer != NULL) ? ClientStringName : &KdcNullString, &SourceClientRealm, // no domain name
NULL, ServerStringName, NULL, &KdcOptions, (PULONG) &KerbErr, NULL, NULL, GET_CLIENT_ADDRESS(ClientAddress), NULL, // no logon guid
NULL // no transitted services
); } }
KerbFreeKdcName( &SourceClientName ); KerbFreeString( &SourceClientRealm ); KerbFreeKdcName( &ServerName ); KerbFreeKey( &ReplyKey ); KerbFreeKey( &SourceTicketKey ); KerbFreeKey( &ServerKey );
KdcFreeKdcReplyBody( &ReplyBody );
KerbFreePrincipalName( &TmpName ); KerbFreeRealm( &TmpRealm ); KdcFreeU2UTicketInfo(&U2UTicketInfo); KdcFreeS4UTicketInfo(&S4UTicketInfo);
if (ReplyPaData != NULL) { KerbFreePreAuthData(ReplyPaData); }
KerbFreeApRequest(UnmarshalledApRequest); KerbFreeAuthenticator(UnmarshalledAuthenticator); KerbFreeTicket(SourceEncryptPart);
KdcFreeInternalTicket(&NewTicket); FreeTicketInfo(&ServerTicketInfo);
KdcFreeKdcReply( &Reply );
if (TransittedServices) { MIDL_user_free(TransittedServices); }
if (S4UDelegationInfo) { #if DBG
D_DebugLog((DEB_T_PAC, "HandleTGSRequest target %wZ\n", &S4UDelegationInfo->S4U2proxyTarget)); for ( ULONG i = 0; i < S4UDelegationInfo->TransitedListSize; i++ ) { D_DebugLog((DEB_T_PAC, "HandleTGSRequest ts %#x: %wZ\n", i, &S4UDelegationInfo->S4UTransitedServices[i])); } #endif // DBG
MIDL_user_free(S4UDelegationInfo); }
return(KerbErr); }
|