|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: tktutil.cxx
//
// Contents: x// Classes:
//
// Functions:
//
// History: 04-Mar-94 wader Created
//
//----------------------------------------------------------------------------
// Place any local #includes files here.
#include "kdcsvr.hxx"
extern "C" { #include <dns.h> // DNS_MAX_NAME_LENGTH
#include <ntdsa.h> // CrackSingleName
} #if DBG
#include <perftimr.hxx>
#endif
#include <userall.h>
#define FILENO FILENO_TKTUTIL
#define KDC_WSZ_GC L"gc"
#define KDC_GC_NAMEPARTS 3
//#define DONT_SUPPORT_OLD_TYPES_KDC 1
//
// Static data
//
//
// Fudge factor for comparing timestamps, because network clocks may
// be out of sync.
// Note: The lowpart is first!
//
LARGE_INTEGER SkewTime;
KERBERR KdcGetTicketInfo( IN PUNICODE_STRING UserName, IN ULONG LookupFlags, IN OPTIONAL PKERB_INTERNAL_NAME PrincipalName, IN OPTIONAL PKERB_REALM Realm, OUT PKDC_TICKET_INFO TicketInfo, OUT PKERB_EXT_ERROR pExtendedError, OUT OPTIONAL SAMPR_HANDLE * UserHandle, IN OPTIONAL ULONG WhichFields, IN OPTIONAL ULONG ExtendedFields, OUT OPTIONAL PUSER_INTERNAL6_INFORMATION * RetUserInfo, OUT OPTIONAL PSID_AND_ATTRIBUTES_LIST GroupMembership );
//+-------------------------------------------------------------------------
//
// Function: KdcBuildNt4Name
//
// Synopsis: Construct an NT4 style name for an account by separating
// the name into a principal & domain name, converting the
// domain name to netbios, and then creating "domain\user" name.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcBuildNt4Name( OUT LPWSTR * Nt4Name, OUT PUNICODE_STRING OutputRealm, IN PUNICODE_STRING Upn ) { ULONG Index; KERBERR KerbErr = KDC_ERR_NONE; LPWSTR OutputName = NULL; PKDC_DOMAIN_INFO DomainInfo = NULL; UNICODE_STRING RealmName; UNICODE_STRING PrincipalName;
//
// Find the first backslash or '@', or '.' in the name
//
RtlInitUnicodeString( OutputRealm, NULL ); *Nt4Name = NULL;
for (Index = Upn->Length/sizeof(WCHAR) - 1; Index > 0 ; Index-- ) { if (Upn->Buffer[Index] == L'@') { break; } }
if (Index == 0) { KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
//
// Pull out the realm name and look it up in the list of domains
//
PrincipalName = *Upn; PrincipalName.Length = (USHORT) Index * sizeof(WCHAR); PrincipalName.MaximumLength = (USHORT) Index * sizeof(WCHAR);
RealmName.Buffer = &Upn->Buffer[Index+1]; RealmName.Length = (USHORT) (Upn->Length - (Index + 1) * sizeof(WCHAR)); RealmName.MaximumLength = RealmName.Length;
KerbErr = KdcLookupDomainName( &DomainInfo, &RealmName, &KdcDomainList );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// We need a netbios name
//
if (DomainInfo->NetbiosName.Length == 0) { //
// Copy out the realm name so we can return a non-authoritative referral
//
if (!NT_SUCCESS(KerbDuplicateString( OutputRealm, &DomainInfo->DnsName ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } KerbErr = KDC_ERR_WRONG_REALM; goto Cleanup; }
//
// now build the output name
//
OutputName = (LPWSTR) MIDL_user_allocate(DomainInfo->NetbiosName.Length + PrincipalName.Length + 2 * sizeof(WCHAR)); if (OutputName == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
RtlCopyMemory( OutputName, DomainInfo->NetbiosName.Buffer, DomainInfo->NetbiosName.Length ); OutputName[DomainInfo->NetbiosName.Length/sizeof(WCHAR)] = L'\\'; RtlCopyMemory( OutputName + DomainInfo->NetbiosName.Length/sizeof(WCHAR) + 1, PrincipalName.Buffer, PrincipalName.Length ); OutputName[1 + (PrincipalName.Length + DomainInfo->NetbiosName.Length)/sizeof(WCHAR)] = L'\0';
*Nt4Name = OutputName; OutputName = NULL; Cleanup:
if (DomainInfo != NULL) { KdcDereferenceDomainInfo( DomainInfo ); } if (OutputName != NULL) { MIDL_user_free( OutputName ); } return(KerbErr);
}
//+-------------------------------------------------------------------------
//
// Function: KdcMatchCrossForestName
//
// Synopsis: Builds a list of the supplemental credentials and then
// encrypts it with the supplied key
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcMatchCrossForestName( IN PKERB_INTERNAL_NAME Principal, OUT PUNICODE_STRING RealmName ) {
NTSTATUS Status = STATUS_SUCCESS; LSA_ROUTING_MATCH_TYPE MatchType; KERBERR KerbErr = KDC_ERR_NONE; UNICODE_STRING UnicodePrincipal = {0};
switch (Principal->NameType) { case KRB_NT_ENTERPRISE_PRINCIPAL: MatchType = RoutingMatchUpn; break; case KRB_NT_SRV_INST: MatchType = RoutingMatchSpn; break; default: return KRB_ERR_GENERIC; }
KerbErr = KerbConvertKdcNameToString( &UnicodePrincipal, Principal, NULL );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Can we match the SPN / UPN to external name space (realm)
//
Status = LsaIForestTrustFindMatch( MatchType, &UnicodePrincipal, RealmName );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "LsaIForestTrustFindMatch failed - %x\n",Status)); goto Cleanup; }
Cleanup: if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; }
KerbFreeString(&UnicodePrincipal);
return KerbErr; }
//+-------------------------------------------------------------------------
//
// Function: KdcCrackNameAtGC
//
// Synopsis: Cracks a name at a GC by first looking it up for
// UPN/SPN and then constructing an NT4-style name to lookup
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcCrackNameAtGC( IN PUNICODE_STRING Upn, IN PKERB_INTERNAL_NAME PrincipalName, IN DS_NAME_FORMAT NameType, OUT PUNICODE_STRING RealmName, OUT PKDC_TICKET_INFO TicketInfo, OUT PBOOLEAN Authoritative, OUT PBOOLEAN Referral, OUT PBOOLEAN CrossForest, OUT PKERB_EXT_ERROR pExtendedError, OUT OPTIONAL SAMPR_HANDLE * UserHandle, IN OPTIONAL ULONG WhichFields, IN OPTIONAL ULONG ExtendedFields, OUT OPTIONAL PUSER_INTERNAL6_INFORMATION * UserInfo, OUT OPTIONAL PSID_AND_ATTRIBUTES_LIST GroupMembership ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status; LPWSTR NullTerminatedName = NULL; UNICODE_STRING CrackedDomain = {0}; UNICODE_STRING LocalCrackedString = {0}; LPWSTR CrackedDnsDomain = NULL; ULONG CrackedDomainLength = (DNS_MAX_NAME_LENGTH+1) * sizeof(WCHAR); LPWSTR CrackedName = NULL; ULONG CrackedNameLength = (UNLEN+DNS_MAX_NAME_LENGTH + 2) * sizeof(WCHAR); ULONG CrackError = 0; BOOLEAN Retry = TRUE; BOOLEAN ReferToRoot = FALSE, UsedHack = FALSE; ULONG NameFlags = DS_NAME_FLAG_TRUST_REFERRAL | DS_NAME_FLAG_GCVERIFY; SECPKG_CALL_INFO CallInfo ;
*Authoritative = TRUE;
#ifdef notyet
//
// Check to see if the name is the machine name, and if so, don't try to
// go to the GC.
//
if ((NameType == DS_USER_PRINCIPAL_NAME) && RtlEqualUnicodeString( SecData.MachineUpn(), Upn, TRUE // case insensitive
)) { DebugLog((DEB_ERROR,"Trying to lookup machine upn %wZ on GC - failing early\n", Upn)); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; } #endif
CrackedDnsDomain = (LPWSTR) MIDL_user_allocate(CrackedDomainLength); if (CrackedDnsDomain == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
CrackedName = (LPWSTR) MIDL_user_allocate(CrackedNameLength); if (CrackedName == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// We can only retry for user principal names, which have a simple
// structure.
//
if (NameType != DS_USER_PRINCIPAL_NAME) { Retry = FALSE; } //
// So we didn't find the account locally. Now try the GC.
//
NullTerminatedName = KerbBuildNullTerminatedString( Upn ); if (NullTerminatedName == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
//
// If we are in a recursive state, then we need to *not* go to
// the GC, and try the crack locally. This is because we'll go in
// to a recursive tailspin and die. this is a hack to escape this
// situation until we work out a better soln. with DS folks.
//
if ( LsaIGetCallInfo( &CallInfo ) ) { if ( CallInfo.Attributes & SECPKG_CALL_RECURSIVE ) { UNICODE_STRING GCString; //
// This problem occurs when trying to crack a name of type:
// gc/dc.domain/domain. We're recursive, & trying to contact
// a gc, so make some assumptions about the name.
//
RtlInitUnicodeString( &GCString, KDC_WSZ_GC );
if ((PrincipalName->NameCount == KDC_GC_NAMEPARTS) && RtlEqualUnicodeString( &GCString, &PrincipalName->Names[0], TRUE )) { if (CrackedDnsDomain != NULL) { MIDL_user_free(CrackedDnsDomain); } if (CrackedName !=NULL) { MIDL_user_free(CrackedName); }
// note : these are gaurenteed to be '\0' terminated.
CrackedDnsDomain = PrincipalName->Names[2].Buffer; CrackedName = PrincipalName->Names[1].Buffer; UsedHack = TRUE;
DebugLog(( DEB_WARN, "Special case hack of %ws to '%ws' at domain '%ws'\n", NullTerminatedName, CrackedName, CrackedDnsDomain ));
Status = STATUS_SUCCESS ; CrackError = DS_NAME_NO_ERROR ; goto LocalHack ; }
} }
//
// If we're in the root domain, we could be getting asked for a
// name outside our forest. Look now, and attempt to
// create the ticket info for that external target realm
//
if (SecData.IsForestRoot() && SecData.IsCrossForestEnabled()) { KerbErr = KdcMatchCrossForestName( PrincipalName, &CrackedDomain );
if (KERB_SUCCESS(KerbErr)) { *CrossForest = TRUE;
} else { DebugLog((DEB_WARN, "Did not match UPN %wZ to cross forest\n", &Upn)); }
}
Retry:
if (!(*CrossForest)) {
Status = CrackSingleName( NameType, NameFlags, NullTerminatedName, DS_UNIQUE_ID_NAME, &CrackedDomainLength, CrackedDnsDomain, &CrackedNameLength, CrackedName, &CrackError ); LocalHack:
if ((Status != STATUS_SUCCESS) || ( ( CrackError != DS_NAME_NO_ERROR ) && ( CrackError != DS_NAME_ERROR_DOMAIN_ONLY ) && ( CrackError != DS_NAME_ERROR_TRUST_REFERRAL)) ) { //
// If the name is a duplicate, log an event
//
if (CrackError == DS_NAME_ERROR_NOT_UNIQUE) { WCHAR LookupType[10];
swprintf(LookupType,L"%d",(ULONG) NameType);
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_NAME_NOT_UNIQUE, 0, NULL, 2, NullTerminatedName, LookupType );
}
DebugLog((DEB_WARN,"Failed to resolve name %ws: 0x%x, %d\n", NullTerminatedName, Status ,CrackError ));
if (Retry) { MIDL_user_free(NullTerminatedName); NullTerminatedName = NULL;
KerbErr = KdcBuildNt4Name( &NullTerminatedName, &CrackedDomain, Upn ); if (!KERB_SUCCESS(KerbErr)) { //
// If we got a wrong realm error, then we can return
// a non-authorititive answer
//
if (KerbErr == KDC_ERR_WRONG_REALM) { *Authoritative = FALSE; KerbErr = KDC_ERR_NONE; } else { goto Cleanup; } } else { NameType = DS_NT4_ACCOUNT_NAME; Retry = FALSE;
//
// Reset lengths
//
CrackedDomainLength = (DNS_MAX_NAME_LENGTH+1) * sizeof(WCHAR); CrackedNameLength = (UNLEN+DNS_MAX_NAME_LENGTH + 2) * sizeof(WCHAR); goto Retry; } } else { KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; } } //
// We got a Xforest referral, go to Root domain
//
else if (CrackError == DS_NAME_ERROR_TRUST_REFERRAL) {
RtlInitUnicodeString( &CrackedDomain, CrackedDnsDomain );
*CrossForest = TRUE; } else { RtlInitUnicodeString( &CrackedDomain, CrackedDnsDomain ); } }
//
// Decide whether we can open the account locally.
//
if (SecData.IsOurRealm( &CrackedDomain ))
{ RtlInitUnicodeString( &LocalCrackedString, CrackedName );
KerbErr = KdcGetTicketInfo( &LocalCrackedString, SAM_OPEN_BY_GUID, NULL, NULL, TicketInfo, pExtendedError, UserHandle, WhichFields, ExtendedFields, UserInfo, GroupMembership ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } } else { //
// Now get a referral for the cracked domain name
//
UNICODE_STRING ForestRoot = {0};
Status = SecData.GetKdcForestRoot(&ForestRoot);
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
ReferToRoot = (((NameType == DS_SERVICE_PRINCIPAL_NAME) && (!SecData.IsForestRoot()) && (*CrossForest)));
KerbErr = KdcFindReferralTarget( TicketInfo, RealmName, pExtendedError, (ReferToRoot ? &ForestRoot : &CrackedDomain), FALSE, // not need ExactMatch
FALSE // not inbound
);
KerbFreeString(&ForestRoot);
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } *Referral = TRUE;
// Mark our referral realm as the cracked domain
if (ReferToRoot) { KerbFreeString(RealmName); KerbDuplicateString( RealmName, &CrackedDomain ); } }
Cleanup: if (!UsedHack) { if (CrackedDnsDomain != NULL) { MIDL_user_free(CrackedDnsDomain); } if (CrackedName !=NULL) { MIDL_user_free(CrackedName); } }
if (!*Authoritative) { KerbFreeString(&CrackedDomain); }
if (NullTerminatedName != NULL) { MIDL_user_free(NullTerminatedName); } return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcNormalize
//
// Synopsis: Takes an input name and returns the appropriate ticket
// information or referral information for that name
//
// Effects: If the name is not local, it may call the GC
//
// Arguments: PrincipalName - name to normalize
// PrincipalRealm - Realm that issued principal name
// RequestRealm - Realm field of a KDC request
// NameFlags - flags about name, may be:
// KDC_NAME_CLIENT or KDC_NAME_SERVER
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcNormalize( IN PKERB_INTERNAL_NAME PrincipalName, IN OPTIONAL PUNICODE_STRING PrincipalRealm, IN OPTIONAL PUNICODE_STRING RequestRealm, IN ULONG NameFlags, OUT PBOOLEAN Referral, OUT PUNICODE_STRING RealmName, OUT PKDC_TICKET_INFO TicketInfo, OUT PKERB_EXT_ERROR pExtendedError, OUT OPTIONAL SAMPR_HANDLE * UserHandle, IN OPTIONAL ULONG WhichFields, IN OPTIONAL ULONG ExtendedFields, OUT OPTIONAL PUSER_INTERNAL6_INFORMATION * UserInfo, OUT OPTIONAL PSID_AND_ATTRIBUTES_LIST GroupMembership ) { NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr = KDC_ERR_NONE; BOOLEAN BuildUpn = FALSE; BOOLEAN CheckUpn = FALSE; BOOLEAN CheckSam = FALSE; BOOLEAN CheckGC = FALSE; BOOLEAN Reparse = FALSE; BOOLEAN Authoritative = TRUE; BOOLEAN CheckForInterDomain = FALSE; BOOLEAN ExactMatch = FALSE; BOOLEAN AddTrailingDollar = FALSE; BOOLEAN CrossForest = FALSE; UNICODE_STRING OutputPrincipal = {0}; UNICODE_STRING OutputRealm = {0}; UNICODE_STRING InputName = {0}; ULONG Index; UNICODE_STRING LocalPrincipalName = {0}; UNICODE_STRING Upn = {0}; LPWSTR NullTerminatedName = NULL; WCHAR CrackedDnsDomain[DNS_MAX_NAME_LENGTH+1]; ULONG CrackedDomainLength = sizeof(CrackedDnsDomain); PSID Sid = NULL; UNICODE_STRING SidName = {0};
TRACE(KDC, KdcNormalize, DEB_FUNCTION);
*Referral = FALSE;
if (!ARGUMENT_PRESENT(PrincipalName)) { DebugLog((DEB_ERROR,"KdcNormalize: Null PrincipalName. Failing\n")); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
//
// Make sure the name is not zero length
//
if ((PrincipalName->NameCount == 0) || (PrincipalName->Names[0].Length == 0)) { DebugLog((DEB_ERROR,"KdcNormalize: trying to crack zero length name. Failing\n")); Status = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
D_DebugLog((DEB_T_TICKETS,"Normalizing name:")); D_KerbPrintKdcName(DEB_T_TICKETS, PrincipalName);
//
// Check if we should look at the GC
//
if ((NameFlags & KDC_NAME_CHECK_GC) != 0) { CheckGC = TRUE; }
switch(PrincipalName->NameType) { default: case KRB_NT_UNKNOWN: //
// Drop through to more interesting name types
//
case KRB_NT_SRV_HST: case KRB_NT_SRV_INST: case KRB_NT_PRINCIPAL_AND_ID: case KRB_NT_SRV_INST_AND_ID:
//
// Get the sid from the name
//
KerbErr = KerbExtractSidFromKdcName( PrincipalName, &Sid ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to extract sid from name : ")); KerbPrintKdcName(DEB_ERROR,PrincipalName); goto Cleanup; } if (Sid != NULL) { SidName.MaximumLength = SidName.Length = (USHORT) RtlLengthSid(Sid); SidName.Buffer = (LPWSTR) Sid; }
case KRB_NT_PRINCIPAL: //
// Principal names are just sam names
//
if (PrincipalName->NameCount == 1) {
//
// If the client supplied our realm name, check SAM - otherwise just
// check for a UPN
//
if (SecData.IsOurRealm(RequestRealm)) { CheckSam = TRUE; }
//
// If we don't find it in SAM, build a UPN and look it up.
//
CheckUpn = TRUE; BuildUpn = TRUE; OutputPrincipal = PrincipalName->Names[0]; if (ARGUMENT_PRESENT(RequestRealm)) { OutputRealm = *RequestRealm; } else { OutputRealm = *SecData.KdcDnsRealmName(); } break; }
//
// Drop through
//
//
// Check to see if these are the 'krbtgt' account
//
if ((PrincipalName->NameCount == 2) && RtlEqualUnicodeString( &PrincipalName->Names[0], SecData.KdcServiceName(), TRUE)) // case insensitive
{ //
// Check if this is for a different domain - if it is for our
// domain but the principal domain is different, swap the
// domain name.
//
if (ARGUMENT_PRESENT(PrincipalRealm) && SecData.IsOurRealm( &PrincipalName->Names[1])) { OutputRealm = *PrincipalRealm; } else { OutputRealm = PrincipalName->Names[1]; }
//
// Strip trailing "."
//
if ((OutputRealm.Length > 0) && (OutputRealm.Buffer[-1 + OutputRealm.Length/sizeof(WCHAR)] == L'.')) { OutputRealm.Length -= sizeof(WCHAR); }
if (!SecData.IsOurRealm( &OutputRealm )) { CheckForInterDomain = TRUE; } else { CheckSam = TRUE; }
OutputPrincipal = PrincipalName->Names[0]; break; }
//
// Drop through
//
case KRB_NT_SRV_XHST:
//
// These names can't be SAM names ( SAM doesn't support this name
// type) and can't be interdomain (or it would have be caught up
// above).
//
// Check this name as a upn/spn.
//
CheckUpn = TRUE; BuildUpn = TRUE;
break; case KRB_NT_ENT_PRINCIPAL_AND_ID:
//
// Get the sid from the name
//
KerbErr = KerbExtractSidFromKdcName( PrincipalName, &Sid ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to extract sid from name : ")); KerbPrintKdcName(DEB_ERROR,PrincipalName); goto Cleanup; } if (Sid != NULL) { SidName.MaximumLength = SidName.Length = (USHORT) RtlLengthSid(Sid); SidName.Buffer = (LPWSTR) Sid; }
case KRB_NT_ENTERPRISE_PRINCIPAL: if (PrincipalName->NameCount != 1) { DebugLog((DEB_ERROR,"Enterprise name with more than one name part!\n")); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; break; } OutputPrincipal = PrincipalName->Names[0];
CheckUpn = TRUE;
//
// If the name wasn't found as a UPN/SPN, reparse and try
// in SAM
//
InputName = PrincipalName->Names[0]; Reparse = TRUE; CheckSam = TRUE;
//
// Check for these on the GC
//
OutputRealm = *SecData.KdcDnsRealmName(); break;
case KRB_NT_MS_PRINCIPAL_AND_ID:
//
// Get the sid from the name
//
KerbErr = KerbExtractSidFromKdcName( PrincipalName, &Sid ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to extract sid from name : ")); KerbPrintKdcName(DEB_ERROR,PrincipalName); goto Cleanup; } if (Sid != NULL) { SidName.MaximumLength = SidName.Length = (USHORT) RtlLengthSid(Sid); SidName.Buffer = (LPWSTR) Sid; }
case KRB_NT_MS_PRINCIPAL: //
// These are domainname \ username names
//
if (PrincipalName->NameCount > 2) { DebugLog((DEB_ERROR,"MS principal has more than two name parts:")); KerbPrintKdcName(DEB_ERROR, PrincipalName);
KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
//
// Never check the GC for these names
//
CheckGC = FALSE;
//
// If there are two names, the first one is the principal, the second
// is the realm
//
if (PrincipalName->NameCount == 2) { DebugLog((DEB_WARN,"Client sent 2-part MS principalname!\n")); OutputPrincipal = PrincipalName->Names[0]; OutputRealm = PrincipalName->Names[1];
//
// Strip trailing "."
//
if ((OutputRealm.Length > 0) && (OutputRealm.Buffer[-1 + OutputRealm.Length/sizeof(WCHAR)] == L'.')) { OutputRealm.Length -= sizeof(WCHAR); }
} else { InputName = PrincipalName->Names[0]; Reparse = TRUE; } break;
case KRB_NT_UID: KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; DebugLog((DEB_WARN,"Unsupported name type: %d\n",PrincipalName->NameType)); goto Cleanup; }
//
// If we have a sid name, try that first, as it is the fastest.
//
if (SidName.Buffer != NULL) { D_DebugLog((DEB_T_TICKETS,"Checking sid name\n")); KerbErr = KdcGetTicketInfo( &SidName, SAM_OPEN_BY_SID, NULL, // no principal name
NULL, // no realm name,
TicketInfo, pExtendedError, UserHandle, WhichFields, ExtendedFields, UserInfo, GroupMembership ); if (KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Some errors aren't recoverable
//
if ((KerbErr == KDC_ERR_MUST_USE_USER2USER) || (KerbErr == KDC_ERR_SVC_UNAVAILABLE)) { goto Cleanup; }
}
//
// If we are supposed to reparse, then the name contains a name that we
// have to process. Look for '@' and '\' separators.
//
if (Reparse) {
DsysAssert(InputName.Length > 0);
//
// Find the first backslash or '@', or '.' in the name
//
for (Index = InputName.Length/sizeof(WCHAR) - 1; Index > 0 ; Index-- ) { if ((InputName.Buffer[Index] == L'\\') || (InputName.Buffer[Index] == L'@')) { break; } }
//
// If the name did not have one of those two separators, it
// must have been of the form "name"
//
if (Index == 0) {
OutputRealm = *SecData.KdcDnsRealmName(); OutputPrincipal = InputName;
//
// Lookup this name in SAM.
//
CheckSam = TRUE; } else { //
// The name had a '\' or an '@', so pick appart the two
// pieces.
//
//
// If the separator was an '@' then the second part of the name
// is the realm. If it was an '\' then the first part is the
// realm.
//
if (InputName.Buffer[Index] == L'@') {
OutputPrincipal = InputName; OutputPrincipal.Length = (USHORT) Index * sizeof(WCHAR); OutputPrincipal.MaximumLength = (USHORT) Index * sizeof(WCHAR);
OutputRealm.Buffer = &InputName.Buffer[Index+1]; OutputRealm.Length = (USHORT) (InputName.Length - (Index + 1) * sizeof(WCHAR)); OutputRealm.MaximumLength = OutputRealm.Length;
//
// Strip off a trailing '.'
//
if ((OutputRealm.Length > 0) && (OutputRealm.Buffer[-1 + OutputRealm.Length/sizeof(WCHAR)] == L'.')) { OutputRealm.Length -= sizeof(WCHAR); }
} else { DsysAssert(InputName.Buffer[Index] == L'\\');
OutputRealm = InputName; OutputRealm.Length = (USHORT) Index * sizeof(WCHAR); OutputRealm.MaximumLength = (USHORT) Index * sizeof(WCHAR);
OutputPrincipal.Buffer = &InputName.Buffer[Index+1]; OutputPrincipal.Length = (USHORT) (InputName.Length - (Index + 1) * sizeof(WCHAR)); OutputPrincipal.MaximumLength = OutputPrincipal.Length;
}
}
if ((OutputRealm.Length > 0) && (OutputRealm.Buffer[-1 + OutputRealm.Length/sizeof(WCHAR)] == L'.')) { OutputRealm.Length -= sizeof(WCHAR); }
//
// If the domain portion is not for our domain, don't check sam
//
if (!SecData.IsOurRealm( &OutputRealm ))
{ CheckForInterDomain = TRUE; CheckSam = FALSE; } else { //
// If we don't have a separate realm for the name,
// check for interdomain. This is because cross-realm
// requests have our own realm name in the name but
// another realm name in the domain.
//
if (RtlEqualUnicodeString( &OutputPrincipal, SecData.KdcServiceName(), TRUE )) {
if (ARGUMENT_PRESENT(PrincipalRealm)) { //
// Try the supplied realm. If it is present, and points
// to a different domain, lookup up interdomain
//
OutputRealm = *PrincipalRealm; if (!SecData.IsOurRealm( &OutputRealm )) { CheckForInterDomain = TRUE; CheckSam = FALSE; } else { CheckSam = TRUE; } } else { CheckSam = TRUE; } } else { CheckSam = TRUE; }
}
}
// We could end up with CheckUpn and BuildUpn with both client names
// and spns. We need to allow spns of the type
// service/hostname to be looked up in sam (without appending an @realm
// to it). If KDC_NAME_SERVER is passed, it must be an spn, so, we
// don't add the @realm
if (CheckUpn) { if (BuildUpn && ((NameFlags & KDC_NAME_SERVER) == 0)) { D_DebugLog((DEB_T_TICKETS,"Building UPN\n")); KerbErr = KerbConvertKdcNameToString( &Upn, PrincipalName, ARGUMENT_PRESENT(RequestRealm) ? RequestRealm : SecData.KdcDnsRealmName() ); } else { KerbErr = KerbConvertKdcNameToString( &Upn, PrincipalName, NULL // no realm name
); } if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } D_DebugLog((DEB_T_TICKETS,"Lookup up upn/spn %wZ\n",&Upn));
KerbErr = KdcGetTicketInfo( &Upn, (NameFlags & KDC_NAME_SERVER) ? SAM_OPEN_BY_SPN : SAM_OPEN_BY_UPN, NULL, // no principal name
NULL, // no realm name,
TicketInfo, pExtendedError, UserHandle, WhichFields, ExtendedFields, UserInfo, GroupMembership );
if (KERB_SUCCESS(KerbErr)) { goto Cleanup; } //
// Some errors aren't recoverable
//
if ((KerbErr == KDC_ERR_MUST_USE_USER2USER) || (KerbErr == KDC_ERR_SVC_UNAVAILABLE)) { goto Cleanup; }
} //
// Next check for sam account names, as some of these may later be looked
// up as UPNs
//
if (CheckSam) {
D_DebugLog((DEB_T_TICKETS,"Checking name in SAM\n")); KerbErr = KdcGetTicketInfo( &OutputPrincipal, 0, // no lookup flags means sam name
NULL, // no principal name
NULL, // no realm name,
TicketInfo, pExtendedError, UserHandle, WhichFields, ExtendedFields, UserInfo, GroupMembership ); if (KERB_SUCCESS(KerbErr)) { goto Cleanup; } //
// Some errors aren't recoverable
//
if ((KerbErr == KDC_ERR_MUST_USE_USER2USER) || (KerbErr == KDC_ERR_SVC_UNAVAILABLE)) { goto Cleanup; }
}
//
// Now, depending on which flags are set, try to do different things.
//
if (CheckForInterDomain) { D_DebugLog((DEB_T_TICKETS,"Checking name interdomain\n")); //
// If the target name is not KRBTGT, this must be a referral.
//
if (!RtlEqualUnicodeString( &OutputPrincipal, SecData.KdcServiceName(), TRUE)) // case insensitive
{ *Referral = TRUE;
} if ((NameFlags & KDC_NAME_FOLLOW_REFERRALS) == 0) { //
// We need an exact match on the domain name
//
if (*Referral) { DebugLog((DEB_ERROR,"Client asked for principal in another realm but no referrals!\n")); KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
//
// We also only accept the krbtgt account name
//
ExactMatch = TRUE;
}
KerbErr = KdcFindReferralTarget( TicketInfo, RealmName, pExtendedError, &OutputRealm, ExactMatch, (NameFlags & KDC_NAME_INBOUND) != 0 ); if (KERB_SUCCESS(KerbErr)) { //
// If the output realm & the realm we asked for is different,
// this is a referral (meaning an we don't have a password
// for the principal name on this machine)
//
if (!KerbCompareUnicodeRealmNames( RealmName, &OutputRealm )) { *Referral = TRUE; } goto Cleanup; }
//
// This is an interdomain TGT, potentialy w/ a target outside of our
// forest. Use the crackname call to determine whether or not to
// allow the call to proceed. If so, go to our root domain.
//
if (!SecData.IsForestRoot()) {
// FESTER:
DebugLog((DEB_T_TICKETS, "Calling KdcCheckForCrossForestReferral %wZ:\n", &OutputRealm));
KerbErr = KdcCheckForCrossForestReferral( TicketInfo, RealmName, pExtendedError, &OutputRealm, NameFlags );
if (KERB_SUCCESS(KerbErr)) { DebugLog((DEB_T_TICKETS, "Got referral (%wZ) destined for x forest - %wZ\n", RealmName,&OutputRealm)); *Referral = TRUE; goto Cleanup;
}
//
// Where did this come from???
// Unless we have knowledge of the DNS domain, we're hosed.
//
else { DebugLog((DEB_T_TICKETS, "Recieved interdomain referral with unknown targetname : %wZ\n", &OutputRealm));
KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; // fake error, see below
goto Cleanup; } }
}
if (CheckGC) { if (Upn.Buffer == NULL) { //
// Build the UPN here.
//
D_DebugLog((DEB_T_TICKETS,"Building UPN\n")); KerbErr = KerbConvertKdcNameToString( &Upn, PrincipalName, ARGUMENT_PRESENT(RequestRealm) ? RequestRealm : SecData.KdcDnsRealmName() ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
} DsysAssert(Upn.Buffer != NULL); D_DebugLog((DEB_T_TICKETS,"Checking name %wZ in GC\n", &Upn));
//
// If the caller doesn't want us to follow referrals, fail here.
//
if ((NameFlags & KDC_NAME_FOLLOW_REFERRALS) == 0) { KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; goto Cleanup; }
//
// This will allow us to open sam locally as well
//
KerbErr = KdcCrackNameAtGC( &Upn, PrincipalName, ((NameFlags & KDC_NAME_CLIENT) != 0) ? DS_USER_PRINCIPAL_NAME : DS_SERVICE_PRINCIPAL_NAME, RealmName, TicketInfo, &Authoritative, Referral, &CrossForest, pExtendedError, UserHandle, WhichFields, ExtendedFields, UserInfo, GroupMembership );
goto Cleanup; }
KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN;
Cleanup: KerbFreeString( &Upn ); KerbFreeString( &LocalPrincipalName ); if (Sid != NULL) { MIDL_user_free(Sid); }
if (KerbErr == KDC_ERR_C_PRINCIPAL_UNKNOWN) { if ((NameFlags & KDC_NAME_SERVER) != 0) { KerbErr = KDC_ERR_S_PRINCIPAL_UNKNOWN; } } return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Function: GetTimeStamps
//
// Synopsis: Gets the current time and clock skew envelope.
//
// Arguments: [ptsFudge] -- (in) amount of clock skew to allow.
// [ptsNow] -- (out) the current time
// [ptsNowPlus] -- (out) the current time plus the skew.
// [ptsNowMinus] -- (out) the current time less the skew
//
// History: 4-23-93 WadeR Created
//
//----------------------------------------------------------------------------
void GetTimeStamps( IN PLARGE_INTEGER ptsFudge, OUT PLARGE_INTEGER ptsNow, OUT PLARGE_INTEGER ptsNowPlus, OUT PLARGE_INTEGER ptsNowMinus ) { TRACE(KDC, GetTimeStamps, DEB_FUNCTION);
GetSystemTimeAsFileTime((PFILETIME) ptsNow ); ptsNowPlus->QuadPart = ptsNow->QuadPart + ptsFudge->QuadPart; ptsNowMinus->QuadPart = ptsNow->QuadPart - ptsFudge->QuadPart; }
//+-------------------------------------------------------------------------
//
// Function: KdcBuildTicketTimesAndFlags
//
// Synopsis: Computes the times and flags for a new ticket
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcBuildTicketTimesAndFlags( IN ULONG ClientPolicyFlags, IN ULONG ServerPolicyFlags, IN PLARGE_INTEGER DomainTicketLifespan, IN PLARGE_INTEGER DomainTicketRenewspan, IN OPTIONAL PLARGE_INTEGER LogoffTime, IN OPTIONAL PLARGE_INTEGER AccountExpiry, IN PKERB_KDC_REQUEST_BODY RequestBody, IN OPTIONAL PKERB_ENCRYPTED_TICKET SourceTicket, IN OUT PKERB_ENCRYPTED_TICKET Ticket, IN OUT OPTIONAL PKERB_EXT_ERROR ExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; LARGE_INTEGER RequestEndTime; LARGE_INTEGER RequestStartTime; LARGE_INTEGER RequestRenewTime;
LARGE_INTEGER SourceEndTime; LARGE_INTEGER SourceRenewTime; LARGE_INTEGER SourceStartTime; ULONG SourceTicketFlags = 0; ULONG FinalTicketFlags = 0; ULONG KdcOptions = 0;
LARGE_INTEGER FinalEndTime; LARGE_INTEGER FinalStartTime; LARGE_INTEGER FinalRenewTime; LARGE_INTEGER LocalLogoffTime; BOOLEAN Renewable = FALSE;
LARGE_INTEGER CurrentTime;
TRACE(KDC, KdcBuildTicketTimesAndFlags, DEB_FUNCTION);
GetSystemTimeAsFileTime((PFILETIME)&CurrentTime); FinalEndTime.QuadPart = 0; FinalStartTime.QuadPart = 0; FinalRenewTime.QuadPart = 0;
KdcOptions = KerbConvertFlagsToUlong(&RequestBody->kdc_options);
//
// Get the force logoff time
//
if (ARGUMENT_PRESENT(LogoffTime)) { LocalLogoffTime = *LogoffTime; } else { LocalLogoffTime = tsInfinity; }
//
// Get the request times out of the request
//
if (RequestBody->bit_mask & KERB_KDC_REQUEST_BODY_starttime_present) { KerbConvertGeneralizedTimeToLargeInt( &RequestStartTime, &RequestBody->KERB_KDC_REQUEST_BODY_starttime, NULL ); } else { RequestStartTime.QuadPart = 0; }
KerbConvertGeneralizedTimeToLargeInt( &RequestEndTime, &RequestBody->endtime, NULL );
if (RequestBody->bit_mask & KERB_KDC_REQUEST_BODY_renew_until_present) { KerbConvertGeneralizedTimeToLargeInt( &RequestRenewTime, &RequestBody->KERB_KDC_REQUEST_BODY_renew_until, NULL ); } else { RequestRenewTime.QuadPart = 0; }
//
// Get the times out of the source ticket (if present)
//
if (ARGUMENT_PRESENT(SourceTicket)) { if (SourceTicket->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present) { KerbConvertGeneralizedTimeToLargeInt( &SourceStartTime, &SourceTicket->KERB_ENCRYPTED_TICKET_starttime, NULL );
} else { SourceStartTime.QuadPart = 0; }
KerbConvertGeneralizedTimeToLargeInt( &SourceEndTime, &SourceTicket->endtime, NULL );
if (SourceTicket->bit_mask & KERB_ENCRYPTED_TICKET_renew_until_present) { KerbConvertGeneralizedTimeToLargeInt( &SourceRenewTime, &SourceTicket->KERB_ENCRYPTED_TICKET_renew_until, NULL );
} else { SourceRenewTime.QuadPart = 0; } SourceTicketFlags = KerbConvertFlagsToUlong(&SourceTicket->flags); } else { //
// Set the maximums in this case, which is probably an AS request.
//
SourceStartTime = CurrentTime; SourceEndTime = tsInfinity; SourceRenewTime = tsInfinity; SourceTicketFlags = 0;
//
// Fill in the source flags from what the client policy & domain policy
// allow
//
if ((ClientPolicyFlags & AUTH_REQ_ALLOW_FORWARDABLE) != 0) { SourceTicketFlags |= KERB_TICKET_FLAGS_forwardable; } if ((ClientPolicyFlags & AUTH_REQ_ALLOW_PROXIABLE) != 0) { SourceTicketFlags |= KERB_TICKET_FLAGS_proxiable; } if ((ClientPolicyFlags & AUTH_REQ_ALLOW_POSTDATE) != 0) { SourceTicketFlags |= KERB_TICKET_FLAGS_may_postdate; } if ((ClientPolicyFlags & AUTH_REQ_ALLOW_RENEWABLE) != 0) { SourceTicketFlags |= KERB_TICKET_FLAGS_renewable; }
}
//
// Start computing the flags, from algorithm in RFC1510 appendix A.6
//
//
// Delegation flags
//
if ((ServerPolicyFlags & AUTH_REQ_OK_AS_DELEGATE) != 0) { FinalTicketFlags |= KERB_TICKET_FLAGS_ok_as_delegate; }
//
// Forward flags
//
if (KdcOptions & KERB_KDC_OPTIONS_forwardable) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_forwardable) && (ServerPolicyFlags & AUTH_REQ_ALLOW_FORWARDABLE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_forwardable; } else { DebugLog((DEB_ERROR,"Asked for forwardable but not allowed\n")); // KerbErr = KDC_ERR_BADOPTION;
// goto Cleanup;
} }
if (KdcOptions & KERB_KDC_OPTIONS_forwarded) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_forwardable) && (ServerPolicyFlags & AUTH_REQ_ALLOW_FORWARDABLE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_forwarded; } else { DebugLog((DEB_ERROR,"Asked for forwarded but not allowed\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } }
if (SourceTicketFlags & KERB_TICKET_FLAGS_forwarded) { FinalTicketFlags |= KERB_TICKET_FLAGS_forwarded; }
//
// preauth flags
//
if (SourceTicketFlags & KERB_TICKET_FLAGS_pre_authent) { FinalTicketFlags |= KERB_TICKET_FLAGS_pre_authent; }
//
// Proxy flags
//
if (KdcOptions & KERB_KDC_OPTIONS_proxiable) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_proxiable) && (ServerPolicyFlags & AUTH_REQ_ALLOW_PROXIABLE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_proxiable; } else { DebugLog((DEB_ERROR,"Asked for proxiable but not allowed\n")); // KerbErr = KDC_ERR_BADOPTION;
// goto Cleanup;
} }
if (KdcOptions & KERB_KDC_OPTIONS_proxy) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_proxiable) && (ServerPolicyFlags & AUTH_REQ_ALLOW_PROXIABLE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_proxy; } else { DebugLog((DEB_ERROR,"Asked for proxy but not allowed\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } }
//
// Postdate
//
if (KdcOptions & KERB_KDC_OPTIONS_allow_postdate) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_may_postdate) && (ServerPolicyFlags & AUTH_REQ_ALLOW_POSTDATE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_may_postdate; } else { DebugLog((DEB_ERROR,"Asked for postdate but not allowed\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; } }
if (KdcOptions & KERB_KDC_OPTIONS_postdated) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_may_postdate) && (ServerPolicyFlags & AUTH_REQ_ALLOW_POSTDATE)) { FinalTicketFlags |= KERB_TICKET_FLAGS_postdated | KERB_TICKET_FLAGS_invalid;
//
// Start time is required here
//
if (RequestStartTime.QuadPart == 0) { DebugLog((DEB_ERROR, "Asked for postdate but start time not present\n")); KerbErr = KDC_ERR_CANNOT_POSTDATE; goto Cleanup; }
} }
//
// Validate
//
if (KdcOptions & KERB_KDC_OPTIONS_validate) { if ((SourceTicketFlags & KERB_TICKET_FLAGS_invalid) == 0) { DebugLog((DEB_ERROR,"Trying to validate a valid ticket\n")); KerbErr = KDC_ERR_POLICY; goto Cleanup; } if ((SourceStartTime.QuadPart == 0) || (SourceStartTime.QuadPart < CurrentTime.QuadPart - SkewTime.QuadPart)) { DebugLog((DEB_ERROR,"Trying to validate a ticket before it is valid\n")); KerbErr = KRB_AP_ERR_TKT_NYV; goto Cleanup; } }
//
// Start filling in time fields
//
if (ARGUMENT_PRESENT(SourceTicket)) { Ticket->authtime = SourceTicket->authtime; } else { KerbConvertLargeIntToGeneralizedTime( &Ticket->authtime, NULL, &CurrentTime );
}
//
// The times are computed differently for renewing a ticket and for
// getting a new ticket.
//
if ((KdcOptions & KERB_KDC_OPTIONS_renew) != 0) { if ((SourceRenewTime.QuadPart == 0) || (SourceStartTime.QuadPart == 0) || ((SourceTicketFlags & KERB_TICKET_FLAGS_renewable) == 0) || ((ServerPolicyFlags & AUTH_REQ_ALLOW_RENEWABLE) == 0)) { DebugLog((DEB_ERROR,"Trying to renew a non-renewable ticket or against policy\n")); KerbErr = KDC_ERR_BADOPTION; goto Cleanup; }
//
// Make sure the renew time is in the future
//
if (SourceRenewTime.QuadPart < CurrentTime.QuadPart) { DebugLog((DEB_ERROR, "Trying to renew a ticket past its renew time\n")); KerbErr = KRB_AP_ERR_TKT_EXPIRED; goto Cleanup; }
//
// Make sure the end time is in the past
//
if (SourceEndTime.QuadPart < CurrentTime.QuadPart) { DebugLog((DEB_ERROR, "Trying to renew an expired ticket\n")); KerbErr = KRB_AP_ERR_TKT_EXPIRED; goto Cleanup; } FinalStartTime = CurrentTime;
//
// The end time is the minimum of the current time plus lifespan
// of the old ticket and the renew until time of the old ticket
//
FinalEndTime.QuadPart = CurrentTime.QuadPart + (SourceEndTime.QuadPart - SourceStartTime.QuadPart); if (FinalEndTime.QuadPart > SourceRenewTime.QuadPart) { FinalEndTime = SourceRenewTime; } FinalRenewTime = SourceRenewTime; FinalTicketFlags = SourceTicketFlags;
Renewable = TRUE; } else { //
// Compute start and end times for normal tickets
//
//
// Set the start time
//
if (RequestStartTime.QuadPart == 0) { FinalStartTime = CurrentTime; } else { FinalStartTime = RequestStartTime; }
//
// Set the end time
//
if (RequestEndTime.QuadPart == 0) { FinalEndTime = tsInfinity; } else { FinalEndTime = RequestEndTime; }
if (FinalEndTime.QuadPart > SourceEndTime.QuadPart) { FinalEndTime = SourceEndTime; }
if (FinalEndTime.QuadPart > CurrentTime.QuadPart + DomainTicketLifespan->QuadPart) { FinalEndTime.QuadPart = CurrentTime.QuadPart + DomainTicketLifespan->QuadPart; }
//
// Check for renewable-ok
//
if ((KdcOptions & KERB_KDC_OPTIONS_renewable_ok) && (FinalEndTime.QuadPart < RequestEndTime.QuadPart) && (SourceTicketFlags & KERB_TICKET_FLAGS_renewable)) { KdcOptions |= KERB_KDC_OPTIONS_renewable; RequestRenewTime = RequestEndTime;
//
// Make sure that the source ticket had a renewtime (it
// should because it is renewable)
//
DsysAssert(SourceRenewTime.QuadPart != 0); if (RequestRenewTime.QuadPart > SourceRenewTime.QuadPart) { RequestRenewTime = SourceRenewTime; } } }
if (!Renewable) { //
// Compute renew times
//
if (RequestRenewTime.QuadPart == 0) { RequestRenewTime = tsInfinity; }
if ((KdcOptions & KERB_KDC_OPTIONS_renewable) && (SourceTicketFlags & KERB_TICKET_FLAGS_renewable) && (ServerPolicyFlags & AUTH_REQ_ALLOW_RENEWABLE)) { FinalRenewTime = RequestRenewTime; if (FinalRenewTime.QuadPart > FinalStartTime.QuadPart + DomainTicketRenewspan->QuadPart) { FinalRenewTime.QuadPart = FinalStartTime.QuadPart + DomainTicketRenewspan->QuadPart; }
DsysAssert(SourceRenewTime.QuadPart != 0);
if (FinalRenewTime.QuadPart > SourceRenewTime.QuadPart) { FinalRenewTime = SourceRenewTime; } FinalTicketFlags |= KERB_TICKET_FLAGS_renewable;
} else { FinalRenewTime.QuadPart = 0; } }
//
// Make sure the final ticket is valid
//
if (FinalStartTime.QuadPart > FinalEndTime.QuadPart) { DebugLog((DEB_ERROR,"Client asked for endtime before starttime\n")); KerbErr = KDC_ERR_BADOPTION;
FILL_EXT_ERROR_EX(ExtendedError, STATUS_TIME_DIFFERENCE_AT_DC, FILENO, __LINE__); goto Cleanup; }
//
// Don't bother with this check - it doesn't really hurt to have a
// renew time less than an end time
//
//
// if ((FinalRenewTime.QuadPart != 0) &&
// (FinalRenewTime.QuadPart < FinalEndTime.QuadPart))
// {
// DebugLog((DEB_ERROR,"Client asked for renew time before endtime\n"));
// KerbErr = KDC_ERR_BADOPTION;
// goto Cleanup;
// }
//
//
// Incorporate the logoff time (according to logon hours) by reseting
// both the final end time and final renew time
//
if (FinalEndTime.QuadPart > LocalLogoffTime.QuadPart) { FinalEndTime.QuadPart = LocalLogoffTime.QuadPart; }
if (FinalRenewTime.QuadPart > LocalLogoffTime.QuadPart) { FinalRenewTime.QuadPart = LocalLogoffTime.QuadPart; }
//
// Tickets good only until acct expires.
// We make the assumption that the sam has
// already checked this against the current time
// when we're checking the logon restrictions.
//
if ((ARGUMENT_PRESENT(AccountExpiry) && (AccountExpiry->QuadPart != 0 ))) { if (FinalEndTime.QuadPart > AccountExpiry->QuadPart) { FinalEndTime.QuadPart = AccountExpiry->QuadPart; }
if (FinalRenewTime.QuadPart > AccountExpiry->QuadPart) { FinalRenewTime.QuadPart = AccountExpiry->QuadPart; }
}
//
// Fill in the times in the ticket
//
KerbConvertLargeIntToGeneralizedTime( &Ticket->KERB_ENCRYPTED_TICKET_starttime, NULL, &FinalStartTime ); Ticket->bit_mask |= KERB_ENCRYPTED_TICKET_starttime_present;
KerbConvertLargeIntToGeneralizedTime( &Ticket->endtime, NULL, &FinalEndTime );
if (FinalRenewTime.QuadPart != 0) { KerbConvertLargeIntToGeneralizedTime( &Ticket->KERB_ENCRYPTED_TICKET_renew_until, NULL, &FinalRenewTime ); Ticket->bit_mask |= KERB_ENCRYPTED_TICKET_renew_until_present;
}
//
// Copy in the flags
//
DsysAssert(Ticket->flags.length == sizeof(ULONG) * 8); *((PULONG) Ticket->flags.value) = KerbConvertUlongToFlagUlong(FinalTicketFlags); Cleanup: return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcGetUserKeys
//
// Synopsis: retrieves user keys
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcGetUserKeys( IN SAMPR_HANDLE UserHandle, IN PUSER_INTERNAL6_INFORMATION UserInfo, OUT PKERB_STORED_CREDENTIAL * Passwords, OUT PKERB_STORED_CREDENTIAL * OldPasswords, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status; PKERB_STORED_CREDENTIAL Keys = NULL; PKERB_STORED_CREDENTIAL StoredCreds = NULL; PKERB_STORED_CREDENTIAL Cred64 = NULL; ULONG CredentialSize = 0; ULONG NewCredentialCount = 0; ULONG NewCredentialSize = 0; ULONG Index, CredentialIndex = 0, Offset; USHORT Flags = 0; PUCHAR Base; BOOLEAN UseStoredCreds = FALSE, UnMarshalledCreds = FALSE; PCRYPTO_SYSTEM NullCryptoSystem = NULL; BOOLEAN UseBuiltins = TRUE; PNT_OWF_PASSWORD OldNtPassword = NULL; NT_OWF_PASSWORD OldPasswordData = {0}; PUSER_ALL_INFORMATION UserAll = &UserInfo->I1;
TRACE(KDC, KdcGetUserKeys, DEB_FUNCTION);
//
// First get any primary credentials
//
Status = SamIRetrievePrimaryCredentials( UserHandle, &GlobalKerberosName, (PVOID *) &StoredCreds, &CredentialSize );
//
// if there is not value, it's O.K we will default to using
// Builtin credentials
//
if (STATUS_DS_NO_ATTRIBUTE_OR_VALUE==Status) { Status = STATUS_SUCCESS; }
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to retrieve primary credentials: 0x%x\n",Status)); KerbErr = KRB_ERR_GENERIC; FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__); goto Cleanup; }
//
// We need to unmarshall these creds from the DS
// They'll be stored in 32 bit format, but we've
// got to put them into 64 bit format.
// KerbUnpack32BitStoredCredential()
//
#ifdef _WIN64
Status = KdcUnpack32BitStoredCredential( (PKERB_STORED_CREDENTIAL32) StoredCreds, &Cred64, &CredentialSize );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to unpack 32bit stored credential, contact Todds - %x\n", Status)); DsysAssert(FALSE); // FATAL - If we ever fail above, contact Todds
goto Cleanup; }
if (NULL != StoredCreds) { LocalFree(StoredCreds); StoredCreds = Cred64; UnMarshalledCreds = TRUE; // diff't allocator
}
#endif
//
// First compute the current passwords
//
//
// Figure out the size of the stored credentials
//
if ((StoredCreds != NULL) && (CredentialSize > sizeof(KERB_STORED_CREDENTIAL) && (StoredCreds->Revision == KERB_PRIMARY_CRED_REVISION) && (CredentialSize > (sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) + StoredCreds->CredentialCount * sizeof(KERB_KEY_DATA) ))) && (StoredCreds->DefaultSalt.Length + (ULONG_PTR) StoredCreds->DefaultSalt.Buffer <= CredentialSize )) { UseStoredCreds = TRUE; Flags = StoredCreds->Flags; if ((UserAll->UserAccountControl & USER_USE_DES_KEY_ONLY) != 0) { UseBuiltins = FALSE; }
NewCredentialSize += StoredCreds->DefaultSalt.Length;
for (Index = 0; Index < StoredCreds->CredentialCount ; Index++ ) { //
// Validat the offsets
//
if ((StoredCreds->Credentials[Index].Key.keyvalue.length + (ULONG_PTR) StoredCreds->Credentials[Index].Key.keyvalue.value <= CredentialSize ) && (StoredCreds->Credentials[Index].Salt.Length + (ULONG_PTR) StoredCreds->Credentials[Index].Salt.Buffer <= CredentialSize ))
{ NewCredentialCount++; NewCredentialSize += sizeof(KERB_KEY_DATA) + StoredCreds->Credentials[Index].Key.keyvalue.length + StoredCreds->Credentials[Index].Salt.Length; } else { LPWSTR Buff = NULL;
DebugLog((DEB_ERROR,"Corrupt credentials for user %wZ\n", &UserAll->UserName ));
DsysAssert(FALSE);
Buff = KerbBuildNullTerminatedString(&UserAll->UserName); if (NULL == Buff) { break; }
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_CORRUPT_CREDENTIALS, 0, // no raw data
NULL, // no raw data
1, // number of strings
Buff );
UseStoredCreds = FALSE; NewCredentialCount = 0; NewCredentialSize = 0;
if (NULL != Buff) { MIDL_user_free(Buff); }
break; }
}
}
//
// If the password length is the size of the OWF password, use it as the
// key. Otherwise hash it. This is for the case where not password is
// set.
//
if (UseBuiltins) { //
// Add a key for RC4_HMAC_NT
//
if (UserAll->NtPasswordPresent) { NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++;
} #ifndef DONT_SUPPORT_OLD_TYPES_KDC
//
// Add a key for RC4_HMAC_OLD & MD4_RC4
if (UserAll->NtPasswordPresent) { NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++;
NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++;
} #endif
//
// if there is no password, treat it as blank
//
if (!(UserAll->LmPasswordPresent || UserAll->NtPasswordPresent)) { NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++; } #ifndef DONT_SUPPORT_OLD_TYPES_KDC
if (!(UserAll->LmPasswordPresent || UserAll->NtPasswordPresent)) { NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++;
NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++; } #endif
}
//
// Add a key for the null crypto system
//
Status = CDLocateCSystem( KERB_ETYPE_NULL, &NullCryptoSystem ); if (NT_SUCCESS(Status)) { NewCredentialSize += sizeof(KERB_KEY_DATA) + NullCryptoSystem->KeySize; NewCredentialCount++; }
//
// Add the space for the base structure
//
NewCredentialSize += sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA));
//
// Allocate space for the credentials and start filling them in
//
Keys = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(NewCredentialSize); if (Keys == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } RtlZeroMemory( Keys, NewCredentialSize );
Offset = sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) + NewCredentialCount * sizeof(KERB_KEY_DATA);
Base = (PUCHAR) Keys;
Keys->CredentialCount = (USHORT) NewCredentialCount; Keys->OldCredentialCount = 0; Keys->Revision = KERB_PRIMARY_CRED_REVISION; Keys->Flags = Flags;
//
// Add the credentials built from the OWF passwords first.
//
if (UseBuiltins) { //
// Create the key for RC4_HMAC_NT
//
if (UserAll->NtPasswordPresent) { RtlCopyMemory( Base+Offset, UserAll->NtPassword.Buffer, UserAll->NtPassword.Length );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_HMAC_NT ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Offset += UserAll->NtPassword.Length;
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset); CredentialIndex++; } #ifndef DONT_SUPPORT_OLD_TYPES_KDC
//
// Create the key for RC4_HMAC_OLD
//
if (UserAll->NtPasswordPresent) { RtlCopyMemory( Base+Offset, UserAll->NtPassword.Buffer, UserAll->NtPassword.Length );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_HMAC_OLD ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } Offset += UserAll->NtPassword.Length; //
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
CredentialIndex++;
RtlCopyMemory( Base+Offset, UserAll->NtPassword.Buffer, UserAll->NtPassword.Length );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_MD4 ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } Offset += UserAll->NtPassword.Length;
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset); CredentialIndex++;
} #endif
//
// If no passwords were present, add the null password
//
if (!(UserAll->LmPasswordPresent || UserAll->NtPasswordPresent)) { KERB_ENCRYPTION_KEY TempKey; UNICODE_STRING NullString; RtlInitUnicodeString( &NullString, NULL ); KerbErr = KerbHashPassword( &NullString, KERB_ETYPE_RC4_HMAC_NT, &TempKey ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Keys->Credentials[CredentialIndex].Key = TempKey; Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset; RtlCopyMemory( Base+Offset, TempKey.keyvalue.value, TempKey.keyvalue.length );
Offset += TempKey.keyvalue.length;
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
CredentialIndex++; KerbFreeKey(&TempKey);
} #ifndef DONT_SUPPORT_OLD_TYPES_KDC
if (!(UserAll->LmPasswordPresent || UserAll->NtPasswordPresent)) { KERB_ENCRYPTION_KEY TempKey; UNICODE_STRING NullString; RtlInitUnicodeString( &NullString, NULL ); KerbErr = KerbHashPassword( &NullString, KERB_ETYPE_RC4_HMAC_OLD, &TempKey ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Keys->Credentials[CredentialIndex].Key = TempKey; Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset; RtlCopyMemory( Base+Offset, TempKey.keyvalue.value, TempKey.keyvalue.length );
Offset += TempKey.keyvalue.length;
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
CredentialIndex++; KerbFreeKey(&TempKey);
KerbErr = KerbHashPassword( &NullString, KERB_ETYPE_RC4_MD4, &TempKey ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
Keys->Credentials[CredentialIndex].Key = TempKey; Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset; RtlCopyMemory( Base+Offset, TempKey.keyvalue.value, TempKey.keyvalue.length );
Offset += TempKey.keyvalue.length; CredentialIndex++; KerbFreeKey(&TempKey); }
#endif
} //
// Add the null crypto system
//
if (NullCryptoSystem != NULL) { UNICODE_STRING NullString; RtlInitUnicodeString( &NullString, NULL );
Status = NullCryptoSystem->HashString( &NullString, Base+Offset ); DsysAssert(NT_SUCCESS(Status));
Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base+Offset; Keys->Credentials[CredentialIndex].Key.keyvalue.length = NullCryptoSystem->KeySize; Keys->Credentials[CredentialIndex].Key.keytype = KERB_ETYPE_NULL;
Offset += NullCryptoSystem->KeySize;
CredentialIndex++;
}
//
// Now add the stored passwords
//
if (UseStoredCreds) { //
// Copy the default salt
//
if (StoredCreds->DefaultSalt.Buffer != NULL) { Keys->DefaultSalt.Buffer = (LPWSTR) (Base+Offset);
RtlCopyMemory( Base + Offset, (PBYTE) StoredCreds->DefaultSalt.Buffer + (ULONG_PTR) StoredCreds, StoredCreds->DefaultSalt.Length ); Offset += StoredCreds->DefaultSalt.Length; Keys->DefaultSalt.Length = Keys->DefaultSalt.MaximumLength = StoredCreds->DefaultSalt.Length; }
for (Index = 0; Index < StoredCreds->CredentialCount ; Index++ ) { //
// Copy the key
//
Keys->Credentials[CredentialIndex] = StoredCreds->Credentials[Index]; Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base+Offset; RtlCopyMemory( Keys->Credentials[CredentialIndex].Key.keyvalue.value, StoredCreds->Credentials[Index].Key.keyvalue.value + (ULONG_PTR) StoredCreds, StoredCreds->Credentials[Index].Key.keyvalue.length ); Offset += StoredCreds->Credentials[Index].Key.keyvalue.length;
//
// Copy the salt
//
if (StoredCreds->Credentials[Index].Salt.Buffer != NULL) { Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base+Offset);
RtlCopyMemory( Base + Offset, (PBYTE) StoredCreds->Credentials[Index].Salt.Buffer + (ULONG_PTR) StoredCreds, StoredCreds->Credentials[Index].Salt.Length ); Offset += StoredCreds->Credentials[Index].Salt.Length; Keys->Credentials[CredentialIndex].Salt.Length = Keys->Credentials[CredentialIndex].Salt.MaximumLength = StoredCreds->Credentials[Index].Salt.Length; }
CredentialIndex++; } }
DsysAssert(CredentialIndex == NewCredentialCount); DsysAssert(Offset == NewCredentialSize); *Passwords = Keys; Keys = NULL;
//
// Now compute the old passwords
//
//
// Figure out the size of the stored credentials
//
NewCredentialCount = 0; NewCredentialSize = 0; CredentialIndex = 0;
if ((StoredCreds != NULL) && (CredentialSize > sizeof(KERB_STORED_CREDENTIAL) && (StoredCreds->Revision == KERB_PRIMARY_CRED_REVISION) && (CredentialSize > (sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) + (StoredCreds->OldCredentialCount + StoredCreds->CredentialCount) * sizeof(KERB_KEY_DATA) )))) { UseStoredCreds = TRUE; Flags = StoredCreds->Flags; if ((UserAll->UserAccountControl & USER_USE_DES_KEY_ONLY) != 0) { UseBuiltins = FALSE; }
for (Index = 0; Index < StoredCreds->OldCredentialCount ; Index++ ) { if (StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.length + (ULONG_PTR) StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.value <= CredentialSize ) { NewCredentialCount++; NewCredentialSize += sizeof(KERB_KEY_DATA) + StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.length; } else { LPWSTR Buff = NULL;
DebugLog((DEB_ERROR,"Corrupt credentials for user %wZ\n", &UserAll->UserName ));
DsysAssert(FALSE);
Buff = KerbBuildNullTerminatedString(&UserAll->UserName); if (NULL == Buff) { break; }
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_CORRUPT_CREDENTIALS, 0, // no raw data
NULL, // no raw data
1, // number of strings
Buff );
if (NULL != Buff) { MIDL_user_free(Buff); }
UseStoredCreds = FALSE; NewCredentialCount = 0; NewCredentialSize = 0; break; }
}
}
//
// If the password length is the size of the OWF password, use it as the
// key. Otherwise hash it. This is for the case where not password is
// set.
//
if (UseBuiltins) { PSAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE PrivateData;
if (UserAll->PrivateData.Length >= sizeof(SAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE)) { PrivateData= (PSAMI_PRIVATE_DATA_PASSWORD_RELATIVE_TYPE) UserAll->PrivateData.Buffer; if (PrivateData->DataType == SamPrivateDataPassword) { //
// The old password is the 2nd entry
//
if (PrivateData->NtPasswordHistory.Length >= 2* sizeof(ENCRYPTED_NT_OWF_PASSWORD)) { //
// Decrypt the old password with the RID. The history starts
// at the first byte after the structure.
//
Status = RtlDecryptNtOwfPwdWithIndex( (PENCRYPTED_NT_OWF_PASSWORD) (PrivateData + 1) + 1, (PLONG)&UserAll->UserId, &OldPasswordData ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to decrypt old password: 0x%x\n",Status)); KerbErr = KRB_ERR_GENERIC; goto Cleanup; } OldNtPassword = &OldPasswordData ; } } } if (OldNtPassword != NULL) { //
// Add an RC4_HMAC_NT key
//
NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD); NewCredentialCount++;
#ifndef DONT_SUPPORT_OLD_TYPES_KDC
//
// Add a key for RC4_HMAC_OLD & RC4_MD4
//
NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD);
NewCredentialCount++; NewCredentialSize += sizeof(KERB_KEY_DATA) + sizeof(NT_OWF_PASSWORD);
NewCredentialCount++; #endif
}
}
//
// Add the space for the base structure
//
NewCredentialSize += sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA));
//
// Allocate space for the credentials and start filling them in
//
Keys = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(NewCredentialSize); if (Keys == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } RtlZeroMemory( Keys, NewCredentialSize );
Offset = sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) + NewCredentialCount * sizeof(KERB_KEY_DATA);
Base = (PUCHAR) Keys;
Keys->CredentialCount = (USHORT) NewCredentialCount; Keys->OldCredentialCount = 0; Keys->Revision = KERB_PRIMARY_CRED_REVISION; Keys->Flags = Flags;
//
// Add the credentials built from the OWF passwords first. We don't
// include a blank password or the null crypt system because they
// were present in the normal password
//
if (UseBuiltins) { //
// Create the key for RC4_HMAC_NT
//
if (OldNtPassword != NULL) {
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
RtlCopyMemory( Base+Offset, OldNtPassword, NT_OWF_PASSWORD_LENGTH );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex++].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_HMAC_NT ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
// We don't know any other way to get the old password length. If
// it was present, it must be sizeof(NT_OWF_PASSWORD)
Offset += sizeof(NT_OWF_PASSWORD);
#ifndef DONT_SUPPORT_OLD_TYPES_KDC
//
// Create the key for RC4_HMAC_OLD
//
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
RtlCopyMemory( Base+Offset, OldNtPassword, NT_OWF_PASSWORD_LENGTH );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex++].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_HMAC_OLD ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
// We don't know any other way to get the old password length. If
// it was present, it must be sizeof(NT_OWF_PASSWORD)
Offset += sizeof(NT_OWF_PASSWORD);
//
// Create the key for RC4_HMAC_OLD
//
//
// Set an empty salt for this key
//
Keys->Credentials[CredentialIndex].Salt.Length = 0; Keys->Credentials[CredentialIndex].Salt.MaximumLength = 0; Keys->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) (Base + Offset);
RtlCopyMemory( Base+Offset, OldNtPassword, NT_OWF_PASSWORD_LENGTH );
KerbErr = KerbCreateKeyFromBuffer( &Keys->Credentials[CredentialIndex++].Key, Base+Offset, UserAll->NtPassword.Length, KERB_ETYPE_RC4_MD4 ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
// We don't know any other way to get the old password length. If
// it was present, it must be sizeof(NT_OWF_PASSWORD)
Offset += sizeof(NT_OWF_PASSWORD);
#endif
} } //
// Now add the stored passwords
//
if (UseStoredCreds) { for (Index = 0; Index < StoredCreds->OldCredentialCount ; Index++ ) { Keys->Credentials[CredentialIndex] = StoredCreds->Credentials[StoredCreds->CredentialCount + Index]; Keys->Credentials[CredentialIndex].Key.keyvalue.value = Base+Offset; RtlCopyMemory( Keys->Credentials[CredentialIndex].Key.keyvalue.value, StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.value + (ULONG_PTR) StoredCreds, StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.length ); Offset += StoredCreds->Credentials[StoredCreds->CredentialCount + Index].Key.keyvalue.length;
//
// Note - don't use salt here.
//
CredentialIndex++; } }
DsysAssert(CredentialIndex == NewCredentialCount); DsysAssert(Offset == NewCredentialSize); *OldPasswords = Keys; Keys = NULL;
KerbErr = KDC_ERR_NONE;
Cleanup: if (StoredCreds != NULL) { if (!UnMarshalledCreds) { LocalFree(StoredCreds); } else { MIDL_user_free(Cred64); } } if (Keys != NULL) { MIDL_user_free(Keys); } return(KerbErr);
}
//+-------------------------------------------------------------------------
//
// Function: KerbDuplicateCredentials
//
// Synopsis: Copies a set of credentials (passwords)
//
// Effects: allocates output with MIDL_user_allocate
//
// Arguments: NewCredentials - recevies new set of credentials
// OldCredentials - contains credentials to copy
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcDuplicateCredentials( OUT PKERB_STORED_CREDENTIAL * NewCredentials, OUT PULONG ReturnCredentialSize, IN PKERB_STORED_CREDENTIAL OldCredentials, IN BOOLEAN MarshallKeys ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_STORED_CREDENTIAL Credential = NULL; ULONG CredentialSize; USHORT Index; PBYTE Where; LONG_PTR Offset;
TRACE(KDC, KdcDuplicateCredentials, DEB_FUNCTION);
//
// If there were no credentials, so be it. We can live with that.
//
if (OldCredentials == NULL) { *NewCredentials = NULL; goto Cleanup; }
//
// Calculate the size of the new credentials by summing the size of
// the base structure plus the keys
//
CredentialSize = sizeof(KERB_STORED_CREDENTIAL) - ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA) + OldCredentials->CredentialCount * sizeof(KERB_KEY_DATA) + OldCredentials->DefaultSalt.Length; for ( Index = 0; Index < OldCredentials->CredentialCount + OldCredentials->OldCredentialCount ; Index++ ) { CredentialSize += OldCredentials->Credentials[Index].Key.keyvalue.length + OldCredentials->Credentials[Index].Salt.Length; }
//
// Allocate the new credential and copy over the old credentials
//
Credential = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(CredentialSize); if (Credential == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
Credential->Revision = OldCredentials->Revision; Credential->Flags = OldCredentials->Flags; Credential->CredentialCount = OldCredentials->CredentialCount; Credential->OldCredentialCount = OldCredentials->OldCredentialCount;
//
// Set the offset for data to be after the last array element
//
RtlCopyMemory( &Credential->Credentials[0], &OldCredentials->Credentials[0], OldCredentials->CredentialCount * sizeof(KERB_KEY_DATA) );
Where = (PBYTE) &Credential->Credentials[Credential->CredentialCount];
if (MarshallKeys) { Offset = (LONG_PTR) Credential; } else { Offset = 0; }
Credential->DefaultSalt = OldCredentials->DefaultSalt; if (Credential->DefaultSalt.Buffer != NULL) { Credential->DefaultSalt.Buffer = (LPWSTR) (Where - Offset); RtlCopyMemory( Where, OldCredentials->DefaultSalt.Buffer, Credential->DefaultSalt.Length ); Where += Credential->DefaultSalt.Length; }
for ( Index = 0; Index < OldCredentials->CredentialCount + OldCredentials->OldCredentialCount ; Index++ ) { Credential->Credentials[Index] = OldCredentials->Credentials[Index]; Credential->Credentials[Index].Key.keyvalue.value = (Where - Offset); RtlCopyMemory( Where, OldCredentials->Credentials[Index].Key.keyvalue.value, OldCredentials->Credentials[Index].Key.keyvalue.length ); Where += OldCredentials->Credentials[Index].Key.keyvalue.length;
if (Credential->Credentials[Index].Salt.Buffer != NULL) { Credential->Credentials[Index].Salt.Buffer = (LPWSTR) (Where - Offset);
RtlCopyMemory( Where, OldCredentials->Credentials[Index].Salt.Buffer, OldCredentials->Credentials[Index].Salt.Length ); Where += OldCredentials->Credentials[Index].Salt.Length; } } DsysAssert(Where - (PUCHAR) Credential == (LONG) CredentialSize);
*NewCredentials = Credential; Credential = NULL; *ReturnCredentialSize = CredentialSize;
Cleanup: if (Credential != NULL) { MIDL_user_free(Credential); } return(KerbErr); }
//+---------------------------------------------------------------------------
//
// Function: KdcGetTicketInfo
//
// Synopsis: Gets the info needed to build a ticket for a principal
// using name of the principal.
//
//
// Effects:
//
// Arguments: Name -- (in) Normalized of principal
// LookupFlags - Flags for SamIGetUserLogonInformation
// PrincipalName - (in) non-normalized principal name
// Realm - (in) client realm, to be used when mapping principals
// from another domain onto accounts in this one.
// TicketInfo -- (out) Ticket info.
// pExtendedError -- (out) Extended error
// UserHandle - receives handle to the user
// WhichFields - optionally specifies additional fields to fetch for RetUserInfo
// ExtendedFields - optionally specifies extended fields to fetch for RetUserInfo
// RetUserInfo - Optionally receives the user all info structure
// GroupMembership - Optionally receives the user's group membership
//
// Returns: KerbErr
//
// Algorithm:
//
// History: 10-Nov-93 WadeR Created
// 22-Mar-95 SuChang Modified to use RIDs
//
//
//----------------------------------------------------------------------------
KERBERR KdcGetTicketInfo( IN PUNICODE_STRING GenericUserName, IN ULONG LookupFlags, IN OPTIONAL PKERB_INTERNAL_NAME PrincipalName, IN OPTIONAL PKERB_REALM Realm, OUT PKDC_TICKET_INFO TicketInfo, OUT PKERB_EXT_ERROR pExtendedError, OUT OPTIONAL SAMPR_HANDLE * UserHandle, IN OPTIONAL ULONG WhichFields, IN OPTIONAL ULONG ExtendedFields, OUT OPTIONAL PUSER_INTERNAL6_INFORMATION * RetUserInfo, OUT OPTIONAL PSID_AND_ATTRIBUTES_LIST GroupMembership ) { NTSTATUS Status = STATUS_NOT_FOUND; KERBERR KerbErr = KDC_ERR_NONE; SID_AND_ATTRIBUTES_LIST LocalMembership; SAMPR_HANDLE LocalUserHandle = NULL; PUSER_INTERNAL6_INFORMATION UserInfo = NULL; PUSER_ALL_INFORMATION UserAll; GUID testGuid; UNICODE_STRING TUserName = {0}; PUNICODE_STRING UserName = NULL; UNICODE_STRING AlternateName = {0}; UNICODE_STRING TempString = {0}; BOOL IsValidGuid = FALSE;
//
// Add the fields we are going to require locally to the WhichFields parameter
//
WhichFields |= USER_ALL_KDC_GET_USER_KEYS | USER_ALL_PASSWORDMUSTCHANGE | USER_ALL_USERACCOUNTCONTROL | USER_ALL_USERID | USER_ALL_USERNAME;
TUserName = *GenericUserName; UserName = &TUserName;
TRACE(KDC, GetTicketInfo, DEB_FUNCTION);
RtlZeroMemory(TicketInfo, sizeof(KDC_TICKET_INFO)); RtlZeroMemory(&LocalMembership, sizeof(SID_AND_ATTRIBUTES_LIST));
if (ARGUMENT_PRESENT( RetUserInfo )) { *RetUserInfo = NULL; }
if (!ARGUMENT_PRESENT(GroupMembership)) { LookupFlags |= SAM_NO_MEMBERSHIPS; }
//
// If this is the krbtgt account, use the cached version
//
if (!ARGUMENT_PRESENT(UserHandle) && !ARGUMENT_PRESENT(RetUserInfo) && !ARGUMENT_PRESENT(GroupMembership) && (KdcState == Running) && RtlEqualUnicodeString( SecData.KdcServiceName(), UserName, TRUE // case insensitive
)) { //
// Duplicate the cached copy of the KRBTGT information
//
D_DebugLog((DEB_T_TICKETS, "Using cached ticket info for krbtgt account\n"));
KerbErr = SecData.GetKrbtgtTicketInfo( TicketInfo ); if (KERB_SUCCESS(KerbErr)) { goto Cleanup; }
}
//
// If we cracked this name at a dc, and it's the same domain, we will
// not be able to generate a referral ticket. So, we need to be able
// to open the sam locally. Further more, crack name and sam lookups
// are much faster with guids (even though we have to do the string
// to guid operation.
//
if (LookupFlags & SAM_OPEN_BY_GUID) { if (IsValidGuid = IsStringGuid(UserName->Buffer, &testGuid)) { UserName->Buffer = (LPWSTR)&testGuid; } }
//
// The caller may provide an empty user name so as to force the lookup
// using the AltSecId. This is used when mapping names from an MIT realm
//
if (UserName->Length > 0) { //
// Open the user account
//
if (IsValidGuid) { D_DebugLog(( DEB_TRACE, "Looking for account %wZ\n", UserName )); } else { D_DebugLog(( DEB_TRACE, "Looking for account %wZ mapped to Guid\n", GenericUserName )); KerbPrintGuid (DEB_TRACE, "", &testGuid); }
Status = SamIGetUserLogonInformation2( GlobalAccountDomainHandle, LookupFlags, UserName, WhichFields, ExtendedFields, &UserInfo, &LocalMembership, &LocalUserHandle );
//
// WASBUG: For now, if we couldn't find the account try again
// with a '$' at the end (if there wasn't one already)
//
if (((Status == STATUS_NOT_FOUND) || (Status == STATUS_NO_SUCH_USER)) && (!IsValidGuid) && ((LookupFlags & ~SAM_NO_MEMBERSHIPS) == 0) && (UserName->Length >= sizeof(WCHAR)) && (UserName->Buffer[UserName->Length/sizeof(WCHAR)-1] != L'$')) { Status = KerbDuplicateString( &TempString, UserName ); if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } DsysAssert(TempString.MaximumLength >= TempString.Length + sizeof(WCHAR)); TempString.Buffer[TempString.Length/sizeof(WCHAR)] = L'$'; TempString.Length += sizeof(WCHAR);
D_DebugLog((DEB_TRACE, "Account not found ,trying machine account %wZ\n", &TempString ));
Status = SamIGetUserLogonInformation2( GlobalAccountDomainHandle, LookupFlags, &TempString, WhichFields, ExtendedFields, &UserInfo, &LocalMembership, &LocalUserHandle );
}
}
//
// If we still can't find the account, try the altsecid using
// the supplied principal name.
//
if (((Status == STATUS_NOT_FOUND) || (Status == STATUS_NO_SUCH_USER)) && ARGUMENT_PRESENT(PrincipalName) ) { KerbErr = KerbBuildAltSecId( &AlternateName, PrincipalName, Realm, NULL // no unicode realm name
);
if (KERB_SUCCESS(KerbErr)) { D_DebugLog((DEB_TRACE,"User account not found, trying alternate id: %wZ\n",&AlternateName )); LookupFlags |= SAM_OPEN_BY_ALTERNATE_ID,
Status = SamIGetUserLogonInformation2( GlobalAccountDomainHandle, LookupFlags, &AlternateName, WhichFields, ExtendedFields, &UserInfo, &LocalMembership, &LocalUserHandle );
} }
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN,"Could not open User %wZ: 0x%x\n", UserName, Status ));
if ((Status == STATUS_NO_SUCH_USER) || (Status == STATUS_NOT_FOUND) || (Status == STATUS_OBJECT_NAME_NOT_FOUND)) { KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN; } else if (Status == STATUS_NO_LOGON_SERVERS) { //If there's no GC, this sam call returns STATUS_NO_LOGON_SERVERS
//and we should return this to the client. see bug 226073
FILL_EXT_ERROR(pExtendedError, Status,FILENO, __LINE__); KerbErr = KDC_ERR_SVC_UNAVAILABLE; } else if (Status != STATUS_INVALID_SERVER_STATE) { WCHAR LookupType[10]; WCHAR AccountName[MAX_PATH+1]; PUNICODE_STRING LookupName = NULL;
if (AlternateName.Buffer != NULL) { LookupName = &AlternateName; } else if (TempString.Buffer != NULL) { LookupName = &TempString; } else { LookupName = UserName; } if (LookupName->Length < MAX_PATH * sizeof(WCHAR)) { RtlCopyMemory( AccountName, LookupName->Buffer, LookupName->Length ); AccountName[LookupName->Length/sizeof(WCHAR)] = L'\0'; } else { RtlCopyMemory( AccountName, LookupName->Buffer, MAX_PATH * sizeof(WCHAR) ); AccountName[MAX_PATH] = L'\0'; }
//
// Log name collisions separately to provide
// more information
//
if (Status == STATUS_OBJECT_NAME_COLLISION) { DS_NAME_FORMAT NameFormat = DS_UNKNOWN_NAME; if ((LookupFlags & SAM_OPEN_BY_UPN) != 0) { NameFormat = DS_USER_PRINCIPAL_NAME; } else if ((LookupFlags & SAM_OPEN_BY_SPN) != 0) { NameFormat = DS_SERVICE_PRINCIPAL_NAME; }
// Potentially deadly error, pass back to caller.
FILL_EXT_ERROR(pExtendedError, Status,FILENO, __LINE__); swprintf(LookupType,L"%d",(ULONG) NameFormat);
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_NAME_NOT_UNIQUE, 0, NULL, 2, AccountName, LookupType );
KerbErr = KDC_ERR_PRINCIPAL_NOT_UNIQUE; } else { swprintf(LookupType,L"0x%x",LookupFlags);
ReportServiceEvent( EVENTLOG_ERROR_TYPE, KDCEVENT_SAM_CALL_FAILED, sizeof(NTSTATUS), &Status, 2, AccountName, LookupType );
KerbErr = KRB_ERR_GENERIC; } } else { // Potentially deadly error, pass back to caller.
FILL_EXT_ERROR(pExtendedError, Status,FILENO, __LINE__); KerbErr = KRB_ERR_GENERIC; } goto Cleanup; }
UserAll = &UserInfo->I1;
KerbErr = KdcGetUserKeys( LocalUserHandle, UserInfo, &TicketInfo->Passwords, &TicketInfo->OldPasswords, pExtendedError );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to get user keys: 0x%x\n",KerbErr)); goto Cleanup; }
if (!NT_SUCCESS(KerbDuplicateString( &TicketInfo->AccountName, &UserAll->UserName ))) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
if ((UserAll->UserAccountControl & USER_TRUSTED_FOR_DELEGATION) != 0) { TicketInfo->fTicketOpts |= AUTH_REQ_OK_AS_DELEGATE; }
//
// TBD: S4U(3)
// Check user account control flag here for s4u2self restrictions
//
//
// TBD: S4UProxy (A)
// Add forwardable flag when you find T2A4D flag (trusted to authenticate
// for delegation).
//
if ((UserAll->UserAccountControl & USER_NOT_DELEGATED) == 0) { TicketInfo->fTicketOpts |= AUTH_REQ_ALLOW_FORWARDABLE | AUTH_REQ_ALLOW_PROXIABLE; }
TicketInfo->fTicketOpts |= AUTH_REQ_ALLOW_POSTDATE | AUTH_REQ_ALLOW_RENEWABLE | AUTH_REQ_ALLOW_NOADDRESS | AUTH_REQ_ALLOW_ENC_TKT_IN_SKEY | AUTH_REQ_ALLOW_VALIDATE; //
// These flags aren't turned on by default:
// AUTH_REQ_VALIDATE_CLIENT
//
//
// Mask with the domain policy flags
//
TicketInfo->fTicketOpts &= SecData.KdcFlags();
TicketInfo->PasswordExpires = UserAll->PasswordMustChange;
TicketInfo->UserAccountControl = UserAll->UserAccountControl; TicketInfo->UserId = UserAll->UserId;
if (ARGUMENT_PRESENT(RetUserInfo)) { *RetUserInfo = UserInfo; UserInfo = NULL; } if (ARGUMENT_PRESENT(GroupMembership)) { *GroupMembership = LocalMembership; RtlZeroMemory( &LocalMembership, sizeof(SID_AND_ATTRIBUTES_LIST) ); }
if (ARGUMENT_PRESENT(UserHandle)) { *UserHandle = LocalUserHandle; LocalUserHandle = NULL; }
Cleanup:
KerbFreeString( &TempString ); KerbFreeString( &AlternateName );
SamIFree_UserInternal6Information( UserInfo );
if (LocalUserHandle != NULL) { SamrCloseHandle(&LocalUserHandle); }
SamIFreeSidAndAttributesList( &LocalMembership );
return KerbErr; }
//+---------------------------------------------------------------------------
//
// Function: FreeTicketInfo
//
// Synopsis: Frees a KDC_TICKET_INFO structure
//
// Effects:
//
// Arguments:
//
// Returns:
//
// Algorithm:
//
// History: 22-Mar-95 SuChang Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID FreeTicketInfo( IN PKDC_TICKET_INFO TicketInfo ) { TRACE(KDC, FreeTicketInfo, DEB_FUNCTION);
if (ARGUMENT_PRESENT(TicketInfo)) { KerbFreeString( &TicketInfo->AccountName ); KerbFreeString( &TicketInfo->TrustedForest );
if (TicketInfo->Passwords != NULL) { MIDL_user_free(TicketInfo->Passwords); TicketInfo->Passwords = NULL; } if (TicketInfo->OldPasswords != NULL) { MIDL_user_free(TicketInfo->OldPasswords); TicketInfo->OldPasswords = NULL; } if (TicketInfo->TrustSid != NULL) { MIDL_user_free(TicketInfo->TrustSid); TicketInfo->TrustSid = NULL; } } }
//--------------------------------------------------------------------
//
// Name: BuildReply
//
// Synopsis: Extracts reply information from an internal ticket
//
// Arguments: pkitTicket - (in) ticket data comes from
// dwNonce - (in) goes into the reply
// pkrReply - (out) reply that is built
//
// Notes: BUG 456265: Need to set tsKeyExpiry properly.
// See 3.1.3, 3.3.3 of the Kerberos V5 R5.2 spec
// tsKeyExpiry is zero for GetTGSTicket, and the
// expiry time of the client's key for GetASTicket.
//
//--------------------------------------------------------------------
KERBERR BuildReply( IN OPTIONAL PKDC_TICKET_INFO ClientInfo, IN ULONG Nonce, IN PKERB_PRINCIPAL_NAME ServerName, IN KERB_REALM ServerRealm, IN PKERB_HOST_ADDRESSES HostAddresses, IN PKERB_TICKET Ticket, OUT PKERB_ENCRYPTED_KDC_REPLY ReplyMessage ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_ENCRYPTED_TICKET EncryptedTicket = (PKERB_ENCRYPTED_TICKET) Ticket->encrypted_part.cipher_text.value; KERB_ENCRYPTED_KDC_REPLY ReplyBody; LARGE_INTEGER CurrentTime;
TRACE(KDC, BuildReply, DEB_FUNCTION);
RtlZeroMemory( &ReplyBody, sizeof(KERB_ENCRYPTED_KDC_REPLY) );
//
// Use the same flags field
//
ReplyBody.flags = ReplyMessage->flags;
ReplyBody.session_key = EncryptedTicket->key;
ReplyBody.last_request = (PKERB_LAST_REQUEST) MIDL_user_allocate(sizeof(KERB_LAST_REQUEST)); if (ReplyBody.last_request == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } RtlZeroMemory( ReplyBody.last_request, sizeof(KERB_LAST_REQUEST) );
ReplyBody.last_request->next = NULL; ReplyBody.last_request->value.last_request_type = 0; GetSystemTimeAsFileTime((PFILETIME)&CurrentTime);
KerbConvertLargeIntToGeneralizedTime( &ReplyBody.last_request->value.last_request_value, NULL, // no usec
&CurrentTime );
ReplyBody.nonce = Nonce;
DsysAssert((ReplyBody.flags.length == EncryptedTicket->flags.length) && (ReplyBody.flags.length== 8 * sizeof(ULONG)));
//
// Assign the flags over
//
*((PULONG)ReplyBody.flags.value) = *((PULONG)EncryptedTicket->flags.value);
if (ARGUMENT_PRESENT(ClientInfo)) {
KerbConvertLargeIntToGeneralizedTime( &ReplyBody.key_expiration, NULL, &ClientInfo->PasswordExpires ); ReplyBody.bit_mask |= key_expiration_present; }
//
// Fill in the times
//
ReplyBody.authtime = EncryptedTicket->authtime; ReplyBody.endtime = EncryptedTicket->endtime;
if (EncryptedTicket->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present) { ReplyBody.KERB_ENCRYPTED_KDC_REPLY_starttime = EncryptedTicket->KERB_ENCRYPTED_TICKET_starttime; ReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_starttime_present;
}
if (EncryptedTicket->bit_mask & KERB_ENCRYPTED_TICKET_renew_until_present) { ReplyBody.KERB_ENCRYPTED_KDC_REPLY_renew_until = EncryptedTicket->KERB_ENCRYPTED_TICKET_renew_until; ReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_renew_until_present; }
ReplyBody.server_realm = ServerRealm;
ReplyBody.server_name = *ServerName;
//
// Fill in the host addresses
//
if (HostAddresses != NULL) { ReplyBody.KERB_ENCRYPTED_KDC_REPLY_client_addresses = HostAddresses; ReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_client_addresses_present; }
*ReplyMessage = ReplyBody; Cleanup:
if (!KERB_SUCCESS(KerbErr)) { if (ReplyBody.last_request != NULL) { MIDL_user_free(ReplyBody.last_request); ReplyBody.last_request = NULL; }
}
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcBuildSupplementalCredentials
//
// Synopsis: Builds a list of the supplemental credentials and then
// encrypts it with the supplied key
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcBuildSupplementalCredentials( IN PUSER_INTERNAL6_INFORMATION UserInfo, IN PKERB_ENCRYPTION_KEY CredentialKey, OUT PPAC_INFO_BUFFER * EncryptedCreds ) { NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; PBYTE Credentials = NULL; ULONG CredentialSize = 0; KERB_ENCRYPTED_DATA EncryptedData = {0}; PPAC_CREDENTIAL_INFO CredInfo = NULL; ULONG EncryptionOverhead; ULONG BlockSize; PPAC_INFO_BUFFER ReturnCreds = NULL; ULONG ReturnCredSize;
Status = PAC_BuildCredentials( (PSAMPR_USER_ALL_INFORMATION)&UserInfo->I1, &Credentials, &CredentialSize );
if (!NT_SUCCESS(Status)) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
KerbErr = KerbAllocateEncryptionBufferWrapper( CredentialKey->keytype, CredentialSize, &EncryptedData.cipher_text.length, &EncryptedData.cipher_text.value );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbEncryptDataEx( &EncryptedData, CredentialSize, Credentials, CredentialKey->keytype, KERB_NON_KERB_SALT, CredentialKey );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Compute the size of the blob returned
//
ReturnCredSize = sizeof(PAC_INFO_BUFFER) + FIELD_OFFSET(PAC_CREDENTIAL_INFO, Data) + EncryptedData.cipher_text.length ;
ReturnCreds = (PPAC_INFO_BUFFER) MIDL_user_allocate(ReturnCredSize); if (ReturnCreds == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } //
// Fill in the outer structure
//
ReturnCreds->ulType = PAC_CREDENTIAL_TYPE; ReturnCreds->cbBufferSize = ReturnCredSize - sizeof(PAC_INFO_BUFFER); ReturnCreds->Data = (PBYTE) (ReturnCreds + 1); CredInfo = (PPAC_CREDENTIAL_INFO) ReturnCreds->Data;
//
// Fill in the inner structure
//
CredInfo->EncryptionType = EncryptedData.encryption_type; if (EncryptedData.bit_mask & version_present) { CredInfo->Version = EncryptedData.version; } else { CredInfo->Version = 0; }
RtlCopyMemory( CredInfo->Data, EncryptedData.cipher_text.value, EncryptedData.cipher_text.length );
*EncryptedCreds = ReturnCreds; ReturnCreds = NULL; Cleanup: if (Credentials != NULL) { MIDL_user_free(Credentials); } if (ReturnCreds != NULL) { MIDL_user_free(ReturnCreds); } if (EncryptedData.cipher_text.value != NULL) { MIDL_user_free(EncryptedData.cipher_text.value); } return(KerbErr);
}
//+-------------------------------------------------------------------------
//
// Function: KdcBuildPacVerifier
//
// Synopsis: Builds a verifier for the PAC. This structure ties the
// PAC to the ticket by including the client name and
// ticket authime in the PAC.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcBuildPacVerifier( IN PTimeStamp ClientId, IN PUNICODE_STRING ClientName, OUT PPAC_INFO_BUFFER * Verifier ) { ULONG ReturnVerifierSize = 0; PPAC_CLIENT_INFO ClientInfo = NULL; PPAC_INFO_BUFFER ReturnVerifier = NULL;
//
// Compute the size of the blob returned
//
ReturnVerifierSize = sizeof(PAC_INFO_BUFFER) + sizeof(PAC_CLIENT_INFO) + ClientName->Length - sizeof(WCHAR) * ANYSIZE_ARRAY;
ReturnVerifier = (PPAC_INFO_BUFFER) MIDL_user_allocate(ReturnVerifierSize); if (ReturnVerifier == NULL) { return(KRB_ERR_GENERIC); } //
// Fill in the outer structure
//
ReturnVerifier->ulType = PAC_CLIENT_INFO_TYPE; ReturnVerifier->cbBufferSize = ReturnVerifierSize - sizeof(PAC_INFO_BUFFER); ReturnVerifier->Data = (PBYTE) (ReturnVerifier + 1); ClientInfo = (PPAC_CLIENT_INFO) ReturnVerifier->Data; ClientInfo->ClientId = *ClientId; ClientInfo->NameLength = ClientName->Length; RtlCopyMemory( ClientInfo->Name, ClientName->Buffer, ClientName->Length ); *Verifier = ReturnVerifier;
return(KDC_ERR_NONE); }
//+---------------------------------------------------------------------------
//
// Name: GetPacAndSuppCred
//
// Synopsis:
//
// Arguments:
//
// Notes: ppPac is allocated using CoTaskMemAlloc and
// ppadlSuppCreds is allocated using operator 'new'.
//
//+---------------------------------------------------------------------------
KERBERR GetPacAndSuppCred( IN PUSER_INTERNAL6_INFORMATION UserInfo, IN PSID_AND_ATTRIBUTES_LIST GroupMembership, IN ULONG SignatureSize, IN OPTIONAL PKERB_ENCRYPTION_KEY CredentialKey, IN OPTIONAL PTimeStamp ClientId, IN OPTIONAL PUNICODE_STRING ClientName, OUT PPACTYPE * Pac, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status; PPACTYPE NewPac = NULL; PPAC_INFO_BUFFER Credentials[2]; ULONG AdditionalDataCount = 0;
TRACE(KDC, GetPACandSuppCred, DEB_FUNCTION);
RtlZeroMemory( Credentials, sizeof(Credentials) );
*Pac = NULL;
if (ARGUMENT_PRESENT(CredentialKey) && (CredentialKey->keyvalue.value != NULL) ) { KerbErr = KdcBuildSupplementalCredentials( UserInfo, CredentialKey, &Credentials[AdditionalDataCount] ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
AdditionalDataCount++; }
if (ARGUMENT_PRESENT(ClientId)) { KerbErr = KdcBuildPacVerifier( ClientId, ClientName, &Credentials[AdditionalDataCount] ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
AdditionalDataCount++; }
//
// NOTE: this is o.k. because we don't lock the secdata while acecssing
// the machine name
//
Status = PAC_Init( (PSAMPR_USER_ALL_INFORMATION)&UserInfo->I1, NULL, GroupMembership, GlobalDomainSid, &GlobalDomainName, SecData.MachineName(), SignatureSize, AdditionalDataCount, Credentials, &NewPac );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to init pac: 0x%x\n",Status)); FILL_EXT_ERROR(pExtendedError, Status, FILENO, __LINE__); KerbErr = KRB_ERR_GENERIC; goto Cleanup; }
*Pac = NewPac; NewPac = NULL;
Cleanup:
while (AdditionalDataCount > 0) { if (Credentials[--AdditionalDataCount] != NULL) { MIDL_user_free(Credentials[AdditionalDataCount]); } }
if (NewPac != NULL) { MIDL_user_free(NewPac); }
return(KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KdcFreeInternalTicket
//
// Synopsis: frees a constructed ticket
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KdcFreeInternalTicket( IN PKERB_TICKET Ticket ) { PKERB_ENCRYPTED_TICKET EncryptedTicket = (PKERB_ENCRYPTED_TICKET) Ticket->encrypted_part.cipher_text.value;
TRACE(KDC, KdcFreeInternalTicket, DEB_FUNCTION);
if (EncryptedTicket != NULL) { KerbFreeKey( &EncryptedTicket->key ); KerbFreePrincipalName(&EncryptedTicket->client_name); if (EncryptedTicket->KERB_ENCRYPTED_TICKET_authorization_data != NULL) { KerbFreeAuthData(EncryptedTicket->KERB_ENCRYPTED_TICKET_authorization_data); } if (EncryptedTicket->transited.contents.value != NULL) { MIDL_user_free(EncryptedTicket->transited.contents.value); } }
KerbFreePrincipalName( &Ticket->server_name ); }
//+-------------------------------------------------------------------------
//
// Function: KdcFreeKdcReply
//
// Synopsis: frees a kdc reply
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KdcFreeKdcReply( IN PKERB_KDC_REPLY Reply ) { TRACE(KDC, KdcFreeKdcReply, DEB_FUNCTION);
KerbFreePrincipalName(&Reply->ticket.server_name);
if (Reply->ticket.encrypted_part.cipher_text.value != NULL) { MIDL_user_free(Reply->ticket.encrypted_part.cipher_text.value); }
if (Reply->encrypted_part.cipher_text.value != NULL) { MIDL_user_free(Reply->encrypted_part.cipher_text.value); } if (Reply->KERB_KDC_REPLY_preauth_data != NULL) { KerbFreePreAuthData((PKERB_PA_DATA_LIST) Reply->KERB_KDC_REPLY_preauth_data); }
}
//+-------------------------------------------------------------------------
//
// Function: KdcFreeKdcReplyBody
//
// Synopsis: frees a constructed KDC reply body
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KdcFreeKdcReplyBody( IN PKERB_ENCRYPTED_KDC_REPLY ReplyBody ) {
TRACE(KDC, KdcFreeKdcReplyBody, DEB_FUNCTION);
//
// The names & the session key are just pointers into the ticket,
// so they don't need to be freed.
//
if (ReplyBody->last_request != NULL) { MIDL_user_free(ReplyBody->last_request); ReplyBody->last_request = NULL; } ReplyBody->KERB_ENCRYPTED_KDC_REPLY_client_addresses = NULL;
}
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyClientAddress
//
// Synopsis: Verifies that the client address is an allowed sender of
// the KDC request.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcVerifyClientAddress( IN SOCKADDR * ClientAddress, IN PKERB_HOST_ADDRESSES Addresses ) { PKERB_HOST_ADDRESSES TempAddress = Addresses;
//
// ISSUE-2001/03/05-markpu
// This routine is wholly inadequate in that it only deals with IPv4
// addresses. Address matching has to be more elaborate than that
//
while (TempAddress != NULL) { if ( TempAddress->value.address_type == KERB_ADDRTYPE_INET && ClientAddress->sa_family == AF_INET ) { struct sockaddr_in * InetAddress = (struct sockaddr_in *) ClientAddress;
//
// Check that the addresses match
//
if (TempAddress->value.address.length == sizeof(ULONG)) { if (RtlEqualMemory( TempAddress->value.address.value, &InetAddress->sin_addr.S_un.S_addr, sizeof(ULONG) )) { return(KDC_ERR_NONE); } }
}
TempAddress = TempAddress->next; }
D_DebugLog((DEB_WARN,"Client address not in address list\n"));
//
// Need to return KRB_AP_ERR_BADADDR but a couple of things must
// be fixed first (client code notified of changes to the machine's IP
// address, copying addresses from the TGT into the TGS request)
//
return(KDC_ERR_NONE); }
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyTgsChecksum
//
// Synopsis: Verify the checksum on a TGS request
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcVerifyTgsChecksum( IN PKERB_KDC_REQUEST_BODY RequestBody, IN PKERB_ENCRYPTION_KEY Key, IN PKERB_CHECKSUM OldChecksum ) { PCHECKSUM_FUNCTION ChecksumFunction; PCHECKSUM_BUFFER CheckBuffer = NULL; KERB_MESSAGE_BUFFER MarshalledRequestBody = {0, NULL}; NTSTATUS Status; KERBERR KerbErr = KDC_ERR_NONE; KERB_CHECKSUM Checksum = {0};
Status = CDLocateCheckSum( OldChecksum->checksum_type, &ChecksumFunction ); if (!NT_SUCCESS(Status)) { KerbErr = KDC_ERR_SUMTYPE_NOSUPP; goto Cleanup; }
//
// Allocate enough space for the checksum
//
Checksum.checksum.value = (PUCHAR) MIDL_user_allocate(ChecksumFunction->CheckSumSize); if (Checksum.checksum.value == NULL) { KerbErr = KRB_ERR_GENERIC; goto Cleanup; } Checksum.checksum.length = ChecksumFunction->CheckSumSize;
//
// Initialize the checksum
//
if ((OldChecksum->checksum_type == KERB_CHECKSUM_REAL_CRC32) || (OldChecksum->checksum_type == KERB_CHECKSUM_CRC32)) { if (ChecksumFunction->Initialize) { Status = ChecksumFunction->Initialize( 0, &CheckBuffer ); } else {
KerbErr = KRB_ERR_GENERIC; } } else { if (NULL != ChecksumFunction->InitializeEx2) { Status = ChecksumFunction->InitializeEx2( Key->keyvalue.value, (ULONG) Key->keyvalue.length, OldChecksum->checksum.value, KERB_TGS_REQ_AP_REQ_AUTH_CKSUM_SALT, &CheckBuffer ); } else if (ChecksumFunction->InitializeEx) { Status = ChecksumFunction->InitializeEx( Key->keyvalue.value, (ULONG) Key->keyvalue.length, KERB_TGS_REQ_AP_REQ_AUTH_CKSUM_SALT, &CheckBuffer ); } else { KerbErr = KRB_ERR_GENERIC; } }
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbPackData( RequestBody, KERB_MARSHALLED_REQUEST_BODY_PDU, &MarshalledRequestBody.BufferSize, &MarshalledRequestBody.Buffer );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
//
// Now checksum the buffer
//
ChecksumFunction->Sum( CheckBuffer, MarshalledRequestBody.BufferSize, MarshalledRequestBody.Buffer );
ChecksumFunction->Finalize( CheckBuffer, Checksum.checksum.value );
//
// Now compare
//
if ((OldChecksum->checksum.length != Checksum.checksum.length) || !RtlEqualMemory( OldChecksum->checksum.value, Checksum.checksum.value, Checksum.checksum.length )) { DebugLog((DEB_ERROR,"Checksum on TGS request body did not match\n")); KerbErr = KRB_AP_ERR_MODIFIED; goto Cleanup; }
Cleanup: if (CheckBuffer != NULL) { ChecksumFunction->Finish(&CheckBuffer); } if (MarshalledRequestBody.Buffer != NULL) { MIDL_user_free(MarshalledRequestBody.Buffer); } if (Checksum.checksum.value != NULL) { MIDL_user_free(Checksum.checksum.value); } return(KerbErr);
}
//+-------------------------------------------------------------------------
//
// Function: KdcVerifyKdcRequest
//
// Synopsis: Verifies that the AP request accompanying a TGS or PAC request
// is valid.
//
// Effects:
//
// Arguments: ApRequest - The AP request to verify
// UnmarshalledRequest - The unmarshalled request,
// returned to avoid needing to
// EncryptedTicket - Receives the ticket granting ticket
// SessionKey - receives the key to use in the reply
//
// Requires:
//
// Returns: kerberos errors
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KdcVerifyKdcRequest( IN PUCHAR RequestMessage, IN ULONG RequestSize, IN OPTIONAL SOCKADDR * ClientAddress, IN BOOLEAN IsKdcRequest, OUT OPTIONAL PKERB_AP_REQUEST * UnmarshalledRequest, OUT OPTIONAL PKERB_AUTHENTICATOR * UnmarshalledAuthenticator, OUT PKERB_ENCRYPTED_TICKET *EncryptedTicket, OUT PKERB_ENCRYPTION_KEY SessionKey, OUT OPTIONAL PKERB_ENCRYPTION_KEY ServerKey, OUT PKDC_TICKET_INFO ServerTicketInfo, OUT PBOOLEAN UseSubKey, OUT PKERB_EXT_ERROR pExtendedError ) { KERBERR KerbErr = KDC_ERR_NONE; PKERB_AP_REQUEST Request = NULL; PKERB_ENCRYPTED_TICKET EncryptPart = NULL; PKERB_AUTHENTICATOR Authenticator = NULL; PKERB_INTERNAL_NAME ServerName = NULL; UNICODE_STRING LocalServerName = {0}; UNICODE_STRING LocalServerRealm = {0}; UNICODE_STRING LocalServerPrincipal = {0}; UNICODE_STRING ServerRealm; PKERB_ENCRYPTION_KEY KeyToUse; BOOLEAN Referral = FALSE; BOOLEAN Retry = TRUE;
TRACE(KDC, KdcVerifyKdcRequest, DEB_FUNCTION);
ServerRealm.Buffer = NULL;
//
// First unpack the KDC request.
//
KerbErr = KerbUnpackApRequest( RequestMessage, RequestSize, &Request ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to unpack KDC request: 0x%x\n",KerbErr)); KerbErr = KDC_ERR_NO_RESPONSE; goto Cleanup; }
KerbErr = KerbConvertPrincipalNameToKdcName( &ServerName, &Request->ticket.server_name ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; }
KerbErr = KerbConvertRealmToUnicodeString( &ServerRealm, &Request->ticket.realm );
if (!KERB_SUCCESS(KerbErr)) { goto Cleanup;
}
//
// Get ticket info for the server
//
KerbErr = KdcNormalize( ServerName, &ServerRealm, &ServerRealm, KDC_NAME_SERVER | KDC_NAME_INBOUND, &Referral, &LocalServerRealm, ServerTicketInfo, pExtendedError, NULL, // no user handle
0L, // no fields to fetch
0L, // no extended fields
NULL, // no user all
NULL // no group membership
); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_WARN, "Trying to get TGS ticket to service in another realm: ")); KerbPrintKdcName(DEB_WARN, ServerName); goto Cleanup; }
//
// Now Check the ticket
//
Retry:
KeyToUse = KerbGetKeyFromList( ServerTicketInfo->Passwords, Request->ticket.encrypted_part.encryption_type );
if (KeyToUse == NULL) { DebugLog((DEB_ERROR,"Server does not have proper key to decrypt ticket: 0x%x\n", Request->ticket.encrypted_part.encryption_type ));
//
// If this is our second attempt, do not overwrite the error code we had
//
if (KERB_SUCCESS(KerbErr)) { KerbErr = KDC_ERR_ETYPE_NOTSUPP; }
goto Cleanup; }
//
// We don't need to supply a service name or service because we've looked up
// the account locally.
//
KerbErr = KerbCheckTicket( &Request->ticket, &Request->authenticator, KeyToUse, Authenticators, &SkewTime, 0, // zero service names
NULL, // any service
NULL, FALSE, // don't check for replay
IsKdcRequest, &EncryptPart, &Authenticator, NULL, SessionKey, UseSubKey );
if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Failed to check ticket : 0x%x for",KerbErr)); KerbPrintKdcName(DEB_ERROR,ServerName );
//
// If an old password is available, give it a try. We remove the
// current password & put the old password in here.
//
if (ServerTicketInfo->OldPasswords && (KerbErr == KRB_AP_ERR_MODIFIED)) { MIDL_user_free(ServerTicketInfo->Passwords); ServerTicketInfo->Passwords = ServerTicketInfo->OldPasswords; ServerTicketInfo->OldPasswords = NULL; goto Retry; }
//
// Here's the case where we're trying to use an expired TGT. Have
// the client retry using a new TGT
//
if (KerbErr == KRB_AP_ERR_TKT_EXPIRED) { FILL_EXT_ERROR_EX(pExtendedError, STATUS_TIME_DIFFERENCE_AT_DC, FILENO, __LINE__); }
goto Cleanup; }
//
// Verify the address from the ticket
//
if ((EncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_client_addresses_present) && ARGUMENT_PRESENT(ClientAddress))
{ ULONG TicketFlags = KerbConvertFlagsToUlong(&EncryptPart->flags);
//
// Only check initial tickets
//
if ((TicketFlags & (KERB_TICKET_FLAGS_forwarded | KERB_TICKET_FLAGS_proxy)) == 0) { KerbErr = KdcVerifyClientAddress( ClientAddress, EncryptPart->KERB_ENCRYPTED_TICKET_client_addresses ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR,"Client sent request with wrong address\n")); goto Cleanup; } } }
//
// Verify that if the server is a trusted domain account, that it is an
// acceptable ticket (transitively). Verify that for non transitive
// trust the client realm is the same as the requesting ticket realm
//
if (((ServerTicketInfo->fTicketOpts & AUTH_REQ_TRANSITIVE_TRUST) == 0) && (ServerTicketInfo->UserId != DOMAIN_USER_RID_KRBTGT)) { if (!KerbCompareRealmNames( &EncryptPart->client_realm, &Request->ticket.realm )) { DebugLog((DEB_WARN,"Client from realm %s attempted to access non transitve trust via %s : illegal\n", &EncryptPart->client_realm, &Request->ticket.realm ));
KerbErr = KDC_ERR_PATH_NOT_ACCEPTED; goto Cleanup; } }
//
// If the caller wanted the server key, return it here.
//
if (ServerKey != NULL) { KerbErr = KerbDuplicateKey( ServerKey, KeyToUse ); if (!KERB_SUCCESS(KerbErr)) { goto Cleanup; } }
*EncryptedTicket = EncryptPart; EncryptPart = NULL; if (ARGUMENT_PRESENT(UnmarshalledRequest)) { *UnmarshalledRequest = Request; Request = NULL; } if (ARGUMENT_PRESENT(UnmarshalledAuthenticator)) { *UnmarshalledAuthenticator = Authenticator; Authenticator = NULL; }
Cleanup: KerbFreeApRequest(Request); KerbFreeString(&LocalServerRealm); KerbFreeKdcName(&ServerName); KerbFreeString(&ServerRealm); KerbFreeAuthenticator(Authenticator); KerbFreeTicket(EncryptPart);
return(KerbErr); }
#if DBG
void PrintRequest( ULONG ulDebLevel, PKERB_KDC_REQUEST_BODY Request ) { TRACE(KDC, PrintRequest, DEB_FUNCTION); }
void PrintTicket( ULONG ulDebLevel, char * pszMessage, PKERB_TICKET pkitTicket) { TRACE(KDC, PrintTicket, DEB_FUNCTION); }
#endif // DBG
|