You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4982 lines
155 KiB
4982 lines
155 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|