You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4498 lines
108 KiB
4498 lines
108 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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 "krbprgma.h"
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntlsa.h>
|
|
#include <samrpc.h>
|
|
#include <samisrv.h>
|
|
#include <dnsapi.h>
|
|
#include <wincrypt.h>
|
|
#include <certca.h>
|
|
}
|
|
#include <kerbcomm.h>
|
|
#include <kerberr.h>
|
|
#include <kerbcon.h>
|
|
#include "debug.h"
|
|
#include <sddl.h>
|
|
#include <malloc.h>
|
|
#include <alloca.h>
|
|
|
|
#endif // WIN32_CHICAGO
|
|
|
|
#define KERB_NAME_PREFIX L"Kerberos:"
|
|
UNICODE_STRING KerbNamePrefix = {sizeof(KERB_NAME_PREFIX) - sizeof(WCHAR), sizeof(KERB_NAME_PREFIX), KERB_NAME_PREFIX };
|
|
UNICODE_STRING KerbNameSeparator = {sizeof(WCHAR), 2*sizeof(WCHAR), L"/" };
|
|
UNICODE_STRING KerbDomainSeparator = {sizeof(WCHAR), 2*sizeof(WCHAR), L"@" };
|
|
|
|
#ifndef SHA1DIGESTLEN
|
|
#define SHA1DIGESTLEN 20
|
|
#endif
|
|
|
|
// Local Prototype */
|
|
BOOL SafeRtlInitString(
|
|
OUT PSTRING DestinationString,
|
|
IN PCSZ SourceString OPTIONAL
|
|
);
|
|
|
|
BOOL SafeRtlInitUnicodeString(
|
|
OUT PUNICODE_STRING DestinationString,
|
|
IN PCWSTR SourceString OPTIONAL
|
|
);
|
|
|
|
LPWSTR
|
|
KerbAllocWStrFromUtf8Str(
|
|
IN PCHAR Utf8String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a UTF8 (zero terminated) string to the corresponding UNICODE
|
|
string.
|
|
|
|
Arguments:
|
|
|
|
Utf8String - Specifies the UTF8 zero terminated string to convert.
|
|
|
|
|
|
Return Value:
|
|
|
|
NULL - There was some error in the conversion.
|
|
|
|
Otherwise, it returns a pointer to the zero terminated UNICODE string in
|
|
an allocated buffer. The buffer must be freed using NetApiBufferFree.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPWSTR UnicodeString = NULL;
|
|
int UnicodeStringLen;
|
|
|
|
//
|
|
// Determine the length of the Unicode string.
|
|
//
|
|
|
|
UnicodeStringLen = MultiByteToWideChar(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
Utf8String,
|
|
-1, // calculate length
|
|
UnicodeString,
|
|
0 );
|
|
|
|
if ( UnicodeStringLen == 0 ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer for the Unicode string.
|
|
//
|
|
|
|
UnicodeString = (LPWSTR) MIDL_user_allocate( (UnicodeStringLen+1)*sizeof(WCHAR) );
|
|
|
|
if ( UnicodeString == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Translate the string to Unicode.
|
|
//
|
|
|
|
UnicodeStringLen = MultiByteToWideChar(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
Utf8String,
|
|
-1,
|
|
UnicodeString,
|
|
UnicodeStringLen );
|
|
|
|
if ( UnicodeStringLen == 0 ) {
|
|
MIDL_user_free( UnicodeString );
|
|
return NULL;
|
|
}
|
|
|
|
UnicodeString[UnicodeStringLen] = L'\0';
|
|
|
|
return UnicodeString;
|
|
}
|
|
|
|
NTSTATUS
|
|
KerbUnicodeStringFromUtf8Str(
|
|
IN PCHAR Utf8String,
|
|
OUT PUNICODE_STRING UnicodeString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a UTF8 (zero terminated) string to the corresponding UNICODE
|
|
string.
|
|
|
|
Arguments:
|
|
|
|
Utf8String - Specifies the UTF8 zero terminated string to convert.
|
|
UnicodeString - Receives the converted unicode string
|
|
|
|
|
|
Return Value:
|
|
|
|
NULL - There was some error in the conversion.
|
|
|
|
Otherwise, it returns a pointer to the zero terminated UNICODE string in
|
|
an allocated buffer. The buffer must be freed using NetApiBufferFree.
|
|
|
|
--*/
|
|
|
|
{
|
|
int UnicodeStringLen;
|
|
ULONG ulLength = 0;
|
|
|
|
//
|
|
// Determine the length of the Unicode string.
|
|
//
|
|
|
|
UnicodeStringLen = MultiByteToWideChar(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
Utf8String,
|
|
-1, // calculate length
|
|
UnicodeString->Buffer,
|
|
0 );
|
|
|
|
if ( UnicodeStringLen == 0 ) {
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// The conversion routine returns space for a null terminator, so
|
|
// adjust for that.
|
|
//
|
|
|
|
// check to make sure size fits into a USHORT value (with NULL appended)
|
|
ulLength = (UnicodeStringLen - 1) * sizeof(WCHAR);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return(STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
UnicodeString->Length = (USHORT)ulLength;
|
|
|
|
if (UnicodeString->MaximumLength < UnicodeString->Length + sizeof(WCHAR))
|
|
{
|
|
return(STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
//
|
|
// Translate the string to Unicode.
|
|
//
|
|
|
|
UnicodeStringLen = MultiByteToWideChar(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
Utf8String,
|
|
-1,
|
|
UnicodeString->Buffer,
|
|
UnicodeStringLen );
|
|
|
|
DsysAssert( UnicodeStringLen != 0 );
|
|
|
|
UnicodeString->Buffer[UnicodeStringLen-1] = L'\0';
|
|
UnicodeString->Length = (USHORT)((UnicodeStringLen-1) * sizeof(WCHAR));
|
|
UnicodeString->MaximumLength = (USHORT)(UnicodeStringLen * sizeof(WCHAR));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PCHAR
|
|
KerbAllocUtf8StrFromUnicodeString(
|
|
IN PUNICODE_STRING UnicodeString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a Unicode (zero terminated) string to the corresponding UTF8
|
|
string.
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - Specifies the Unicode zero terminated string to convert.
|
|
|
|
|
|
Return Value:
|
|
|
|
NULL - There was some error in the conversion.
|
|
|
|
Otherwise, it returns a pointer to the zero terminated UTF8 string in
|
|
an allocated buffer. The buffer must be freed using NetApiBufferFree.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHAR Utf8String = NULL;
|
|
int Utf8StringLen;
|
|
|
|
//
|
|
// If the length is zero, return a null string.
|
|
//
|
|
|
|
if (UnicodeString->Length == 0)
|
|
{
|
|
Utf8String = (PCHAR) MIDL_user_allocate(sizeof(CHAR));
|
|
if (Utf8String != NULL)
|
|
{
|
|
*Utf8String = '\0';
|
|
}
|
|
return(Utf8String);
|
|
}
|
|
|
|
//
|
|
// Determine the length of the Unicode string.
|
|
//
|
|
|
|
Utf8StringLen = WideCharToMultiByte(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
UnicodeString->Buffer,
|
|
UnicodeString->Length / sizeof(WCHAR),
|
|
Utf8String,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( Utf8StringLen == 0 ) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer for the Unicode string.
|
|
//
|
|
|
|
Utf8String = (PCHAR) MIDL_user_allocate( Utf8StringLen+1 );
|
|
|
|
if ( Utf8String == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Translate the string to Unicode.
|
|
//
|
|
|
|
Utf8StringLen = WideCharToMultiByte(
|
|
#ifndef WIN32_CHICAGO
|
|
CP_UTF8,
|
|
#else // WIN32_CHICAGO
|
|
CP_OEMCP,
|
|
#endif // WIN32_CHICAGO
|
|
0, // All characters can be mapped.
|
|
UnicodeString->Buffer,
|
|
UnicodeString->Length / sizeof(WCHAR),
|
|
Utf8String,
|
|
Utf8StringLen,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( Utf8StringLen == 0 ) {
|
|
MIDL_user_free( Utf8String );
|
|
return NULL;
|
|
}
|
|
|
|
Utf8String[Utf8StringLen] = '\0';
|
|
|
|
return Utf8String;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbUnicodeStringToKerbString
|
|
//
|
|
// Synopsis: Converts a UNICODE_STRING to a kerberos-ansi string
|
|
//
|
|
// Effects: allocates destination with MIDL_user_allocate
|
|
//
|
|
// Arguments: KerbString - receives ansi-ized string
|
|
// String - containes source unicode string
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This routine hides the details of whether we use UTF-8 or
|
|
// unicode->ansi conversion
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbUnicodeStringToKerbString(
|
|
OUT PSTRING KerbString,
|
|
IN PUNICODE_STRING String
|
|
)
|
|
{
|
|
STRING TempString;
|
|
BOOL fAssigned = FALSE;
|
|
|
|
if (!ARGUMENT_PRESENT(KerbString))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
TempString.Buffer = KerbAllocUtf8StrFromUnicodeString(String);
|
|
if (TempString.Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
fAssigned = SafeRtlInitString(
|
|
&TempString,
|
|
TempString.Buffer
|
|
);
|
|
if (fAssigned == FALSE)
|
|
{
|
|
return(KRB_ERR_GENERIC); // string length would not fit into USHORT
|
|
}
|
|
|
|
*KerbString = TempString;
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbStringToUnicodeString
|
|
//
|
|
// Synopsis: Converts a kerberos string to a unicode string
|
|
//
|
|
// Effects: allocates result string with MIDL_user_allocate
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbStringToUnicodeString(
|
|
OUT PUNICODE_STRING String,
|
|
IN PSTRING KerbString
|
|
)
|
|
{
|
|
PCHAR TerminatedString;
|
|
UNICODE_STRING TempString;
|
|
BOOL fAssigned = FALSE;
|
|
|
|
if (!ARGUMENT_PRESENT(KerbString) || !ARGUMENT_PRESENT(String))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
//
|
|
// Null terminate the string
|
|
//
|
|
|
|
if ((KerbString->MaximumLength > KerbString->Length) &&
|
|
(KerbString->Buffer[KerbString->Length] == '\0'))
|
|
{
|
|
TerminatedString = KerbString->Buffer;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Validate inputs before doing alloc.. This is because the Length can only be USHORT_MAX - 1 - sizeof(CHAR)
|
|
// or we'll set max_length == 0.
|
|
//
|
|
if ( KerbString->Length > KERB_MAX_STRING )
|
|
{
|
|
return (KRB_ERR_GENERIC);
|
|
}
|
|
|
|
TerminatedString = (PCHAR) MIDL_user_allocate(KerbString->Length + sizeof(CHAR));
|
|
if (TerminatedString == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
RtlCopyMemory(
|
|
TerminatedString,
|
|
KerbString->Buffer,
|
|
KerbString->Length
|
|
);
|
|
TerminatedString[KerbString->Length] = '\0';
|
|
}
|
|
|
|
TempString.Buffer = KerbAllocWStrFromUtf8Str(
|
|
TerminatedString
|
|
);
|
|
if (TerminatedString != KerbString->Buffer)
|
|
{
|
|
MIDL_user_free(TerminatedString);
|
|
}
|
|
|
|
if (TempString.Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
fAssigned = SafeRtlInitUnicodeString(
|
|
&TempString,
|
|
TempString.Buffer
|
|
);
|
|
|
|
if (fAssigned == FALSE)
|
|
{
|
|
MIDL_user_free(TempString.Buffer);
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
*String = TempString;
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbComparePrincipalNames
|
|
//
|
|
// Synopsis: Compares two principal names for equality
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Name1 - the first principal name
|
|
// Name2 - the second principal name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE for eqaulity, FALSE for non-equality.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
KerbComparePrincipalNames(
|
|
IN PKERB_PRINCIPAL_NAME Name1,
|
|
IN PKERB_PRINCIPAL_NAME Name2
|
|
)
|
|
{
|
|
BOOLEAN Result = TRUE;
|
|
PKERB_PRINCIPAL_NAME_ELEM NextName1, NextName2;
|
|
|
|
if ((Name1 == NULL) && (Name2 == NULL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ((Name1 == NULL) || (Name2 == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the name types are known, make sure they match.
|
|
//
|
|
|
|
if ((Name1->name_type != KRB_NT_UNKNOWN) &&
|
|
(Name2->name_type != KRB_NT_UNKNOWN) &&
|
|
(Name1->name_type != Name2->name_type))
|
|
{
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NextName1 = Name1->name_string;
|
|
NextName2 = Name2->name_string;
|
|
|
|
while ((NextName1 != NULL) && (NextName2 != NULL))
|
|
{
|
|
if (lstrcmpiA(
|
|
NextName1->value,
|
|
NextName2->value
|
|
) != 0)
|
|
{
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
NextName1 = NextName1->next;
|
|
NextName2 = NextName2->next;
|
|
|
|
}
|
|
|
|
//
|
|
// if one has more names than the other, fail
|
|
//
|
|
|
|
if (!((NextName1 == NULL) && (NextName2 == NULL)))
|
|
{
|
|
Result = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// BUG 455493: transitional code
|
|
//
|
|
|
|
if (Result == FALSE)
|
|
{
|
|
KERBERR KerbErr;
|
|
ULONG NameType;
|
|
UNICODE_STRING UName1;
|
|
UNICODE_STRING UName2;
|
|
|
|
KerbErr = KerbConvertPrincipalNameToString(
|
|
&UName1,
|
|
&NameType,
|
|
Name1
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
KerbErr = KerbConvertPrincipalNameToString(
|
|
&UName2,
|
|
&NameType,
|
|
Name2
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
KerbFreeString(&UName1);
|
|
return(FALSE);
|
|
}
|
|
Result = RtlEqualUnicodeString(
|
|
&UName1,
|
|
&UName2,
|
|
TRUE
|
|
);
|
|
|
|
KerbFreeString( &UName1 );
|
|
KerbFreeString( &UName2 );
|
|
|
|
// FESTER
|
|
// IF THIS ASSERT GETS FIRED, Contact Todds (0x30864)
|
|
// This code *should* be transitional, and I've not seen it called,
|
|
// but the only way to be certain is to add Assert and let it rot in
|
|
// Whistler... Remove before B2.
|
|
//
|
|
if (Result)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Assert about to fire. Dead code called. Contact Todds\n"));
|
|
DsysAssert(FALSE);
|
|
}
|
|
}
|
|
return (Result);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompareRealmNames
|
|
//
|
|
// Synopsis: Compares two realm names for equality
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Realm1 - First realm to compare
|
|
// Realm2 - Second realm to compare
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE if they are equal, FALSE otherwise
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
KerbCompareRealmNames(
|
|
IN PKERB_REALM Realm1,
|
|
IN PKERB_REALM Realm2
|
|
)
|
|
{
|
|
INT len1;
|
|
INT len2;
|
|
INT retval;
|
|
|
|
|
|
if ((Realm1 == NULL) && (Realm2 == NULL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ((Realm1 == NULL) || (Realm2 == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
len1 = (INT) strlen( *Realm1 );
|
|
len2 = (INT) strlen( *Realm2 );
|
|
|
|
//
|
|
// Check if any trailing '.' need to be stripped off
|
|
//
|
|
|
|
if ( len2 != len1 )
|
|
{
|
|
if ( len2 == len1+1 )
|
|
{
|
|
if ( (*Realm2)[len1] != '.' )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// len1 is comparable length
|
|
//
|
|
}
|
|
else if ( len2+1 == len1 )
|
|
{
|
|
if ( (*Realm1)[len2] != '.' )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// len1 is set to comparable length
|
|
//
|
|
|
|
len1 = len2;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
retval = CompareStringA( GetSystemDefaultLCID(),
|
|
LOCALE_USE_CP_ACP | NORM_IGNORECASE,
|
|
(*Realm1),
|
|
len1,
|
|
(*Realm2),
|
|
len1
|
|
);
|
|
|
|
return ( retval == CSTR_EQUAL );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompareUnicodeRealmNames
|
|
//
|
|
// Synopsis: Compares two realm names for equality
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Realm1 - First realm to compare
|
|
// Realm2 - Second realm to compare
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE if they are equal, FALSE otherwise
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
KerbCompareUnicodeRealmNames(
|
|
IN PUNICODE_STRING Domain1,
|
|
IN PUNICODE_STRING Domain2
|
|
)
|
|
{
|
|
UNICODE_STRING Realm1 = {0};
|
|
UNICODE_STRING Realm2 = {0};
|
|
|
|
if ((Domain1 == NULL) && (Domain2 == NULL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ((Domain1 == NULL) || (Domain2 == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
else if ((Domain1->Buffer == NULL) && (Domain2->Buffer == NULL))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
else if ((Domain1->Buffer == NULL) || (Domain2->Buffer == NULL))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
Realm1 = *Domain1;
|
|
Realm2 = *Domain2;
|
|
|
|
//
|
|
// Check if any trailing '.' need to be stripped off
|
|
//
|
|
|
|
if ( Realm2.Length != Realm1.Length )
|
|
{
|
|
if ( Realm2.Length == Realm1.Length+sizeof(WCHAR) )
|
|
{
|
|
if ( Realm2.Buffer[Realm1.Length / sizeof(WCHAR)] != '.' )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
Realm2.Length = Realm1.Length;
|
|
}
|
|
}
|
|
else if ( Realm2.Length+sizeof(WCHAR) == Realm1.Length )
|
|
{
|
|
if ( Realm1.Buffer[Realm2.Length / sizeof(WCHAR)] != '.' )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
Realm1.Length = Realm2.Length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// compare only comparable length of string
|
|
//
|
|
|
|
return( RtlEqualUnicodeString( &Realm1, &Realm2, TRUE ));
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcFreeKdcName
|
|
//
|
|
// Synopsis: Frees all parts of a KDC name structure
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeKdcName(
|
|
IN PKERB_INTERNAL_NAME * KdcName
|
|
)
|
|
{
|
|
if (*KdcName != NULL)
|
|
{
|
|
MIDL_user_free(*KdcName);
|
|
*KdcName = NULL;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertPrincipalNameToKdcName
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertPrincipalNameToKdcName(
|
|
OUT PKERB_INTERNAL_NAME * OutputName,
|
|
IN PKERB_PRINCIPAL_NAME PrincipalName
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
ULONG NameCount = 0;
|
|
ULONG Index;
|
|
STRING Names[MAX_NAME_ELEMENTS+1];
|
|
PKERB_PRINCIPAL_NAME_ELEM NameElement;
|
|
PKERB_INTERNAL_NAME KdcName = NULL;
|
|
ULONG NameSize = 0;
|
|
ULONG NameLength = 0;
|
|
PUCHAR Where;
|
|
BOOL fAssigned = FALSE;
|
|
|
|
NameElement = PrincipalName->name_string;
|
|
while (NameElement!= NULL)
|
|
{
|
|
// Verify we do not overrun a USHORT length
|
|
fAssigned = SafeRtlInitString(
|
|
&Names[NameCount],
|
|
NameElement->value
|
|
);
|
|
if (fAssigned == FALSE)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"KerbConvertPrincipalNameToKdcName:Name part too long\n"));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
NameLength += (Names[NameCount].Length + 1) * sizeof(WCHAR);
|
|
NameCount++;
|
|
NameElement = NameElement->next;
|
|
if (NameCount > MAX_NAME_ELEMENTS)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Too many name parts: %d\n",NameCount));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
}
|
|
|
|
// check to make sure size fits into a USHORT value (with NULL appended)
|
|
// NameLength will be casted to USHORT below.
|
|
if (NameLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"KerbConvertPrincipalNameToKdcName: Overall size too large\n"));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Now we have the count of parts, so allocate the destination structure
|
|
//
|
|
|
|
if (NameCount == 0)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Illegal name with zero parts\n"));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
NameSize = KERB_INTERNAL_NAME_SIZE(NameCount) + NameLength;
|
|
KdcName = (PKERB_INTERNAL_NAME) MIDL_user_allocate(NameSize);
|
|
if (KdcName == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
KdcName,
|
|
NameSize
|
|
);
|
|
KdcName->NameCount = (USHORT) NameCount;
|
|
KdcName->NameType = (USHORT) PrincipalName->name_type;
|
|
|
|
Where = (PUCHAR) KdcName + KERB_INTERNAL_NAME_SIZE(NameCount);
|
|
|
|
//
|
|
// Now convert all the strings from the temporary array into
|
|
// UNICODE_STRINGs in the final array
|
|
//
|
|
|
|
for (Index = 0; Index < NameCount ; Index++ )
|
|
{
|
|
KdcName->Names[Index].Length = 0;
|
|
KdcName->Names[Index].MaximumLength = (USHORT)NameLength;
|
|
KdcName->Names[Index].Buffer = (LPWSTR) Where;
|
|
Status = KerbUnicodeStringFromUtf8Str(
|
|
Names[Index].Buffer,
|
|
&KdcName->Names[Index]
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
Where += KdcName->Names[Index].MaximumLength;
|
|
NameLength = NameLength - KdcName->Names[Index].MaximumLength;
|
|
}
|
|
*OutputName = KdcName;
|
|
KdcName = NULL;
|
|
|
|
Cleanup:
|
|
if (KdcName != NULL)
|
|
{
|
|
KerbFreeKdcName(&KdcName);
|
|
}
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCovnertKdcNameToPrincipalName
|
|
//
|
|
// Synopsis: Converts a KDC name to a Principal name & allocates output with
|
|
// MIDL_user_allocate
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertKdcNameToPrincipalName(
|
|
OUT PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN PKERB_INTERNAL_NAME KdcName
|
|
)
|
|
{
|
|
PKERB_PRINCIPAL_NAME_ELEM Elem = NULL;
|
|
PKERB_PRINCIPAL_NAME_ELEM * Last;
|
|
STRING TempKerbString = {0};
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
ULONG Index;
|
|
|
|
PrincipalName->name_type = (int) KdcName->NameType;
|
|
PrincipalName->name_string = NULL;
|
|
Last = &PrincipalName->name_string;
|
|
|
|
//
|
|
// Index through the KDC name and add each element to the list
|
|
//
|
|
|
|
for (Index = 0; Index < KdcName->NameCount ; Index++ )
|
|
{
|
|
KerbErr = KerbUnicodeStringToKerbString(
|
|
&TempKerbString,
|
|
&KdcName->Names[Index]
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
Elem = (PKERB_PRINCIPAL_NAME_ELEM) MIDL_user_allocate(sizeof(KERB_PRINCIPAL_NAME_ELEM));
|
|
if (Elem == NULL)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
Elem->value = TempKerbString.Buffer;
|
|
Elem->next = NULL;
|
|
*Last = Elem;
|
|
Last = &Elem->next;
|
|
TempKerbString.Buffer = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
//
|
|
// Cleanup the principal name
|
|
//
|
|
|
|
KerbFreePrincipalName(PrincipalName);
|
|
|
|
if (TempKerbString.Buffer)
|
|
{
|
|
MIDL_user_free(TempKerbString.Buffer);
|
|
TempKerbString.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbEqualKdcNames
|
|
//
|
|
// Synopsis: Compares to KDC names for equality
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE if the names are identical
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
KerbEqualKdcNames(
|
|
IN PKERB_INTERNAL_NAME Name1,
|
|
IN PKERB_INTERNAL_NAME Name2
|
|
)
|
|
{
|
|
BOOLEAN Equal = TRUE;
|
|
ULONG Index;
|
|
|
|
if ((Name1 == NULL) && (Name2 == NULL))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if ((Name1 == NULL) || (Name2 == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Special case some Microsoft name types
|
|
//
|
|
|
|
if (Name1->NameCount != Name2->NameCount)
|
|
{
|
|
Equal = FALSE;
|
|
}
|
|
else
|
|
{
|
|
for (Index = 0; Index < Name1->NameCount ; Index++ )
|
|
{
|
|
if (!RtlEqualUnicodeString(
|
|
&Name1->Names[Index],
|
|
&Name2->Names[Index],
|
|
TRUE // case insensitive
|
|
))
|
|
{
|
|
Equal = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(Equal);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbComparePrincipalNameToKdcName
|
|
//
|
|
// Synopsis: Compares a princial name to a KDC name by first converting
|
|
// and then comparing.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbCompareKdcNameToPrincipalName(
|
|
IN PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN PKERB_INTERNAL_NAME KdcName,
|
|
OUT PBOOLEAN Result
|
|
)
|
|
{
|
|
PKERB_INTERNAL_NAME TempName = NULL;
|
|
|
|
DsysAssert(Result);
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToKdcName(
|
|
&TempName,
|
|
PrincipalName
|
|
)))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
*Result = KerbEqualKdcNames( TempName, KdcName );
|
|
|
|
KerbFreeKdcName( &TempName );
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDuplicateKdcName
|
|
//
|
|
// Synopsis: Duplicates an internal name by copying the pointer and
|
|
// referencing the structure.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbDuplicateKdcName(
|
|
OUT PKERB_INTERNAL_NAME * Destination,
|
|
IN PKERB_INTERNAL_NAME Source
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
USHORT Index;
|
|
PKERB_INTERNAL_NAME KdcName = NULL;
|
|
ULONG NameSize = 0;
|
|
ULONG NameLength = 0;
|
|
PUCHAR Where;
|
|
|
|
|
|
|
|
//
|
|
// Now we have the count of parts, so allocate the destination structure
|
|
//
|
|
|
|
if (Source->NameCount == 0)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Illegal name with zero parts\n"));
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
for (Index = 0; Index < Source->NameCount ; Index++ )
|
|
{
|
|
NameLength += Source->Names[Index].Length + sizeof(WCHAR);
|
|
}
|
|
|
|
NameSize = KERB_INTERNAL_NAME_SIZE(Source->NameCount) + NameLength;
|
|
KdcName = (PKERB_INTERNAL_NAME) MIDL_user_allocate(NameSize);
|
|
if (KdcName == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
KdcName,
|
|
NameSize
|
|
);
|
|
KdcName->NameCount = (USHORT) Source->NameCount;
|
|
Where = (PUCHAR) KdcName + KERB_INTERNAL_NAME_SIZE(Source->NameCount);
|
|
|
|
//
|
|
// Now convert all the strings from the temporary array into
|
|
// UNICODE_STRINGs in the final array
|
|
//
|
|
|
|
for (Index = 0; Index < Source->NameCount ; Index++ )
|
|
{
|
|
if (Source->Names[Index].Length > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Cleanup; // length will not fit into USHORT value
|
|
}
|
|
KdcName->Names[Index].Length = Source->Names[Index].Length;
|
|
KdcName->Names[Index].MaximumLength = Source->Names[Index].Length + sizeof(WCHAR);
|
|
KdcName->Names[Index].Buffer = (LPWSTR) Where;
|
|
RtlCopyMemory(
|
|
Where,
|
|
Source->Names[Index].Buffer,
|
|
Source->Names[Index].Length
|
|
);
|
|
KdcName->Names[Index].Buffer[Source->Names[Index].Length / sizeof(WCHAR)] = L'\0';
|
|
Where += KdcName->Names[Index].MaximumLength;
|
|
}
|
|
KdcName->NameType = Source->NameType;
|
|
*Destination = KdcName;
|
|
KdcName = NULL;
|
|
|
|
Cleanup:
|
|
if (KdcName != NULL)
|
|
{
|
|
KerbFreeKdcName(&KdcName);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
#ifdef RETAIL_LOG_SUPPORT
|
|
VOID
|
|
KerbPrintKdcNameEx(
|
|
IN ULONG DebugLevel,
|
|
IN ULONG InfoLevel,
|
|
IN PKERB_INTERNAL_NAME Name
|
|
)
|
|
{
|
|
ULONG Index;
|
|
if ((InfoLevel & DebugLevel) != 0)
|
|
{
|
|
for (Index = 0; Name && (Index < Name->NameCount); Index++)
|
|
{
|
|
DebugLog((DebugLevel | DSYSDBG_CLEAN, " %wZ ", &Name->Names[Index]));
|
|
}
|
|
DebugLog((DebugLevel | DSYSDBG_CLEAN, "\n"));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KerbPrintPrincipalName(
|
|
IN ULONG DebugLevel,
|
|
IN PKERB_PRINCIPAL_NAME Name
|
|
)
|
|
{
|
|
KERBERR KerbErr;
|
|
PKERB_INTERNAL_NAME KdcName = NULL;
|
|
|
|
KerbErr = KerbConvertPrincipalNameToKdcName(
|
|
&KdcName,
|
|
Name
|
|
);
|
|
|
|
if (KERB_SUCCESS(KerbErr))
|
|
{
|
|
KerbPrintKdcNameEx(DebugLevel, DebugLevel, KdcName);
|
|
KerbFreeKdcName(&KdcName);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
KerbPrintKerbRealm(
|
|
IN ULONG DebugLevel,
|
|
IN PKERB_REALM Realm
|
|
)
|
|
{
|
|
KERBERR KerbErr;
|
|
UNICODE_STRING RealmName = {0};
|
|
|
|
KerbErr = KerbConvertRealmToUnicodeString(
|
|
&RealmName,
|
|
Realm
|
|
);
|
|
|
|
if (KERB_SUCCESS(KerbErr))
|
|
{
|
|
DebugLog((DebugLevel, "%wZ\n", &RealmName));
|
|
KerbFreeString(&RealmName);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertStringToKdcName
|
|
//
|
|
// Synopsis: Converts a string to a KRB_NT_MS_PRINCIPAL kdc name
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertStringToKdcName(
|
|
OUT PKERB_INTERNAL_NAME * PrincipalName,
|
|
IN PUNICODE_STRING String
|
|
)
|
|
{
|
|
PKERB_INTERNAL_NAME LocalName = NULL;
|
|
|
|
LocalName = (PKERB_INTERNAL_NAME) MIDL_user_allocate(KERB_INTERNAL_NAME_SIZE(1) + String->Length + sizeof(WCHAR));
|
|
if (LocalName == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
LocalName->NameCount = 1;
|
|
LocalName->NameType = KRB_NT_MS_PRINCIPAL;
|
|
LocalName->Names[0].Length = String->Length;
|
|
LocalName->Names[0].MaximumLength = String->Length + sizeof(WCHAR);
|
|
LocalName->Names[0].Buffer = (LPWSTR) ((PUCHAR) LocalName + KERB_INTERNAL_NAME_SIZE(1));
|
|
RtlCopyMemory(
|
|
LocalName->Names[0].Buffer,
|
|
String->Buffer,
|
|
String->Length
|
|
);
|
|
LocalName->Names[0].Buffer[String->Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
*PrincipalName = LocalName;
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertKdcNameToString
|
|
//
|
|
// Synopsis: Converts a KdcName to a '/' separated unicode string.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertKdcNameToString(
|
|
OUT PUNICODE_STRING String,
|
|
IN PKERB_INTERNAL_NAME PrincipalName,
|
|
IN OPTIONAL PUNICODE_STRING RealmName
|
|
)
|
|
{
|
|
ULONG StringLength = 0;
|
|
ULONG Index;
|
|
PBYTE Where;
|
|
|
|
if (!ARGUMENT_PRESENT(PrincipalName) || !ARGUMENT_PRESENT(String))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Count up the size of the name parts
|
|
//
|
|
|
|
for (Index = 0; Index < PrincipalName->NameCount ; Index++ )
|
|
{
|
|
StringLength = StringLength + PrincipalName->Names[Index].Length;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(RealmName) && (RealmName->Length != 0))
|
|
{
|
|
StringLength = StringLength + (sizeof(WCHAR) + RealmName->Length);
|
|
}
|
|
|
|
//
|
|
// Add in '/' separators and a null terminator
|
|
//
|
|
|
|
DsysAssert(PrincipalName->NameCount > 0);
|
|
StringLength = StringLength + (PrincipalName->NameCount * sizeof(WCHAR));
|
|
|
|
if ((StringLength - sizeof(WCHAR)) > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return(KRB_ERR_GENERIC); // required size too large for Length
|
|
}
|
|
|
|
String->Buffer = (LPWSTR) MIDL_user_allocate(StringLength);
|
|
if (String->Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
String->MaximumLength = (USHORT)StringLength;
|
|
String->Length = (USHORT)(StringLength - sizeof(WCHAR));
|
|
|
|
Where = (PBYTE) String->Buffer;
|
|
|
|
for (Index = 0; Index < PrincipalName->NameCount ; Index++ )
|
|
{
|
|
//
|
|
// Add a '/' before every segment but the first
|
|
//
|
|
|
|
if (Index != 0)
|
|
{
|
|
*((LPWSTR)(Where)) = L'/';
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
RtlCopyMemory(
|
|
Where,
|
|
PrincipalName->Names[Index].Buffer,
|
|
PrincipalName->Names[Index].Length
|
|
);
|
|
Where += PrincipalName->Names[Index].Length;
|
|
}
|
|
if (ARGUMENT_PRESENT(RealmName) && (RealmName->Length != 0))
|
|
{
|
|
*((LPWSTR)(Where)) = L'@';
|
|
Where += sizeof(WCHAR);
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
RealmName->Buffer,
|
|
RealmName->Length
|
|
);
|
|
Where += RealmName->Length;
|
|
|
|
}
|
|
|
|
*((LPWSTR)(Where)) = L'\0';
|
|
Where += sizeof(WCHAR);
|
|
DsysAssert((Where - (PUCHAR) String->Buffer) == (LONG)StringLength);
|
|
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildFullServiceName
|
|
//
|
|
// Synopsis: Combines a service name and domain name to make a full
|
|
// service name.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// Servicename - Name of service
|
|
// FullServiceName - Receives full service name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbBuildFullServiceName(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
OUT PUNICODE_STRING FullServiceName
|
|
)
|
|
{
|
|
PUCHAR Where;
|
|
ULONG ulLength = 0;
|
|
|
|
FullServiceName->Buffer = NULL;
|
|
|
|
ulLength = (ULONG)DomainName->Length +
|
|
(ULONG)ServiceName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return(KRB_ERR_GENERIC); // required size too large for Length
|
|
}
|
|
|
|
FullServiceName->Length = (USHORT)ulLength;
|
|
|
|
FullServiceName->MaximumLength =
|
|
FullServiceName->Length + sizeof(WCHAR);
|
|
|
|
FullServiceName->Buffer = (LPWSTR) MIDL_user_allocate(
|
|
FullServiceName->MaximumLength
|
|
);
|
|
if (FullServiceName->Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
Where = (PUCHAR) FullServiceName->Buffer;
|
|
|
|
RtlCopyMemory(
|
|
FullServiceName->Buffer,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
Where += DomainName->Length;
|
|
|
|
if ((DomainName->Length !=0) && (ServiceName->Length != 0))
|
|
{
|
|
*(LPWSTR) Where = L'\\';
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
|
|
Where += ServiceName->Length;
|
|
FullServiceName->Length = (USHORT)(Where - (PUCHAR) FullServiceName->Buffer);
|
|
*(LPWSTR) Where = L'\0';
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildEmailName
|
|
//
|
|
// Synopsis: Combines a service name and domain name to make an email
|
|
// name = "service@domain".
|
|
//
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// Servicename - Name of service
|
|
// FullServiceName - Receives full service name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbBuildEmailName(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
OUT PUNICODE_STRING EmailName
|
|
)
|
|
{
|
|
PUCHAR Where;
|
|
ULONG ulLength;
|
|
|
|
EmailName->Buffer = NULL;
|
|
|
|
ulLength = (ULONG)DomainName->Length +
|
|
(ULONG)ServiceName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return(KRB_ERR_GENERIC); // required size too large for Length
|
|
}
|
|
|
|
EmailName->Length = (USHORT)ulLength;
|
|
|
|
EmailName->MaximumLength =
|
|
EmailName->Length + sizeof(WCHAR);
|
|
|
|
EmailName->Buffer = (LPWSTR) MIDL_user_allocate(
|
|
EmailName->MaximumLength
|
|
);
|
|
if (EmailName->Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
Where = (PUCHAR) EmailName->Buffer;
|
|
|
|
RtlCopyMemory(
|
|
EmailName->Buffer,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
Where += ServiceName->Length;
|
|
|
|
*(LPWSTR) Where = L'@';
|
|
Where += sizeof(WCHAR);
|
|
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
|
|
Where += DomainName->Length;
|
|
EmailName->Length = (USHORT)(Where - (PUCHAR) EmailName->Buffer);
|
|
*(LPWSTR) Where = L'\0';
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildUnicodeSpn
|
|
//
|
|
// Synopsis: Builds a 2 part SPN
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// ServiceName - Name of service
|
|
// Spn - Receives full service name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbBuildUnicodeSpn(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
OUT PUNICODE_STRING UnicodeSpn
|
|
)
|
|
|
|
{
|
|
|
|
PWSTR Spn, tmp;
|
|
ULONG BuffSize;
|
|
BOOL fAssigned = FALSE;
|
|
|
|
BuffSize = DomainName->MaximumLength +
|
|
ServiceName->MaximumLength +
|
|
(sizeof(WCHAR) * 2);
|
|
|
|
Spn = (PWSTR) MIDL_user_allocate(BuffSize);
|
|
if (NULL == Spn)
|
|
{
|
|
return KRB_ERR_GENERIC;
|
|
}
|
|
|
|
tmp = Spn;
|
|
|
|
RtlCopyMemory(
|
|
Spn,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
|
|
tmp += (ServiceName->Length / sizeof(WCHAR));
|
|
*tmp = L'/';
|
|
|
|
RtlCopyMemory(
|
|
++tmp,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
|
|
tmp += (DomainName->Length / sizeof(WCHAR));
|
|
*tmp = L'\0';
|
|
|
|
fAssigned = SafeRtlInitUnicodeString(
|
|
UnicodeSpn,
|
|
Spn
|
|
);
|
|
|
|
if (fAssigned == FALSE)
|
|
{
|
|
return(KRB_ERR_GENERIC); // string length would not fit into USHORT
|
|
}
|
|
|
|
return KDC_ERR_NONE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildFullServiceKdcName
|
|
//
|
|
// Synopsis: Combines a service name and domain name to make a full
|
|
// service name. If the name type is MS_PRINCIPAL they are
|
|
// combined into one portion of the name, otherise left in
|
|
// two portions
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// ServiceName - Name of service
|
|
// NameType - Type of name to produce
|
|
// FullServiceName - Receives full service name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbBuildFullServiceKdcName(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
IN ULONG NameType,
|
|
OUT PKERB_INTERNAL_NAME * FullServiceName
|
|
)
|
|
{
|
|
|
|
return(KerbBuildFullServiceKdcNameWithSid(
|
|
DomainName,
|
|
ServiceName,
|
|
NULL,
|
|
NameType,
|
|
FullServiceName
|
|
));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildKpasswdName
|
|
//
|
|
// Synopsis: Builds the name of the kpasswd service
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbBuildKpasswdName(
|
|
OUT PKERB_INTERNAL_NAME * KpasswdName
|
|
)
|
|
{
|
|
UNICODE_STRING KpasswdServiceNames[2];
|
|
|
|
//
|
|
// Build the service name for the ticket
|
|
//
|
|
|
|
(void)RtlInitUnicodeString(
|
|
&KpasswdServiceNames[0],
|
|
KERB_KPASSWD_FIRST_NAME
|
|
);
|
|
|
|
(void)RtlInitUnicodeString(
|
|
&KpasswdServiceNames[1],
|
|
KERB_KPASSWD_SECOND_NAME
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
|
|
&KpasswdServiceNames[1],
|
|
&KpasswdServiceNames[0],
|
|
KRB_NT_SRV_INST,
|
|
KpasswdName
|
|
)))
|
|
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildFullServiceKdcNameWithSid
|
|
//
|
|
// Synopsis: Combines a service name and domain name to make a full
|
|
// service name. If the name type is MS_PRINCIPAL they are
|
|
// combined into one portion of the name, otherise left in
|
|
// two portions. If a sid is presenet, it is tacked on as
|
|
// the last segment of the name
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// ServiceName - Name of service
|
|
// Sid - Optionally contains the sid to use
|
|
// NameType - Type of name to produce
|
|
// FullServiceName - Receives full service name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbBuildFullServiceKdcNameWithSid(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
IN OPTIONAL PSID Sid,
|
|
IN ULONG NameType,
|
|
OUT PKERB_INTERNAL_NAME * FullServiceName
|
|
)
|
|
{
|
|
PKERB_INTERNAL_NAME FinalName = NULL;
|
|
PUCHAR Where;
|
|
ULONG NameParts;
|
|
ULONG NameLength = 0;
|
|
ULONG ulLength = 0;
|
|
WCHAR SidBuffer[256];
|
|
UNICODE_STRING SidString;
|
|
KERBERR KerbErr = KRB_ERR_GENERIC;
|
|
|
|
SidString.Length = 0;
|
|
SidString.MaximumLength = sizeof(SidBuffer);
|
|
SidString.Buffer = SidBuffer;
|
|
|
|
if ((NameType == KRB_NT_MS_PRINCIPAL) ||
|
|
(NameType == KRB_NT_MS_PRINCIPAL_AND_ID))
|
|
|
|
{
|
|
NameParts = 1;
|
|
NameLength = DomainName->Length + ServiceName->Length + 2*sizeof(WCHAR);
|
|
}
|
|
else if ((NameType == KRB_NT_PRINCIPAL) ||
|
|
(NameType == KRB_NT_PRINCIPAL_AND_ID) ||
|
|
(NameType == KRB_NT_ENTERPRISE_PRINCIPAL) ||
|
|
(NameType == KRB_NT_ENT_PRINCIPAL_AND_ID))
|
|
{
|
|
NameParts = 1;
|
|
NameLength = ServiceName->Length + sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
NameParts = 2;
|
|
NameLength = DomainName->Length + ServiceName->Length + 2*sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// If a SID is present, add another name part
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(Sid))
|
|
{
|
|
NTSTATUS Status;
|
|
Status = KerbConvertSidToString(
|
|
Sid,
|
|
&SidString,
|
|
FALSE // don't allocate
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
NameParts++;
|
|
NameLength += SidString.Length + sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
*FullServiceName = NULL;
|
|
|
|
FinalName = (PKERB_INTERNAL_NAME) MIDL_user_allocate(KERB_INTERNAL_NAME_SIZE(NameParts) + NameLength);
|
|
if (FinalName == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
RtlZeroMemory(
|
|
FinalName,
|
|
KERB_INTERNAL_NAME_SIZE(NameParts) + NameLength
|
|
);
|
|
|
|
Where = (PUCHAR) FinalName + KERB_INTERNAL_NAME_SIZE(NameParts);
|
|
FinalName->NameType = (USHORT) NameType;
|
|
FinalName->NameCount = (USHORT) NameParts;
|
|
|
|
if ((NameType == KRB_NT_MS_PRINCIPAL) ||
|
|
(NameType == KRB_NT_MS_PRINCIPAL_AND_ID))
|
|
{
|
|
//
|
|
// If the domain name does not have an initial '\', reserve space for one
|
|
//
|
|
|
|
FinalName->Names[0].Buffer = (LPWSTR) Where;
|
|
|
|
// This is dependent on our naming conventions.
|
|
//
|
|
// The full service name is the '\' domain name ':' service name.
|
|
//
|
|
|
|
ulLength = (ULONG)DomainName->Length +
|
|
(ULONG)ServiceName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC; // required size too large for Length
|
|
goto Cleanup;
|
|
}
|
|
|
|
FinalName->Names[0].Length = (USHORT)ulLength;
|
|
|
|
FinalName->Names[0].MaximumLength =
|
|
FinalName->Names[0].Length + sizeof(WCHAR);
|
|
|
|
|
|
RtlCopyMemory(
|
|
FinalName->Names[0].Buffer,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
Where += DomainName->Length;
|
|
|
|
if ((DomainName->Length !=0) && (ServiceName->Length != 0))
|
|
{
|
|
*(LPWSTR) Where = L'\\';
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
|
|
Where += ServiceName->Length;
|
|
ulLength = (ULONG)(Where - (PUCHAR) FinalName->Names[0].Buffer);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC; // required size too large for Length
|
|
goto Cleanup;
|
|
}
|
|
|
|
FinalName->Names[0].Length = (USHORT)ulLength;
|
|
*(LPWSTR) Where = L'\0';
|
|
}
|
|
else if ((NameType == KRB_NT_PRINCIPAL) ||
|
|
(NameType == KRB_NT_PRINCIPAL_AND_ID) ||
|
|
(NameType == KRB_NT_ENTERPRISE_PRINCIPAL)||
|
|
(NameType == KRB_NT_ENT_PRINCIPAL_AND_ID))
|
|
{
|
|
//
|
|
// Principals have no domain name
|
|
//
|
|
|
|
FinalName->Names[0].Length = ServiceName->Length;
|
|
FinalName->Names[0].MaximumLength = ServiceName->Length + sizeof(WCHAR);
|
|
FinalName->Names[0].Buffer = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
Where += ServiceName->Length;
|
|
*((LPWSTR) Where) = L'\0';
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
FinalName->Names[0].Length = ServiceName->Length;
|
|
FinalName->Names[0].MaximumLength = ServiceName->Length + sizeof(WCHAR);
|
|
FinalName->Names[0].Buffer = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length
|
|
);
|
|
Where += ServiceName->Length;
|
|
*((LPWSTR) Where) = L'\0';
|
|
Where += sizeof(WCHAR);
|
|
|
|
|
|
FinalName->Names[1].Length = DomainName->Length;
|
|
FinalName->Names[1].MaximumLength = DomainName->Length + sizeof(WCHAR);
|
|
FinalName->Names[1].Buffer = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
Where += DomainName->Length;
|
|
*((LPWSTR) Where) = L'\0';
|
|
Where += sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
//
|
|
// Append the string, if present
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(Sid))
|
|
{
|
|
FinalName->Names[NameParts-1].Length = SidString.Length;
|
|
FinalName->Names[NameParts-1].MaximumLength = SidString.Length + sizeof(WCHAR);
|
|
FinalName->Names[NameParts-1].Buffer = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
SidString.Buffer,
|
|
SidString.Length
|
|
);
|
|
Where += SidString.Length;
|
|
*((LPWSTR) Where) = L'\0';
|
|
}
|
|
|
|
*FullServiceName = FinalName;
|
|
FinalName = NULL;
|
|
KerbErr = KDC_ERR_NONE;
|
|
|
|
Cleanup:
|
|
|
|
if (FinalName)
|
|
{
|
|
MIDL_user_free(FinalName);
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbExtractSidFromKdcName
|
|
//
|
|
// Synopsis: Extracts the sid portion from a KDC name with a sid. This
|
|
// routine also decrements the name count so that future
|
|
// users of the name don't see the sid.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbExtractSidFromKdcName(
|
|
IN OUT PKERB_INTERNAL_NAME Name,
|
|
OUT PSID * Sid
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
DsysAssert(Sid);
|
|
|
|
//
|
|
// The sid is in the last portion of the name.
|
|
//
|
|
|
|
Status = KerbConvertStringToSid(
|
|
&Name->Names[Name->NameCount-1],
|
|
Sid
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Name->NameCount--;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the name wasn't a sid, return success. If it was another
|
|
// problem, return an error
|
|
//
|
|
|
|
if (Status != STATUS_INVALID_PARAMETER)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
}
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDuplicateStringEx
|
|
//
|
|
// Synopsis: Duplicates a UNICODE_STRING. If the source string buffer is
|
|
// NULL the destionation will be too. The EX function doesn't
|
|
// automatically NULL terminate your buffer...
|
|
//
|
|
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
|
|
//
|
|
// Arguments: DestinationString - Receives a copy of the source string
|
|
// SourceString - String to copy
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS - the copy succeeded
|
|
// STATUS_INSUFFICIENT_RESOURCES - the call to allocate
|
|
// memory failed.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbDuplicateStringEx(
|
|
OUT PUNICODE_STRING DestinationString,
|
|
IN OPTIONAL PUNICODE_STRING SourceString,
|
|
IN BOOLEAN NullTerminate
|
|
)
|
|
{
|
|
ULONG Buffsize = SourceString->Length + (NullTerminate ? sizeof(WCHAR) : 0);
|
|
|
|
if ((SourceString == NULL) || (SourceString->Buffer == NULL))
|
|
{
|
|
DestinationString->Buffer = NULL;
|
|
DestinationString->Length = DestinationString->MaximumLength = 0;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// Detect potential B.0.s here on USHORT
|
|
//
|
|
if (SourceString->Length > KERB_MAX_UNICODE_STRING )
|
|
{
|
|
// DsysAssert(FALSE);
|
|
return (STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
DestinationString->Buffer = (LPWSTR) MIDL_user_allocate(Buffsize);
|
|
if (DestinationString->Buffer == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
DestinationString->Length = SourceString->Length;
|
|
DestinationString->MaximumLength = (USHORT) Buffsize;
|
|
RtlCopyMemory(
|
|
DestinationString->Buffer,
|
|
SourceString->Buffer,
|
|
SourceString->Length
|
|
);
|
|
|
|
|
|
if (NullTerminate)
|
|
{
|
|
DestinationString->Buffer[SourceString->Length/sizeof(WCHAR)] = L'\0';
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDuplicateString
|
|
//
|
|
// Synopsis: Duplicates a UNICODE_STRING. If the source string buffer is
|
|
// NULL the destionation will be too.
|
|
//
|
|
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
|
|
//
|
|
// Arguments: DestinationString - Receives a copy of the source string
|
|
// SourceString - String to copy
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS - the copy succeeded
|
|
// STATUS_INSUFFICIENT_RESOURCES - the call to allocate
|
|
// memory failed.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbDuplicateString(
|
|
OUT PUNICODE_STRING DestinationString,
|
|
IN OPTIONAL PUNICODE_STRING SourceString
|
|
)
|
|
{
|
|
return(KerbDuplicateStringEx(
|
|
DestinationString,
|
|
SourceString,
|
|
TRUE
|
|
));
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildNullTerminatedString
|
|
//
|
|
// Synopsis: Converts a UNICODE_STRING to a NULL-termianted string
|
|
//
|
|
// Effects: allocates return with MIDL_user_allocate
|
|
//
|
|
// Arguments: String - string to null terminate.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: NULL on error, a pointer on success
|
|
//
|
|
// Notes: an input string with a NULL buffer pointe results in a
|
|
// return string consisting of just "\0"
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
LPWSTR
|
|
KerbBuildNullTerminatedString(
|
|
IN PUNICODE_STRING String
|
|
)
|
|
{
|
|
LPWSTR ReturnString;
|
|
|
|
ReturnString = (LPWSTR) MIDL_user_allocate(String->Length + sizeof(WCHAR));
|
|
if (ReturnString == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
if (String->Buffer != NULL)
|
|
{
|
|
RtlCopyMemory(
|
|
ReturnString,
|
|
String->Buffer,
|
|
String->Length
|
|
);
|
|
}
|
|
ReturnString[String->Length/sizeof(WCHAR)] = L'\0';
|
|
return(ReturnString);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeString
|
|
//
|
|
// Synopsis: Frees a string allocated by KerbDuplicateString
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: String - Optionally points to a UNICODE_STRING
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KerbFreeString(
|
|
IN OPTIONAL PUNICODE_STRING String
|
|
)
|
|
{
|
|
if (ARGUMENT_PRESENT(String) && String->Buffer != NULL)
|
|
{
|
|
MIDL_user_free(String->Buffer);
|
|
ZeroMemory(String, sizeof(UNICODE_STRING));
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeRealm
|
|
//
|
|
// Synopsis: Frees a realm allcoated with KerbConvertXXXToRealm
|
|
//
|
|
// Effects: null out the realm.
|
|
//
|
|
// Arguments: Realm - Realm to free
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KerbFreeRealm(
|
|
IN PKERB_REALM Realm
|
|
)
|
|
{
|
|
if (*Realm != NULL)
|
|
{
|
|
MIDL_user_free(*Realm);
|
|
*Realm = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompareUnicodeRealmToKerbRealm
|
|
//
|
|
// Synopsis: Compares a kerb_realm to a unicode kerb realm
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
KERBERR
|
|
KerbCompareUnicodeRealmToKerbRealm(
|
|
IN PKERB_REALM KerbRealm,
|
|
IN PUNICODE_STRING UnicodeRealm,
|
|
OUT PBOOLEAN Result
|
|
)
|
|
{
|
|
KERB_REALM TempName = NULL;
|
|
|
|
|
|
if (!KERB_SUCCESS(KerbConvertUnicodeStringToRealm(
|
|
&TempName,
|
|
UnicodeRealm
|
|
)))
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
*Result = KerbCompareRealmNames(&TempName, KerbRealm);
|
|
|
|
KerbFreeRealm( &TempName );
|
|
return(KDC_ERR_NONE);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreePrincipalName
|
|
//
|
|
// Synopsis: Frees a principal name allocated with KerbConvertxxxToPrincipalName
|
|
//
|
|
// Effects: zeros out principal name so it won't be freed again
|
|
//
|
|
// Arguments: Name - The name to free
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KerbFreePrincipalName(
|
|
IN PKERB_PRINCIPAL_NAME Name
|
|
)
|
|
{
|
|
PKERB_PRINCIPAL_NAME_ELEM Elem,NextElem;
|
|
|
|
Elem = Name->name_string;
|
|
while (Elem != NULL)
|
|
{
|
|
if (Elem->value != NULL)
|
|
{
|
|
MIDL_user_free(Elem->value);
|
|
}
|
|
NextElem = Elem->next;
|
|
MIDL_user_free(Elem);
|
|
Elem = NextElem;
|
|
}
|
|
Name->name_string = NULL;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertUnicodeStringToRealm
|
|
//
|
|
// Synopsis: Converts a unicode-string form of a domain name to a
|
|
// KERB_REALM structure.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Realm - the realm
|
|
// String - The string to convert
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbConvertUnicodeStringToRealm(
|
|
OUT PKERB_REALM Realm,
|
|
IN PUNICODE_STRING String
|
|
)
|
|
{
|
|
KERBERR KerbErr;
|
|
STRING TempString = {0};
|
|
|
|
*Realm = NULL;
|
|
KerbErr = KerbUnicodeStringToKerbString(
|
|
&TempString,
|
|
String
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
return(KerbErr);
|
|
}
|
|
*Realm = TempString.Buffer;
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertRealmToUnicodeString
|
|
//
|
|
// Synopsis: Converts a KERB_REALM structure to a unicode-string form
|
|
// of a domain name.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: String - the unicode realm name
|
|
// Realm - the realm to convert
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertRealmToUnicodeString(
|
|
OUT PUNICODE_STRING String,
|
|
IN PKERB_REALM Realm
|
|
)
|
|
{
|
|
KERBERR Status;
|
|
STRING TempString;
|
|
BOOL fAssigned = FALSE;
|
|
|
|
fAssigned = SafeRtlInitString(
|
|
&TempString,
|
|
*Realm
|
|
);
|
|
if (fAssigned == FALSE)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
Status = KerbStringToUnicodeString(
|
|
String,
|
|
&TempString
|
|
);
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDuplicateRealm
|
|
//
|
|
// Synopsis: Duplciates a realm name
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Realm - the realm
|
|
// SourceRealm - The realm to duplicate
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbDuplicateRealm(
|
|
OUT PKERB_REALM Realm,
|
|
IN KERB_REALM SourceRealm
|
|
)
|
|
{
|
|
ULONG RealmLength;
|
|
|
|
if (ARGUMENT_PRESENT(SourceRealm))
|
|
{
|
|
RealmLength = lstrlenA(SourceRealm);
|
|
*Realm = (PCHAR) MIDL_user_allocate(RealmLength + sizeof(CHAR));
|
|
if (*Realm == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
RtlCopyMemory(
|
|
*Realm,
|
|
SourceRealm,
|
|
RealmLength + sizeof(CHAR)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
*Realm = NULL;
|
|
}
|
|
return(KDC_ERR_NONE);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCompareStringToPrincipalName
|
|
//
|
|
// Synopsis: Compares a unicode string name to a principal name for
|
|
// equality
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: PrincipalName - kerberos principal name
|
|
// String - String name
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: TRUE if one of the principal names matches the string name
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOLEAN
|
|
KerbCompareStringToPrincipalName(
|
|
IN PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN PUNICODE_STRING String
|
|
)
|
|
{
|
|
KERBERR Status;
|
|
BOOLEAN FoundMatch = FALSE;
|
|
UNICODE_STRING TempString = {0};
|
|
ULONG NameType;
|
|
|
|
Status = KerbConvertPrincipalNameToString(
|
|
&TempString,
|
|
&NameType,
|
|
PrincipalName
|
|
);
|
|
|
|
if (!KERB_SUCCESS(Status))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if (RtlEqualUnicodeString(
|
|
&TempString,
|
|
String,
|
|
TRUE // case insensitive
|
|
))
|
|
{
|
|
FoundMatch = TRUE;
|
|
}
|
|
|
|
KerbFreeString(&TempString);
|
|
return(FoundMatch);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertStringToPrincipalName
|
|
//
|
|
// Synopsis: converts a string to a principal name
|
|
//
|
|
// Effects: allocate memory
|
|
//
|
|
// Arguments: PrincipalName - receives the principal name
|
|
// String - the string name to convert
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or STATUS_INSUFFICIENT_MEMORY
|
|
//
|
|
// Notes: principalname->name_value must be freed with MIDL_user_free and
|
|
// principalname->name_value->value must be freed with
|
|
// MIDL_user_Free.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertStringToPrincipalName(
|
|
OUT PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN PUNICODE_STRING String,
|
|
IN ULONG NameType
|
|
)
|
|
{
|
|
PKERB_PRINCIPAL_NAME_ELEM Elem;
|
|
STRING TempKerbString;
|
|
KERBERR Status = KDC_ERR_NONE;
|
|
UNICODE_STRING TempElemString;
|
|
UNICODE_STRING TempString;
|
|
ULONG Index;
|
|
ULONG ulLength;
|
|
|
|
RtlZeroMemory(
|
|
PrincipalName,
|
|
sizeof(KERB_PRINCIPAL_NAME)
|
|
);
|
|
|
|
PrincipalName->name_type = (int) NameType;
|
|
|
|
//
|
|
// MS principals are stuck all in one string
|
|
//
|
|
|
|
if (NameType == KRB_NT_MS_PRINCIPAL)
|
|
{
|
|
Status = KerbUnicodeStringToKerbString(
|
|
&TempKerbString,
|
|
String
|
|
);
|
|
|
|
if (!KERB_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
Elem = (PKERB_PRINCIPAL_NAME_ELEM) MIDL_user_allocate(sizeof(KERB_PRINCIPAL_NAME_ELEM));
|
|
if (Elem == NULL)
|
|
{
|
|
MIDL_user_free(TempKerbString.Buffer);
|
|
Status = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
Elem->value = TempKerbString.Buffer;
|
|
Elem->next = PrincipalName->name_string;
|
|
PrincipalName->name_string = Elem;
|
|
|
|
Status = KDC_ERR_NONE;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Go through the string. If we hit a '\\' separator, split
|
|
// the name there into another component.
|
|
//
|
|
|
|
TempString = *String;
|
|
Index = 0;
|
|
|
|
while (Index <= TempString.Length / sizeof(WCHAR))
|
|
{
|
|
if ((Index == TempString.Length/sizeof(WCHAR)) ||
|
|
(TempString.Buffer[Index] == L'\\') )
|
|
{
|
|
//
|
|
// Build the element string
|
|
//
|
|
|
|
ulLength = Index * sizeof(WCHAR);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
Status = KRB_ERR_GENERIC; // length exceed USHORT range
|
|
goto Cleanup;
|
|
}
|
|
|
|
TempElemString.Buffer = TempString.Buffer;
|
|
TempElemString.MaximumLength = (USHORT) ulLength;
|
|
TempElemString.Length = TempElemString.MaximumLength;
|
|
|
|
Status = KerbUnicodeStringToKerbString(
|
|
&TempKerbString,
|
|
&TempElemString
|
|
);
|
|
|
|
if (!KERB_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Elem = (PKERB_PRINCIPAL_NAME_ELEM) MIDL_user_allocate(sizeof(KERB_PRINCIPAL_NAME_ELEM));
|
|
if (Elem == NULL)
|
|
{
|
|
MIDL_user_free(TempKerbString.Buffer);
|
|
Status = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Elem->value = TempKerbString.Buffer;
|
|
Elem->next = PrincipalName->name_string;
|
|
PrincipalName->name_string = Elem;
|
|
|
|
//
|
|
// Reset the string to be the remains of the name
|
|
//
|
|
|
|
if (Index != TempString.Length / sizeof(WCHAR))
|
|
{
|
|
ulLength = (Index+1) * sizeof(WCHAR);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
Status = KRB_ERR_GENERIC; // length exceed USHORT range
|
|
goto Cleanup;
|
|
}
|
|
TempString.Buffer = TempString.Buffer + Index + 1;
|
|
TempString.Length = TempString.Length - (USHORT) ulLength;
|
|
TempString.MaximumLength = TempString.MaximumLength - (USHORT) ulLength;
|
|
Index = 0;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!KERB_SUCCESS(Status))
|
|
{
|
|
KerbFreePrincipalName(PrincipalName);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDuplicatePrincipalName
|
|
//
|
|
// Synopsis: Duplicates a principal name
|
|
//
|
|
// Effects: allocate memory
|
|
//
|
|
// Arguments: PrincipalName - receives the principal name
|
|
// SourcePrincipalName - the name to copy
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or STATUS_INSUFFICIENT_MEMORY
|
|
//
|
|
// Notes: principalname->name_value must be freed with MIDL_user_free and
|
|
// principalname->name_value->value must be freed with
|
|
// MIDL_user_Free.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbDuplicatePrincipalName(
|
|
OUT PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN PKERB_PRINCIPAL_NAME SourcePrincipalName
|
|
)
|
|
{
|
|
KERBERR Status = KDC_ERR_NONE;
|
|
ULONG NameLen;
|
|
PKERB_PRINCIPAL_NAME_ELEM SourceElem;
|
|
PKERB_PRINCIPAL_NAME_ELEM DestElem;
|
|
PKERB_PRINCIPAL_NAME_ELEM * NextElem;
|
|
|
|
// DSYSASSERT(PrincipalName);
|
|
// DSYSASSERT(SourcePrincipalName);
|
|
|
|
RtlZeroMemory(
|
|
PrincipalName,
|
|
sizeof(KERB_PRINCIPAL_NAME)
|
|
);
|
|
|
|
//
|
|
// Fill in correct name type
|
|
//
|
|
|
|
PrincipalName->name_type = SourcePrincipalName->name_type;
|
|
SourceElem = SourcePrincipalName->name_string;
|
|
NextElem = &PrincipalName->name_string;
|
|
|
|
*NextElem = NULL;
|
|
while (SourceElem != NULL)
|
|
{
|
|
DestElem = (PKERB_PRINCIPAL_NAME_ELEM) MIDL_user_allocate(sizeof(KERB_PRINCIPAL_NAME_ELEM));
|
|
if (DestElem == NULL)
|
|
{
|
|
Status = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
NameLen = lstrlenA(SourceElem->value);
|
|
|
|
DestElem->value = (PCHAR) MIDL_user_allocate(NameLen + sizeof(CHAR));
|
|
if (DestElem->value == NULL)
|
|
{
|
|
MIDL_user_free(DestElem);
|
|
Status = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
RtlCopyMemory(
|
|
DestElem->value,
|
|
SourceElem->value,
|
|
NameLen + sizeof(CHAR)
|
|
);
|
|
DestElem->next = NULL;
|
|
*NextElem = DestElem;
|
|
NextElem = &DestElem->next;
|
|
SourceElem = SourceElem->next;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!KERB_SUCCESS(Status))
|
|
{
|
|
KerbFreePrincipalName(PrincipalName);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertPrincipalNameToString
|
|
//
|
|
// Synopsis: Converts a KERB_PRINCIPAL_NAME to a unicode string
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertPrincipalNameToString(
|
|
OUT PUNICODE_STRING String,
|
|
OUT PULONG NameType,
|
|
IN PKERB_PRINCIPAL_NAME PrincipalName
|
|
)
|
|
{
|
|
KERBERR KerbErr;
|
|
STRING TempAnsiString;
|
|
ULONG StringLength = 0;
|
|
ULONG NameParts = 0;
|
|
ULONG Index;
|
|
PCHAR Where;
|
|
PKERB_PRINCIPAL_NAME_ELEM NameElements[MAX_NAME_ELEMENTS+1];
|
|
|
|
*NameType = (ULONG) PrincipalName->name_type;
|
|
NameElements[NameParts] = PrincipalName->name_string;
|
|
|
|
while (NameElements[NameParts] != NULL)
|
|
{
|
|
//
|
|
// add in a separator plus the length of the element
|
|
//
|
|
|
|
StringLength += lstrlenA(NameElements[NameParts]->value) + 1;
|
|
NameElements[NameParts+1] = NameElements[NameParts]->next;
|
|
NameParts++;
|
|
if (NameParts >= MAX_NAME_ELEMENTS)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Too many name parts: %d\n",NameParts));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure there is at least one name part
|
|
//
|
|
|
|
if (NameParts == 0)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Test for overflow on USHORT lengths
|
|
//
|
|
|
|
if ( (StringLength - sizeof(CHAR)) > KERB_MAX_STRING )
|
|
{
|
|
return (KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Now build the name, backwards to front, with '\\' separators
|
|
//
|
|
|
|
TempAnsiString.Length = (USHORT) StringLength - sizeof(CHAR);
|
|
TempAnsiString.MaximumLength = (USHORT) StringLength;
|
|
|
|
SafeAllocaAllocate( TempAnsiString.Buffer, StringLength );
|
|
|
|
if (TempAnsiString.Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
Where = TempAnsiString.Buffer;
|
|
for (Index = 0; Index < NameParts; Index++ )
|
|
{
|
|
ULONG NameLength = lstrlenA(NameElements[Index]->value);
|
|
RtlCopyMemory(
|
|
Where,
|
|
NameElements[Index]->value,
|
|
NameLength
|
|
);
|
|
|
|
Where += NameLength;
|
|
|
|
//
|
|
// Add either a separating '\' or a trailing '\0'
|
|
//
|
|
|
|
if (Index != NameParts - 1)
|
|
{
|
|
*Where = '/';
|
|
}
|
|
else
|
|
{
|
|
*Where = '\0';
|
|
}
|
|
Where++;
|
|
}
|
|
|
|
DsysAssert(Where - TempAnsiString.Buffer == TempAnsiString.MaximumLength);
|
|
|
|
KerbErr = KerbStringToUnicodeString(
|
|
String,
|
|
&TempAnsiString
|
|
);
|
|
|
|
SafeAllocaFree( TempAnsiString.Buffer );
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertPrincipalNameToFullServiceString
|
|
//
|
|
// Synopsis: Converts a KERB_PRINCIPAL_NAME to a unicode string with
|
|
// a realm name
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbConvertPrincipalNameToFullServiceString(
|
|
OUT PUNICODE_STRING String,
|
|
IN PKERB_PRINCIPAL_NAME PrincipalName,
|
|
IN KERB_REALM RealmName
|
|
)
|
|
{
|
|
KERBERR KerbErr;
|
|
STRING TempAnsiString;
|
|
ULONG StringLength = 0;
|
|
ULONG NameParts = 0;
|
|
ULONG NameLength;
|
|
ULONG Index;
|
|
PCHAR Where;
|
|
PKERB_PRINCIPAL_NAME_ELEM NameElements[MAX_NAME_ELEMENTS+1];
|
|
|
|
NameElements[NameParts] = PrincipalName->name_string;
|
|
while (NameElements[NameParts] != NULL)
|
|
{
|
|
//
|
|
// add in a separator plus the length of the element
|
|
//
|
|
|
|
StringLength += lstrlenA(NameElements[NameParts]->value) + 1;
|
|
NameElements[NameParts+1] = NameElements[NameParts]->next;
|
|
NameParts++;
|
|
if (NameParts >= MAX_NAME_ELEMENTS)
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Too many name parts: %d\n",NameParts));
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure there is at least one name part
|
|
//
|
|
|
|
if (NameParts == 0)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Add in space for the "@" and the realm
|
|
//
|
|
|
|
StringLength += lstrlenA(RealmName) + 1;
|
|
|
|
//
|
|
// Test for overflow on USHORT lengths
|
|
//
|
|
|
|
if ( (StringLength - sizeof(CHAR)) > KERB_MAX_STRING )
|
|
{
|
|
return (KRB_ERR_GENERIC);
|
|
}
|
|
|
|
//
|
|
// Now build the name, backwards to front, with '\\' separators
|
|
//
|
|
|
|
TempAnsiString.Length = (USHORT) StringLength - sizeof(CHAR);
|
|
TempAnsiString.MaximumLength = (USHORT) StringLength;
|
|
SafeAllocaAllocate( TempAnsiString.Buffer, StringLength );
|
|
if (TempAnsiString.Buffer == NULL)
|
|
{
|
|
return(KRB_ERR_GENERIC);
|
|
}
|
|
|
|
Where = TempAnsiString.Buffer;
|
|
for (Index = 0; Index < NameParts; Index++ )
|
|
{
|
|
NameLength = lstrlenA(NameElements[Index]->value);
|
|
RtlCopyMemory(
|
|
Where,
|
|
NameElements[Index]->value,
|
|
NameLength
|
|
);
|
|
|
|
Where += NameLength;
|
|
|
|
//
|
|
// Add either a separating '\' or a trailing '\0'
|
|
//
|
|
|
|
if (Index != NameParts - 1)
|
|
{
|
|
*Where = '/';
|
|
}
|
|
else
|
|
{
|
|
*Where = '@';
|
|
}
|
|
Where++;
|
|
}
|
|
|
|
NameLength = lstrlenA(RealmName);
|
|
RtlCopyMemory(
|
|
Where,
|
|
RealmName,
|
|
NameLength
|
|
);
|
|
|
|
Where += NameLength;
|
|
|
|
//
|
|
// Add either a trailing '\0'
|
|
//
|
|
|
|
*Where = '\0';
|
|
Where++;
|
|
|
|
DsysAssert(Where - TempAnsiString.Buffer == TempAnsiString.MaximumLength);
|
|
|
|
KerbErr = KerbStringToUnicodeString(
|
|
String,
|
|
&TempAnsiString
|
|
);
|
|
|
|
SafeAllocaFree( TempAnsiString.Buffer );
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildKeySalt
|
|
//
|
|
// Synopsis: Combines a service name and domain name to make a key salt.
|
|
// For machine account it is DOMAINNAMEhostmachinenamedomainname
|
|
// For users it is DOMAINNAMEusername
|
|
// For trusted domains it is DOMAINNAMEkrbtgtservicename
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: DomainName - Domain name of service
|
|
// Servicename - Name of service
|
|
// AccountType - Type of account, which changes the salt
|
|
// KeySalt - Receives the key salt
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_NONE or KRB_ERR_GENERIC
|
|
//
|
|
// Notes: This is dependent on our DS naming conventions
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbBuildKeySalt(
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING ServiceName,
|
|
IN KERB_ACCOUNT_TYPE AccountType,
|
|
OUT PUNICODE_STRING KeySalt
|
|
)
|
|
{
|
|
PUCHAR Where;
|
|
ULONG FinalLength;
|
|
ULONG ulLength = 0;
|
|
USHORT DeadSpace = 0;
|
|
USHORT Index;
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
|
|
DsysAssert(KeySalt);
|
|
|
|
KeySalt->Buffer = NULL;
|
|
|
|
//
|
|
// If there is no domain name, this is a UPN so build a UPN salt.
|
|
//
|
|
|
|
if (DomainName->Length == 0)
|
|
{
|
|
return(KerbBuildKeySaltFromUpn(
|
|
ServiceName,
|
|
KeySalt ));
|
|
}
|
|
|
|
FinalLength = (ULONG)DomainName->Length +
|
|
(ULONG)ServiceName->Length;
|
|
|
|
//
|
|
// Add in any fixed strings, such as "host" for machines or "krbtgt" for
|
|
// interdomain accounts
|
|
//
|
|
|
|
if (AccountType == MachineAccount)
|
|
{
|
|
//
|
|
// Check to see if the name is already a "host/..." name. If so,
|
|
// we don't need to do this work.
|
|
//
|
|
|
|
if ((ServiceName->Length > sizeof(KERB_HOST_STRING) &&
|
|
(_wcsnicmp(
|
|
ServiceName->Buffer,
|
|
KERB_HOST_STRING,
|
|
(sizeof(KERB_HOST_STRING) - sizeof(WCHAR)) / sizeof(WCHAR)) == 0) &&
|
|
(ServiceName->Buffer[(sizeof(KERB_HOST_STRING) - sizeof(WCHAR)) / sizeof(WCHAR)] == L'/')))
|
|
{
|
|
AccountType = UserAccount;
|
|
}
|
|
else
|
|
{
|
|
FinalLength += sizeof(KERB_HOST_STRING) - sizeof(WCHAR);
|
|
|
|
//
|
|
// Add in the rest of the DNS name of the principal
|
|
// as well
|
|
//
|
|
|
|
FinalLength += DomainName->Length + sizeof(WCHAR);
|
|
}
|
|
}
|
|
else if (AccountType == DomainTrustAccount)
|
|
{
|
|
FinalLength += sizeof(KDC_PRINCIPAL_NAME) - sizeof(WCHAR);
|
|
}
|
|
else if (AccountType == UnknownAccount)
|
|
{
|
|
for (Index = 0; Index < ServiceName->Length/ sizeof(WCHAR) ; Index++ )
|
|
{
|
|
if (ServiceName->Buffer[Index] == L'/')
|
|
{
|
|
DeadSpace += sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
FinalLength = FinalLength - DeadSpace;
|
|
}
|
|
|
|
//
|
|
// Detect and reject overflows
|
|
//
|
|
|
|
if (FinalLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
KeySalt->Length = 0;
|
|
KeySalt->MaximumLength = (USHORT) FinalLength + sizeof(WCHAR);
|
|
KeySalt->Buffer = (LPWSTR) MIDL_user_allocate(KeySalt->MaximumLength);
|
|
|
|
if (KeySalt->Buffer == NULL)
|
|
{
|
|
return KRB_ERR_GENERIC;
|
|
}
|
|
|
|
Where = (PUCHAR) KeySalt->Buffer;
|
|
|
|
RtlCopyMemory(
|
|
KeySalt->Buffer,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
// KeySalt->Length = KeySalt->Length + DomainName->Length;
|
|
ulLength = (ULONG)KeySalt->Length + (ULONG)DomainName->Length;
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
KeySalt->Length = (USHORT)ulLength;
|
|
|
|
Where += DomainName->Length;
|
|
|
|
//
|
|
// Add in any fixed strings, such as "host" for machines or "krbtgt" for
|
|
// interdomain accounts
|
|
//
|
|
|
|
if (AccountType == MachineAccount)
|
|
{
|
|
USHORT DontCopyChars = 0;
|
|
UNICODE_STRING LowerCase = {0};
|
|
NTSTATUS Status;
|
|
|
|
ulLength = 0;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
KERB_HOST_STRING,
|
|
sizeof(KERB_HOST_STRING) - sizeof(WCHAR)
|
|
);
|
|
Where += sizeof(KERB_HOST_STRING) - sizeof(WCHAR);
|
|
|
|
//
|
|
// The service name may have a '$' at the end - if so, don't copy
|
|
// it.
|
|
//
|
|
|
|
if ((ServiceName->Length >= sizeof(WCHAR)) &&
|
|
(ServiceName->Buffer[-1 + ServiceName->Length / sizeof(WCHAR)] == L'$'))
|
|
{
|
|
DontCopyChars = 1;
|
|
}
|
|
|
|
LowerCase.Buffer = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length - sizeof(WCHAR) * DontCopyChars
|
|
);
|
|
|
|
Where += ServiceName->Length - sizeof(WCHAR) * DontCopyChars;
|
|
// LowerCase.Length += ServiceName->Length - sizeof(WCHAR) * DontCopyChars;
|
|
ulLength += ServiceName->Length - sizeof(WCHAR) * DontCopyChars;
|
|
|
|
//
|
|
// add in the rest of the DNS name of the server
|
|
//
|
|
|
|
*(LPWSTR) Where = L'.';
|
|
Where += sizeof(WCHAR);
|
|
// LowerCase.Length += sizeof(WCHAR);
|
|
ulLength += sizeof(WCHAR);
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
DomainName->Buffer,
|
|
DomainName->Length
|
|
);
|
|
Where += DomainName->Length;
|
|
// LowerCase.Length = LowerCase.Length + DomainName->Length;
|
|
ulLength = ulLength + DomainName->Length;
|
|
|
|
// Test for overflow on USHORT lengths
|
|
if (ulLength > KERB_MAX_UNICODE_STRING )
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LowerCase.Length = (USHORT) ulLength;
|
|
LowerCase.MaximumLength = LowerCase.Length;
|
|
|
|
Status = RtlDowncaseUnicodeString(
|
|
&LowerCase,
|
|
&LowerCase,
|
|
FALSE
|
|
);
|
|
|
|
DsysAssert(NT_SUCCESS(Status));
|
|
}
|
|
else if (AccountType == DomainTrustAccount)
|
|
{
|
|
ULONG DontCopyChars = 0;
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
KDC_PRINCIPAL_NAME,
|
|
sizeof(KDC_PRINCIPAL_NAME) - sizeof(WCHAR)
|
|
);
|
|
Where += sizeof(KDC_PRINCIPAL_NAME) - sizeof(WCHAR);
|
|
|
|
//
|
|
// The service name may have a '$' at the end - if so, don't copy
|
|
// it.
|
|
//
|
|
|
|
if ((ServiceName->Length >= sizeof(WCHAR)) &&
|
|
(ServiceName->Buffer[-1 + ServiceName->Length / sizeof(WCHAR)] == L'$'))
|
|
{
|
|
DontCopyChars = 1;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Where,
|
|
ServiceName->Buffer,
|
|
ServiceName->Length - sizeof(WCHAR) * DontCopyChars
|
|
);
|
|
|
|
Where += ServiceName->Length - sizeof(WCHAR) * DontCopyChars;
|
|
}
|
|
else if (AccountType == UnknownAccount)
|
|
{
|
|
//
|
|
// Pull out an '/' from unknown accounts
|
|
//
|
|
|
|
for (Index = 0; Index < ServiceName->Length / sizeof(WCHAR) ; Index++)
|
|
{
|
|
if (ServiceName->Buffer[Index] != L'/')
|
|
{
|
|
*((LPWSTR) Where) = ServiceName->Buffer[Index];
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (Index = 0; Index < ServiceName->Length / sizeof(WCHAR); Index++ )
|
|
{
|
|
if (ServiceName->Buffer[Index] != L'/')
|
|
{
|
|
*((LPWSTR)Where) = ServiceName->Buffer[Index];
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for USHORT overflow on length
|
|
|
|
ulLength = (ULONG)(Where - (PUCHAR) KeySalt->Buffer);
|
|
|
|
if (ulLength > KERB_MAX_UNICODE_STRING )
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
KeySalt->Length = (USHORT) ulLength;
|
|
*(LPWSTR) Where = L'\0';
|
|
KerbErr = KDC_ERR_NONE;
|
|
|
|
Cleanup:
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
if (KeySalt->Buffer)
|
|
{
|
|
MIDL_user_free(KeySalt->Buffer);
|
|
KeySalt->Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildKeySaltFromUpn
|
|
//
|
|
// Synopsis: Creaes salt from a UPN
|
|
//
|
|
// Effects:
|
|
// For users it is DOMAINNAMEusername
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
KERBERR
|
|
KerbBuildKeySaltFromUpn(
|
|
IN PUNICODE_STRING Upn,
|
|
OUT PUNICODE_STRING Salt
|
|
)
|
|
{
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
UNICODE_STRING RealUpn = {0};
|
|
UNICODE_STRING RealmName = {0};
|
|
UNICODE_STRING LocalSalt = {0};
|
|
ULONG Index;
|
|
ULONG ulLength = 0;
|
|
|
|
//
|
|
// If there is an "@" in UPN, strip it out & use the dns domain name
|
|
//
|
|
|
|
RealUpn = *Upn;
|
|
for (Index = 0; Index < RealUpn.Length/sizeof(WCHAR) ; Index++ )
|
|
{
|
|
if (RealUpn.Buffer[Index] == L'@')
|
|
{
|
|
RealUpn.Length = (USHORT) (Index * sizeof(WCHAR));
|
|
RealmName.Buffer = &RealUpn.Buffer[Index+1];
|
|
RealmName.Length = Upn->Length - RealUpn.Length - sizeof(WCHAR);
|
|
RealmName.MaximumLength = RealmName.Length;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RealmName.Length == 0)
|
|
{
|
|
KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create the salt. It starts off with the domain name & then has the
|
|
// UPN without any of the / pieces
|
|
//
|
|
|
|
ulLength = (ULONG)RealmName.Length + (ULONG)RealUpn.Length;
|
|
|
|
// Test for overflow on USHORT lengths
|
|
if (ulLength > KERB_MAX_UNICODE_STRING )
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalSalt.MaximumLength = (USHORT)ulLength;
|
|
LocalSalt.Length = 0;
|
|
LocalSalt.Buffer = (LPWSTR) MIDL_user_allocate(LocalSalt.MaximumLength);
|
|
if (LocalSalt.Buffer == NULL)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
LocalSalt.Buffer,
|
|
RealmName.Buffer,
|
|
RealmName.Length
|
|
);
|
|
// LocalSalt.Length = LocalSalt.Length + RealmName.Length;
|
|
ulLength = RealmName.Length;
|
|
|
|
//
|
|
// Add in the real upn but leave out any "/" marks
|
|
//
|
|
|
|
for (Index = 0; Index < RealUpn.Length/sizeof(WCHAR) ; Index++ )
|
|
{
|
|
if (RealUpn.Buffer[Index] != L'/')
|
|
{
|
|
LocalSalt.Buffer[ulLength / sizeof(WCHAR)] = RealUpn.Buffer[Index];
|
|
ulLength += sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
// Test for overflow on USHORT lengths - no need already done on MAX size possible
|
|
LocalSalt.Length = (USHORT)ulLength;
|
|
|
|
//
|
|
// We have to lowercase the username for users
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
CharLowerBuff(&(LocalSalt.Buffer[RealmName.Length/sizeof(WCHAR)]), RealUpn.Length/sizeof(WCHAR));
|
|
#endif // WIN32_CHICAGO
|
|
|
|
*Salt = LocalSalt; // give memory to caller
|
|
LocalSalt.Buffer = NULL;
|
|
|
|
Cleanup:
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertSidToString
|
|
//
|
|
// Synopsis: Converts a sid to a string using RtlConvertSidToUnicodeString
|
|
// but with a different allocator.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbConvertSidToString(
|
|
IN PSID Sid,
|
|
OUT PUNICODE_STRING String,
|
|
IN BOOLEAN AllocateDestination
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
WCHAR Buffer[256];
|
|
UNICODE_STRING TempString;
|
|
|
|
if (AllocateDestination)
|
|
{
|
|
TempString.Length = 0;
|
|
TempString.MaximumLength = sizeof(Buffer);
|
|
TempString.Buffer = Buffer;
|
|
}
|
|
else
|
|
{
|
|
TempString = *String;
|
|
}
|
|
|
|
Status = RtlConvertSidToUnicodeString(
|
|
&TempString,
|
|
Sid,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (!AllocateDestination)
|
|
{
|
|
*String = TempString;
|
|
}
|
|
else
|
|
{
|
|
String->Buffer = (LPWSTR) MIDL_user_allocate(TempString.Length+sizeof(WCHAR));
|
|
if (String->Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
String->Length = TempString.Length;
|
|
String->MaximumLength = TempString.Length+sizeof(WCHAR);
|
|
RtlCopyMemory(
|
|
String->Buffer,
|
|
TempString.Buffer,
|
|
TempString.Length
|
|
);
|
|
String->Buffer[TempString.Length / sizeof(WCHAR)] = L'\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbConvertStringToSid
|
|
//
|
|
// Synopsis: Converts back a sid from KerbConvertSidToString. If the
|
|
// string is malformed, it will return STATUS_INVALID_PARAMTER
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbConvertStringToSid(
|
|
IN PUNICODE_STRING String,
|
|
OUT PSID * Sid
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
WCHAR Buffer[256];
|
|
PSID SidT;
|
|
|
|
*Sid = NULL;
|
|
|
|
if ( String->Length + sizeof( WCHAR ) <= sizeof( Buffer )) {
|
|
|
|
RtlCopyMemory( Buffer, String->Buffer, String->Length );
|
|
|
|
} else {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Buffer[String->Length / sizeof( WCHAR )] = L'\0';
|
|
|
|
if ( ConvertStringSidToSidW(
|
|
Buffer,
|
|
&SidT )) {
|
|
|
|
Status = KerbDuplicateSid(
|
|
Sid,
|
|
SidT
|
|
);
|
|
|
|
LocalFree( SidT );
|
|
|
|
} else {
|
|
|
|
switch( GetLastError()) {
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
|
|
case ERROR_INVALID_SID:
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
default:
|
|
DsysAssert( FALSE ); // add mapping for the error code
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildAltSecId
|
|
//
|
|
// Synopsis: Builds the name for the alt-sec-id field lookup
|
|
//
|
|
// Effects: Converts a principal name from name1 name2 name3 to
|
|
// "kerberos:name1/name2/name3@realm"
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
KERBERR
|
|
KerbBuildAltSecId(
|
|
OUT PUNICODE_STRING AlternateName,
|
|
IN PKERB_INTERNAL_NAME PrincipalName,
|
|
IN OPTIONAL PKERB_REALM Realm,
|
|
IN OPTIONAL PUNICODE_STRING UnicodeRealm
|
|
)
|
|
{
|
|
ULONG StringLength = sizeof(KERB_NAME_PREFIX) - sizeof(WCHAR);
|
|
ULONG Index;
|
|
UNICODE_STRING TempString = {0};
|
|
UNICODE_STRING LocalRealm = {0};
|
|
KERBERR KerbErr = KDC_ERR_NONE;
|
|
|
|
*AlternateName = TempString;
|
|
|
|
if (ARGUMENT_PRESENT(UnicodeRealm))
|
|
{
|
|
LocalRealm = *UnicodeRealm;
|
|
}
|
|
else if (ARGUMENT_PRESENT(Realm))
|
|
{
|
|
KerbErr = KerbConvertRealmToUnicodeString(
|
|
&LocalRealm,
|
|
Realm
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (PrincipalName->NameCount == 0)
|
|
{
|
|
KerbErr = KDC_ERR_C_PRINCIPAL_UNKNOWN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add in the size of all the components of the name plus a separtor
|
|
// or a null terminator.
|
|
//
|
|
|
|
for (Index = 0; Index < PrincipalName->NameCount; Index++ )
|
|
{
|
|
StringLength += PrincipalName->Names[Index].Length + sizeof(WCHAR);
|
|
}
|
|
|
|
if (LocalRealm.Length != 0)
|
|
{
|
|
StringLength += sizeof(WCHAR) + // for @
|
|
LocalRealm.Length;
|
|
}
|
|
|
|
//
|
|
// Now build the name, front to back (differently from KerbConvertPrincipalNameToString()
|
|
//
|
|
|
|
// Test for overflow on USHORT lengths
|
|
if (StringLength > KERB_MAX_UNICODE_STRING ) // actually more accurate to use (StringLength - 1) > MAX
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TempString.Buffer = (LPWSTR) MIDL_user_allocate(StringLength);
|
|
if (TempString.Buffer == NULL)
|
|
{
|
|
KerbErr = KRB_ERR_GENERIC;
|
|
goto Cleanup;
|
|
}
|
|
TempString.Length = 0;
|
|
TempString.MaximumLength = (USHORT) StringLength;
|
|
|
|
//
|
|
// Now start appending the various portions to the string - max length already tested for overflow
|
|
//
|
|
|
|
RtlAppendUnicodeStringToString(
|
|
&TempString,
|
|
&KerbNamePrefix
|
|
);
|
|
|
|
for (Index = 0; Index < PrincipalName->NameCount ; Index++ )
|
|
{
|
|
if (Index != 0)
|
|
{
|
|
RtlAppendUnicodeStringToString(
|
|
&TempString,
|
|
&KerbNameSeparator
|
|
);
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(
|
|
&TempString,
|
|
&PrincipalName->Names[Index]
|
|
);
|
|
}
|
|
|
|
if (LocalRealm.Length != 0)
|
|
{
|
|
RtlAppendUnicodeStringToString(
|
|
&TempString,
|
|
&KerbDomainSeparator
|
|
);
|
|
RtlAppendUnicodeStringToString(
|
|
&TempString,
|
|
&LocalRealm
|
|
);
|
|
}
|
|
|
|
*AlternateName = TempString;
|
|
|
|
Cleanup:
|
|
|
|
if (!ARGUMENT_PRESENT(UnicodeRealm))
|
|
{
|
|
KerbFreeString(&LocalRealm);
|
|
}
|
|
|
|
return(KerbErr);
|
|
}
|
|
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetPrincipalNameFromCertificate
|
|
//
|
|
// Synopsis: Derives the principal name from a certificate
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbGetPrincipalNameFromCertificate(
|
|
IN PCCERT_CONTEXT ClientCert,
|
|
OUT PUNICODE_STRING String
|
|
)
|
|
{
|
|
UNICODE_STRING NameString = {0};
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ExtensionIndex = 0;
|
|
BOOL fAssigned = FALSE;
|
|
PCERT_ALT_NAME_INFO AltName=NULL;
|
|
PCERT_NAME_VALUE PrincipalNameBlob = NULL;
|
|
LPWSTR CertNameString = NULL;
|
|
|
|
CRYPT_DECODE_PARA DecodePara = {sizeof(CRYPT_DECODE_PARA),
|
|
MIDL_user_allocate,
|
|
MIDL_user_free };
|
|
|
|
|
|
//
|
|
// Get the client name from the cert
|
|
//
|
|
|
|
// See if cert has UPN in AltSubjectName->otherName
|
|
for(ExtensionIndex = 0;
|
|
ExtensionIndex < ClientCert->pCertInfo->cExtension;
|
|
ExtensionIndex++)
|
|
{
|
|
if(strcmp(ClientCert->pCertInfo->rgExtension[ExtensionIndex].pszObjId,
|
|
szOID_SUBJECT_ALT_NAME2) == 0)
|
|
{
|
|
DWORD AltNameStructSize = 0;
|
|
ULONG CertAltNameIndex = 0;
|
|
if(CryptDecodeObjectEx(ClientCert->dwCertEncodingType,
|
|
X509_ALTERNATE_NAME,
|
|
ClientCert->pCertInfo->rgExtension[ExtensionIndex].Value.pbData,
|
|
ClientCert->pCertInfo->rgExtension[ExtensionIndex].Value.cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG,
|
|
&DecodePara,
|
|
(PVOID)&AltName,
|
|
&AltNameStructSize))
|
|
{
|
|
|
|
for(CertAltNameIndex = 0; CertAltNameIndex < AltName->cAltEntry; CertAltNameIndex++)
|
|
{
|
|
PCERT_ALT_NAME_ENTRY AltNameEntry = &AltName->rgAltEntry[CertAltNameIndex];
|
|
if((CERT_ALT_NAME_OTHER_NAME == AltNameEntry->dwAltNameChoice) &&
|
|
(NULL != AltNameEntry->pOtherName) &&
|
|
(0 == strcmp(szOID_NT_PRINCIPAL_NAME, AltNameEntry->pOtherName->pszObjId)))
|
|
{
|
|
DWORD PrincipalNameBlobSize = 0;
|
|
|
|
// We found a UPN!
|
|
if(CryptDecodeObjectEx(ClientCert->dwCertEncodingType,
|
|
X509_UNICODE_ANY_STRING,
|
|
AltNameEntry->pOtherName->Value.pbData,
|
|
AltNameEntry->pOtherName->Value.cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG,
|
|
&DecodePara,
|
|
(PVOID)&PrincipalNameBlob,
|
|
&PrincipalNameBlobSize))
|
|
{
|
|
|
|
fAssigned = SafeRtlInitUnicodeString(&NameString,
|
|
(LPCWSTR)PrincipalNameBlob->Value.pbData);
|
|
if (fAssigned == FALSE)
|
|
{
|
|
Status = STATUS_NAME_TOO_LONG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if(NameString.Length)
|
|
{
|
|
break;
|
|
}
|
|
|
|
MIDL_user_free(PrincipalNameBlob);
|
|
PrincipalNameBlob = NULL;
|
|
}
|
|
}
|
|
}
|
|
if(NameString.Length)
|
|
{
|
|
break;
|
|
}
|
|
MIDL_user_free(AltName);
|
|
AltName = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(0 != NameString.Length)
|
|
{
|
|
Status = KerbDuplicateString(String, &NameString);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to normalize name "));
|
|
// KerbPrintKdcName(DEB_ERROR,ClientName);
|
|
goto Cleanup;
|
|
}
|
|
|
|
D_DebugLog((DEB_TRACE,"UPN from certificate is %wZ\n",&NameString));
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_PKINIT_NAME_MISMATCH;
|
|
D_DebugLog((DEB_ERROR,"No valid name in Sclogon certificate\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if(PrincipalNameBlob)
|
|
{
|
|
MIDL_user_free(PrincipalNameBlob);
|
|
}
|
|
if(AltName)
|
|
{
|
|
MIDL_user_free(AltName);
|
|
}
|
|
if(CertNameString)
|
|
{
|
|
MIDL_user_free(CertNameString);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
SafeRtlInitString(
|
|
OUT PSTRING DestinationString,
|
|
IN PCSZ SourceString OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The RtlInitString function initializes an NT counted string.
|
|
The DestinationString is initialized to point to the SourceString
|
|
and the Length and MaximumLength fields of DestinationString are
|
|
initialized to the length of the SourceString, which is zero if
|
|
SourceString is not specified.
|
|
|
|
Arguments:
|
|
|
|
DestinationString - Pointer to the counted string to initialize
|
|
|
|
SourceString - Optional pointer to a null terminated string that
|
|
the counted string is to point to.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if assignment took place, FALSE on error - such as USHORT overflow
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Length;
|
|
|
|
DestinationString->Buffer = (PCHAR)SourceString;
|
|
if (ARGUMENT_PRESENT( SourceString )) {
|
|
Length = (ULONG)strlen(SourceString);
|
|
if (Length > KERB_MAX_STRING)
|
|
{
|
|
return FALSE; // length will not fit into USHORT value
|
|
}
|
|
DestinationString->Length = (USHORT)Length;
|
|
DestinationString->MaximumLength = (USHORT)(Length+1);
|
|
}
|
|
else {
|
|
DestinationString->Length = 0;
|
|
DestinationString->MaximumLength = 0;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SafeRtlInitUnicodeString(
|
|
OUT PUNICODE_STRING DestinationString,
|
|
IN PCWSTR SourceString OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The RtlInitUnicodeString function initializes an NT counted
|
|
unicode string. The DestinationString is initialized to point to
|
|
the SourceString and the Length and MaximumLength fields of
|
|
DestinationString are initialized to the length of the SourceString,
|
|
which is zero if SourceString is not specified.
|
|
|
|
Arguments:
|
|
|
|
DestinationString - Pointer to the counted string to initialize
|
|
|
|
SourceString - Optional pointer to a null terminated unicode string that
|
|
the counted string is to point to.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Length;
|
|
|
|
DestinationString->Buffer = (PWSTR)SourceString;
|
|
if (ARGUMENT_PRESENT( SourceString )) {
|
|
Length = (ULONG)wcslen( SourceString ) * sizeof( WCHAR );
|
|
ASSERT( Length <= KERB_MAX_UNICODE_STRING );
|
|
if (Length > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return FALSE; // length will not fit into USHORT value
|
|
}
|
|
DestinationString->Length = (USHORT)Length;
|
|
DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
|
|
}
|
|
else {
|
|
DestinationString->MaximumLength = 0;
|
|
DestinationString->Length = 0;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetCertificateHash
|
|
//
|
|
// Synopsis: Gets the SHA1 hash of a certificate.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbGetCertificateHash(
|
|
OUT LPBYTE pCertHash,
|
|
IN ULONG cbCertHash,
|
|
IN PCCERT_CONTEXT pCertContext
|
|
)
|
|
{
|
|
ULONG cbHash = cbCertHash;
|
|
|
|
if ( CertGetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
pCertHash,
|
|
&cbHash
|
|
) == FALSE )
|
|
{
|
|
return( STATUS_PKINIT_FAILURE );
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateUnicodeStringFromHash
|
|
//
|
|
// Synopsis: Creates a hash string from a hash blob
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbCreateUnicodeStringFromBlob(
|
|
IN PBYTE Blob,
|
|
IN ULONG BlobSize,
|
|
IN OUT PUNICODE_STRING String,
|
|
IN BOOLEAN ReverseOrder
|
|
)
|
|
{
|
|
LPWSTR out = NULL;
|
|
WCHAR buff[256];
|
|
PBYTE pb;
|
|
ULONG cb, length;
|
|
|
|
|
|
//
|
|
// Don't overflow USHORT.
|
|
//
|
|
length = ((2 * BlobSize + 1) * sizeof(WCHAR));
|
|
if ( length > KERB_MAX_UNICODE_STRING )
|
|
{
|
|
return(STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
|
|
out = (LPWSTR) MIDL_user_allocate(length);
|
|
if ( out == NULL )
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if ( ReverseOrder )
|
|
{
|
|
pb = Blob;
|
|
for ( cb = BlobSize; cb > 0; cb--, pb++ )
|
|
{
|
|
swprintf(buff, L"%02X", *pb);
|
|
wcscat(out, buff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pb = Blob + ( BlobSize - 1 );
|
|
for ( cb = BlobSize; cb > 0; cb--, pb-- )
|
|
{
|
|
swprintf(buff, L"%02X", *pb);
|
|
wcscat(out, buff);
|
|
}
|
|
}
|
|
|
|
|
|
out[(2*BlobSize)] = L'\0';
|
|
|
|
RtlInitUnicodeString(
|
|
String,
|
|
out
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetCertificateString
|
|
//
|
|
// Synopsis: Gets the specified name type from a cert context
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbGetCertificateString(
|
|
IN PCCERT_CONTEXT Certificate,
|
|
IN DWORD Type,
|
|
IN DWORD Flags,
|
|
IN OUT PUNICODE_STRING CertString
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LPWSTR wszRet = NULL;
|
|
DWORD dwChars = 0;
|
|
ULONG len;
|
|
|
|
dwChars = CertGetNameStringW(
|
|
Certificate,
|
|
Type,
|
|
Flags,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// If dwChars is 1, then it is an empty string. No reason
|
|
// to do anything more, as Subject wasn't found.
|
|
//
|
|
if (dwChars == 1)
|
|
{
|
|
DebugLog((DEB_ERROR, "Missing %x from cert %p\n", Type, Certificate));
|
|
DsysAssert(FALSE);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
len = (dwChars + 1) * sizeof(WCHAR);
|
|
if (len > KERB_MAX_UNICODE_STRING)
|
|
{
|
|
return(STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
|
|
wszRet = (LPWSTR) MIDL_user_allocate(len);
|
|
if ( NULL == wszRet)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
dwChars = CertGetNameStringW(
|
|
Certificate,
|
|
Type,
|
|
Flags,
|
|
NULL,
|
|
wszRet,
|
|
dwChars
|
|
);
|
|
|
|
//
|
|
// Huh?
|
|
//
|
|
if (dwChars == 1)
|
|
{
|
|
DebugLog((DEB_ERROR, "Missing %x from cert %p\n", Type, Certificate));
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
CertString,
|
|
wszRet
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if (!NT_SUCCESS( Status ))
|
|
{
|
|
if ( wszRet )
|
|
{
|
|
MIDL_user_free( wszRet );
|
|
}
|
|
|
|
ZeroMemory(
|
|
CertString,
|
|
sizeof(UNICODE_STRING)
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetCertificateHashString
|
|
//
|
|
// Synopsis: Creates a string from a hash.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//----
|
|
NTSTATUS
|
|
KerbGetCertificateHashString(
|
|
IN PCCERT_CONTEXT Certificate,
|
|
IN OUT PUNICODE_STRING HashString
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG HashSize = SHA1DIGESTLEN;
|
|
BYTE Hash[SHA1DIGESTLEN];
|
|
|
|
|
|
Status = KerbGetCertificateHash(
|
|
Hash,
|
|
HashSize,
|
|
Certificate
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status ))
|
|
{
|
|
DebugLog((DEB_ERROR, "KerbGetCertificateHash failed %x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbCreateUnicodeStringFromBlob(
|
|
Hash,
|
|
HashSize,
|
|
HashString,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status ))
|
|
{
|
|
DebugLog((DEB_ERROR, "KerbCreateUnicodeStringFromHash failed %x\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetCertificateIssuer
|
|
//
|
|
// Synopsis: Gets the simply display name from a certificate.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//----
|
|
NTSTATUS
|
|
KerbGetCertificateIssuer(
|
|
IN PCCERT_CONTEXT Certificate,
|
|
IN OUT PUNICODE_STRING Issuer
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Status = KerbGetCertificateString(
|
|
Certificate,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
CERT_NAME_ISSUER_FLAG,
|
|
Issuer
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|