4131 lines
96 KiB
C++
4131 lines
96 KiB
C++
|
//+-----------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation 1991 - 1992
|
||
|
//
|
||
|
// File: tickets.c
|
||
|
//
|
||
|
// Contents: Ticket bundling code
|
||
|
//
|
||
|
//
|
||
|
// History: 6 Dec 91, RichardW Created
|
||
|
// 04 Jun 92 RichardW NT-ized
|
||
|
// 08-Jun-93 WadeR Converted to C++, rewrote packing code
|
||
|
//
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef WIN32_CHICAGO
|
||
|
#include<kerb.hxx>
|
||
|
#include<kerbp.h>
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
extern "C"
|
||
|
{
|
||
|
#include <nt.h>
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <windows.h>
|
||
|
#include <ntlsa.h>
|
||
|
#include <samrpc.h>
|
||
|
#include <samisrv.h>
|
||
|
#include <lsarpc.h>
|
||
|
#include <lsaisrv.h>
|
||
|
#include <lsaitf.h>
|
||
|
#include <wincrypt.h>
|
||
|
}
|
||
|
#include <kerbcomm.h>
|
||
|
#include <kerberr.h>
|
||
|
#include <kerbcon.h>
|
||
|
#include <midles.h>
|
||
|
#include <authen.hxx>
|
||
|
#include <tostring.hxx>
|
||
|
#include "debug.h"
|
||
|
#include "fileno.h"
|
||
|
#else// WIN32_CHICAGO
|
||
|
#include "tostring.hxx"
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
#include <utils.hxx>
|
||
|
|
||
|
#define FILENO FILENO_TICKETS
|
||
|
|
||
|
//
|
||
|
// Debugging support.
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
#ifdef DEBUG_SUPPORT
|
||
|
|
||
|
DEBUG_KEY KSuppDebugKeys[] = { {DEB_ERROR, "Error"},
|
||
|
{DEB_WARN, "Warning"},
|
||
|
{DEB_TRACE, "Trace"},
|
||
|
{DEB_T_SOCK, "Sock"},
|
||
|
{0, NULL }
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
DEFINE_DEBUG_DEFER(KSupp, KSuppDebugKeys);
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
RTL_CRITICAL_SECTION OssCriticalSection;
|
||
|
BOOLEAN TicketsInitialized;
|
||
|
BOOLEAN KerbUseFastDecodeAlloc = FALSE;
|
||
|
|
||
|
#define I_LsaIThreadAlloc MIDL_user_allocate
|
||
|
#define I_LsaIThreadFree MIDL_user_free
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertGeneralizedTimeToLargeInt
|
||
|
//
|
||
|
// Synopsis: Converts a generalized time (ASN.1 format) to a large integer
|
||
|
// (NT format)
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: TimeStamp - receives NT-style time
|
||
|
// ClientTime - client generalized time
|
||
|
// ClientUsec - client micro second count
|
||
|
//
|
||
|
// Requires: none
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbConvertGeneralizedTimeToLargeInt(
|
||
|
OUT PTimeStamp TimeStamp,
|
||
|
IN PKERB_TIME ClientTime,
|
||
|
IN int ClientUsec
|
||
|
)
|
||
|
{
|
||
|
KERB_TIME ZeroTime;
|
||
|
TIME_FIELDS TimeFields;
|
||
|
|
||
|
//
|
||
|
// Special case zero time
|
||
|
//
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&ZeroTime,
|
||
|
sizeof(KERB_TIME)
|
||
|
);
|
||
|
|
||
|
ZeroTime.universal = TRUE;
|
||
|
|
||
|
//
|
||
|
// Skip this check after 3/1/97 - no clients should send this sort of
|
||
|
// zero time
|
||
|
//
|
||
|
|
||
|
|
||
|
if (RtlEqualMemory(
|
||
|
&ZeroTime,
|
||
|
ClientTime,
|
||
|
sizeof(KERB_TIME)
|
||
|
))
|
||
|
{
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
TimeStamp->QuadPart = 0;
|
||
|
#else // WIN32_CHICAGO
|
||
|
*TimeStamp = 0;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check for MIT zero time
|
||
|
//
|
||
|
|
||
|
ZeroTime.year = 1970;
|
||
|
ZeroTime.month = 1;
|
||
|
ZeroTime.day = 1;
|
||
|
|
||
|
if (RtlEqualMemory(
|
||
|
&ZeroTime,
|
||
|
ClientTime,
|
||
|
sizeof(KERB_TIME)
|
||
|
))
|
||
|
{
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
TimeStamp->QuadPart = 0;
|
||
|
#else // WIN32_CHICAGO
|
||
|
*TimeStamp = 0;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TimeFields.Year = ClientTime->year;
|
||
|
TimeFields.Month = ClientTime->month;
|
||
|
TimeFields.Day = ClientTime->day;
|
||
|
TimeFields.Hour = ClientTime->hour;
|
||
|
TimeFields.Minute = ClientTime->minute;
|
||
|
TimeFields.Second = ClientTime->second;
|
||
|
TimeFields.Milliseconds = ClientTime->millisecond; // to convert from micro to milli
|
||
|
TimeFields.Weekday = 0;
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
RtlTimeFieldsToTime(
|
||
|
&TimeFields,
|
||
|
TimeStamp
|
||
|
);
|
||
|
#else // WIN32_CHICAGO
|
||
|
LARGE_INTEGER TempTimeStamp;
|
||
|
RtlTimeFieldsToTime(
|
||
|
&TimeFields,
|
||
|
&TempTimeStamp
|
||
|
);
|
||
|
*TimeStamp = TempTimeStamp.QuadPart;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// add in any micro seconds
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
TimeStamp->QuadPart += ClientUsec * 10;
|
||
|
#else // WIN32_CHICAGO
|
||
|
*TimeStamp += ClientUsec * 10;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertLargeIntToGeneralizedTime
|
||
|
//
|
||
|
// Synopsis: Converts a large integer to ageneralized time
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: ClientTime - receives generalized time
|
||
|
// ClientUsec - receives micro second count
|
||
|
// TimeStamp - contains NT-style time
|
||
|
//
|
||
|
// Requires: none
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbConvertLargeIntToGeneralizedTime(
|
||
|
OUT PKERB_TIME ClientTime,
|
||
|
OUT OPTIONAL int * ClientUsec,
|
||
|
IN PTimeStamp TimeStamp
|
||
|
)
|
||
|
{
|
||
|
TIME_FIELDS TimeFields;
|
||
|
|
||
|
//
|
||
|
// Special case zero time
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if (TimeStamp->QuadPart == 0)
|
||
|
#else // WIN32_CHICAGO
|
||
|
if (*TimeStamp == 0)
|
||
|
#endif // WIN32_CHICAGO
|
||
|
{
|
||
|
RtlZeroMemory(
|
||
|
ClientTime,
|
||
|
sizeof(KERB_TIME)
|
||
|
);
|
||
|
//
|
||
|
// For MIT compatibility, time zero is 1/1/70
|
||
|
//
|
||
|
|
||
|
ClientTime->year = 1970;
|
||
|
ClientTime->month = 1;
|
||
|
ClientTime->day = 1;
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ClientUsec))
|
||
|
|
||
|
{
|
||
|
*ClientUsec = 0;
|
||
|
}
|
||
|
ClientTime->universal = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
RtlTimeToTimeFields(
|
||
|
TimeStamp,
|
||
|
&TimeFields
|
||
|
);
|
||
|
#else // WIN32_CHICAGO
|
||
|
RtlTimeToTimeFields(
|
||
|
(LARGE_INTEGER*)TimeStamp,
|
||
|
&TimeFields
|
||
|
);
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// Generalized times can only contains years up to four digits.
|
||
|
//
|
||
|
|
||
|
if (TimeFields.Year > 2037)
|
||
|
{
|
||
|
ClientTime->year = 2037;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ClientTime->year = TimeFields.Year;
|
||
|
}
|
||
|
ClientTime->month = (ASN1uint8_t) TimeFields.Month;
|
||
|
ClientTime->day = (ASN1uint8_t) TimeFields.Day;
|
||
|
ClientTime->hour = (ASN1uint8_t) TimeFields.Hour;
|
||
|
ClientTime->minute = (ASN1uint8_t) TimeFields.Minute;
|
||
|
ClientTime->second = (ASN1uint8_t) TimeFields.Second;
|
||
|
|
||
|
// MIT kerberos does not support millseconds
|
||
|
//
|
||
|
|
||
|
ClientTime->millisecond = 0;
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ClientUsec))
|
||
|
{
|
||
|
//
|
||
|
// Since we don't include milliseconds above, use the whole
|
||
|
// thing here.
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
*ClientUsec = (TimeStamp->LowPart / 10) % 1000000;
|
||
|
#else // WIN32_CHICAGO
|
||
|
*ClientUsec = (int) ((*TimeStamp / 10) % 1000000);
|
||
|
#endif // WIN32_CHICAGO
|
||
|
}
|
||
|
|
||
|
ClientTime->diff = 0;
|
||
|
ClientTime->universal = TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
KerbConvertLargeIntToGeneralizedTimeWrapper(
|
||
|
OUT PKERB_TIME ClientTime,
|
||
|
OUT OPTIONAL long * ClientUsec,
|
||
|
IN PTimeStamp TimeStamp
|
||
|
)
|
||
|
{
|
||
|
int temp;
|
||
|
|
||
|
if (ClientUsec != NULL)
|
||
|
{
|
||
|
KerbConvertLargeIntToGeneralizedTime(
|
||
|
ClientTime,
|
||
|
&temp,
|
||
|
TimeStamp
|
||
|
);
|
||
|
|
||
|
*ClientUsec = temp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KerbConvertLargeIntToGeneralizedTime(
|
||
|
ClientTime,
|
||
|
NULL,
|
||
|
TimeStamp
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeHostAddresses
|
||
|
//
|
||
|
// Synopsis: Frees a host address allocated with KerbBuildHostAddresses
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Addresses - The name to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbFreeHostAddresses(
|
||
|
IN PKERB_HOST_ADDRESSES Addresses
|
||
|
)
|
||
|
{
|
||
|
PKERB_HOST_ADDRESSES Elem,NextElem;
|
||
|
|
||
|
Elem = Addresses;
|
||
|
while (Elem != NULL)
|
||
|
{
|
||
|
if (Elem->value.address.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(Elem->value.address.value);
|
||
|
}
|
||
|
NextElem = Elem->next;
|
||
|
MIDL_user_free(Elem);
|
||
|
Elem = NextElem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCheckTimeSkew
|
||
|
//
|
||
|
// Synopsis: Verifies the supplied time is within the skew of another
|
||
|
// supplied time
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
BOOLEAN
|
||
|
KerbCheckTimeSkew(
|
||
|
IN PTimeStamp CurrentTime,
|
||
|
IN PTimeStamp ClientTime,
|
||
|
IN PTimeStamp AllowedSkew
|
||
|
)
|
||
|
{
|
||
|
TimeStamp TimePlus, TimeMinus;
|
||
|
|
||
|
TimePlus.QuadPart = CurrentTime->QuadPart + AllowedSkew->QuadPart;
|
||
|
TimeMinus.QuadPart = CurrentTime->QuadPart - AllowedSkew->QuadPart;
|
||
|
|
||
|
if ((ClientTime->QuadPart > TimePlus.QuadPart) ||
|
||
|
(ClientTime->QuadPart < TimeMinus.QuadPart))
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbVerifyTicket
|
||
|
//
|
||
|
// Synopsis: Verifies that the specified ticket is valid by checking
|
||
|
// for valid times, flags, and server principal name. This is
|
||
|
// called by KerbCheckTicket to verify an AP request and by the
|
||
|
// KDC to verify additional tickets in TGS request
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR
|
||
|
KerbVerifyTicket(
|
||
|
IN PKERB_TICKET PackedTicket,
|
||
|
IN ULONG NameCount,
|
||
|
IN OPTIONAL PUNICODE_STRING ServiceNames,
|
||
|
IN PUNICODE_STRING ServiceRealm,
|
||
|
IN PKERB_ENCRYPTION_KEY ServiceKey,
|
||
|
IN OPTIONAL PTimeStamp SkewTime,
|
||
|
OUT PKERB_ENCRYPTED_TICKET * DecryptedTicket
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
UNICODE_STRING TicketRealm = {0};
|
||
|
PKERB_ENCRYPTED_TICKET EncryptPart = NULL;
|
||
|
TimeStamp TimePlus, TimeMinus, TimeNow, StartTime,EndTime, Time2Plus;
|
||
|
ULONG TicketFlags = 0;
|
||
|
|
||
|
#ifdef notedef
|
||
|
if ( ARGUMENT_PRESENT(ServiceNames) )
|
||
|
{
|
||
|
ULONG Index;
|
||
|
|
||
|
KerbErr = KRB_AP_ERR_NOT_US;
|
||
|
|
||
|
//
|
||
|
// Loop through names looking for a match
|
||
|
//
|
||
|
|
||
|
for (Index = 0; Index < NameCount ; Index++ )
|
||
|
{
|
||
|
if (KerbCompareStringToPrincipalName(
|
||
|
&PackedTicket->server_name,
|
||
|
&ServiceNames[Index]
|
||
|
) )
|
||
|
{
|
||
|
KerbErr = KDC_ERR_NONE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog(( DEB_WARN, "KLIN(%x) Ticket (%s) not for this service (%wZ).\n",
|
||
|
KLIN(FILENO, __LINE__),
|
||
|
PackedTicket->server_name.name_string->value,
|
||
|
&ServiceNames[0] ));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ServiceRealm))
|
||
|
{
|
||
|
KerbErr = KerbConvertRealmToUnicodeString(
|
||
|
&TicketRealm,
|
||
|
&PackedTicket->realm
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
if (!KerbCompareUnicodeRealmNames(
|
||
|
&TicketRealm,
|
||
|
ServiceRealm
|
||
|
))
|
||
|
{
|
||
|
KerbErr = KRB_AP_ERR_NOT_US;
|
||
|
DebugLog(( DEB_WARN, "KLIN(%x) Ticket (%wZ) not for this realm (%wZ).\n",
|
||
|
KLIN(FILENO, __LINE__), &TicketRealm, ServiceRealm ));
|
||
|
goto Cleanup;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Unpack ticket.
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbUnpackTicket(
|
||
|
PackedTicket,
|
||
|
ServiceKey,
|
||
|
&EncryptPart
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "KLIN(%x) KerbUnpackTicket failed: 0x%x",
|
||
|
KLIN(FILENO, __LINE__), KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (PackedTicket->ticket_version != KERBEROS_VERSION)
|
||
|
{
|
||
|
DebugLog(( DEB_WARN, "KLIN(%x) Ticket has bad version %d\n",
|
||
|
KLIN(FILENO, __LINE__),PackedTicket->ticket_version ));
|
||
|
KerbErr = KRB_AP_ERR_BADVERSION;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the caller provided a skew time, check the times on the ticket.
|
||
|
// Otherwise it is up to the caller to check that the ticket times are
|
||
|
// correct
|
||
|
//
|
||
|
|
||
|
|
||
|
if (ARGUMENT_PRESENT(SkewTime))
|
||
|
{
|
||
|
//
|
||
|
// Check the times on the ticket. We do this last because when the KDC
|
||
|
// wants to renew a ticket, the timestamps may be incorrect, but it will
|
||
|
// accept the ticket anyway. This way the KDC can be certain when the
|
||
|
// times are wrong that everything else is OK.
|
||
|
//
|
||
|
|
||
|
GetSystemTimeAsFileTime((PFILETIME) &TimeNow );
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
TimePlus.QuadPart = TimeNow.QuadPart + SkewTime->QuadPart;
|
||
|
Time2Plus.QuadPart = TimePlus.QuadPart + SkewTime->QuadPart;
|
||
|
TimeMinus.QuadPart = TimeNow.QuadPart - SkewTime->QuadPart;
|
||
|
#else // WIN32_CHICAGO
|
||
|
TimePlus = TimeNow + *SkewTime;
|
||
|
Time2Plus = TimePlus + *SkewTime;
|
||
|
TimeMinus = TimeNow - *SkewTime;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
KerbConvertGeneralizedTimeToLargeInt(
|
||
|
&EndTime,
|
||
|
&EncryptPart->endtime,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Did the ticket expire already?
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if ( EndTime.QuadPart < TimeMinus.QuadPart )
|
||
|
#else // WIN32_CHICAGO
|
||
|
if ( EndTime < TimeMinus )
|
||
|
#endif // WIN32_CHICAGO
|
||
|
{
|
||
|
DebugLog(( DEB_WARN, "KLIN(%x) KerbCheckTicket: ticket is expired.\n",
|
||
|
KLIN(FILENO, __LINE__)));
|
||
|
|
||
|
KerbErr = KRB_AP_ERR_TKT_EXPIRED;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Is the ticket valid yet?
|
||
|
//
|
||
|
|
||
|
if (EncryptPart->bit_mask & KERB_ENCRYPTED_TICKET_starttime_present)
|
||
|
{
|
||
|
KerbConvertGeneralizedTimeToLargeInt(
|
||
|
&StartTime,
|
||
|
&EncryptPart->KERB_ENCRYPTED_TICKET_starttime,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
|
||
|
TicketFlags = KerbConvertFlagsToUlong(
|
||
|
&EncryptPart->flags
|
||
|
);
|
||
|
//
|
||
|
// BUG 403734: Look into this a bit more
|
||
|
// We don't check for tickets that aren't valid yet, as
|
||
|
// our KDC doesn't normally hand out post dated tickets. As long
|
||
|
// as the end time is valid, that is good enough for us.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Does the ticket start in the future? Allow twice the skew in
|
||
|
// the reverse direction.
|
||
|
//
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if ( (StartTime.QuadPart > Time2Plus.QuadPart) ||
|
||
|
#else // WIN32_CHICAGO
|
||
|
if ( (StartTime > Time2Plus) ||
|
||
|
#endif // WIN32_CHICAGO
|
||
|
((TicketFlags & KERB_TICKET_FLAGS_invalid) != 0 ))
|
||
|
{
|
||
|
KerbErr = KRB_AP_ERR_TKT_NYV;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*DecryptedTicket = EncryptPart;
|
||
|
EncryptPart = NULL;
|
||
|
|
||
|
Cleanup:
|
||
|
if (EncryptPart != NULL)
|
||
|
{
|
||
|
KerbFreeTicket(EncryptPart);
|
||
|
}
|
||
|
KerbFreeString(&TicketRealm);
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCheckTicket
|
||
|
//
|
||
|
// Synopsis: Decrypts a ticket and authenticator, verifies them.
|
||
|
//
|
||
|
// Effects: decrypts the ticket and authenticator (in place) allocates mem.
|
||
|
//
|
||
|
// Arguments: [PackedTicket] -- Encrypted ticket
|
||
|
// [PackedTicketSize] - Size of encrypted ticket
|
||
|
// [pedAuth] -- Encrypted authenticator
|
||
|
// [pkKey] -- Key to decrypt ticket with
|
||
|
// [alAuthenList] -- List of authenticators to check against
|
||
|
// [NameCount] -- Count of service names
|
||
|
// [pwzServiceName] -- Name of service (may be NULL).
|
||
|
// [CheckForReplay] -- If TRUE, check authenticator cache for replay
|
||
|
// [KdcRequest] -- If TRUE, this is the ticket in a TGS req
|
||
|
// [pkitTicket] -- Decrypted ticket
|
||
|
// [pkiaAuth] -- Decrypted authenticator
|
||
|
// [pkTicketKey] -- Session key from ticket
|
||
|
// [pkSessionKey] -- Session key to use
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE if everything is OK, else error.
|
||
|
//
|
||
|
// History: 4-04-93 WadeR Created
|
||
|
//
|
||
|
// Notes: The caller must call KerbFreeTicket and
|
||
|
// KerbFreeAuthenticator on pkitTicket and pkiaAuth,
|
||
|
// respectively.
|
||
|
//
|
||
|
// If pwzServiceName == NULL, it won't check the service name.
|
||
|
//
|
||
|
// See sections 3.2.3 and A.10 of the Kerberos V5 R5.2 spec
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbCheckTicket(
|
||
|
IN PKERB_TICKET PackedTicket,
|
||
|
IN PKERB_ENCRYPTED_DATA EncryptedAuthenticator,
|
||
|
IN PKERB_ENCRYPTION_KEY pkKey,
|
||
|
IN OUT CAuthenticatorList * AuthenticatorList,
|
||
|
IN PTimeStamp SkewTime,
|
||
|
IN ULONG NameCount,
|
||
|
IN PUNICODE_STRING ServiceNames,
|
||
|
IN PUNICODE_STRING ServiceRealm,
|
||
|
IN BOOLEAN CheckForReplay,
|
||
|
IN BOOLEAN KdcRequest,
|
||
|
OUT PKERB_ENCRYPTED_TICKET * EncryptTicket,
|
||
|
OUT PKERB_AUTHENTICATOR * Authenticator,
|
||
|
OUT OPTIONAL PKERB_ENCRYPTION_KEY pkTicketKey,
|
||
|
OUT PKERB_ENCRYPTION_KEY pkSessionKey,
|
||
|
OUT PBOOLEAN UseSubKey
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_ENCRYPTED_TICKET EncryptPart = NULL;
|
||
|
LARGE_INTEGER AuthenticatorTime;
|
||
|
|
||
|
|
||
|
//
|
||
|
// The caller will free these, so we must make sure they are valid
|
||
|
// if we return before putting anything in them. This will zero out
|
||
|
// all of the pointers in them, so it's safe to free them later.
|
||
|
//
|
||
|
|
||
|
*EncryptTicket = NULL;
|
||
|
*Authenticator = NULL;
|
||
|
*UseSubKey = FALSE;
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
pkSessionKey,
|
||
|
sizeof(KERB_ENCRYPTION_KEY)
|
||
|
);
|
||
|
if (ARGUMENT_PRESENT(pkTicketKey))
|
||
|
{
|
||
|
*pkTicketKey = *pkSessionKey;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Is the ticket for this service?
|
||
|
// ServerName in ticket is different length then ServerName passed in,
|
||
|
// or same length but contents don't match.
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// If either of KerbUnpackTicket or KerbUnpackAuthenticator
|
||
|
// get bad data, they could access violate.
|
||
|
//
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
|
||
|
KerbErr = KerbVerifyTicket(
|
||
|
PackedTicket,
|
||
|
NameCount,
|
||
|
ServiceNames,
|
||
|
ServiceRealm,
|
||
|
pkKey,
|
||
|
SkewTime,
|
||
|
&EncryptPart
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Unpack Authenticator.
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbUnpackAuthenticator(
|
||
|
&EncryptPart->key,
|
||
|
EncryptedAuthenticator,
|
||
|
KdcRequest,
|
||
|
Authenticator
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_WARN,"KerbUnpackAuthenticator failed: 0x%x\n", KerbErr) );
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Check the contents of the authenticator
|
||
|
//
|
||
|
if ((*Authenticator)->authenticator_version != KERBEROS_VERSION)
|
||
|
{
|
||
|
DebugLog(( DEB_WARN, "Authenticator has bad version %d\n",
|
||
|
(*Authenticator)->authenticator_version ));
|
||
|
KerbErr = KRB_AP_ERR_BADVERSION;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!KerbComparePrincipalNames(
|
||
|
&EncryptPart->client_name,
|
||
|
&(*Authenticator)->client_name
|
||
|
) ||
|
||
|
!KerbCompareRealmNames(
|
||
|
&EncryptPart->client_realm,
|
||
|
&(*Authenticator)->client_realm
|
||
|
) )
|
||
|
{
|
||
|
DebugLog(( DEB_WARN, "Authenticator principal != ticket principal\n"));
|
||
|
KerbErr = KRB_AP_ERR_BADMATCH;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Make sure the authenticator isn't a repeat, or too old.
|
||
|
//
|
||
|
|
||
|
if (CheckForReplay)
|
||
|
{
|
||
|
KerbConvertGeneralizedTimeToLargeInt(
|
||
|
&AuthenticatorTime,
|
||
|
&(*Authenticator)->client_time,
|
||
|
(*Authenticator)->client_usec
|
||
|
);
|
||
|
|
||
|
KerbErr = (KERBERR) AuthenticatorList->Check(
|
||
|
EncryptedAuthenticator->cipher_text.value,
|
||
|
EncryptedAuthenticator->cipher_text.length,
|
||
|
NULL,
|
||
|
0,
|
||
|
&AuthenticatorTime,
|
||
|
TRUE
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_WARN,"Failed authenticator check: 0x%x\n",KerbErr));
|
||
|
__leave;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
// Any exceptions are likely from bad ticket data being unmarshalled.
|
||
|
DebugLog(( DEB_WARN, "Exception 0x%X in KerbCheckTicket (likely bad ticket or auth.\n",
|
||
|
GetExceptionCode() ));
|
||
|
KerbErr = KRB_AP_ERR_BADVERSION;
|
||
|
}
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
//
|
||
|
// Extract the correct session key. If the Sub-session key in the
|
||
|
// Authenticator is present, use it. Otherwise, use the session key
|
||
|
// from the ticket.
|
||
|
//
|
||
|
|
||
|
if (((*Authenticator)->bit_mask & KERB_AUTHENTICATOR_subkey_present) != 0)
|
||
|
{
|
||
|
D_DebugLog(( DEB_TRACE, "Using sub session key from authenticator.\n" ));
|
||
|
KerbErr = KerbDuplicateKey(
|
||
|
pkSessionKey,
|
||
|
&(*Authenticator)->KERB_AUTHENTICATOR_subkey
|
||
|
);
|
||
|
*UseSubKey = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KerbErr = KerbDuplicateKey(
|
||
|
pkSessionKey,
|
||
|
&EncryptPart->key
|
||
|
);
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The reply has to be encrypted with the ticket key, not the new
|
||
|
// session key
|
||
|
//
|
||
|
|
||
|
if (ARGUMENT_PRESENT(pkTicketKey))
|
||
|
{
|
||
|
KerbErr = KerbDuplicateKey(
|
||
|
pkTicketKey,
|
||
|
&EncryptPart->key
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
*EncryptTicket = EncryptPart;
|
||
|
EncryptPart = NULL;
|
||
|
|
||
|
Cleanup:
|
||
|
if (EncryptPart != NULL)
|
||
|
{
|
||
|
KerbFreeTicket(EncryptPart);
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
KerbFreeKey(pkSessionKey);
|
||
|
if (ARGUMENT_PRESENT(pkTicketKey))
|
||
|
{
|
||
|
KerbFreeKey(pkTicketKey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbDuplicateSid
|
||
|
//
|
||
|
// Synopsis: Duplicates a SID
|
||
|
//
|
||
|
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
|
||
|
//
|
||
|
// Arguments: DestinationSid - Receives a copy of the SourceSid
|
||
|
// SourceSid - SID to copy
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: STATUS_SUCCESS - the copy succeeded
|
||
|
// STATUS_INSUFFICIENT_RESOURCES - the call to allocate memory
|
||
|
// failed
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbDuplicateSid(
|
||
|
OUT PSID * DestinationSid,
|
||
|
IN PSID SourceSid
|
||
|
)
|
||
|
{
|
||
|
ULONG SidSize;
|
||
|
|
||
|
if (!RtlValidSid(SourceSid))
|
||
|
{
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
DsysAssert(RtlValidSid(SourceSid));
|
||
|
|
||
|
SidSize = RtlLengthSid(SourceSid);
|
||
|
*DestinationSid = (PSID) MIDL_user_allocate( SidSize );
|
||
|
if (*DestinationSid == NULL)
|
||
|
{
|
||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
|
}
|
||
|
RtlCopyMemory(
|
||
|
*DestinationSid,
|
||
|
SourceSid,
|
||
|
SidSize
|
||
|
);
|
||
|
return(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
|
||
|
//
|
||
|
// Ticket pack/unpack code.
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
struct BufferState
|
||
|
{
|
||
|
PBYTE pbBufferPointer;
|
||
|
ULONG cbBufferSize;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
AllocFcn( void * pvState, char ** ppbOut, unsigned int * pulSize )
|
||
|
{
|
||
|
BufferState* state = (BufferState*) pvState;
|
||
|
|
||
|
//
|
||
|
// MIDL pickling calls this routine with the size of the object
|
||
|
// obtained by _GetSize(). This routine must return a buffer in
|
||
|
// ppbOut with at least *pulSize bytes.
|
||
|
//
|
||
|
|
||
|
DsysAssert( state->pbBufferPointer != NULL );
|
||
|
DsysAssert( state->cbBufferSize >= *pulSize );
|
||
|
|
||
|
*ppbOut = (char*)state->pbBufferPointer;
|
||
|
state->pbBufferPointer += *pulSize;
|
||
|
state->cbBufferSize -= *pulSize;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
WriteFcn( void * pvState, char * pbOut, unsigned int ulSize )
|
||
|
{
|
||
|
//
|
||
|
// Since the data was pickled directly to the target buffer, don't
|
||
|
// do anything here.
|
||
|
//
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
ReadFcn( void * pvState, char ** ppbOut, unsigned int * pulSize )
|
||
|
{
|
||
|
BufferState* state = (BufferState*) pvState;
|
||
|
|
||
|
//
|
||
|
// MIDL pickling calls this routine with the size to read.
|
||
|
// This routine must return a buffer in ppbOut which contains the
|
||
|
// encoded data.
|
||
|
//
|
||
|
|
||
|
DsysAssert( state->pbBufferPointer != NULL );
|
||
|
DsysAssert( state->cbBufferSize >= *pulSize );
|
||
|
|
||
|
*ppbOut = (char*)state->pbBufferPointer;
|
||
|
state->pbBufferPointer += *pulSize;
|
||
|
state->cbBufferSize -= *pulSize;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbPackTicket
|
||
|
//
|
||
|
// Synopsis: Packs a KerbInternalTicket to a KerbTicket
|
||
|
//
|
||
|
// Effects: Allocates the KerbTicket via MIDL.
|
||
|
//
|
||
|
// Arguments: [InternalTicket] -- Internal ticket to pack. Those fields
|
||
|
// reused in the packed ticket are zeroed.
|
||
|
// [pkKey] -- Key to pack it with
|
||
|
// [EncryptionType] -- Encryption type to use
|
||
|
// [PackedTicket] -- (out) encrypted ticket. Only the encrypt_part
|
||
|
// is allocated.
|
||
|
//
|
||
|
// History: 08-Jun-93 WadeR Created
|
||
|
//
|
||
|
// Notes: The MES encoding needs to be changed to ASN1 encoding
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbPackTicket(
|
||
|
IN PKERB_TICKET InternalTicket,
|
||
|
IN PKERB_ENCRYPTION_KEY pkKey,
|
||
|
IN ULONG EncryptionType,
|
||
|
OUT PKERB_TICKET PackedTicket
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_TICKET OutputTicket = 0;
|
||
|
PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL;
|
||
|
ULONG cbEncryptedPart;
|
||
|
KERB_TICKET TemporaryTicket;
|
||
|
PUCHAR MarshalledEncryptPart = NULL;
|
||
|
ULONG EncryptionOverhead;
|
||
|
ULONG BlockSize;
|
||
|
|
||
|
//
|
||
|
// Pack the data into the encrypted portion.
|
||
|
//
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&TemporaryTicket,
|
||
|
sizeof(KERB_TICKET)
|
||
|
);
|
||
|
|
||
|
EncryptedTicket = (PKERB_ENCRYPTED_TICKET) InternalTicket->encrypted_part.cipher_text.value;
|
||
|
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
EncryptedTicket,
|
||
|
KERB_ENCRYPTED_TICKET_PDU,
|
||
|
&cbEncryptedPart,
|
||
|
&MarshalledEncryptPart
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to marshall ticket: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// And encrypt it.
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
TemporaryTicket = *InternalTicket;
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&InternalTicket->realm,
|
||
|
sizeof(KERB_REALM)
|
||
|
);
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&InternalTicket->server_name,
|
||
|
sizeof(KERB_PRINCIPAL_NAME)
|
||
|
);
|
||
|
|
||
|
|
||
|
KerbErr = KerbAllocateEncryptionBufferWrapper(
|
||
|
EncryptionType,
|
||
|
cbEncryptedPart,
|
||
|
&TemporaryTicket.encrypted_part.cipher_text.length,
|
||
|
&TemporaryTicket.encrypted_part.cipher_text.value
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
KerbErr = KerbEncryptDataEx(
|
||
|
&TemporaryTicket.encrypted_part,
|
||
|
cbEncryptedPart,
|
||
|
MarshalledEncryptPart,
|
||
|
EncryptionType,
|
||
|
KERB_TICKET_SALT,
|
||
|
pkKey
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to encrypt data: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
*PackedTicket = TemporaryTicket;
|
||
|
|
||
|
Cleanup:
|
||
|
if (MarshalledEncryptPart != NULL)
|
||
|
{
|
||
|
MIDL_user_free(MarshalledEncryptPart);
|
||
|
}
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
if (TemporaryTicket.encrypted_part.cipher_text.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(TemporaryTicket.encrypted_part.cipher_text.value);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbUnpackTicket
|
||
|
//
|
||
|
// Synopsis: Decrypts and unpacks the encyrpted part of aticket.
|
||
|
//
|
||
|
// Effects: Allocates memory, decrypts pktTicket in place
|
||
|
//
|
||
|
// Arguments: [PackedTicket] -- ticket to unpack
|
||
|
// [PackedTicketSize] -- length of packed ticket
|
||
|
// [pkKey] -- key to unpack it with
|
||
|
// [InternalTicket] -- (out) unpacked ticket
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE or error from decrypt
|
||
|
//
|
||
|
// Signals: Any exception the MIDL unpacking code throws.
|
||
|
//
|
||
|
// History: 09-Jun-93 WadeR Created
|
||
|
//
|
||
|
// Notes: Free InternalTicket with KerbFreeTicket, below.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbUnpackTicket(
|
||
|
IN PKERB_TICKET PackedTicket,
|
||
|
IN PKERB_ENCRYPTION_KEY pkKey,
|
||
|
OUT PKERB_ENCRYPTED_TICKET * InternalTicket
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_TICKET DecryptedTicket = NULL;
|
||
|
PUCHAR EncryptedPart = NULL;
|
||
|
ULONG EncryptSize;
|
||
|
PKERB_ENCRYPTED_TICKET EncryptedTicket = NULL;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Now decrypt the encrypted part of the ticket
|
||
|
//
|
||
|
|
||
|
EncryptedPart = (PUCHAR) MIDL_user_allocate(PackedTicket->encrypted_part.cipher_text.length);
|
||
|
if (EncryptedPart == NULL)
|
||
|
{
|
||
|
return(KRB_ERR_GENERIC);
|
||
|
}
|
||
|
|
||
|
EncryptSize = PackedTicket->encrypted_part.cipher_text.length;
|
||
|
KerbErr = KerbDecryptDataEx(
|
||
|
&PackedTicket->encrypted_part,
|
||
|
pkKey,
|
||
|
KERB_TICKET_SALT,
|
||
|
&EncryptSize,
|
||
|
EncryptedPart
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to decrypt ticket: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbUnpackData(
|
||
|
EncryptedPart,
|
||
|
EncryptSize,
|
||
|
KERB_ENCRYPTED_TICKET_PDU,
|
||
|
(PVOID *) &EncryptedTicket
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to unmarshall ticket: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
*InternalTicket = EncryptedTicket;
|
||
|
Cleanup:
|
||
|
if (EncryptedPart != NULL)
|
||
|
{
|
||
|
MIDL_user_free(EncryptedPart);
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCreateAuthenticator
|
||
|
//
|
||
|
// Synopsis: Creates an authenticator for a client to pass to a service
|
||
|
//
|
||
|
// Effects: Encrypts pedAuthenticator
|
||
|
//
|
||
|
// Arguments: [pkKey] -- (in) session key from the ticket this
|
||
|
// authenticator is for.
|
||
|
// [dwEncrType] -- (in) Desired encryption type
|
||
|
// [dwSeq] -- (in) nonce for authenticator
|
||
|
// [ClientName] -- (in) name of principal
|
||
|
// [ClientRealm] -- (in) logon realm of principal
|
||
|
// [SkewTime] -- (in) Skew of server's time
|
||
|
// [pkSubKey] -- (in) desired sub key (may be NULL)
|
||
|
// [GssChecksum] -- (in) optional checksum message to put in authenticator
|
||
|
// [KdcRequest] -- (in) If TRUE, this is an authenticator for a KDC request
|
||
|
// and we use a different salt
|
||
|
// [Authenticator]-- (out) completed authenticator
|
||
|
//
|
||
|
// History: 4-28-93 WadeR Created
|
||
|
//
|
||
|
// Notes: If pkKey is NULL, a null subkey is used.
|
||
|
//
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbCreateAuthenticator(
|
||
|
IN PKERB_ENCRYPTION_KEY pkKey,
|
||
|
IN ULONG EncryptionType,
|
||
|
IN ULONG SequenceNumber,
|
||
|
IN PKERB_INTERNAL_NAME ClientName,
|
||
|
IN PUNICODE_STRING ClientRealm,
|
||
|
IN OPTIONAL PTimeStamp SkewTime,
|
||
|
IN PKERB_ENCRYPTION_KEY pkSubKey,
|
||
|
IN OPTIONAL PKERB_CHECKSUM GssChecksum,
|
||
|
IN BOOLEAN KdcRequest,
|
||
|
OUT PKERB_ENCRYPTED_DATA Authenticator
|
||
|
)
|
||
|
{
|
||
|
KERB_AUTHENTICATOR InternalAuthenticator;
|
||
|
PKERB_AUTHENTICATOR AuthPointer = &InternalAuthenticator;
|
||
|
ULONG cbAuthenticator;
|
||
|
ULONG cbTotal;
|
||
|
PUCHAR PackedAuthenticator = NULL;
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
TimeStamp TimeToUse;
|
||
|
ULONG EncryptionOverhead;
|
||
|
ULONG BlockSize;
|
||
|
|
||
|
Authenticator->cipher_text.value = NULL;
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&InternalAuthenticator,
|
||
|
sizeof(KERB_AUTHENTICATOR)
|
||
|
);
|
||
|
|
||
|
// Build an authenticator
|
||
|
|
||
|
InternalAuthenticator.authenticator_version = KERBEROS_VERSION;
|
||
|
|
||
|
// Use "InitString" because we will marshall and then discard the
|
||
|
// InternalAthenticator. Therefore it's not a problem having the
|
||
|
// string point to memory we don't own.
|
||
|
|
||
|
KerbErr = KerbConvertUnicodeStringToRealm(
|
||
|
&InternalAuthenticator.client_realm,
|
||
|
ClientRealm
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
KerbErr = KerbConvertKdcNameToPrincipalName(
|
||
|
&InternalAuthenticator.client_name,
|
||
|
ClientName
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stick the correct time in the authenticator
|
||
|
//
|
||
|
|
||
|
GetSystemTimeAsFileTime((PFILETIME)&TimeToUse);
|
||
|
if (ARGUMENT_PRESENT(SkewTime))
|
||
|
{
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
TimeToUse.QuadPart += SkewTime->QuadPart;
|
||
|
#else // WIN32_CHICAGO
|
||
|
TimeToUse += *SkewTime;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
}
|
||
|
|
||
|
KerbConvertLargeIntToGeneralizedTimeWrapper(
|
||
|
&InternalAuthenticator.client_time,
|
||
|
&InternalAuthenticator.client_usec,
|
||
|
&TimeToUse
|
||
|
);
|
||
|
|
||
|
InternalAuthenticator.bit_mask |= KERB_AUTHENTICATOR_sequence_number_present;
|
||
|
|
||
|
ASN1intx_setuint32(
|
||
|
&InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number,
|
||
|
SequenceNumber
|
||
|
);
|
||
|
if (InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number.value == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
if (ARGUMENT_PRESENT(pkSubKey))
|
||
|
{
|
||
|
InternalAuthenticator.bit_mask |= KERB_AUTHENTICATOR_subkey_present;
|
||
|
InternalAuthenticator.KERB_AUTHENTICATOR_subkey = *pkSubKey;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the GSS checksum is present, include it and set it in the bitmask
|
||
|
//
|
||
|
|
||
|
if (ARGUMENT_PRESENT(GssChecksum))
|
||
|
{
|
||
|
InternalAuthenticator.checksum = *GssChecksum;
|
||
|
InternalAuthenticator.bit_mask |= checksum_present;
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
AuthPointer,
|
||
|
KERB_AUTHENTICATOR_PDU,
|
||
|
&cbAuthenticator,
|
||
|
&PackedAuthenticator
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to marshall authenticator: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we need to encrypt the buffer
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbAllocateEncryptionBufferWrapper(
|
||
|
EncryptionType,
|
||
|
cbAuthenticator,
|
||
|
&Authenticator->cipher_text.length,
|
||
|
&Authenticator->cipher_text.value
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
KerbErr = KerbEncryptDataEx(
|
||
|
Authenticator,
|
||
|
cbAuthenticator,
|
||
|
PackedAuthenticator,
|
||
|
EncryptionType,
|
||
|
KdcRequest ? KERB_TGS_REQ_AP_REQ_AUTH_SALT : KERB_AP_REQ_AUTH_SALT,
|
||
|
pkKey
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to encrypt data: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
Cleanup:
|
||
|
KerbFreePrincipalName(&InternalAuthenticator.client_name);
|
||
|
KerbFreeRealm(&InternalAuthenticator.client_realm);
|
||
|
if (InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number.value != NULL)
|
||
|
{
|
||
|
ASN1intx_free(&InternalAuthenticator.KERB_AUTHENTICATOR_sequence_number);
|
||
|
}
|
||
|
if (PackedAuthenticator != NULL)
|
||
|
{
|
||
|
MIDL_user_free(PackedAuthenticator);
|
||
|
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbUnpackAuthenticator
|
||
|
//
|
||
|
// Synopsis: Unpacks and decrypts an authenticator
|
||
|
//
|
||
|
// Effects: allocates memory for output authenticator
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbUnpackAuthenticator(
|
||
|
IN PKERB_ENCRYPTION_KEY Key,
|
||
|
IN PKERB_ENCRYPTED_DATA EncryptedAuthenticator,
|
||
|
IN BOOLEAN KdcRequest,
|
||
|
OUT PKERB_AUTHENTICATOR * Authenticator
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PUCHAR EncryptedPart;
|
||
|
ULONG EncryptedSize;
|
||
|
ULONG Pdu = KERB_AUTHENTICATOR_PDU;
|
||
|
|
||
|
*Authenticator = NULL;
|
||
|
//
|
||
|
// Decrypt it
|
||
|
//
|
||
|
|
||
|
|
||
|
EncryptedPart = (PUCHAR) MIDL_user_allocate(EncryptedAuthenticator->cipher_text.length);
|
||
|
if (EncryptedPart == NULL)
|
||
|
{
|
||
|
return(KRB_ERR_GENERIC);
|
||
|
}
|
||
|
|
||
|
EncryptedSize = EncryptedAuthenticator->cipher_text.length;
|
||
|
KerbErr = KerbDecryptDataEx(
|
||
|
EncryptedAuthenticator,
|
||
|
Key,
|
||
|
KdcRequest ? KERB_TGS_REQ_AP_REQ_AUTH_SALT : KERB_AP_REQ_AUTH_SALT,
|
||
|
&EncryptedSize,
|
||
|
EncryptedPart
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to decrypt authenticator: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unpack it
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbUnpackData(
|
||
|
EncryptedPart,
|
||
|
EncryptedSize,
|
||
|
Pdu,
|
||
|
(PVOID *) Authenticator
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to unmarshall authenticator: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Cleanup:
|
||
|
if (EncryptedPart != NULL)
|
||
|
{
|
||
|
MIDL_user_free(EncryptedPart);
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr) && (*Authenticator != NULL))
|
||
|
{
|
||
|
MIDL_user_free(*Authenticator);
|
||
|
*Authenticator = NULL;
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// KDC Reply stuff
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbPackKdcReplyBody
|
||
|
//
|
||
|
// Synopsis: Marshalls a the body of a KDC reply
|
||
|
//
|
||
|
// Effects: allocates value of encrypted reply
|
||
|
//
|
||
|
// Arguments: ReplyBody - The reply body to marshall
|
||
|
// Key - The key to encrypt the reply
|
||
|
// EncryptionType - the algorithm to encrypt with
|
||
|
// Pdu - Pdu to pack with, eith AS or TGS reply
|
||
|
// EncryptedReplyBody - receives the encrypted and marshalled reply
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbPackKdcReplyBody(
|
||
|
IN PKERB_ENCRYPTED_KDC_REPLY ReplyBody,
|
||
|
IN PKERB_ENCRYPTION_KEY Key,
|
||
|
IN ULONG EncryptionType,
|
||
|
IN ULONG Pdu,
|
||
|
OUT PKERB_ENCRYPTED_DATA EncryptedReply
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
ULONG BodySize;
|
||
|
ULONG EncryptionOverhead;
|
||
|
PUCHAR MarshalledReply = NULL;
|
||
|
ULONG TotalSize;
|
||
|
ULONG BlockSize = 0;
|
||
|
|
||
|
EncryptedReply->cipher_text.value = NULL;
|
||
|
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
ReplyBody,
|
||
|
Pdu,
|
||
|
&BodySize,
|
||
|
&MarshalledReply
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to marshall kdc reply body: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we need to encrypt this into the encrypted data structure.
|
||
|
//
|
||
|
|
||
|
|
||
|
//
|
||
|
// First get the overhead size
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbGetEncryptionOverhead(
|
||
|
EncryptionType,
|
||
|
&EncryptionOverhead,
|
||
|
&BlockSize
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
DsysAssert(BlockSize <= 8);
|
||
|
|
||
|
|
||
|
TotalSize = ROUND_UP_COUNT(EncryptionOverhead + BodySize, BlockSize);
|
||
|
|
||
|
EncryptedReply->cipher_text.length = TotalSize;
|
||
|
EncryptedReply->cipher_text.value = (PUCHAR) MIDL_user_allocate(TotalSize);
|
||
|
if (EncryptedReply->cipher_text.value == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now encrypt the buffer
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbEncryptDataEx(
|
||
|
EncryptedReply,
|
||
|
BodySize,
|
||
|
MarshalledReply,
|
||
|
EncryptionType,
|
||
|
(Pdu == KERB_AS_REPLY_PDU) ? KERB_AS_REP_SALT : KERB_TGS_REP_SALT,
|
||
|
Key
|
||
|
);
|
||
|
|
||
|
#ifndef USE_FOR_CYBERSAFE
|
||
|
EncryptedReply->version = 1;
|
||
|
EncryptedReply->bit_mask |= version_present;
|
||
|
#endif
|
||
|
|
||
|
Cleanup:
|
||
|
if (MarshalledReply != NULL)
|
||
|
{
|
||
|
MIDL_user_free(MarshalledReply);
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr) && (EncryptedReply->cipher_text.value != NULL))
|
||
|
{
|
||
|
MIDL_user_free(EncryptedReply->cipher_text.value);
|
||
|
EncryptedReply->cipher_text.value = NULL;
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbUnpackKdcReplyBody
|
||
|
//
|
||
|
// Synopsis: Unpacks a KDC reply body
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: EncryptedReplyBody - an encrypted marshalled reply body.
|
||
|
// Key - Key to decrypt the reply.
|
||
|
// Pdu - PDU of reply body (eithe AS or TGS)
|
||
|
// ReplyBody - receives the decrypted reply body, allocated with
|
||
|
// MIDL_user_allocate.
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbUnpackKdcReplyBody(
|
||
|
IN PKERB_ENCRYPTED_DATA EncryptedReplyBody,
|
||
|
IN PKERB_ENCRYPTION_KEY Key,
|
||
|
IN ULONG Pdu,
|
||
|
OUT PKERB_ENCRYPTED_KDC_REPLY * ReplyBody
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PUCHAR MarshalledReply = NULL;
|
||
|
ULONG ReplySize;
|
||
|
|
||
|
*ReplyBody = NULL;
|
||
|
MarshalledReply = (PUCHAR) MIDL_user_allocate(EncryptedReplyBody->cipher_text.length);
|
||
|
|
||
|
if (MarshalledReply == NULL)
|
||
|
{
|
||
|
return(KRB_ERR_GENERIC);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First decrypt the buffer
|
||
|
//
|
||
|
|
||
|
ReplySize = EncryptedReplyBody->cipher_text.length;
|
||
|
KerbErr = KerbDecryptDataEx(
|
||
|
EncryptedReplyBody,
|
||
|
Key,
|
||
|
(Pdu == KERB_AS_REPLY_PDU) ? KERB_AS_REP_SALT : KERB_TGS_REP_SALT,
|
||
|
&ReplySize,
|
||
|
MarshalledReply
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbUnpackData(
|
||
|
MarshalledReply,
|
||
|
ReplySize,
|
||
|
Pdu,
|
||
|
(PVOID *) ReplyBody
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// MIT KDCs send back TGS reply bodies instead of AS reply bodies
|
||
|
// so try TGS here
|
||
|
//
|
||
|
|
||
|
if (Pdu == KERB_ENCRYPTED_AS_REPLY_PDU)
|
||
|
{
|
||
|
KerbErr = KerbUnpackData(
|
||
|
MarshalledReply,
|
||
|
ReplySize,
|
||
|
KERB_ENCRYPTED_TGS_REPLY_PDU,
|
||
|
(PVOID *) ReplyBody
|
||
|
);
|
||
|
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to unmarshall kdc reply body: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
Cleanup:
|
||
|
if (MarshalledReply != NULL)
|
||
|
{
|
||
|
MIDL_user_free(MarshalledReply);
|
||
|
}
|
||
|
if (!KERB_SUCCESS(KerbErr) && (*ReplyBody != NULL))
|
||
|
{
|
||
|
MIDL_user_free(*ReplyBody);
|
||
|
*ReplyBody = NULL;
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFindAuthDataEntry
|
||
|
//
|
||
|
// Synopsis: Finds a specific entry in an authorization data structure
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: EntryId - ID of the entry to locate
|
||
|
// AuthData - the authorization data to search
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: NULL if it wasn't found of the auth data entry
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
PKERB_AUTHORIZATION_DATA
|
||
|
KerbFindAuthDataEntry(
|
||
|
IN ULONG EntryId,
|
||
|
IN PKERB_AUTHORIZATION_DATA AuthData
|
||
|
)
|
||
|
{
|
||
|
PKERB_AUTHORIZATION_DATA TempData = AuthData;
|
||
|
|
||
|
while (TempData != NULL)
|
||
|
{
|
||
|
if (TempData->value.auth_data_type == (int) EntryId)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
TempData = TempData->next;
|
||
|
}
|
||
|
return(TempData);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFindPreAuthDataEntry
|
||
|
//
|
||
|
// Synopsis: Finds a specific entry in an authorization data structure
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: EntryId - ID of the entry to locate
|
||
|
// AuthData - the authorization data to search
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: NULL if it wasn't found of the auth data entry
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
PKERB_PA_DATA
|
||
|
KerbFindPreAuthDataEntry(
|
||
|
IN ULONG EntryId,
|
||
|
IN PKERB_PA_DATA_LIST AuthData
|
||
|
)
|
||
|
{
|
||
|
PKERB_PA_DATA_LIST TempData = AuthData;
|
||
|
|
||
|
while (TempData != NULL)
|
||
|
{
|
||
|
if (TempData->value.preauth_data_type == (int) EntryId)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
TempData = TempData->next;
|
||
|
}
|
||
|
return(TempData != NULL ? &TempData->value : NULL);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreePreAuthData
|
||
|
//
|
||
|
// Synopsis: Frees a pa-data list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: PreAuthData - data to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbFreePreAuthData(
|
||
|
IN OPTIONAL PKERB_PA_DATA_LIST PreAuthData
|
||
|
)
|
||
|
{
|
||
|
PKERB_PA_DATA_LIST Next,Last;
|
||
|
|
||
|
Next = PreAuthData;
|
||
|
|
||
|
while (Next != NULL)
|
||
|
{
|
||
|
Last = Next->next;
|
||
|
if (Next->value.preauth_data.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(Next->value.preauth_data.value);
|
||
|
}
|
||
|
MIDL_user_free(Next);
|
||
|
Next = Last;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeAuthData
|
||
|
//
|
||
|
// Synopsis: Frees and auth data structure that was allocated in
|
||
|
// pieces
|
||
|
//
|
||
|
// Effects: frees with MIDL_user_Free
|
||
|
//
|
||
|
// Arguments: AuthData - the auth data to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbFreeAuthData(
|
||
|
IN PKERB_AUTHORIZATION_DATA AuthData
|
||
|
)
|
||
|
{
|
||
|
PKERB_AUTHORIZATION_DATA TempData1,TempData2;
|
||
|
|
||
|
TempData1 = AuthData;
|
||
|
while (TempData1 != NULL)
|
||
|
{
|
||
|
TempData2 = TempData1->next;
|
||
|
if (TempData1->value.auth_data.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(TempData1->value.auth_data.value);
|
||
|
}
|
||
|
MIDL_user_free(TempData1);
|
||
|
TempData1 = TempData2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCopyAndAppendAuthData
|
||
|
//
|
||
|
// Synopsis: copies the elements from the input auth data and appends
|
||
|
// them to the end of the output auth data.
|
||
|
//
|
||
|
// Effects: allocates each auth data with MIDL_user_allocate
|
||
|
//
|
||
|
// Arguments: OutputAuthData - receives list of append auth data
|
||
|
// InputAuthData - optionally contains auth data to append
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC;
|
||
|
//
|
||
|
// Notes: on failure output auth data will be freed and set to NULL.
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbCopyAndAppendAuthData(
|
||
|
OUT PKERB_AUTHORIZATION_DATA * OutputAuthData,
|
||
|
IN PKERB_AUTHORIZATION_DATA InputAuthData
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_AUTHORIZATION_DATA *LastEntry = OutputAuthData;
|
||
|
PKERB_AUTHORIZATION_DATA TempEntry = NULL;
|
||
|
|
||
|
//
|
||
|
// Find the end of the list
|
||
|
//
|
||
|
|
||
|
while (*LastEntry != NULL)
|
||
|
{
|
||
|
LastEntry = &((*LastEntry)->next);
|
||
|
}
|
||
|
|
||
|
while (InputAuthData != NULL)
|
||
|
{
|
||
|
//
|
||
|
// copy the existing entry
|
||
|
//
|
||
|
|
||
|
TempEntry = (PKERB_AUTHORIZATION_DATA) MIDL_user_allocate(sizeof(KERB_AUTHORIZATION_DATA));
|
||
|
if (TempEntry == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
TempEntry->value.auth_data.length = InputAuthData->value.auth_data.length;
|
||
|
TempEntry->value.auth_data_type = InputAuthData->value.auth_data_type;
|
||
|
TempEntry->next = NULL;
|
||
|
|
||
|
TempEntry->value.auth_data.value = (PUCHAR) MIDL_user_allocate(InputAuthData->value.auth_data.length);
|
||
|
|
||
|
if (TempEntry->value.auth_data.value == NULL)
|
||
|
{
|
||
|
MIDL_user_free(TempEntry);
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
TempEntry->value.auth_data.value,
|
||
|
InputAuthData->value.auth_data.value,
|
||
|
InputAuthData->value.auth_data.length
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// add it to the end of the list
|
||
|
//
|
||
|
|
||
|
*LastEntry = TempEntry;
|
||
|
LastEntry = &TempEntry->next;
|
||
|
InputAuthData = InputAuthData->next;
|
||
|
}
|
||
|
|
||
|
KerbErr = KDC_ERR_NONE;
|
||
|
|
||
|
Cleanup:
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
KerbFreeAuthData(*OutputAuthData);
|
||
|
*OutputAuthData = NULL;
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertCryptListToArray
|
||
|
//
|
||
|
// Synopsis: Converts a linked-list crypt vector to an array of ULONGs
|
||
|
//
|
||
|
// Effects: allocates return with MIDL_user_allocate
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR
|
||
|
KerbConvertCryptListToArray(
|
||
|
OUT PULONG * ETypeArray,
|
||
|
OUT PULONG ETypeCount,
|
||
|
IN PKERB_CRYPT_LIST CryptList
|
||
|
)
|
||
|
|
||
|
{
|
||
|
KERBERR Status = KDC_ERR_NONE;
|
||
|
PKERB_CRYPT_LIST NextEType;
|
||
|
ULONG ClientETypeCount;
|
||
|
PULONG ClientETypes = NULL;
|
||
|
|
||
|
//
|
||
|
// Build a vector of the client encrypt types
|
||
|
//
|
||
|
|
||
|
NextEType = CryptList;
|
||
|
|
||
|
ClientETypeCount = 0;
|
||
|
while (NextEType != NULL)
|
||
|
{
|
||
|
ClientETypeCount++;
|
||
|
NextEType = NextEType->next;
|
||
|
}
|
||
|
|
||
|
ClientETypes = (PULONG) MIDL_user_allocate(sizeof(ULONG) * ClientETypeCount);
|
||
|
if (ClientETypes == NULL)
|
||
|
{
|
||
|
Status = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
NextEType = CryptList;
|
||
|
|
||
|
ClientETypeCount = 0;
|
||
|
while (NextEType != NULL)
|
||
|
{
|
||
|
ClientETypes[ClientETypeCount] = NextEType->value;
|
||
|
ClientETypeCount++;
|
||
|
NextEType = NextEType->next;
|
||
|
}
|
||
|
*ETypeCount = ClientETypeCount;
|
||
|
*ETypeArray = ClientETypes;
|
||
|
|
||
|
Cleanup:
|
||
|
return(Status);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertArrayToCryptList
|
||
|
//
|
||
|
// Synopsis: Converts an array of encryption to types to a linked list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbConvertArrayToCryptList(
|
||
|
OUT PKERB_CRYPT_LIST * CryptList,
|
||
|
IN PULONG ETypeArray,
|
||
|
IN ULONG ETypeCount
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
ULONG Index;
|
||
|
PKERB_CRYPT_LIST ListHead = NULL;
|
||
|
PKERB_CRYPT_LIST ListTail = NULL;
|
||
|
PKERB_CRYPT_LIST NewListEntry = NULL;
|
||
|
|
||
|
//
|
||
|
// If there no encryption types, bail out now.
|
||
|
//
|
||
|
|
||
|
if (ETypeCount == 0)
|
||
|
{
|
||
|
*CryptList = NULL;
|
||
|
return(KDC_ERR_NONE);
|
||
|
}
|
||
|
|
||
|
for (Index = 0; Index < ETypeCount ; Index++ )
|
||
|
{
|
||
|
NewListEntry = (PKERB_CRYPT_LIST) MIDL_user_allocate(sizeof(KERB_CRYPT_LIST));
|
||
|
if (NewListEntry == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
NewListEntry->value = ETypeArray[Index];
|
||
|
NewListEntry->next = NULL;
|
||
|
if (ListTail != NULL)
|
||
|
{
|
||
|
ListTail->next = NewListEntry;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DsysAssert(ListHead == NULL);
|
||
|
ListHead = NewListEntry;
|
||
|
}
|
||
|
ListTail = NewListEntry;
|
||
|
}
|
||
|
|
||
|
*CryptList = ListHead;
|
||
|
ListHead = NULL;
|
||
|
|
||
|
Cleanup:
|
||
|
while (ListHead != NULL)
|
||
|
{
|
||
|
NewListEntry = ListHead->next;
|
||
|
MIDL_user_free(ListHead);
|
||
|
ListHead = NewListEntry;
|
||
|
}
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertKeysToCryptList
|
||
|
//
|
||
|
// Synopsis: Converts an array of keys to types to a linked list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbConvertKeysToCryptList(
|
||
|
OUT PKERB_CRYPT_LIST * CryptList,
|
||
|
IN PKERB_STORED_CREDENTIAL Keys
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
ULONG Index;
|
||
|
PKERB_CRYPT_LIST ListHead = NULL;
|
||
|
PKERB_CRYPT_LIST ListTail = NULL;
|
||
|
PKERB_CRYPT_LIST NewListEntry = NULL;
|
||
|
|
||
|
//
|
||
|
// If there no encryption types, bail out now.
|
||
|
//
|
||
|
|
||
|
if (Keys->CredentialCount == 0)
|
||
|
{
|
||
|
*CryptList = NULL;
|
||
|
return(KDC_ERR_NONE);
|
||
|
}
|
||
|
|
||
|
for (Index = 0; Index < Keys->CredentialCount ; Index++ )
|
||
|
{
|
||
|
NewListEntry = (PKERB_CRYPT_LIST) MIDL_user_allocate(sizeof(KERB_CRYPT_LIST));
|
||
|
if (NewListEntry == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
NewListEntry->value = Keys->Credentials[Index].Key.keytype;
|
||
|
NewListEntry->next = NULL;
|
||
|
if (ListTail != NULL)
|
||
|
{
|
||
|
ListTail->next = NewListEntry;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DsysAssert(ListHead == NULL);
|
||
|
ListHead = NewListEntry;
|
||
|
}
|
||
|
ListTail = NewListEntry;
|
||
|
}
|
||
|
|
||
|
*CryptList = ListHead;
|
||
|
ListHead = NULL;
|
||
|
|
||
|
Cleanup:
|
||
|
while (ListHead != NULL)
|
||
|
{
|
||
|
NewListEntry = ListHead->next;
|
||
|
MIDL_user_free(ListHead);
|
||
|
ListHead = NewListEntry;
|
||
|
}
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeCryptList
|
||
|
//
|
||
|
// Synopsis: Frees a list of crypt types
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: CryptList - List to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbFreeCryptList(
|
||
|
IN PKERB_CRYPT_LIST CryptList
|
||
|
)
|
||
|
{
|
||
|
PKERB_CRYPT_LIST ListHead = CryptList;
|
||
|
PKERB_CRYPT_LIST NewListEntry;
|
||
|
|
||
|
while (ListHead != NULL)
|
||
|
{
|
||
|
NewListEntry = ListHead->next;
|
||
|
MIDL_user_free(ListHead);
|
||
|
ListHead = NewListEntry;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCreateApRequest
|
||
|
//
|
||
|
// Synopsis: builds an AP request message
|
||
|
//
|
||
|
// Effects: allocates memory with MIDL_user_allocate
|
||
|
//
|
||
|
// Arguments: ClientName - Name of client
|
||
|
// ClientRealm - Realm of client
|
||
|
// SessionKey - Session key for the ticket
|
||
|
// SubSessionKey - obtional sub Session key for the authenticator
|
||
|
// Nonce - Nonce to use in authenticator
|
||
|
// ServiceTicket - Ticket for service to put in request
|
||
|
// ApOptions - Options to stick in AP request
|
||
|
// GssChecksum - Checksum for GSS compatibility containing
|
||
|
// context options and delegation info.
|
||
|
// KdcRequest - if TRUE, this is an AP request for a TGS req
|
||
|
// ServerSkewTime - Optional skew of server's time
|
||
|
// RequestSize - Receives size of the marshalled request
|
||
|
// Request - Receives the marshalled request
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE on success, KRB_ERR_GENERIC on memory or
|
||
|
// marshalling failure
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbCreateApRequest(
|
||
|
IN PKERB_INTERNAL_NAME ClientName,
|
||
|
IN PUNICODE_STRING ClientRealm,
|
||
|
IN PKERB_ENCRYPTION_KEY SessionKey,
|
||
|
IN OPTIONAL PKERB_ENCRYPTION_KEY SubSessionKey,
|
||
|
IN ULONG Nonce,
|
||
|
IN PKERB_TICKET ServiceTicket,
|
||
|
IN ULONG ApOptions,
|
||
|
IN OPTIONAL PKERB_CHECKSUM GssChecksum,
|
||
|
IN OPTIONAL PTimeStamp ServerSkewTime,
|
||
|
IN BOOLEAN KdcRequest,
|
||
|
OUT PULONG RequestSize,
|
||
|
OUT PUCHAR * Request
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
KERB_AP_REQUEST ApRequest;
|
||
|
ULONG ApFlags;
|
||
|
|
||
|
*Request = NULL;
|
||
|
RtlZeroMemory(
|
||
|
&ApRequest,
|
||
|
sizeof(KERB_AP_REQUEST)
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Fill in the AP request structure.
|
||
|
//
|
||
|
|
||
|
ApRequest.version = KERBEROS_VERSION;
|
||
|
ApRequest.message_type = KRB_AP_REQ;
|
||
|
ApFlags = KerbConvertUlongToFlagUlong(ApOptions);
|
||
|
ApRequest.ap_options.value = (PUCHAR) &ApFlags;
|
||
|
ApRequest.ap_options.length = sizeof(ULONG) * 8;
|
||
|
ApRequest.ticket = *ServiceTicket;
|
||
|
|
||
|
//
|
||
|
// Create the authenticator for the request
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
KerbErr = KerbCreateAuthenticator(
|
||
|
SessionKey,
|
||
|
SessionKey->keytype,
|
||
|
Nonce,
|
||
|
ClientName,
|
||
|
ClientRealm,
|
||
|
ServerSkewTime,
|
||
|
SubSessionKey,
|
||
|
GssChecksum,
|
||
|
KdcRequest,
|
||
|
&ApRequest.authenticator
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to build authenticator: 0x%x\n",
|
||
|
KerbErr ));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now marshall the request
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbPackApRequest(
|
||
|
&ApRequest,
|
||
|
RequestSize,
|
||
|
Request
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to pack AP request: 0x%x\n",KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Cleanup:
|
||
|
if (ApRequest.authenticator.cipher_text.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(ApRequest.authenticator.cipher_text.value);
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbInitAsn
|
||
|
//
|
||
|
// Synopsis: Initializes asn1 marshalling code
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: none
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE on success, KRB_ERR_GENERIC on failure
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
BOOL fKRB5ModuleStarted = FALSE;
|
||
|
|
||
|
KERBERR
|
||
|
KerbInitAsn(
|
||
|
IN OUT ASN1encoding_t * pEnc,
|
||
|
IN OUT ASN1decoding_t * pDec
|
||
|
)
|
||
|
{
|
||
|
int Result;
|
||
|
KERBERR KerbErr = KRB_ERR_GENERIC;
|
||
|
ASN1error_e Asn1Err;
|
||
|
|
||
|
if (!fKRB5ModuleStarted)
|
||
|
{
|
||
|
fKRB5ModuleStarted = TRUE;
|
||
|
KRB5_Module_Startup();
|
||
|
}
|
||
|
|
||
|
if (pEnc != NULL)
|
||
|
{
|
||
|
Asn1Err = ASN1_CreateEncoder(
|
||
|
KRB5_Module,
|
||
|
pEnc,
|
||
|
NULL, // pbBuf
|
||
|
0, // cbBufSize
|
||
|
NULL // pParent
|
||
|
);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Asn1Err = ASN1_CreateDecoder(
|
||
|
KRB5_Module,
|
||
|
pDec,
|
||
|
NULL, // pbBuf
|
||
|
0, // cbBufSize
|
||
|
NULL // pParent
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (ASN1_SUCCESS != Asn1Err)
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Failed to init ASN1: 0x%x\n",Asn1Err));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbErr = KDC_ERR_NONE;
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbTermAsn
|
||
|
//
|
||
|
// Synopsis: terminates an ASN world
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbTermAsn(
|
||
|
IN ASN1encoding_t pEnc,
|
||
|
IN ASN1decoding_t pDec
|
||
|
)
|
||
|
{
|
||
|
if (pEnc != NULL)
|
||
|
{
|
||
|
ASN1_CloseEncoder(pEnc);
|
||
|
}
|
||
|
else if (pDec != NULL)
|
||
|
{
|
||
|
ASN1_CloseDecoder(pDec);
|
||
|
}
|
||
|
|
||
|
//KRB5_Module_Cleanup();
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbPackData
|
||
|
//
|
||
|
// Synopsis: Packs a datatype using ASN.1 encoding
|
||
|
//
|
||
|
// Effects: allocates memory with MIDL_user_allocate
|
||
|
//
|
||
|
// Arguments: Data - The message to marshall/pack.
|
||
|
// PduValue - The PDU for the message type
|
||
|
// DataSize - receives the size of the marshalled message in
|
||
|
// bytes.
|
||
|
// MarshalledData - receives a pointer to the marshalled
|
||
|
// message buffer.
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbPackData(
|
||
|
IN PVOID Data,
|
||
|
IN ULONG PduValue,
|
||
|
OUT PULONG DataSize,
|
||
|
OUT PUCHAR * MarshalledData
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
int Result = 0;
|
||
|
PUCHAR Buffer = NULL;
|
||
|
ASN1encoding_t pEnc = NULL;
|
||
|
ASN1error_e Asn1Err;
|
||
|
|
||
|
KerbErr = KerbInitAsn(
|
||
|
&pEnc, // we are encoding
|
||
|
NULL
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Encode the data type.
|
||
|
//
|
||
|
|
||
|
D_DebugLog((DEB_TRACE,"encoding pdu #%d\n",PduValue));
|
||
|
Asn1Err = ASN1_Encode(
|
||
|
pEnc,
|
||
|
Data,
|
||
|
PduValue,
|
||
|
ASN1ENCODE_ALLOCATEBUFFER,
|
||
|
NULL, // pbBuf
|
||
|
0 // cbBufSize
|
||
|
);
|
||
|
|
||
|
if (!ASN1_SUCCEEDED(Asn1Err))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to encode data: %d\n",Asn1Err));
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// when the oss compiler was used the allocation routines were configurable.
|
||
|
// therefore, the encoded data could just be free'd using our
|
||
|
// deallocator. in the new model we cannot configure the allocation routines
|
||
|
// for encoding.
|
||
|
|
||
|
// so we do not have to go and change every place where a free
|
||
|
// of an encoded buffer is done, use our allocator to allocate a new buffer,
|
||
|
// then copy the encoded data to it, and free the buffer that was allocated by
|
||
|
// the encoding engine. THIS SHOULD BE CHANGED FOR BETTER PERFORMANCE
|
||
|
//
|
||
|
|
||
|
*MarshalledData = (PUCHAR) MIDL_user_allocate(pEnc->len);
|
||
|
if (*MarshalledData == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
*DataSize = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RtlCopyMemory(*MarshalledData, pEnc->buf, pEnc->len);
|
||
|
*DataSize = pEnc->len;
|
||
|
|
||
|
}
|
||
|
|
||
|
ASN1_FreeEncoded(pEnc, pEnc->buf);
|
||
|
}
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
KerbTermAsn(pEnc, NULL);
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbUnpackData
|
||
|
//
|
||
|
// Synopsis: Unpacks an message from the ASN.1 encoding
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Data - Buffer containing the reply message.
|
||
|
// DataSize - Size of the reply message in bytes
|
||
|
// Reply - receives a KERB_ENCRYPTED_DATA structure allocated with
|
||
|
// MIDL_user_allocate.
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbUnpackData(
|
||
|
IN PUCHAR Data,
|
||
|
IN ULONG DataSize,
|
||
|
IN ULONG PduValue,
|
||
|
OUT PVOID * DecodedData
|
||
|
)
|
||
|
{
|
||
|
int Result;
|
||
|
ULONG OldPduValue;
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
ASN1decoding_t pDec = NULL;
|
||
|
ASN1error_e Asn1Err;
|
||
|
|
||
|
if ((DataSize == 0) || (Data == NULL))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Trying to unpack NULL data\n"));
|
||
|
return(KRB_ERR_GENERIC);
|
||
|
}
|
||
|
|
||
|
|
||
|
KerbErr = KerbInitAsn(
|
||
|
NULL,
|
||
|
&pDec // we are decoding
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
*DecodedData = NULL;
|
||
|
Asn1Err = ASN1_Decode(
|
||
|
pDec,
|
||
|
DecodedData,
|
||
|
PduValue,
|
||
|
ASN1DECODE_SETBUFFER,
|
||
|
(BYTE *) Data,
|
||
|
DataSize
|
||
|
);
|
||
|
|
||
|
if (!ASN1_SUCCEEDED(Asn1Err))
|
||
|
{
|
||
|
|
||
|
if ((ASN1_ERR_BADARGS == Asn1Err) ||
|
||
|
(ASN1_ERR_EOD == Asn1Err))
|
||
|
{
|
||
|
D_DebugLog((DEB_TRACE,"More input required to decode data %d.\n",PduValue));
|
||
|
KerbErr = KDC_ERR_MORE_DATA;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ASN1_ERR_BADTAG != Asn1Err)
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to decode data: %d\n", Asn1Err ));
|
||
|
}
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
}
|
||
|
*DecodedData = NULL;
|
||
|
}
|
||
|
|
||
|
KerbTermAsn(NULL, pDec);
|
||
|
|
||
|
return(KerbErr);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeData
|
||
|
//
|
||
|
// Synopsis: Frees a structure unpacked by the ASN1 decoder
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbFreeData(
|
||
|
IN ULONG PduValue,
|
||
|
IN PVOID Data
|
||
|
)
|
||
|
{
|
||
|
ASN1decoding_t pDec = NULL;
|
||
|
|
||
|
|
||
|
if (ARGUMENT_PRESENT(Data))
|
||
|
{
|
||
|
KERBERR KerbErr;
|
||
|
KerbErr = KerbInitAsn(
|
||
|
NULL,
|
||
|
&pDec // this is a decoded structure
|
||
|
);
|
||
|
|
||
|
if (KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
ASN1_FreeDecoded(pDec, Data, PduValue);
|
||
|
|
||
|
KerbTermAsn(NULL, pDec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeTicketExtensions
|
||
|
//
|
||
|
// Synopsis: Frees a host address allocated with KerbDuplicateTicketExtensions
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Addresses - The name to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbFreeTicketExtensions(
|
||
|
IN PKERB_TICKET_EXTENSIONS Extensions
|
||
|
)
|
||
|
{
|
||
|
PKERB_TICKET_EXTENSIONS Elem,NextElem;
|
||
|
|
||
|
Elem = Extensions;
|
||
|
while (Elem != NULL)
|
||
|
{
|
||
|
if (Elem->value.te_data.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(Elem->value.te_data.value);
|
||
|
}
|
||
|
NextElem = Elem->next;
|
||
|
MIDL_user_free(Elem);
|
||
|
Elem = NextElem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbDuplicateTicketExtensions
|
||
|
//
|
||
|
// Synopsis: duplicates the ticket extensions field from a ticket
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
KERBERR
|
||
|
KerbDuplicateTicketExtensions(
|
||
|
OUT PKERB_TICKET_EXTENSIONS * Dest,
|
||
|
IN PKERB_TICKET_EXTENSIONS Source
|
||
|
)
|
||
|
{
|
||
|
KERBERR Status = KDC_ERR_NONE;
|
||
|
PKERB_TICKET_EXTENSIONS SourceElem;
|
||
|
PKERB_TICKET_EXTENSIONS DestElem;
|
||
|
PKERB_TICKET_EXTENSIONS * NextElem;
|
||
|
|
||
|
*Dest = NULL;
|
||
|
|
||
|
|
||
|
|
||
|
SourceElem = Source;
|
||
|
NextElem = Dest;
|
||
|
|
||
|
while (SourceElem != NULL)
|
||
|
{
|
||
|
DestElem = (PKERB_TICKET_EXTENSIONS) MIDL_user_allocate(sizeof(KERB_TICKET_EXTENSIONS));
|
||
|
if (DestElem == NULL)
|
||
|
{
|
||
|
Status = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
*DestElem = *SourceElem;
|
||
|
DestElem->value.te_data.value = (PUCHAR) MIDL_user_allocate(SourceElem->value.te_data.length);
|
||
|
if (DestElem->value.te_data.value == NULL)
|
||
|
{
|
||
|
MIDL_user_free(DestElem);
|
||
|
Status = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
RtlCopyMemory(
|
||
|
DestElem->value.te_data.value,
|
||
|
SourceElem->value.te_data.value,
|
||
|
SourceElem->value.te_data.length
|
||
|
);
|
||
|
DestElem->next = NULL;
|
||
|
*NextElem = DestElem;
|
||
|
NextElem = &DestElem->next;
|
||
|
SourceElem = SourceElem->next;
|
||
|
}
|
||
|
|
||
|
Cleanup:
|
||
|
if (!KERB_SUCCESS(Status))
|
||
|
{
|
||
|
KerbFreeTicketExtensions(*Dest);
|
||
|
*Dest = NULL;
|
||
|
}
|
||
|
return(Status);
|
||
|
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbDuplicateTicket
|
||
|
//
|
||
|
// Synopsis: Duplicates a ticket so the original may be freed
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Dest - Destination, receives duplicate
|
||
|
// Source - Source ticket
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC;
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR NTAPI
|
||
|
KerbDuplicateTicket(
|
||
|
OUT PKERB_TICKET Dest,
|
||
|
IN PKERB_TICKET Source
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
RtlZeroMemory(
|
||
|
Dest,
|
||
|
sizeof(KERB_TICKET)
|
||
|
);
|
||
|
|
||
|
Dest->ticket_version = Source->ticket_version;
|
||
|
KerbErr = KerbDuplicatePrincipalName(
|
||
|
&Dest->server_name,
|
||
|
&Source->server_name
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbDuplicateRealm(
|
||
|
&Dest->realm,
|
||
|
Source->realm
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbDuplicateTicketExtensions(
|
||
|
&Dest->ticket_extensions,
|
||
|
Source->ticket_extensions
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Dest->encrypted_part = Source->encrypted_part;
|
||
|
Dest->encrypted_part.cipher_text.value = (PUCHAR) MIDL_user_allocate(Dest->encrypted_part.cipher_text.length);
|
||
|
if (Dest->encrypted_part.cipher_text.value == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
RtlCopyMemory(
|
||
|
Dest->encrypted_part.cipher_text.value,
|
||
|
Source->encrypted_part.cipher_text.value,
|
||
|
Dest->encrypted_part.cipher_text.length
|
||
|
);
|
||
|
|
||
|
Cleanup:
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
KerbFreeDuplicatedTicket(Dest);
|
||
|
}
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeDuplicatedTicket
|
||
|
//
|
||
|
// Synopsis: Frees ticket duplicated with KerbDuplicateTicket
|
||
|
//
|
||
|
// Effects: frees memory
|
||
|
//
|
||
|
// Arguments: Ticket - ticket to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: none
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbFreeDuplicatedTicket(
|
||
|
IN PKERB_TICKET Ticket
|
||
|
)
|
||
|
{
|
||
|
KerbFreePrincipalName(
|
||
|
&Ticket->server_name
|
||
|
);
|
||
|
KerbFreeRealm(
|
||
|
&Ticket->realm
|
||
|
);
|
||
|
if (Ticket->encrypted_part.cipher_text.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(Ticket->encrypted_part.cipher_text.value);
|
||
|
}
|
||
|
KerbFreeTicketExtensions(
|
||
|
Ticket->ticket_extensions
|
||
|
);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbBuildErrorMessageEx
|
||
|
//
|
||
|
// Synopsis: Builds an error message
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: marshalled error message, to be freed with MIDL_user_free
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR
|
||
|
KerbBuildErrorMessageEx(
|
||
|
IN KERBERR ErrorCode,
|
||
|
IN OPTIONAL PKERB_EXT_ERROR pExtendedError,
|
||
|
IN PUNICODE_STRING ServerRealm,
|
||
|
IN PKERB_INTERNAL_NAME ServerName,
|
||
|
IN OPTIONAL PUNICODE_STRING ClientRealm,
|
||
|
IN PBYTE ErrorData,
|
||
|
IN ULONG ErrorDataSize,
|
||
|
OUT PULONG ErrorMessageSize,
|
||
|
OUT PUCHAR* ErrorMessage
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
KERB_ERROR Error;
|
||
|
KERB_TYPED_DATA Data = {0};
|
||
|
TimeStamp TimeNow;
|
||
|
|
||
|
*ErrorMessageSize = 0;
|
||
|
*ErrorMessage = NULL;
|
||
|
|
||
|
GetSystemTimeAsFileTime(
|
||
|
(PFILETIME) &TimeNow
|
||
|
);
|
||
|
|
||
|
RtlZeroMemory(
|
||
|
&Error,
|
||
|
sizeof(KERB_ERROR)
|
||
|
);
|
||
|
|
||
|
DsysAssert(ErrorCode != KDC_ERR_MORE_DATA);
|
||
|
|
||
|
Error.version = KERBEROS_VERSION;
|
||
|
Error.message_type = KRB_ERROR;
|
||
|
|
||
|
KerbConvertLargeIntToGeneralizedTimeWrapper(
|
||
|
&Error.server_time,
|
||
|
&Error.server_usec,
|
||
|
&TimeNow
|
||
|
);
|
||
|
|
||
|
Error.error_code = ErrorCode;
|
||
|
|
||
|
//
|
||
|
// Ignore errors because this is already an error return
|
||
|
//
|
||
|
|
||
|
KerbConvertUnicodeStringToRealm(
|
||
|
&Error.realm,
|
||
|
ServerRealm
|
||
|
);
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ClientRealm) && (ClientRealm->Buffer != NULL))
|
||
|
{
|
||
|
KerbConvertUnicodeStringToRealm(
|
||
|
&Error.client_realm,
|
||
|
ClientRealm
|
||
|
);
|
||
|
|
||
|
Error.bit_mask |= client_realm_present;
|
||
|
}
|
||
|
|
||
|
KerbConvertKdcNameToPrincipalName(
|
||
|
&Error.server_name,
|
||
|
ServerName
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Small problem here. We may have preauth data that we want
|
||
|
// to return to the client, instead of extended errors. To
|
||
|
// avoid this, we just make sure that we only return extended
|
||
|
// errors if no ErrorData previously set.
|
||
|
//
|
||
|
|
||
|
if (ARGUMENT_PRESENT(ErrorData))
|
||
|
{
|
||
|
Error.error_data.length = (int) ErrorDataSize;
|
||
|
Error.error_data.value = ErrorData;
|
||
|
Error.bit_mask |= error_data_present;
|
||
|
}
|
||
|
else if (ARGUMENT_PRESENT(pExtendedError) && !EXT_ERROR_SUCCESS((*pExtendedError)))
|
||
|
{
|
||
|
Data.data_type = TD_EXTENDED_ERROR;
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
pExtendedError,
|
||
|
KERB_EXT_ERROR_PDU,
|
||
|
&Data.data_value.length,
|
||
|
&Data.data_value.value
|
||
|
);
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "KerbBuildErrorMessageEx failed To pack extended error %#x!\n", KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Error.bit_mask |= error_data_present;
|
||
|
|
||
|
KerbErr = TypedDataListPushFront(
|
||
|
NULL,
|
||
|
&Data,
|
||
|
&Error.error_data.length,
|
||
|
&Error.error_data.value
|
||
|
);
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "KerbBuildErrorMessageEx failed To pack typed data %#x!\n", KerbErr));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
&Error,
|
||
|
KERB_ERROR_PDU,
|
||
|
ErrorMessageSize,
|
||
|
ErrorMessage
|
||
|
);
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
KerbFreeRealm(
|
||
|
&Error.realm
|
||
|
);
|
||
|
|
||
|
KerbFreeRealm(
|
||
|
&Error.client_realm
|
||
|
);
|
||
|
|
||
|
KerbFreePrincipalName(
|
||
|
&Error.server_name
|
||
|
);
|
||
|
|
||
|
if (Data.data_value.value && Data.data_value.length)
|
||
|
{
|
||
|
MIDL_user_free(Data.data_value.value);
|
||
|
}
|
||
|
|
||
|
if ((ErrorData != Error.error_data.value)
|
||
|
&& Error.error_data.value
|
||
|
&& Error.error_data.length)
|
||
|
{
|
||
|
MIDL_user_free(Error.error_data.value);
|
||
|
}
|
||
|
|
||
|
return (KerbErr);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbBuildExtendedError
|
||
|
//
|
||
|
// Synopsis: Packs the extended error data structure into a
|
||
|
// KERB_ERROR_METHOD_DATA structure for return to
|
||
|
// client
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: pExtendedError, pointer to extended error
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KERBERR to indicate successful packing
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
KERBERR
|
||
|
KerbBuildExtendedError(
|
||
|
IN PKERB_EXT_ERROR pExtendedError,
|
||
|
OUT PULONG ExtErrorSize,
|
||
|
OUT PBYTE* ExtErrorData
|
||
|
)
|
||
|
{
|
||
|
KERB_ERROR_METHOD_DATA ErrorMethodData;
|
||
|
KERBERR KerbErr;
|
||
|
|
||
|
ErrorMethodData.data_type = TD_EXTENDED_ERROR;
|
||
|
ErrorMethodData.bit_mask |= data_value_present;
|
||
|
ErrorMethodData.data_value.value = (PBYTE) pExtendedError;
|
||
|
ErrorMethodData.data_value.length = sizeof(KERB_EXT_ERROR);
|
||
|
|
||
|
KerbErr = KerbPackData(
|
||
|
&ErrorMethodData,
|
||
|
KERB_EXT_ERROR_PDU,
|
||
|
ExtErrorSize,
|
||
|
ExtErrorData
|
||
|
);
|
||
|
|
||
|
return (KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbGetKeyFromList
|
||
|
//
|
||
|
// Synopsis: Gets the key of the appropriate encryption type off the list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Passwords - list of keys
|
||
|
// EncryptionType - Encryption type to use
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: The found key, or NULL if one wasn't found
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
PKERB_ENCRYPTION_KEY
|
||
|
KerbGetKeyFromList(
|
||
|
IN PKERB_STORED_CREDENTIAL Passwords,
|
||
|
IN ULONG EncryptionType
|
||
|
)
|
||
|
{
|
||
|
ULONG Index;
|
||
|
|
||
|
if (!ARGUMENT_PRESENT(Passwords))
|
||
|
{
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
for (Index = 0; Index < Passwords->CredentialCount ; Index++ )
|
||
|
{
|
||
|
if (Passwords->Credentials[Index].Key.keytype == (int) EncryptionType)
|
||
|
{
|
||
|
return(&Passwords->Credentials[Index].Key);
|
||
|
}
|
||
|
}
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFindCommonCryptSystem
|
||
|
//
|
||
|
// Synopsis: Finds a common crypt system including availablity
|
||
|
// of passwords.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: CryptList - List of client's crypto systems
|
||
|
// Passwords - List of passwords
|
||
|
// MorePassword - Optionally another list of passwords to consider
|
||
|
// CommonCryptSystem - Receives common crypo system ID
|
||
|
// Key - Receives key for common crypt system
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: KDC_ERR_ETYPE_NOTSUPP if no common system can be found
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbFindCommonCryptSystem(
|
||
|
IN PKERB_CRYPT_LIST CryptList,
|
||
|
IN PKERB_STORED_CREDENTIAL Passwords,
|
||
|
IN OPTIONAL PKERB_STORED_CREDENTIAL MorePasswords,
|
||
|
OUT PULONG CommonCryptSystem,
|
||
|
OUT PKERB_ENCRYPTION_KEY * Key
|
||
|
)
|
||
|
{
|
||
|
ULONG PasswordTypes[KERB_MAX_CRYPTO_SYSTEMS] = {0};
|
||
|
|
||
|
PULONG pCryptoSystems = NULL;
|
||
|
ULONG CryptoSystems[KERB_MAX_CRYPTO_SYSTEMS];
|
||
|
ULONG PasswordCount;
|
||
|
ULONG CryptoCount;
|
||
|
ULONG Index;
|
||
|
PKERB_CRYPT_LIST NextEType;
|
||
|
ULONG Index2;
|
||
|
ULONG KeyCount;
|
||
|
KERBERR KerbErr = KDC_ERR_ETYPE_NOTSUPP;
|
||
|
|
||
|
|
||
|
if ((Passwords == NULL ) || (CryptList == NULL))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Null password or crypt list passed to KerbFindCommonCryptSystem\n"));
|
||
|
return(KDC_ERR_ETYPE_NOTSUPP);
|
||
|
}
|
||
|
PasswordCount = Passwords->CredentialCount;
|
||
|
|
||
|
|
||
|
if (PasswordCount >= KERB_MAX_CRYPTO_SYSTEMS)
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR, "Got more than 20 crypto systems in password list\n"));
|
||
|
DsysAssert(PasswordCount < KERB_MAX_CRYPTO_SYSTEMS);
|
||
|
return(KDC_ERR_ETYPE_NOTSUPP);
|
||
|
}
|
||
|
|
||
|
|
||
|
KeyCount = 0;
|
||
|
for (Index = 0; Index < PasswordCount ; Index++ )
|
||
|
{
|
||
|
|
||
|
if (ARGUMENT_PRESENT(MorePasswords))
|
||
|
{
|
||
|
for (Index2 = 0; Index2 < MorePasswords->CredentialCount; Index2++ )
|
||
|
{
|
||
|
if (Passwords->Credentials[Index].Key.keytype == MorePasswords->Credentials[Index2].Key.keytype)
|
||
|
{
|
||
|
PasswordTypes[KeyCount++] = (ULONG) Passwords->Credentials[Index].Key.keytype;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PasswordTypes[KeyCount++] = (ULONG) Passwords->Credentials[Index].Key.keytype;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CryptoCount = 0;
|
||
|
NextEType = CryptList;
|
||
|
|
||
|
|
||
|
while (NextEType != NULL)
|
||
|
{
|
||
|
NextEType = NextEType->next;
|
||
|
CryptoCount++;
|
||
|
|
||
|
// restrict to 100 crypt systems, even on a slowbuffer.
|
||
|
if (CryptoCount > KERB_MAX_CRYPTO_SYSTEMS_SLOWBUFF)
|
||
|
{
|
||
|
return(KDC_ERR_ETYPE_NOTSUPP);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (CryptoCount >= KERB_MAX_CRYPTO_SYSTEMS)
|
||
|
{
|
||
|
pCryptoSystems = (PULONG) MIDL_user_allocate(CryptoCount * sizeof(ULONG));
|
||
|
if (NULL == pCryptoSystems)
|
||
|
{
|
||
|
return ( KRB_ERR_GENERIC );
|
||
|
}
|
||
|
}
|
||
|
else // fast buff
|
||
|
{
|
||
|
pCryptoSystems = CryptoSystems;
|
||
|
}
|
||
|
|
||
|
|
||
|
// populate values
|
||
|
NextEType = CryptList;
|
||
|
Index = 0;
|
||
|
|
||
|
while (NextEType != NULL)
|
||
|
{
|
||
|
pCryptoSystems[Index] = NextEType->value;
|
||
|
NextEType = NextEType->next;
|
||
|
Index++;
|
||
|
}
|
||
|
|
||
|
DsysAssert(Index == CryptoCount);
|
||
|
|
||
|
if (!NT_SUCCESS(CDFindCommonCSystemWithKey(
|
||
|
CryptoCount,
|
||
|
pCryptoSystems,
|
||
|
PasswordCount,
|
||
|
PasswordTypes,
|
||
|
CommonCryptSystem
|
||
|
)))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "KLIN(%x) Missing common crypt system\n", KLIN(FILENO, __LINE__)));
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now find the key to return.
|
||
|
//
|
||
|
|
||
|
for (Index = 0; Index < Passwords->CredentialCount ; Index++ )
|
||
|
{
|
||
|
if (Passwords->Credentials[Index].Key.keytype == (int) *CommonCryptSystem)
|
||
|
{
|
||
|
*Key = &Passwords->Credentials[Index].Key;
|
||
|
KerbErr = KDC_ERR_NONE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"KLIN(%x) Couldn't find password type after finding common csystem!\n",
|
||
|
KLIN(FILENO, __LINE__)));
|
||
|
}
|
||
|
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
|
||
|
if ((pCryptoSystems != NULL) &&
|
||
|
(pCryptoSystems != CryptoSystems))
|
||
|
{
|
||
|
MIDL_user_free(pCryptoSystems);
|
||
|
}
|
||
|
|
||
|
|
||
|
return (KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbMapKerbError
|
||
|
//
|
||
|
// Synopsis: Maps a kerb error to an NTSTATUS
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbMapKerbError(
|
||
|
IN KERBERR KerbError
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
switch(KerbError) {
|
||
|
case KDC_ERR_NONE:
|
||
|
Status = STATUS_SUCCESS;
|
||
|
break;
|
||
|
case KDC_ERR_CLIENT_REVOKED:
|
||
|
Status = STATUS_ACCOUNT_DISABLED;
|
||
|
break;
|
||
|
case KDC_ERR_KEY_EXPIRED:
|
||
|
Status = STATUS_PASSWORD_EXPIRED;
|
||
|
break;
|
||
|
case KRB_ERR_GENERIC:
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
break;
|
||
|
case KRB_AP_ERR_SKEW:
|
||
|
case KRB_AP_ERR_TKT_NYV:
|
||
|
// Note this was added because of the following scenario:
|
||
|
// Let's say the dc and the client have the correct time. And the
|
||
|
// server's time is off. We aren't going to get rid of the ticket for the
|
||
|
// server on the client because it hasn't expired yet. But, the server
|
||
|
// thinks it has. If event logging was turned on, then admins could look
|
||
|
// at the server's event log and potentially deduce that the server's
|
||
|
// time is off relative to the dc.
|
||
|
case KRB_AP_ERR_TKT_EXPIRED:
|
||
|
Status = STATUS_TIME_DIFFERENCE_AT_DC;
|
||
|
break;
|
||
|
case KDC_ERR_POLICY:
|
||
|
Status = STATUS_ACCOUNT_RESTRICTION;
|
||
|
break;
|
||
|
case KDC_ERR_C_PRINCIPAL_UNKNOWN:
|
||
|
Status = STATUS_NO_SUCH_USER;
|
||
|
break;
|
||
|
case KDC_ERR_S_PRINCIPAL_UNKNOWN:
|
||
|
Status = STATUS_NO_TRUST_SAM_ACCOUNT;
|
||
|
break;
|
||
|
case KRB_AP_ERR_MODIFIED:
|
||
|
case KDC_ERR_PREAUTH_FAILED:
|
||
|
Status = STATUS_WRONG_PASSWORD;
|
||
|
break;
|
||
|
case KRB_ERR_RESPONSE_TOO_BIG:
|
||
|
Status = STATUS_INVALID_BUFFER_SIZE;
|
||
|
break;
|
||
|
case KDC_ERR_PADATA_TYPE_NOSUPP:
|
||
|
Status = STATUS_NOT_SUPPORTED;
|
||
|
break;
|
||
|
case KRB_AP_ERR_NOT_US:
|
||
|
Status = SEC_E_WRONG_PRINCIPAL;
|
||
|
break;
|
||
|
|
||
|
case KDC_ERR_SVC_UNAVAILABLE:
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
break;
|
||
|
case KDC_ERR_WRONG_REALM:
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
break;
|
||
|
case KDC_ERR_CANT_VERIFY_CERTIFICATE:
|
||
|
Status = TRUST_E_SYSTEM_ERROR;
|
||
|
break;
|
||
|
case KDC_ERR_INVALID_CERTIFICATE:
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
break;
|
||
|
case KDC_ERR_REVOKED_CERTIFICATE:
|
||
|
Status = CRYPT_E_REVOKED;
|
||
|
break;
|
||
|
case KDC_ERR_REVOCATION_STATUS_UNKNOWN:
|
||
|
Status = CRYPT_E_NO_REVOCATION_CHECK;
|
||
|
break;
|
||
|
case KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
|
||
|
Status = CRYPT_E_REVOCATION_OFFLINE;
|
||
|
break;
|
||
|
case KDC_ERR_CLIENT_NAME_MISMATCH:
|
||
|
case KERB_PKINIT_CLIENT_NAME_MISMATCH:
|
||
|
case KDC_ERR_KDC_NAME_MISMATCH:
|
||
|
Status = STATUS_PKINIT_NAME_MISMATCH;
|
||
|
break;
|
||
|
case KDC_ERR_PATH_NOT_ACCEPTED:
|
||
|
Status = STATUS_TRUST_FAILURE;
|
||
|
break;
|
||
|
case KDC_ERR_ETYPE_NOTSUPP:
|
||
|
Status = STATUS_KDC_UNKNOWN_ETYPE;
|
||
|
break;
|
||
|
case KDC_ERR_MUST_USE_USER2USER:
|
||
|
case KRB_AP_ERR_USER_TO_USER_REQUIRED:
|
||
|
Status = STATUS_USER2USER_REQUIRED;
|
||
|
break;
|
||
|
case KRB_AP_ERR_NOKEY:
|
||
|
Status = STATUS_NO_KERB_KEY;
|
||
|
break;
|
||
|
default:
|
||
|
Status = STATUS_LOGON_FAILURE;
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbMakeDomainRelativeSid
|
||
|
//
|
||
|
// Synopsis: Given a domain Id and a relative ID create the corresponding
|
||
|
// SID allocated with MIDL_user_allocate.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: DomainId - The template SID to use.
|
||
|
//
|
||
|
// RelativeId - The relative Id to append to the DomainId.
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: Sid - Returns a pointer to a buffer allocated from
|
||
|
// MIDL_user_allocate containing the resultant Sid.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
PSID
|
||
|
KerbMakeDomainRelativeSid(
|
||
|
IN PSID DomainId,
|
||
|
IN ULONG RelativeId
|
||
|
)
|
||
|
{
|
||
|
UCHAR DomainIdSubAuthorityCount;
|
||
|
ULONG Size;
|
||
|
PSID Sid;
|
||
|
|
||
|
//
|
||
|
// Allocate a Sid which has one more sub-authority than the domain ID.
|
||
|
//
|
||
|
|
||
|
DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId ));
|
||
|
Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);
|
||
|
|
||
|
if ((Sid = MIDL_user_allocate( Size )) == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the new SID to have the same inital value as the
|
||
|
// domain ID.
|
||
|
//
|
||
|
|
||
|
if ( !NT_SUCCESS( RtlCopySid( Size, Sid, DomainId ) ) ) {
|
||
|
MIDL_user_free( Sid );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Adjust the sub-authority count and
|
||
|
// add the relative Id unique to the newly allocated SID
|
||
|
//
|
||
|
|
||
|
(*(RtlSubAuthorityCountSid( Sid ))) ++;
|
||
|
*RtlSubAuthoritySid( Sid, DomainIdSubAuthorityCount ) = RelativeId;
|
||
|
|
||
|
|
||
|
return Sid;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeCertificateList
|
||
|
//
|
||
|
// Synopsis: Frees a list of certificates created by KerbCreateCertificateList
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbFreeCertificateList(
|
||
|
IN PKERB_CERTIFICATE_LIST Certificates
|
||
|
)
|
||
|
{
|
||
|
PKERB_CERTIFICATE_LIST Last,Next;
|
||
|
|
||
|
Last = NULL;
|
||
|
Next = Certificates;
|
||
|
while (Next != NULL)
|
||
|
{
|
||
|
Last = Next;
|
||
|
Next = Next->next;
|
||
|
if (Last->value.cert_data.value != NULL)
|
||
|
{
|
||
|
MIDL_user_free(Last->value.cert_data.value);
|
||
|
}
|
||
|
MIDL_user_free(Last);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCreateCertificateList
|
||
|
//
|
||
|
// Synopsis: Creates a list of certificates from a cert context
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Certficates - receives list of certificates.
|
||
|
// CertContext - Context containing certificates
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR
|
||
|
KerbCreateCertificateList(
|
||
|
OUT PKERB_CERTIFICATE_LIST * Certificates,
|
||
|
IN PCCERT_CONTEXT CertContext
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_CERTIFICATE_LIST ListEntry = NULL;
|
||
|
|
||
|
if (!ARGUMENT_PRESENT(CertContext))
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
//
|
||
|
// Croft up a bogus certificate entry
|
||
|
//
|
||
|
|
||
|
ListEntry = (PKERB_CERTIFICATE_LIST) MIDL_user_allocate(sizeof(KERB_CERTIFICATE_LIST));
|
||
|
if (ListEntry == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
ListEntry->next = NULL;
|
||
|
ListEntry->value.cert_type = KERB_CERTIFICATE_TYPE_X509;
|
||
|
ListEntry->value.cert_data.length = CertContext->cbCertEncoded;
|
||
|
ListEntry->value.cert_data.value = (PUCHAR) MIDL_user_allocate(ListEntry->value.cert_data.length);
|
||
|
if (ListEntry->value.cert_data.value == NULL)
|
||
|
{
|
||
|
KerbErr = KRB_ERR_GENERIC;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
RtlCopyMemory(
|
||
|
ListEntry->value.cert_data.value,
|
||
|
CertContext->pbCertEncoded,
|
||
|
CertContext->cbCertEncoded
|
||
|
);
|
||
|
*Certificates = ListEntry;
|
||
|
ListEntry = NULL;
|
||
|
|
||
|
Cleanup:
|
||
|
KerbFreeCertificateList(ListEntry);
|
||
|
return(KerbErr);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertFlagsToUlong
|
||
|
//
|
||
|
// Synopsis: Converts a bit-stream flags field into a ULONG
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
KerbConvertFlagsToUlong(
|
||
|
IN PVOID Flags
|
||
|
)
|
||
|
{
|
||
|
ULONG Output = 0;
|
||
|
PUCHAR OutputPointer = &((PUCHAR) &Output)[3];
|
||
|
ULONG Index = 0;
|
||
|
PKERB_TICKET_FLAGS InternalFlags = (PKERB_TICKET_FLAGS) Flags;
|
||
|
ULONG InternalLength;
|
||
|
|
||
|
if (InternalFlags->length > 32)
|
||
|
{
|
||
|
InternalLength = 32;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
InternalLength = (ULONG) InternalFlags->length;
|
||
|
}
|
||
|
|
||
|
while (InternalLength > 7)
|
||
|
{
|
||
|
*OutputPointer = InternalFlags->value[Index++];
|
||
|
OutputPointer--;
|
||
|
InternalLength -= 8;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the remaining bits, masking off what should be zero
|
||
|
//
|
||
|
|
||
|
if (InternalLength != 0)
|
||
|
{
|
||
|
*OutputPointer = InternalFlags->value[Index] & ~((1 << (8-InternalLength)) - 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
return(Output);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbConvertUlongToFlagUlong
|
||
|
//
|
||
|
// Synopsis: Converts the byte order of a ULONG into that used by flags
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG
|
||
|
KerbConvertUlongToFlagUlong(
|
||
|
IN ULONG Flag
|
||
|
)
|
||
|
{
|
||
|
ULONG ReturnFlag;
|
||
|
|
||
|
((PUCHAR) &ReturnFlag)[0] = ((PUCHAR) &Flag)[3];
|
||
|
((PUCHAR) &ReturnFlag)[1] = ((PUCHAR) &Flag)[2];
|
||
|
((PUCHAR) &ReturnFlag)[2] = ((PUCHAR) &Flag)[1];
|
||
|
((PUCHAR) &ReturnFlag)[3] = ((PUCHAR) &Flag)[0];
|
||
|
|
||
|
return(ReturnFlag);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCompareObjectIds
|
||
|
//
|
||
|
// Synopsis: Compares two object IDs for equality
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
KerbCompareObjectIds(
|
||
|
IN PKERB_OBJECT_ID Object1,
|
||
|
IN PKERB_OBJECT_ID Object2
|
||
|
)
|
||
|
{
|
||
|
while (Object1 != NULL)
|
||
|
{
|
||
|
|
||
|
if (Object2 == NULL)
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
if (Object1->value != Object2->value)
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
Object1 = Object1->next;
|
||
|
Object2 = Object2->next;
|
||
|
}
|
||
|
|
||
|
if (Object2 != NULL)
|
||
|
{
|
||
|
return(FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return(TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KdcGetClientNetbiosAddress
|
||
|
//
|
||
|
// Synopsis: Gets the client's netbios address from the list of
|
||
|
// addresses it sends.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
KERBERR
|
||
|
KerbGetClientNetbiosAddress(
|
||
|
OUT PUNICODE_STRING ClientNetbiosAddress,
|
||
|
IN PKERB_HOST_ADDRESSES Addresses
|
||
|
)
|
||
|
{
|
||
|
PKERB_HOST_ADDRESSES TempAddress = Addresses;
|
||
|
STRING TempString;
|
||
|
KERBERR KerbErr;
|
||
|
|
||
|
RtlInitUnicodeString(
|
||
|
ClientNetbiosAddress,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
while (TempAddress != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Check for netbios
|
||
|
//
|
||
|
|
||
|
if (TempAddress->value.address_type == KERB_ADDRTYPE_NETBIOS)
|
||
|
{
|
||
|
//
|
||
|
// Copy out the string
|
||
|
//
|
||
|
|
||
|
TempString.Buffer = (PCHAR) TempAddress->value.address.value;
|
||
|
TempString.Length = TempString.MaximumLength = (USHORT) TempAddress->value.address.length;
|
||
|
|
||
|
KerbErr = KerbStringToUnicodeString(
|
||
|
ClientNetbiosAddress,
|
||
|
&TempString
|
||
|
);
|
||
|
if (KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
//
|
||
|
// Strip trailing spaces
|
||
|
//
|
||
|
|
||
|
if (ClientNetbiosAddress->Length >= sizeof(WCHAR))
|
||
|
{
|
||
|
while ((ClientNetbiosAddress->Length > 0) &&
|
||
|
(ClientNetbiosAddress->Buffer[(ClientNetbiosAddress->Length / sizeof(WCHAR))-1] == L' '))
|
||
|
{
|
||
|
ClientNetbiosAddress->Length -= sizeof(WCHAR);
|
||
|
}
|
||
|
return(KDC_ERR_NONE);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
TempAddress = TempAddress->next;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// It is o.k. to not have a netbios name
|
||
|
//
|
||
|
|
||
|
return(KDC_ERR_NONE);
|
||
|
}
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbGetPacFromAuthData
|
||
|
//
|
||
|
// Synopsis: Gets the PAC from the auth data list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
KERBERR
|
||
|
KerbGetPacFromAuthData(
|
||
|
IN PKERB_AUTHORIZATION_DATA AuthData,
|
||
|
OUT PKERB_IF_RELEVANT_AUTH_DATA ** ReturnIfRelevantData,
|
||
|
OUT PKERB_AUTHORIZATION_DATA * Pac
|
||
|
)
|
||
|
{
|
||
|
KERBERR KerbErr = KDC_ERR_NONE;
|
||
|
PKERB_AUTHORIZATION_DATA PacAuthData = NULL;
|
||
|
PKERB_AUTHORIZATION_DATA RelevantAuthData = NULL;
|
||
|
PKERB_IF_RELEVANT_AUTH_DATA * IfRelevantData = NULL;
|
||
|
|
||
|
*ReturnIfRelevantData = NULL;
|
||
|
*Pac = NULL;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Look for the if-relevant data
|
||
|
//
|
||
|
|
||
|
RelevantAuthData = KerbFindAuthDataEntry(
|
||
|
KERB_AUTH_DATA_IF_RELEVANT,
|
||
|
AuthData
|
||
|
);
|
||
|
if (RelevantAuthData != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Unpack it
|
||
|
//
|
||
|
|
||
|
KerbErr = KerbUnpackData(
|
||
|
RelevantAuthData->value.auth_data.value,
|
||
|
RelevantAuthData->value.auth_data.length,
|
||
|
PKERB_IF_RELEVANT_AUTH_DATA_PDU,
|
||
|
(PVOID *) &IfRelevantData
|
||
|
);
|
||
|
if (KERB_SUCCESS(KerbErr))
|
||
|
{
|
||
|
//
|
||
|
// Look for the PAC in the if-relevant data
|
||
|
//
|
||
|
|
||
|
PacAuthData = KerbFindAuthDataEntry(
|
||
|
KERB_AUTH_DATA_PAC,
|
||
|
*IfRelevantData
|
||
|
);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// We don't mind if we couldn't unpack it.
|
||
|
// Tickets do not always have PAC information.
|
||
|
//
|
||
|
|
||
|
KerbErr = KDC_ERR_NONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we didn't find it in the if-relevant data, look outside
|
||
|
//
|
||
|
|
||
|
if (PacAuthData == NULL)
|
||
|
{
|
||
|
PacAuthData = KerbFindAuthDataEntry(
|
||
|
KERB_AUTH_DATA_PAC,
|
||
|
AuthData
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the PAC to return it
|
||
|
//
|
||
|
|
||
|
if (PacAuthData != NULL)
|
||
|
{
|
||
|
*Pac = PacAuthData;
|
||
|
}
|
||
|
|
||
|
*ReturnIfRelevantData = IfRelevantData;
|
||
|
IfRelevantData = NULL;
|
||
|
|
||
|
|
||
|
return(KerbErr);
|
||
|
}
|
||
|
|
||
|
|
||
|
#if DBG
|
||
|
#define KERB_DEBUG_WARN_LEVEL 0x0002
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: DebugDisplayTime
|
||
|
//
|
||
|
// Synopsis: Displays a FILETIME
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
DebugDisplayTime(
|
||
|
IN ULONG DebugLevel,
|
||
|
IN FILETIME *pFileTime
|
||
|
)
|
||
|
{
|
||
|
CHAR pszTime[256];
|
||
|
SYSTEMTIME SystemTime;
|
||
|
|
||
|
if (DebugLevel & KERB_DEBUG_WARN_LEVEL)
|
||
|
{
|
||
|
FileTimeToSystemTime(pFileTime, &SystemTime);
|
||
|
|
||
|
DebugLog((DEB_ERROR," %02d:%02d:%02d - %02d %02d %04d\n",
|
||
|
SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,
|
||
|
SystemTime.wDay,SystemTime.wMonth,SystemTime.wYear));
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|