664 lines
18 KiB
C++
664 lines
18 KiB
C++
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1997
|
|
//
|
|
// File: transit.cxx
|
|
//
|
|
// Contents: Code for compressing transitive realm list
|
|
//
|
|
//
|
|
// History:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
#include "kdcsvr.hxx"
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KerbAppendString
|
|
//
|
|
// Synopsis: Appends to unicode strings together and allocates the output
|
|
//
|
|
// Effects:
|
|
//
|
|
// Parameters: Output - Output appended String
|
|
// InputTail - Trailing portion of input
|
|
// InputHead - Head potion of input
|
|
//
|
|
//
|
|
// Return:
|
|
//
|
|
// Notes:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbAppendString(
|
|
OUT PUNICODE_STRING Output,
|
|
IN PUNICODE_STRING InputTail,
|
|
IN PUNICODE_STRING InputHead
|
|
)
|
|
{
|
|
Output->Buffer = NULL;
|
|
Output->Length = InputHead->Length + InputTail->Length;
|
|
if ((InputHead->Buffer == NULL) && (InputTail->Buffer == NULL))
|
|
{
|
|
Output->MaximumLength = 0;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
Output->MaximumLength = Output->Length + sizeof(WCHAR);
|
|
}
|
|
|
|
Output->Buffer = (LPWSTR) MIDL_user_allocate(Output->MaximumLength);
|
|
if (Output->Buffer == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Output->Buffer,
|
|
InputHead->Buffer,
|
|
InputHead->Length
|
|
);
|
|
RtlCopyMemory(
|
|
Output->Buffer + InputHead->Length / sizeof(WCHAR),
|
|
InputTail->Buffer,
|
|
InputTail->Length
|
|
);
|
|
Output->Buffer[Output->Length/sizeof(WCHAR)] = L'\0';
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
typedef enum _KERB_DOMAIN_COMPARISON {
|
|
Above,
|
|
Below,
|
|
Equal,
|
|
NotEqual
|
|
} KERB_DOMAIN_COMPARISON, *PKERB_DOMAIN_COMPARISON;
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompareDomains
|
|
//
|
|
// Synopsis: Compares two domain names and returns whether one is a
|
|
// prefix of the other, and the offset of the prefix.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Return: Above - domain1 is a postfix of domain2
|
|
// Below - domain2 is a postfix of domain1
|
|
// Equal - the domains are equal
|
|
// NotEqual - the domains are not equal and not above or below
|
|
//
|
|
// Notes: This does not work for x-500 realm names (/foo/bar)
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
KERB_DOMAIN_COMPARISON
|
|
KerbCompareDomains(
|
|
IN PUNICODE_STRING Domain1,
|
|
IN PUNICODE_STRING Domain2,
|
|
OUT PULONG PostfixOffset
|
|
)
|
|
{
|
|
ULONG Index;
|
|
UNICODE_STRING TempString;
|
|
|
|
if (Domain1->Length > Domain2->Length)
|
|
{
|
|
TempString.Length = TempString.MaximumLength = Domain2->Length;
|
|
TempString.Buffer = Domain1->Buffer + (Domain1->Length - Domain2->Length) / sizeof(WCHAR);
|
|
|
|
if (RtlEqualUnicodeString(
|
|
&TempString,
|
|
Domain2,
|
|
TRUE) && TempString.Buffer[-1] == '.')
|
|
{
|
|
*PostfixOffset = (Domain1->Length - Domain2->Length) / sizeof(WCHAR);
|
|
return(Below);
|
|
}
|
|
else
|
|
{
|
|
return(NotEqual);
|
|
}
|
|
}
|
|
else if (Domain1->Length < Domain2->Length)
|
|
{
|
|
TempString.Length = TempString.MaximumLength = Domain1->Length;
|
|
TempString.Buffer = Domain2->Buffer + (Domain2->Length - Domain1->Length) / sizeof(WCHAR);
|
|
|
|
if (RtlEqualUnicodeString(
|
|
&TempString,
|
|
Domain1,
|
|
TRUE) && TempString.Buffer[-1] == '.')
|
|
{
|
|
*PostfixOffset = (Domain2->Length - Domain1->Length) / sizeof(WCHAR);
|
|
return(Above);
|
|
}
|
|
else
|
|
{
|
|
return(NotEqual);
|
|
}
|
|
|
|
}
|
|
else if (RtlEqualUnicodeString(Domain1,Domain2, TRUE))
|
|
{
|
|
*PostfixOffset = 0;
|
|
return(Equal);
|
|
}
|
|
else
|
|
{
|
|
return(NotEqual);
|
|
}
|
|
}
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KdcExpandTranistedRealms
|
|
//
|
|
// Synopsis: Expands the transited realm field into an array of realms
|
|
//
|
|
// Effects: Allocates an array of realm names
|
|
//
|
|
// Parameters: FullRealmList - receives the full list of realms
|
|
// CountOfRealms - receveies the number of entries in the list
|
|
// TranistedList - The transited field to expand
|
|
//
|
|
// Return:
|
|
//
|
|
// Notes:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KdcExpandTransitedRealms(
|
|
OUT PUNICODE_STRING * FullRealmList,
|
|
OUT PULONG CountOfRealms,
|
|
IN PUNICODE_STRING TransitedList
|
|
)
|
|
{
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
ULONG RealmCount;
|
|
ULONG Index;
|
|
ULONG RealmIndex;
|
|
PUNICODE_STRING RealmList = NULL;
|
|
UNICODE_STRING CurrentRealm;
|
|
|
|
*FullRealmList = NULL;
|
|
*CountOfRealms = 0;
|
|
|
|
//
|
|
// First count the number of realms in the tranisted list. We can do
|
|
// this by counting the number of ',' in the list. Note: if the encoding
|
|
// is compressed by using a null entry to include all domains in a path
|
|
// up or down a hierarchy, this code will fail.
|
|
//
|
|
|
|
if (TransitedList->Length == 0)
|
|
{
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
RealmCount = 1;
|
|
for (Index = 0; Index < TransitedList->Length / sizeof(WCHAR) ; Index++ )
|
|
{
|
|
if (TransitedList->Buffer[Index] == ',')
|
|
{
|
|
RealmCount++;
|
|
|
|
//
|
|
// Check for a ',,' compression indicating that all the
|
|
// realms between two names have been traversed.
|
|
// BUG 453997: we don't handle this yet.
|
|
//
|
|
|
|
if ( (Index == 0) ||
|
|
(Index == (TransitedList->Length / sizeof(WCHAR)) -1) ||
|
|
(TransitedList->Buffer[Index-1] == L','))
|
|
|
|
{
|
|
DebugLog((DEB_ERROR,"BUG 453997:: hit overcompressed transited encoding: %wZ\n",
|
|
TransitedList ));
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have a the count of realms. Allocate an array of UNICODE_STRING
|
|
// structures to hold the realms.
|
|
//
|
|
|
|
RealmList = (PUNICODE_STRING) MIDL_user_allocate(RealmCount * sizeof(UNICODE_STRING));
|
|
if (RealmList == NULL)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
RtlZeroMemory(
|
|
RealmList,
|
|
RealmCount * sizeof(UNICODE_STRING)
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Now loop through and insert the full names of all the domains into
|
|
// the list
|
|
//
|
|
|
|
RealmIndex = 0;
|
|
CurrentRealm = *TransitedList;
|
|
for (Index = 0; Index <= TransitedList->Length / sizeof(WCHAR) ; Index++ )
|
|
{
|
|
//
|
|
// If we hit the end of the string or found a ',', split off a
|
|
// new realm.
|
|
//
|
|
|
|
if ((Index == TransitedList->Length / sizeof(WCHAR)) ||
|
|
(TransitedList->Buffer[Index] == ',' ))
|
|
{
|
|
|
|
//
|
|
// Check for a ',,' compression indicating that all the
|
|
// realms between two names have been traversed.
|
|
// BUG 453997:: we don't handle this yet.
|
|
//
|
|
|
|
if ( (Index == 0) ||
|
|
(Index == (TransitedList->Length / sizeof(WCHAR)) -1) ||
|
|
(TransitedList->Buffer[Index-1] == L','))
|
|
|
|
{
|
|
DebugLog((DEB_ERROR,"BUG 453997:: hit overcompressed transited encoding: %wZ\n",
|
|
TransitedList ));
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CurrentRealm.Length = CurrentRealm.MaximumLength =
|
|
(USHORT)(&TransitedList->Buffer[Index] - CurrentRealm.Buffer) * sizeof(WCHAR);
|
|
|
|
//
|
|
// Check for a trailing '.' - if so, append it
|
|
// to the parent
|
|
//
|
|
|
|
if (TransitedList->Buffer[Index-1] == '.')
|
|
{
|
|
//
|
|
// This is a compressed name, so append it to the previous
|
|
// name
|
|
//
|
|
if (RealmIndex == 0)
|
|
{
|
|
DebugLog((DEB_ERROR,"First element in transited encoding has a trailing '.': %wZ\n",
|
|
TransitedList ));
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
if (!NT_SUCCESS(KerbAppendString(
|
|
&RealmList[RealmIndex],
|
|
&RealmList[RealmIndex-1],
|
|
&CurrentRealm)))
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if ((RealmIndex != 0) && (CurrentRealm.Buffer[0] == '/'))
|
|
{
|
|
if (!NT_SUCCESS(KerbAppendString(
|
|
&RealmList[RealmIndex],
|
|
&CurrentRealm,
|
|
&RealmList[RealmIndex-1]
|
|
)))
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (!NT_SUCCESS(KerbDuplicateString(
|
|
&RealmList[RealmIndex],
|
|
&CurrentRealm)))
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CurrentRealm.Buffer =CurrentRealm.Buffer + 1 + CurrentRealm.Length/sizeof(WCHAR);
|
|
RealmIndex++;
|
|
}
|
|
}
|
|
DsysAssert(RealmIndex == RealmCount);
|
|
|
|
*FullRealmList = RealmList;
|
|
*CountOfRealms = RealmCount;
|
|
Cleanup:
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
if (RealmList != NULL)
|
|
{
|
|
for (RealmIndex = 0; RealmIndex < RealmCount ; RealmIndex++ )
|
|
{
|
|
KerbFreeString(&RealmList[RealmIndex]);
|
|
}
|
|
MIDL_user_free(RealmList);
|
|
}
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KdcCompressTransitedRealms
|
|
//
|
|
// Synopsis: Compresses an ordered list of realms by removing
|
|
// redundant information.
|
|
//
|
|
// Effects: Allocates an output string
|
|
//
|
|
// Parameters: CompressedRealms - receives the compressed list of realms
|
|
// RealmList - List of domains to compress
|
|
// RealmCount - number of entries in realm list
|
|
// NewRealm - new realm to add to the lsit
|
|
// NewRealmIndex - Location before which to insert the new
|
|
// realm
|
|
//
|
|
// Return:
|
|
//
|
|
// Notes:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KdcCompressTransitedRealms(
|
|
OUT PUNICODE_STRING CompressedRealms,
|
|
IN PUNICODE_STRING RealmList,
|
|
IN ULONG RealmCount,
|
|
IN PUNICODE_STRING NewRealm,
|
|
IN ULONG NewRealmIndex
|
|
)
|
|
{
|
|
UNICODE_STRING OutputRealms;
|
|
WCHAR OutputRealmBuffer[1000];
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
ULONG Index;
|
|
ULONG InsertionIndex = NewRealmIndex;
|
|
PWCHAR Where;
|
|
PUNICODE_STRING PreviousName = NULL;
|
|
PUNICODE_STRING CurrentName = NULL;
|
|
ULONG PostfixOffset;
|
|
KERB_DOMAIN_COMPARISON Comparison;
|
|
UNICODE_STRING NameToAdd;
|
|
|
|
RtlInitUnicodeString(
|
|
CompressedRealms,
|
|
NULL
|
|
);
|
|
|
|
OutputRealms.Buffer = OutputRealmBuffer;
|
|
OutputRealms.MaximumLength = sizeof(OutputRealmBuffer);
|
|
OutputRealms.Length = 0;
|
|
Where = OutputRealms.Buffer;
|
|
|
|
Index = 0;
|
|
while (Index <= RealmCount)
|
|
{
|
|
PreviousName = CurrentName;
|
|
|
|
//
|
|
// If this is the index to insert, add the new realm
|
|
//
|
|
|
|
if (InsertionIndex == Index)
|
|
{
|
|
CurrentName = NewRealm;
|
|
}
|
|
else if (Index == RealmCount)
|
|
{
|
|
//
|
|
// If we already added all the original realms, get out now
|
|
//
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CurrentName = &RealmList[Index];
|
|
}
|
|
|
|
NameToAdd = *CurrentName;
|
|
|
|
//
|
|
// If the previous name is above this one, lop off the postfix from
|
|
// this name
|
|
//
|
|
|
|
if ((PreviousName != NULL) &&
|
|
KerbCompareDomains(
|
|
PreviousName,
|
|
CurrentName,
|
|
&PostfixOffset
|
|
) == Above)
|
|
{
|
|
NameToAdd.Length = (USHORT) PostfixOffset * sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
DsysAssert(OutputRealms.Length + NameToAdd.Length + sizeof(WCHAR) < OutputRealms.MaximumLength);
|
|
if (OutputRealms.Length + NameToAdd.Length + sizeof(WCHAR) > OutputRealms.MaximumLength)
|
|
{
|
|
//
|
|
// BUG 453652: wrong error
|
|
//
|
|
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (OutputRealms.Length != 0)
|
|
{
|
|
*Where++ = L',';
|
|
OutputRealms.Length += sizeof(WCHAR);
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
Where,
|
|
NameToAdd.Buffer,
|
|
NameToAdd.Length
|
|
);
|
|
Where += NameToAdd.Length/sizeof(WCHAR);
|
|
OutputRealms.Length += NameToAdd.Length;
|
|
|
|
//
|
|
// If we inserted the transited realm here, run through the loop
|
|
// again with the same index.
|
|
//
|
|
|
|
if (InsertionIndex == Index)
|
|
{
|
|
InsertionIndex = 0xffffffff;
|
|
}
|
|
else
|
|
{
|
|
Index++;
|
|
}
|
|
}
|
|
|
|
*Where++ = L'\0';
|
|
|
|
|
|
if (!NT_SUCCESS(KerbDuplicateString(
|
|
CompressedRealms,
|
|
&OutputRealms)))
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
Cleanup:
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Function: KdcInsertTransitedRealm
|
|
//
|
|
// Synopsis: Inserts the referree's realm into the tranisted encoding
|
|
// in a ticket. This uses domain-x500-compress which
|
|
// eliminates redundant information when one domain is the
|
|
// prefix or suffix of another.
|
|
//
|
|
// Effects: Allocates output buffer
|
|
//
|
|
// Parameters: NewTransitedField - receives the new tranisted field, to
|
|
// be freed with KerbFreeString
|
|
// OldTransitedField - the existing transited frield.
|
|
// ClientRealm - Realm of client (from ticket)
|
|
// TransitedRealm - Realm of referring domain
|
|
// OurRealm - Our realm name
|
|
//
|
|
// Return:
|
|
//
|
|
// Notes:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KdcInsertTransitedRealm(
|
|
OUT PUNICODE_STRING NewTransitedField,
|
|
IN PUNICODE_STRING OldTransitedField,
|
|
IN PUNICODE_STRING ClientRealm,
|
|
IN PUNICODE_STRING TransitedRealm,
|
|
IN PUNICODE_STRING OurRealm
|
|
)
|
|
{
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
PUNICODE_STRING FullDomainList = NULL;
|
|
ULONG CountOfDomains;
|
|
ULONG NewEntryIndex = 0xffffffff;
|
|
UNICODE_STRING NewDomain;
|
|
ULONG PostfixOffset;
|
|
ULONG Index;
|
|
KERB_DOMAIN_COMPARISON Comparison = NotEqual;
|
|
KERB_DOMAIN_COMPARISON LastComparison;
|
|
|
|
//
|
|
// The first thing to do is to expand the existing transited field. This
|
|
// is because the compression scheme does not allow new domains to simply
|
|
// append or insert information - the encoding of existing realms
|
|
// can change. For example, going from a domain to its parent means
|
|
// that the original domain can be encoded as a prefix of the parent
|
|
// whereas originally it was a name unto itself.
|
|
//
|
|
|
|
D_DebugLog((DEB_T_TRANSIT, "Inserted realm %wZ into list %wZ for client fomr %wZ\n",
|
|
TransitedRealm, OldTransitedField, ClientRealm ));
|
|
|
|
KerbErr = KdcExpandTransitedRealms(
|
|
&FullDomainList,
|
|
&CountOfDomains,
|
|
OldTransitedField
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now loop through the domains. Based on the compression, we know that
|
|
// higher up domains come first.
|
|
//
|
|
|
|
for (Index = 0; Index < CountOfDomains ; Index++ )
|
|
{
|
|
LastComparison = Comparison;
|
|
|
|
Comparison = KerbCompareDomains(
|
|
TransitedRealm,
|
|
&FullDomainList[Index],
|
|
&PostfixOffset
|
|
);
|
|
if (Comparison == Above)
|
|
{
|
|
//
|
|
// If the new domain is above an existing domain, it gets inserted
|
|
// before the existing domain because all the existing domains
|
|
// are ordered from top to bottom
|
|
//
|
|
NewEntryIndex = Index;
|
|
break;
|
|
}
|
|
else if (Comparison == Below)
|
|
{
|
|
//
|
|
// There may be other domains below which are closer, so
|
|
// store the result and continue.
|
|
//
|
|
LastComparison = Comparison;
|
|
}
|
|
else if (Comparison == NotEqual)
|
|
{
|
|
//
|
|
// The domains aren't above or below each other. If the last
|
|
// comparison was below, stick the domain underneath it.
|
|
//
|
|
if (LastComparison == Below)
|
|
{
|
|
NewEntryIndex = Index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't find a place for it, stick it in at the end.
|
|
//
|
|
|
|
if (NewEntryIndex == 0xffffffff)
|
|
{
|
|
NewEntryIndex = Index;
|
|
}
|
|
|
|
//
|
|
// Now build the new encoding
|
|
//
|
|
|
|
KerbErr = KdcCompressTransitedRealms(
|
|
NewTransitedField,
|
|
FullDomainList,
|
|
CountOfDomains,
|
|
TransitedRealm,
|
|
NewEntryIndex
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
if (FullDomainList != NULL)
|
|
{
|
|
for (Index = 0; Index < CountOfDomains ; Index++ )
|
|
{
|
|
KerbFreeString(&FullDomainList[Index]);
|
|
}
|
|
MIDL_user_free(FullDomainList);
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|