|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: kerbutil.cxx
//
// Contents: Utility functions for Kerberos package
//
//
// History: 16-April-1996 Created MikeSw
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#ifndef WIN32_CHICAGO
#include <nb30.h>
#else // WIN32_CHICAGO
#define NCBNAMSZ 16
#endif // WIN32_CHICAGO
#include <userapi.h> // for gss support routines
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif
GUID GUID_NULL = {0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
//+-------------------------------------------------------------------------
//
// Function: KerbSplitFullServiceName
//
// Synopsis: Splits a full service name into a domain name and a
// service name. The output strings point into the input
// string's buffer and should not be freed
//
// Effects:
//
// Arguments: FullServiceName - The full service name in a domain\service
// format
// DomainName - Receives the domain portion of the full service
// name in a 'domain' format
// ServiceName - Receives the service name in a 'service' format
//
// Requires:
//
// Returns: STATUS_INVALID_PARAMETER if the service name does not
// match the correct format.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbSplitFullServiceName( IN PUNICODE_STRING FullServiceName, OUT PUNICODE_STRING DomainName, OUT PUNICODE_STRING ServiceName ) { UNICODE_STRING TempDomainName; UNICODE_STRING TempServiceName;
TempDomainName = *FullServiceName;
//
// Find the split between domain and service name
//
TempDomainName.Length = 0; while ((TempDomainName.Length < FullServiceName->Length) && (TempDomainName.Buffer[TempDomainName.Length/sizeof(WCHAR)] != L'\\') && (TempDomainName.Buffer[TempDomainName.Length/sizeof(WCHAR)] != L'@') ) { TempDomainName.Length += sizeof(WCHAR); }
//
// In this case, there is no separator
//
if (TempDomainName.Length == FullServiceName->Length) { *ServiceName = *FullServiceName; EMPTY_UNICODE_STRING( DomainName ); return(STATUS_SUCCESS); }
//
// If the separator is an "@" switch the doman & service portion
//
if (TempDomainName.Buffer[TempDomainName.Length/sizeof(WCHAR)] == L'@') { TempServiceName = TempDomainName;
TempDomainName.Buffer = TempServiceName.Buffer + TempServiceName.Length/sizeof(WCHAR) + 1; TempServiceName.MaximumLength = TempServiceName.Length;
//
// The Domain name is everything else
//
TempDomainName.Length = FullServiceName->Length - TempServiceName.Length - sizeof(WCHAR); TempDomainName.MaximumLength = TempDomainName.Length; } else { TempServiceName.Buffer = TempDomainName.Buffer + TempDomainName.Length/sizeof(WCHAR) + 1; TempDomainName.MaximumLength = TempDomainName.Length;
//
// The service name is everything else
//
TempServiceName.Length = FullServiceName->Length - TempDomainName.Length - sizeof(WCHAR); TempServiceName.MaximumLength = TempServiceName.Length;
}
//
// We could be pointing at the end of the buffer. Set this to NULL
//
if (TempServiceName.Length == 0) { TempServiceName.Buffer = NULL; }
if (TempDomainName.Length == 0) { TempDomainName.Buffer = NULL; }
*ServiceName = TempServiceName; *DomainName = TempDomainName;
return(STATUS_SUCCESS);
}
//+-------------------------------------------------------------------------
//
// Function: KerbAllocateNonce
//
// Synopsis: Allocates a locally unique number
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns: the nonce
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG KerbAllocateNonce( VOID ) { LUID TempLuid; TimeStamp CurrentTime;
NtAllocateLocallyUniqueId(&TempLuid); GetSystemTimeAsFileTime((PFILETIME) &CurrentTime); #ifndef WIN32_CHICAGO
return(0x7fffffff & (TempLuid.LowPart ^ TempLuid.HighPart ^ CurrentTime.LowPart ^ CurrentTime.HighPart)); #else // WIN32_CHICAGO
return(0x7fffffff & ((ULONG)(TempLuid.LowPart ^ TempLuid.HighPart ^ CurrentTime))); #endif // WIN32_CHICAGO
}
//+-------------------------------------------------------------------------
//
// Function: KerbAllocate
//
// Synopsis: Allocate memory in either lsa mode or user mode
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
PVOID KerbAllocate( IN SIZE_T BufferSize ) { PVOID pBuffer = NULL; if (KerberosState == KerberosLsaMode) { pBuffer = LsaFunctions->AllocateLsaHeap((ULONG) BufferSize); // Lsa helper routine zeroes the memory.
} else { DsysAssert(KerberosState == KerberosUserMode); pBuffer = LocalAlloc(0,BufferSize); if (pBuffer) { RtlZeroMemory (pBuffer, BufferSize); } }
return pBuffer; }
//+-------------------------------------------------------------------------
//
// Function: KerbFree
//
// Synopsis: Free memory in either lsa mode or user mode
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbFree( IN PVOID Buffer ) { if (ARGUMENT_PRESENT(Buffer)) { if (KerberosState == KerberosLsaMode) { LsaFunctions->FreeLsaHeap(Buffer); } else { DsysAssert(KerberosState == KerberosUserMode); LocalFree(Buffer); } } }
//+-------------------------------------------------------------------------
//
// Function: KerbStringToUnicodeString()
//
// Synopsis: Takes a ansi string and (1) unicodes it, (2) copies it
//
// Effects:
//
// Arguments: pDest must be initialized unicode string
//
// Requires:
//
// Returns: Free .buffer using KerbFree()
//
// Notes:
//
//--------------------------------------------------------------------------
BOOLEAN KerbMbStringToUnicodeString(PUNICODE_STRING pDest, char * pszString) { int cbNewString = 0; USHORT cbOriginalString; BOOLEAN fRet = FALSE;
cbOriginalString = (USHORT) strlen(pszString) + 1;
cbNewString = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, pszString, cbOriginalString, NULL, 0 );
if ( cbNewString && ( cbNewString < KERB_MAX_UNICODE_STRING )) { pDest->Buffer = (PWSTR) KerbAllocate(cbNewString); if (NULL == pDest->Buffer) { return FALSE; }
if (MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, pszString, cbOriginalString, pDest->Buffer, cbNewString)) { pDest->Length = (USHORT) cbNewString; pDest->MaximumLength = (USHORT) cbNewString; fRet = TRUE; } } return fRet; }
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbWaitForEvent
//
// Synopsis: Wait up to Timeout seconds for EventName to be triggered.
//
// Effects:
//
// Arguments: EventName - Name of event to wait on
// Timeout - Timeout for event (in seconds).
//
// Requires:
//
// Returns: STATUS_SUCCESS - Indicates Event was set.
// STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
//
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbWaitForEvent( IN LPWSTR EventName, IN ULONG Timeout ) { NTSTATUS Status;
HANDLE EventHandle; OBJECT_ATTRIBUTES EventAttributes; UNICODE_STRING EventNameString; LARGE_INTEGER LocalTimeout;
//
// Create an event for us to wait on.
//
RtlInitUnicodeString( &EventNameString, EventName); InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL);
Status = NtCreateEvent( &EventHandle, SYNCHRONIZE, &EventAttributes, NotificationEvent, (BOOLEAN) FALSE // The event is initially not signaled
);
if ( !NT_SUCCESS(Status)) {
//
// If the event already exists, the server beat us to creating it.
// Just open it.
//
if( Status == STATUS_OBJECT_NAME_EXISTS || Status == STATUS_OBJECT_NAME_COLLISION ) {
Status = NtOpenEvent( &EventHandle, SYNCHRONIZE, &EventAttributes );
} if ( !NT_SUCCESS(Status)) { KdPrint(("[MSV1_0] OpenEvent failed %lx\n", Status )); return Status; } }
//
// Wait for NETLOGON to initialize. Wait a maximum of Timeout seconds.
//
LocalTimeout.QuadPart = ((LONGLONG)(Timeout)) * (-10000000); Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout); (VOID) NtClose( EventHandle );
if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) { if ( Status == STATUS_TIMEOUT ) { Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
} return Status; }
return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbWaitForKdc
//
// Synopsis: Wait up to Timeout seconds for the netlogon service to start.
//
// Effects:
//
// Arguments: Timeout - Timeout for netlogon (in seconds).
//
// Requires:
//
// Returns: STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
// STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbWaitForKdc( IN ULONG Timeout ) { NTSTATUS Status; ULONG NetStatus; SC_HANDLE ScManagerHandle = NULL; SC_HANDLE ServiceHandle = NULL; SERVICE_STATUS ServiceStatus; LPQUERY_SERVICE_CONFIG ServiceConfig; LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL; QUERY_SERVICE_CONFIG DummyServiceConfig; DWORD ServiceConfigSize; BOOLEAN AutoStart = FALSE;
//
// If the KDC service is currently running,
// skip the rest of the tests.
//
Status = KerbWaitForEvent( KDC_START_EVENT, 0 );
if ( NT_SUCCESS(Status) ) { KerbKdcStarted = TRUE; return Status; }
//
// Open a handle to the KDC Service.
//
ScManagerHandle = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
if (ScManagerHandle == NULL) { DebugLog((DEB_ERROR, " KerbWaitForKdc: OpenSCManager failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
ServiceHandle = OpenService( ScManagerHandle, SERVICE_KDC, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
if ( ServiceHandle == NULL ) { D_DebugLog((DEB_ERROR, "KerbWaitForKdc: OpenService failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// If the KDC service isn't configured to be automatically started
// by the service controller, don't bother waiting for it to start -
// just check to see if it is started.
//
// ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
// won't allow a null pointer, yet.
if ( QueryServiceConfig( ServiceHandle, &DummyServiceConfig, sizeof(DummyServiceConfig), &ServiceConfigSize )) {
ServiceConfig = &DummyServiceConfig;
} else {
NetStatus = GetLastError(); if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) { D_DebugLog((DEB_ERROR,"KerbWaitForKdc: QueryServiceConfig failed: " "%lu. %ws, line %d\n", NetStatus, THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
SafeAllocaAllocate(AllocServiceConfig, ServiceConfigSize);
ServiceConfig = AllocServiceConfig;
if ( AllocServiceConfig == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; }
if ( !QueryServiceConfig( ServiceHandle, ServiceConfig, ServiceConfigSize, &ServiceConfigSize )) {
D_DebugLog((DEB_ERROR, "KerbWaitForKdc: QueryServiceConfig " "failed again: %lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; } }
if ( ServiceConfig->dwStartType == SERVICE_AUTO_START ) {
AutoStart = TRUE; }
//
// Loop waiting for the KDC service to start.
//
for (;;) {
//
// Query the status of the KDC service.
//
if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
D_DebugLog((DEB_ERROR, "KerbWaitForKdc: QueryServiceStatus failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__ )); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// Return or continue waiting depending on the state of
// the KDC service.
//
switch( ServiceStatus.dwCurrentState) { case SERVICE_RUNNING: Status = STATUS_SUCCESS; goto Cleanup;
case SERVICE_STOPPED:
//
// If KDC failed to start,
// error out now. The caller has waited long enough to start.
//
if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){ #if DBG
D_DebugLog((DEB_ERROR, "KerbWaitForKdc: " "KDC service couldn't start: %lu %lx. %ws, line %d\n", ServiceStatus.dwWin32ExitCode, ServiceStatus.dwWin32ExitCode, THIS_FILE, __LINE__ )); if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) { D_DebugLog((DEB_ERROR, " Service specific error code: %lu %lx. %ws, line %d\n", ServiceStatus.dwServiceSpecificExitCode, ServiceStatus.dwServiceSpecificExitCode, THIS_FILE, __LINE__ )); } #endif // DBG
Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// If KDC has never been started on this boot,
// continue waiting for it to start.
//
break;
//
// If KDC is trying to start up now,
// continue waiting for it to start.
//
case SERVICE_START_PENDING: break;
//
// Any other state is bogus.
//
default: D_DebugLog((DEB_ERROR, "KerbWaitForKdc: " "Invalid service state: %lu. %ws, line %d\n", ServiceStatus.dwCurrentState, THIS_FILE, __LINE__ )); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup;
}
//
// If the service wasn't auto start, don't bother waiting and
// retrying
//
if ((ServiceStatus.dwCurrentState) != SERVICE_START_PENDING && !AutoStart) { break; }
//
// Wait a second for the KDC service to start.
// If it has successfully started, just return now.
//
Status = KerbWaitForEvent( KDC_START_EVENT, 1 );
if ( Status != STATUS_NETLOGON_NOT_STARTED ) { goto Cleanup; }
//
// If we've waited long enough for KDC to start,
// time out now.
//
if ( (--Timeout) == 0 ) { Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
}
/* NOT REACHED */
Cleanup:
if ( ScManagerHandle != NULL ) { (VOID) CloseServiceHandle(ScManagerHandle); }
if ( ServiceHandle != NULL ) { (VOID) CloseServiceHandle(ServiceHandle); }
SafeAllocaFree( AllocServiceConfig );
if (NT_SUCCESS(Status)) { KerbKdcStarted = TRUE; } else { KerbKdcStarted = FALSE; } return Status; }
//+-------------------------------------------------------------------------
//
// Function: KerbWaitForService
//
// Synopsis: Wait up to Timeout seconds for the service to start.
//
// Effects:
//
// Arguments: ServiceName - Name of service to wait for
// ServiceEvent - Optionally has event name signalling that
// service is started
// Timeout - Timeout for netlogon (in seconds).
//
// Requires:
//
// Returns: STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
// STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbWaitForService( IN LPWSTR ServiceName, IN OPTIONAL LPWSTR ServiceEvent, IN ULONG Timeout ) { NTSTATUS Status = STATUS_SUCCESS; ULONG NetStatus; SC_HANDLE ScManagerHandle = NULL; SC_HANDLE ServiceHandle = NULL; SERVICE_STATUS ServiceStatus; LPQUERY_SERVICE_CONFIG ServiceConfig; LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL; QUERY_SERVICE_CONFIG DummyServiceConfig; DWORD ServiceConfigSize; BOOLEAN AutoStart = FALSE;
if (ARGUMENT_PRESENT(ServiceEvent)) { //
// If the KDC service is currently running,
// skip the rest of the tests.
//
Status = KerbWaitForEvent( ServiceEvent, 0 );
if ( NT_SUCCESS(Status) ) { return Status; }
}
//
// Open a handle to the Service.
//
ScManagerHandle = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
if (ScManagerHandle == NULL) { D_DebugLog((DEB_ERROR, " KerbWaitForService: OpenSCManager failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
ServiceHandle = OpenService( ScManagerHandle, ServiceName, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
if ( ServiceHandle == NULL ) { D_DebugLog((DEB_ERROR, "KerbWaitForService: OpenService failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// If the KDC service isn't configured to be automatically started
// by the service controller, don't bother waiting for it to start -
// just check to see if it is started.
//
// ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
// won't allow a null pointer, yet.
if ( QueryServiceConfig( ServiceHandle, &DummyServiceConfig, sizeof(DummyServiceConfig), &ServiceConfigSize )) {
ServiceConfig = &DummyServiceConfig;
} else {
NetStatus = GetLastError(); if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) { D_DebugLog((DEB_ERROR,"KerbWaitForService: QueryServiceConfig failed: " "%lu. %ws, line %d\n", NetStatus, THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
SafeAllocaAllocate(AllocServiceConfig, ServiceConfigSize);
ServiceConfig = AllocServiceConfig;
if ( AllocServiceConfig == NULL ) { Status = STATUS_NO_MEMORY; goto Cleanup; }
if ( !QueryServiceConfig( ServiceHandle, ServiceConfig, ServiceConfigSize, &ServiceConfigSize )) {
D_DebugLog((DEB_ERROR, "KerbWaitForService: QueryServiceConfig " "failed again: %lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; } }
if ( ServiceConfig->dwStartType == SERVICE_AUTO_START ) {
AutoStart = TRUE; }
//
// Loop waiting for the KDC service to start.
//
for (;;) {
//
// Query the status of the KDC service.
//
if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
D_DebugLog((DEB_ERROR, "KerbWaitForService: QueryServiceStatus failed: " "%lu. %ws, line %d\n", GetLastError(), THIS_FILE, __LINE__ )); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// Return or continue waiting depending on the state of
// the KDC service.
//
switch( ServiceStatus.dwCurrentState) { case SERVICE_RUNNING: Status = STATUS_SUCCESS; goto Cleanup;
case SERVICE_STOPPED:
//
// If KDC failed to start,
// error out now. The caller has waited long enough to start.
//
if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){ #if DBG
D_DebugLog((DEB_ERROR, "KerbWaitForService: " "%ws service couldn't start: %lu %lx. %ws, line %d\n", ServiceName, ServiceStatus.dwWin32ExitCode, ServiceStatus.dwWin32ExitCode, THIS_FILE, __LINE__ )); if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) { D_DebugLog((DEB_ERROR, " Service specific error code: %lu %lx. %ws, line %d\n", ServiceStatus.dwServiceSpecificExitCode, ServiceStatus.dwServiceSpecificExitCode, THIS_FILE, __LINE__ )); } #endif // DBG
Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; }
//
// If service has never been started on this boot,
// continue waiting for it to start.
//
break;
//
// If service is trying to start up now,
// continue waiting for it to start.
//
case SERVICE_START_PENDING: break;
//
// Any other state is bogus.
//
default: D_DebugLog((DEB_ERROR, "KerbWaitForService: " "Invalid service state: %lu. %ws, line %d\n", ServiceStatus.dwCurrentState, THIS_FILE, __LINE__ )); Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup;
}
//
// If the service wasn't auto start, don't bother waiting and
// retrying
//
if (!AutoStart) { break; }
//
// Wait a second for the KDC service to start.
// If it has successfully started, just return now.
//
if (ARGUMENT_PRESENT(ServiceEvent)) { Status = KerbWaitForEvent( ServiceEvent, 1 );
if ( Status != STATUS_NETLOGON_NOT_STARTED ) { goto Cleanup; }
} else { Sleep(1000); }
//
// If we've waited long enough for KDC to start,
// time out now.
//
if ( (--Timeout) == 0 ) { Status = STATUS_NETLOGON_NOT_STARTED; goto Cleanup; } }
/* NOT REACHED */
Cleanup:
if ( ScManagerHandle != NULL ) { (VOID) CloseServiceHandle(ScManagerHandle); }
if ( ServiceHandle != NULL ) { (VOID) CloseServiceHandle(ServiceHandle); }
SafeAllocaFree(AllocServiceConfig);
return Status; } #endif // WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbMapContextFlags
//
// Synopsis: Maps the ISC_RET_xx flags to ASC_RET_xxx flags
//
// Effects:
//
// Arguments: ContextFlags - Flags to map
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
struct _KERB_FLAG_MAPPING { ULONG InitFlag; ULONG AcceptFlag; } KerbContextFlagMappingTable[] = { {ISC_RET_EXTENDED_ERROR, ASC_RET_EXTENDED_ERROR}, {ISC_RET_INTEGRITY , ASC_RET_INTEGRITY }, {ISC_RET_IDENTIFY, ASC_RET_IDENTIFY }, {ISC_RET_NULL_SESSION, ASC_RET_NULL_SESSION } };
#define KERB_CONTEXT_FLAG_IDENTICAL 0xFFF & ~( ISC_RET_USED_COLLECTED_CREDS | ISC_RET_USED_SUPPLIED_CREDS)
ULONG KerbMapContextFlags( IN ULONG ContextFlags ) { ULONG OutputFlags; ULONG Index;
//
// First copy the identical flags
//
OutputFlags = ContextFlags & KERB_CONTEXT_FLAG_IDENTICAL; for (Index = 0; Index < sizeof(KerbContextFlagMappingTable) / (2 * sizeof(ULONG)) ;Index++ ) { if ((ContextFlags & KerbContextFlagMappingTable[Index].InitFlag) != 0) { OutputFlags |= KerbContextFlagMappingTable[Index].AcceptFlag; } } return(OutputFlags); }
//+-------------------------------------------------------------------------
//
// Function: KerbIsIpAddress
//
// Synopsis: Checks to see if a target name is an IP address
//
// Effects: none
//
// Arguments: TargetName - Name to check
//
// Requires:
//
// Returns: TRUE if the name is an ip address
//
// Notes: IP address consist of only digits and periods, possibly
// with a terminating '$'.
//
//
//--------------------------------------------------------------------------
BOOLEAN KerbIsIpAddress( IN PUNICODE_STRING TargetName ) { ULONG Index; ULONG PeriodCount = 0;
//
// Null names are not IP addresses.
//
if (!ARGUMENT_PRESENT(TargetName) || (TargetName->Length == 0)) { return(FALSE); }
for (Index = 0; Index < TargetName->Length/sizeof(WCHAR) ; Index++ ) { switch(TargetName->Buffer[Index]) { case L'0': case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': case L'8': case L'9': continue; case L'$': //
// Only allow this at the end.
//
if (Index != (TargetName->Length/sizeof(WCHAR) -1) ) { return(FALSE); } continue; case L'.': PeriodCount++; break; default: return(FALSE); } }
//
// We require a period in the name, so return the FoundPeriod flag
//
if (PeriodCount == 3) { return(TRUE); } else { return(FALSE); } }
//+-------------------------------------------------------------------------
//
// Function: KerbHidePassword
//
// Synopsis: obscures a password in memory
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbHidePassword( IN OUT PUNICODE_STRING Password ) { LsaFunctions->LsaProtectMemory( Password->Buffer, (ULONG)Password->MaximumLength ); }
//+-------------------------------------------------------------------------
//
// Function: KerbRevealPassword
//
// Synopsis: Reveals a password that has been hidden
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbRevealPassword( IN OUT PUNICODE_STRING HiddenPassword ) { LsaFunctions->LsaUnprotectMemory( HiddenPassword->Buffer, (ULONG)HiddenPassword->MaximumLength ); }
//+-------------------------------------------------------------------------
//
// Function: KerbDuplicatePassword
//
// Synopsis: Duplicates a UNICODE_STRING. If the source string buffer is
// NULL the destionation will be too. The MaximumLength contains
// room for encryption padding data.
//
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
//
// Arguments: DestinationString - Receives a copy of the source string
// SourceString - String to copy
//
// Requires:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbDuplicatePassword( OUT PUNICODE_STRING DestinationString, IN OPTIONAL PUNICODE_STRING SourceString ) { NTSTATUS Status = STATUS_SUCCESS;
DestinationString->Buffer = NULL; DestinationString->Length = DestinationString->MaximumLength = 0;
if ((ARGUMENT_PRESENT(SourceString)) && (SourceString->Buffer != NULL)) { USHORT PaddingLength;
PaddingLength = RTL_ENCRYPT_MEMORY_SIZE - (SourceString->Length % RTL_ENCRYPT_MEMORY_SIZE);
if( PaddingLength == RTL_ENCRYPT_MEMORY_SIZE ) { PaddingLength = 0; }
//
// Make sure we don't overflow maximum length w/ pwd + max padding
// pwd won't be 65k long :)
//
if (SourceString->Length > (KERB_MAX_UNICODE_STRING - RTL_ENCRYPT_MEMORY_SIZE)) { return STATUS_ILL_FORMED_PASSWORD; }
DestinationString->Buffer = (LPWSTR) MIDL_user_allocate( SourceString->Length + PaddingLength );
if (DestinationString->Buffer != NULL) { DestinationString->Length = SourceString->Length; DestinationString->MaximumLength = SourceString->Length + PaddingLength; if( DestinationString->MaximumLength == SourceString->MaximumLength ) { //
// duplicating an already padded buffer -- pickup the original
// pad.
//
RtlCopyMemory( DestinationString->Buffer, SourceString->Buffer, SourceString->MaximumLength ); } else {
//
// duplicating an unpadded buffer -- pickup only the string
// and fill the rest with pad.
//
RtlCopyMemory( DestinationString->Buffer, SourceString->Buffer, SourceString->Length ); } } else { Status = STATUS_NO_MEMORY; } }
return Status; }
#ifdef notdef
// use this if we ever need to map errors in kerb to something else.
//+-------------------------------------------------------------------------
//
// Function: KerbMapKerbNtStatusToNtStatus
//
// Synopsis: Maps an NT status code to a security status
// Here's the package's chance to send back generic NtStatus
// errors
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbMapKerbNtStatusToNtStatus( IN NTSTATUS Status ) { return(Status); } #endif
void * __cdecl operator new( size_t nSize ) { return((LPVOID)LocalAlloc(LPTR, nSize)); }
void __cdecl operator delete( void *pv ) { LocalFree((HLOCAL)pv); }
//+-------------------------------------------------------------------------
//
// Function: KerbExtractDomainName
//
// Synopsis: Extracts the domain name from a principal name
//
// Effects: Allocates the destination string
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbExtractDomainName( OUT PUNICODE_STRING DomainName, IN PKERB_INTERNAL_NAME PrincipalName, IN PUNICODE_STRING TicketSourceDomain ) { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING TempPrincipal; UNICODE_STRING TempDomain = NULL_UNICODE_STRING;
EMPTY_UNICODE_STRING( DomainName );
//
// We do different things depending on the name type:
// - for NT_MS_PRINCIPAL we call KerbSplitFullServiceName, then do the
// same as for other name types
// - For all other names, if the first portion is "krbtgt" then
// we use the second portion of the name, otherwise the
// TicketSourceRealm
//
if (PrincipalName->NameType == KRB_NT_MS_PRINCIPAL) { if (PrincipalName->NameCount != 1) { D_DebugLog((DEB_ERROR,"Principal name has more than one name. %ws, line %d\n", THIS_FILE, __LINE__ )); Status = STATUS_TOO_MANY_PRINCIPALS; return(Status); } else { Status = KerbSplitFullServiceName( &PrincipalName->Names[0], &TempDomain, &TempPrincipal ); if (!NT_SUCCESS(Status)) { return(Status); }
} } else {
//
// The principal name is the first portion. If there are exactly
// two portions, the domain name is the second portion
//
TempPrincipal = PrincipalName->Names[0]; if (PrincipalName->NameCount == 2) { TempDomain = PrincipalName->Names[1]; } else { TempDomain = *TicketSourceDomain; }
}
//
// Check to see if the principal is "krbtgt" - if it is, the domain
// is TempDomain - otherwise it is TicketSourceDomain.
//
if (RtlEqualUnicodeString( &TempPrincipal, &KerbGlobalKdcServiceName, TRUE // case insensitive
)) { Status = KerbDuplicateString( DomainName, &TempDomain ); } else { Status = KerbDuplicateString( DomainName, TicketSourceDomain ); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbUtcTimeToLocalTime
//
// Synopsis: Converts system time (used internally) to local time, which
// is returned to callers.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbUtcTimeToLocalTime( OUT PTimeStamp LocalTime, IN PTimeStamp SystemTime ) { #ifndef WIN32_CHICAGO
NTSTATUS Status; Status = RtlSystemTimeToLocalTime( SystemTime, LocalTime ); DsysAssert(NT_SUCCESS(Status)); #else
BOOL Result; Result = FileTimeToLocalFileTime( (PFILETIME) SystemTime, (PFILETIME) LocalTime ); DsysAssert(Result); #endif
}
//+-------------------------------------------------------------------------
//
// Function: KerbConvertKdcOptionsToTicketFlags
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG KerbConvertKdcOptionsToTicketFlags( IN ULONG KdcOptions ) { ULONG TicketFlags = 0;
if ((KdcOptions & KERB_KDC_OPTIONS_forwardable) != 0) { TicketFlags |= KERB_TICKET_FLAGS_forwardable; }
if ((KdcOptions & KERB_KDC_OPTIONS_forwarded) != 0) { TicketFlags |= KERB_TICKET_FLAGS_forwarded; }
if ((KdcOptions & KERB_KDC_OPTIONS_proxiable) != 0) { TicketFlags |= KERB_TICKET_FLAGS_proxiable; }
if ((KdcOptions & KERB_KDC_OPTIONS_proxy) != 0) { TicketFlags |= KERB_TICKET_FLAGS_proxy; }
if ((KdcOptions & KERB_KDC_OPTIONS_postdated) != 0) { TicketFlags |= KERB_TICKET_FLAGS_postdated; }
if ((KdcOptions & KERB_KDC_OPTIONS_allow_postdate) != 0) { TicketFlags |= KERB_TICKET_FLAGS_may_postdate; }
if ((KdcOptions & KERB_KDC_OPTIONS_renewable) != 0) { TicketFlags |= KERB_TICKET_FLAGS_renewable; }
return(TicketFlags); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetAddressListFromWinsock
//
// Synopsis: gets the list of addresses from a winsock ioctl
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetAddressListFromWinsock( OUT LPSOCKET_ADDRESS_LIST * SocketAddressList ) { ULONG BytesReturned = 150; LPSOCKET_ADDRESS_LIST AddressList = NULL; INT i,j; ULONG NetStatus; NTSTATUS Status = STATUS_SUCCESS; SOCKET AddressSocket = INVALID_SOCKET;
#ifdef WIN32_CHICAGO
j = 0; AddressList = (LPSOCKET_ADDRESS_LIST) MIDL_user_allocate(sizeof(SOCKET_ADDRESS_LIST)); if (AddressList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } #else // WIN32_CHICAGO
AddressSocket = WSASocket( AF_INET, SOCK_DGRAM, 0, // PF_INET,
NULL, 0, 0 );
if ( AddressSocket == INVALID_SOCKET ) {
NetStatus = WSAGetLastError(); D_DebugLog((DEB_ERROR,"WSASocket failed with %ld. %ws, line %d\n", NetStatus, THIS_FILE, __LINE__ )); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
for (;;) {
//
// Allocate a buffer that should be big enough.
//
if ( AddressList != NULL ) { MIDL_user_free( AddressList ); }
AddressList = (LPSOCKET_ADDRESS_LIST) MIDL_user_allocate( BytesReturned );
if ( AddressList == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
//
// Get the list of IP addresses
//
NetStatus = WSAIoctl( AddressSocket, SIO_ADDRESS_LIST_QUERY, NULL, // No input buffer
0, // No input buffer
(PVOID) AddressList, BytesReturned, &BytesReturned, NULL, // No overlapped,
NULL ); // Not async
if ( NetStatus != 0 ) { NetStatus = WSAGetLastError(); //
// If the buffer isn't big enough, try again.
//
if ( NetStatus == WSAEFAULT ) { continue; }
D_DebugLog((DEB_ERROR,"KerbGetAddressListFromWinsock: Cannot WSAIoctl SIO_ADDRESS_LIST_QUERY %ld %ld. %ws, line %d\n", NetStatus, BytesReturned, THIS_FILE, __LINE__)); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
break; }
//
// Weed out any zero IP addresses and other invalid addresses
//
for ( i = 0, j = 0; i < AddressList->iAddressCount; i++ ) { PSOCKET_ADDRESS SocketAddress;
//
// Copy this address to the front of the list.
//
AddressList->Address[j] = AddressList->Address[i];
//
// If the address isn't valid,
// skip it.
//
SocketAddress = &AddressList->Address[j];
if ( SocketAddress->iSockaddrLength == 0 || SocketAddress->lpSockaddr == NULL || SocketAddress->lpSockaddr->sa_family != AF_INET || ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
} else {
//
// Otherwise keep it.
//
j++; } } #endif // WIN32_CHICAGO
AddressList->iAddressCount = j; *SocketAddressList = AddressList; AddressList = NULL;
Cleanup: if (AddressList != NULL) { MIDL_user_free(AddressList); }
if ( AddressSocket != INVALID_SOCKET ) { closesocket(AddressSocket); }
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbBuildHostAddresses
//
// Synopsis: Builds a list of host addresses to go in a KDC request
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildHostAddresses( IN BOOLEAN IncludeIpAddresses, IN BOOLEAN IncludeNetbiosAddresses, OUT PKERB_HOST_ADDRESSES * HostAddresses ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_HOST_ADDRESSES Addresses = NULL; PKERB_HOST_ADDRESSES TempAddress = NULL; BOOLEAN LockHeld = FALSE;
#ifndef WIN32_CHICAGO
KerbGlobalReadLock(); LockHeld = TRUE;
//
// Check to see if we've gotten out addresses from Netlogon yet.
//
if ( IncludeIpAddresses && KerbGlobalIpAddressCount == 0) { LPSOCKET_ADDRESS_LIST SocketAddressList = NULL; KerbGlobalReleaseLock(); LockHeld = FALSE;
//
// We haven't get them now
//
Status = KerbGetAddressListFromWinsock( &SocketAddressList );
if (NT_SUCCESS(Status)) { Status = KerbUpdateGlobalAddresses( SocketAddressList->Address, SocketAddressList->iAddressCount ); MIDL_user_free(SocketAddressList); } else { KerbGlobalWriteLock(); KerbGlobalIpAddressesInitialized = TRUE; KerbGlobalReleaseLock(); } KerbGlobalReadLock(); LockHeld = TRUE; }
//
// On failure don't bother inserting the IP addresses
//
if ( Status == STATUS_SUCCESS && IncludeIpAddresses ) {
ULONG Index;
for (Index = 0; Index < KerbGlobalIpAddressCount ; Index++ ) { TempAddress = (PKERB_HOST_ADDRESSES) KerbAllocate(sizeof(KERB_HOST_ADDRESSES)); if (TempAddress == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } TempAddress->value.address_type = KERB_ADDRTYPE_INET; TempAddress->value.address.length = 4; TempAddress->value.address.value = (PUCHAR) KerbAllocate(4); if (TempAddress->value.address.value == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
RtlCopyMemory ( TempAddress->value.address.value, &KerbGlobalIpAddresses[Index].sin_addr.S_un.S_addr, 4 ); TempAddress->next = Addresses; Addresses = TempAddress; TempAddress = NULL; } } else { Status = STATUS_SUCCESS; }
#endif // WIN32_CHICAGO
//
// Insert the netbios address (if it will fit)
//
if (IncludeNetbiosAddresses && KerbGlobalKerbMachineName.Length < NCBNAMSZ) { TempAddress = (PKERB_HOST_ADDRESSES) KerbAllocate(sizeof(KERB_HOST_ADDRESSES)); if (TempAddress == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } TempAddress->value.address_type = KERB_ADDRTYPE_NETBIOS; TempAddress->value.address.length = NCBNAMSZ; TempAddress->value.address.value = (PUCHAR) KerbAllocate(NCBNAMSZ); if (TempAddress->value.address.value == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyMemory( TempAddress->value.address.value, KerbGlobalKerbMachineName.Buffer, KerbGlobalKerbMachineName.Length ); memset( TempAddress->value.address.value + KerbGlobalKerbMachineName.Length, ' ', // space
NCBNAMSZ - KerbGlobalKerbMachineName.Length );
TempAddress->next = Addresses; Addresses = TempAddress; TempAddress = NULL;
}
*HostAddresses = Addresses; Addresses = NULL;
Cleanup:
if (LockHeld) { KerbGlobalReleaseLock(); } if (TempAddress != NULL) { if (TempAddress->value.address.value != NULL) { KerbFree(TempAddress->value.address.value); } KerbFree(TempAddress); } if (Addresses != NULL) { //KerbFreeHostAddresses(Addresses);
while (Addresses != NULL) { TempAddress = Addresses; Addresses = Addresses->next; if (TempAddress->value.address.value != NULL) { KerbFree(TempAddress->value.address.value); } KerbFree(TempAddress); } }
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbBuildGssErrorMessage
//
// Synopsis: Builds an error message with GSS framing, if necessary
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildGssErrorMessage( IN KERBERR Error, IN PBYTE ErrorData, IN ULONG ErrorDataSize, IN PKERB_CONTEXT Context, OUT PULONG ErrorMessageSize, OUT PBYTE * ErrorMessage ) { KERBERR KerbErr = KDC_ERR_NONE; NTSTATUS Status = STATUS_SUCCESS; PUNICODE_STRING pDomain = NULL; PBYTE RawErrorMessage = NULL; ULONG RawErrorMessageSize = 0; PBYTE EncodedErrorData = NULL; ULONG EncodedErrorDataSize = 0; PBYTE MessageStart = NULL; KERB_ERROR_METHOD_DATA ApErrorData = {0}; PKERB_INTERNAL_NAME Spn = NULL; gss_OID MechId;
//
// First, convert the error data to a specified type
//
if (Error == KRB_AP_ERR_SKEW) { ApErrorData.data_type = KERB_AP_ERR_TYPE_SKEW_RECOVERY; ApErrorData.data_value.value = NULL; ApErrorData.data_value.length = 0;
KerbErr = KerbPackData( &ApErrorData, KERB_ERROR_METHOD_DATA_PDU, &EncodedErrorDataSize, &EncodedErrorData ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } } else if (ErrorDataSize != 0) { if (Error == KRB_AP_ERR_USER_TO_USER_REQUIRED) { EncodedErrorData = ErrorData; EncodedErrorDataSize = ErrorDataSize; } else { ApErrorData.data_type = KERB_AP_ERR_TYPE_NTSTATUS; ApErrorData.data_value.value = ErrorData; ApErrorData.data_value.length = ErrorDataSize; ApErrorData.bit_mask |= data_value_present;
KerbErr = KerbPackData( &ApErrorData, KERB_ERROR_METHOD_DATA_PDU, &EncodedErrorDataSize, &EncodedErrorData ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } } }
//
// First build the error message
//
KerbGlobalReadLock();
if (Context->ServerPrincipalName.Buffer != NULL) { KerbErr = KerbConvertStringToKdcName( &Spn, &Context->ServerPrincipalName );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } } else { Spn = KerbGlobalMitMachineServiceName; }
if (KerbGlobalDnsDomainName.Buffer != NULL) { pDomain = &KerbGlobalDnsDomainName; } else if (KerbGlobalDomainName.Buffer != NULL) { pDomain = &KerbGlobalDomainName; }
KerbErr = KerbBuildErrorMessageEx( Error, NULL, // no extended error
pDomain, Spn, NULL, // no client realm
EncodedErrorData, EncodedErrorDataSize, &RawErrorMessageSize, &RawErrorMessage );
KerbGlobalReleaseLock(); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
//
// Figure out what OID to use
//
KerbReadLockContexts();
//
// For DCE style we don't use an OID
//
if ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) { KerbUnlockContexts(); *ErrorMessage = RawErrorMessage; *ErrorMessageSize = RawErrorMessageSize; RawErrorMessage = NULL; goto Cleanup; }
if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { MechId = gss_mech_krb5_u2u; } else { MechId = gss_mech_krb5_new; } KerbUnlockContexts();
*ErrorMessageSize = g_token_size(MechId, RawErrorMessageSize);
*ErrorMessage = (PBYTE) KerbAllocate(*ErrorMessageSize); if (*ErrorMessage == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
//
// the g_make_token_header will reset this to point to the end of the
// header
//
MessageStart = *ErrorMessage;
g_make_token_header( MechId, RawErrorMessageSize, &MessageStart, KG_TOK_CTX_ERROR );
RtlCopyMemory( MessageStart, RawErrorMessage, RawErrorMessageSize );
Cleanup: if (RawErrorMessage != NULL) { MIDL_user_free(RawErrorMessage); } if (EncodedErrorData != ErrorData) { MIDL_user_free(EncodedErrorData); }
if (Spn != NULL && Context->ServerPrincipalName.Buffer != NULL) { MIDL_user_free(Spn); }
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbReceiveErrorMessage
//
// Synopsis: Unpacks an error message from a context request
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbReceiveErrorMessage( IN PBYTE ErrorMessage, IN ULONG ErrorMessageSize, IN PKERB_CONTEXT Context, OUT PKERB_ERROR * DecodedErrorMessage, OUT PKERB_ERROR_METHOD_DATA * ErrorData ) { NTSTATUS Status = STATUS_SUCCESS; KERBERR KerbErr; PBYTE MessageStart = NULL; ULONG MessageSize = 0; BOOLEAN VerifiedHeader = FALSE; gss_OID MechId = NULL;
KerbReadLockContexts();
//
// For DCE style we don't use an OID
//
if ((Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER) != 0) { MechId = gss_mech_krb5_u2u; } else { MechId = gss_mech_krb5_new; } KerbUnlockContexts();
//
// First try pull off the header
//
MessageSize = ErrorMessageSize; MessageStart = ErrorMessage;
if (!g_verify_token_header( MechId, (INT *) &MessageSize, &MessageStart, KG_TOK_CTX_ERROR, ErrorMessageSize )) { //
// If we couldn't find the header, try it without
// a header
//
MessageSize = ErrorMessageSize; MessageStart = ErrorMessage; } else { VerifiedHeader = TRUE; }
KerbErr = KerbUnpackKerbError( MessageStart, MessageSize, DecodedErrorMessage ); if (!KERB_SUCCESS(KerbErr)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (((*DecodedErrorMessage)->bit_mask & error_data_present) != 0) { KerbUnpackErrorMethodData( *DecodedErrorMessage, ErrorData ); } else { Status = KerbMapKerbError(KerbErr); } Cleanup: return(Status); }
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbUnpackErrorMethodData
//
// Synopsis: This routine unpacks extended error information from
// a KERB_ERROR message
//
// Effects:
//
// Arguments: Unpacked error message. Returns extended error to
// be freed using KerbFree
//
// Requires:
//
// Returns: NTSTATUS
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBERR KerbUnpackErrorMethodData( IN PKERB_ERROR ErrorMessage, IN OUT OPTIONAL PKERB_ERROR_METHOD_DATA * ppErrorData ) {
PKERB_ERROR_METHOD_DATA pErrorData = NULL; KERBERR KerbErr = KDC_ERR_NONE;
if (ARGUMENT_PRESENT(ppErrorData)) { *ppErrorData = NULL; }
if ((ErrorMessage->bit_mask & error_data_present) == 0) { return (KRB_ERR_GENERIC); }
KerbErr = KerbUnpackData( ErrorMessage->error_data.value, ErrorMessage->error_data.length, KERB_ERROR_METHOD_DATA_PDU, (void**) &pErrorData );
if (KERB_SUCCESS(KerbErr) && ARGUMENT_PRESENT(ppErrorData) && (NULL != pErrorData)) { *ppErrorData = pErrorData; pErrorData = NULL; }
if (pErrorData) { KerbFreeData(KERB_ERROR_METHOD_DATA_PDU, pErrorData); }
return (KerbErr); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetDnsHostName
//
// Synopsis: This routine gets DnsHostName of this machine.
//
// Effects:
//
// Arguments: DnsHostName - Returns the DNS Host Name of the machine.
// Will return a NULL string if this machine has no DNS host name.
// Free this buffer using KerbFreeString.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetDnsHostName( OUT PUNICODE_STRING DnsHostName ) { NTSTATUS Status = STATUS_SUCCESS;
WCHAR LocalDnsUnicodeHostName[DNS_MAX_NAME_BUFFER_LENGTH+1]; ULONG LocalDnsUnicodeHostNameLen = DNS_MAX_NAME_BUFFER_LENGTH+1; LPWSTR ConfiguredDnsName = LocalDnsUnicodeHostName; UNICODE_STRING HostName;
RtlInitUnicodeString( DnsHostName, NULL ); //
// Get the DNS host name.
//
if (!GetComputerNameEx( ComputerNameDnsHostname, ConfiguredDnsName, &LocalDnsUnicodeHostNameLen)) { goto Cleanup; }
ConfiguredDnsName = &LocalDnsUnicodeHostName[LocalDnsUnicodeHostNameLen]; *ConfiguredDnsName = L'.'; ConfiguredDnsName++;
//
// Now get the DNS domain name
//
LocalDnsUnicodeHostNameLen = DNS_MAX_NAME_BUFFER_LENGTH - LocalDnsUnicodeHostNameLen;
if (!GetComputerNameEx( ComputerNameDnsDomain, ConfiguredDnsName, &LocalDnsUnicodeHostNameLen )) { goto Cleanup; }
RtlInitUnicodeString( &HostName, LocalDnsUnicodeHostName );
Status = RtlDowncaseUnicodeString( &HostName, &HostName, FALSE // don't allocate destination
); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbDuplicateString( DnsHostName, &HostName );
Cleanup: return Status; }
#endif // WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbIsThisOurDomain
//
// Synopsis: Compares a domain name to the local domain anme
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOLEAN KerbIsThisOurDomain( IN PUNICODE_STRING DomainName ) { BOOLEAN Result; KerbGlobalReadLock();
Result = KerbCompareUnicodeRealmNames( DomainName, &KerbGlobalDnsDomainName ) || RtlEqualUnicodeString( DomainName, &KerbGlobalDomainName, TRUE );
KerbGlobalReleaseLock(); return(Result); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetOurDomainName
//
// Synopsis: Copies the machines dns domain name, if available,
// netbios otherwise.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetOurDomainName( OUT PUNICODE_STRING DomainName ) { NTSTATUS Status; KerbGlobalReadLock();
if (KerbGlobalDnsDomainName.Length != 0) { Status = KerbDuplicateString( DomainName, &KerbGlobalDnsDomainName ); } else { Status = KerbDuplicateString( DomainName, &KerbGlobalDomainName ); }
KerbGlobalReleaseLock(); return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetGlobalRole
//
// Synopsis: Returns the current role of the machine
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
KERBEROS_MACHINE_ROLE KerbGetGlobalRole( VOID ) { KERBEROS_MACHINE_ROLE Role; KerbGlobalReadLock(); Role = KerbGlobalRole; KerbGlobalReleaseLock(); return(Role); }
//+-------------------------------------------------------------------------
//
// Function: KerbSetComputerName
//
// Synopsis: Sets all computer-name related global variables
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbSetComputerName( VOID ) { UNICODE_STRING LocalMachineName; STRING LocalKerbMachineName;
UNICODE_STRING OldMachineName; STRING OldKerbMachineName;
ULONG ComputerNameLength; NTSTATUS Status; BOOLEAN LockHeld = FALSE; #ifdef WIN32_CHICAGO
CHAR TempAnsiBuffer[MAX_COMPUTERNAME_LENGTH + 1]; ComputerNameLength = sizeof(TempAnsiBuffer); #endif
LocalMachineName.Buffer = NULL; LocalKerbMachineName.Buffer = NULL;
#ifndef WIN32_CHICAGO
ComputerNameLength = 0; if (GetComputerNameW( NULL, &ComputerNameLength )) { D_DebugLog((DEB_ERROR,"Succeeded to get computer name when failure expected! %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
LocalMachineName.Buffer = (LPWSTR) KerbAllocate( ((ComputerNameLength + 1) * sizeof(WCHAR)) ); if (LocalMachineName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } #endif // WIN32_CHICAGO
#ifndef WIN32_CHICAGO
if (!GetComputerNameW( LocalMachineName.Buffer, &ComputerNameLength )) #else // WIN32_CHICAGO
if (!GetComputerName( TempAnsiBuffer, &ComputerNameLength )) #endif // WIN32_CHICAGO
{ D_DebugLog((DEB_ERROR,"Failed to get computer name: %d. %ws, line %d\n",GetLastError(), THIS_FILE, __LINE__)); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
#ifndef WIN32_CHICAGO
LocalMachineName.Length = (USHORT)(ComputerNameLength * sizeof(WCHAR)); LocalMachineName.MaximumLength = LocalMachineName.Length + sizeof(WCHAR); #else
RtlCreateUnicodeStringFromAsciiz (&LocalMachineName, TempAnsiBuffer); // KerbFree (TempAnsiBuffer);
#endif // WIN32_CHICAGO
//
// Build the ansi format
//
if (!KERB_SUCCESS(KerbUnicodeStringToKerbString( &LocalKerbMachineName, &LocalMachineName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
//
// free the current globals, and update to point at new values.
//
KerbGlobalWriteLock(); LockHeld = TRUE;
OldMachineName = KerbGlobalMachineName; OldKerbMachineName = KerbGlobalKerbMachineName;
KerbGlobalMachineName = LocalMachineName; KerbGlobalKerbMachineName = LocalKerbMachineName;
//
// now, see if the netbios machine name changed versus the prior
// value.
//
if( OldMachineName.Buffer != NULL ) { if(!RtlEqualUnicodeString( &OldMachineName, &LocalMachineName, FALSE )) { D_DebugLog((DEB_WARN,"Netbios computer name change detected.\n")); KerbGlobalMachineNameChanged = TRUE; } }
KerbGlobalReleaseLock(); LockHeld = FALSE;
LocalMachineName.Buffer = NULL; LocalKerbMachineName.Buffer = NULL;
KerbFreeString( &OldMachineName ); KerbFreeString( (PUNICODE_STRING)&OldKerbMachineName );
Status = STATUS_SUCCESS;
Cleanup:
if( LockHeld ) { KerbGlobalReleaseLock(); }
KerbFreeString( &LocalMachineName ); KerbFreeString( (PUNICODE_STRING)&LocalKerbMachineName );
return Status; }
//
//
// Routine Description:
//
// This function checks the system to see if
// we are running on the personal version of
// the operating system.
//
// The personal version is denoted by the product
// id equal to WINNT, which is really workstation,
// and the product suite containing the personal
// suite string.
//
BOOLEAN KerbRunningPersonal( VOID ) { OSVERSIONINFOEXW OsVer = {0}; ULONGLONG ConditionMask = 0;
OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); OsVer.wSuiteMask = VER_SUITE_PERSONAL; OsVer.wProductType = VER_NT_WORKSTATION;
VER_SET_CONDITION( ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL ); VER_SET_CONDITION( ConditionMask, VER_SUITENAME, VER_AND );
return RtlVerifyVersionInfo( &OsVer, VER_PRODUCT_TYPE | VER_SUITENAME, ConditionMask) == STATUS_SUCCESS; }
BOOLEAN KerbRunningServer( VOID ) { OSVERSIONINFOEXW OsVer = {0}; ULONGLONG ConditionMask = 0;
OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); OsVer.wProductType = VER_NT_DOMAIN_CONTROLLER;
VER_SET_CONDITION( ConditionMask, VER_PRODUCT_TYPE, VER_GREATER_EQUAL );
return RtlVerifyVersionInfo( &OsVer, VER_PRODUCT_TYPE , ConditionMask) == STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbSetDomainName
//
// Synopsis: Sets all domain-name related global variables
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbSetDomainName( IN PUNICODE_STRING DomainName, IN PUNICODE_STRING DnsDomainName, IN PSID DomainSid, IN GUID DomainGuid ) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN AcquiredLock = FALSE; UNICODE_STRING TempDomainName = {0}; UNICODE_STRING TempDnsDomainName = {0}; UNICODE_STRING TempMachineServiceName = {0}; PKERB_INTERNAL_NAME TempMitMachineServiceName = NULL; PSID TempDomainSid = NULL; UNICODE_STRING TempString; WCHAR MachineAccountName[CNLEN + 2]; // for null and '$'
UNICODE_STRING DnsString = {0}; #ifndef WIN32_CHICAGO
LUID SystemLogonId = SYSTEM_LUID; LUID NetworkServiceLogonId = NETWORKSERVICE_LUID; PKERB_LOGON_SESSION SystemLogonSession = NULL; PKERB_LOGON_SESSION NetworkServiceLogonSession = NULL; #endif
static TimeStamp KerbSetDomainNameTime = {0}; static ULONG NumOfUpdatedLuids = 0;
NtQuerySystemTime(&KerbSetDomainNameTime);
//
// Copy the domain name / sid
//
Status = KerbDuplicateString( &TempDomainName, DomainName );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbDuplicateString( &TempDnsDomainName, DnsDomainName );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If we are in an NT domain, uppercase the dns domain name
//
#ifndef WIN32_CHICAGO
if (DomainSid != NULL) #endif
{ Status = RtlUpcaseUnicodeString( &TempDnsDomainName, &TempDnsDomainName, FALSE // don't allocate
); if (!NT_SUCCESS(Status)) { goto Cleanup; } }
#ifndef WIN32_CHICAGO
if (DomainSid != NULL) { Status = KerbDuplicateSid( &TempDomainSid, DomainSid ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
} #endif
//
// Create the new machine names
//
KerbGlobalReadLock();
ASSERT( (KerbGlobalMachineName.Length <= (CNLEN * sizeof(WCHAR)) ) );
RtlCopyMemory( MachineAccountName, KerbGlobalMachineName.Buffer, KerbGlobalMachineName.Length );
MachineAccountName[KerbGlobalMachineName.Length/sizeof(WCHAR)] = SSI_ACCOUNT_NAME_POSTFIX_CHAR; MachineAccountName[1 + KerbGlobalMachineName.Length/sizeof(WCHAR)] = L'\0';
KerbGlobalReleaseLock();
RtlInitUnicodeString( &TempString, MachineAccountName );
Status = KerbDuplicateString( &TempMachineServiceName, &TempString ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
#ifndef WIN32_CHICAGO
//
// Now build the MIT version of our machine service name
//
Status = KerbGetDnsHostName( &DnsString ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
RtlInitUnicodeString( &TempString, KERB_HOST_STRING );
if (!KERB_SUCCESS(KerbBuildFullServiceKdcName( &DnsString, &TempString, KRB_NT_SRV_HST, &TempMitMachineServiceName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
//
// Acquire the global lock so we can update the data
//
if (!KerbGlobalWriteLock()) { D_DebugLog((DEB_ERROR, "Failed to acquire global resource. Not changing domain. %ws, line %d\n", THIS_FILE, __LINE__)); goto Cleanup; } AcquiredLock = TRUE; #endif // WIN32_CHICAGO
//
// Copy all the data to the global structures
//
// If we're NT4, we don't have a dns domain name
// If we're joined to an MIT domain, we don't have a domain GUID and we
// have a dns domain name
if ((DomainGuid == GUID_NULL) && (TempDnsDomainName.Length == 0)) { KerbGlobalDomainIsPreNT5 = TRUE; } else { KerbGlobalDomainIsPreNT5 = FALSE; }
KerbFreeString(&KerbGlobalDomainName); KerbGlobalDomainName = TempDomainName; TempDomainName.Buffer = NULL;
KerbFreeString(&KerbGlobalDnsDomainName); KerbGlobalDnsDomainName = TempDnsDomainName; TempDnsDomainName.Buffer = NULL;
KerbFreeString(&KerbGlobalMachineServiceName); KerbGlobalMachineServiceName = TempMachineServiceName; TempMachineServiceName.Buffer = NULL;
#ifndef WIN32_CHICAGO
KerbFreeKdcName(&KerbGlobalMitMachineServiceName); KerbGlobalMitMachineServiceName = TempMitMachineServiceName; TempMitMachineServiceName = NULL;
if (KerbGlobalDomainSid != NULL) { KerbFree(KerbGlobalDomainSid); } KerbGlobalDomainSid = TempDomainSid; TempDomainSid = NULL;
//
// Update the role on non DCs. The role of a DC never changes.
// Demotion requires a reboot so that the domain controller role
// will not change.
//
if (KerbGlobalRole != KerbRoleDomainController) {
if (KerbRunningPersonal()) { KerbGlobalRole = KerbRoleRealmlessWksta; } else if (DomainSid == NULL ) { // No machine account, nor associate w/ MIT realm
if (DnsDomainName->Length == 0 ) { KerbGlobalRole = KerbRoleRealmlessWksta; } // Member of MIT realm, but not a domain
else { KerbGlobalRole = KerbRoleStandalone; } } else { KerbGlobalRole = KerbRoleWorkstation; } }
KerbGlobalReleaseLock(); AcquiredLock = FALSE;
//
// Update the system/networkservice logon session, if there is one
//
SystemLogonSession = KerbReferenceLogonSession( &SystemLogonId, FALSE // don't unlink
);
if (SystemLogonSession) { KerbWriteLockLogonSessions(SystemLogonSession);
D_DebugLog((DEB_TRACE_CRED, "KerbSetDomainName change localsystem domain name from %wZ to %wZ\n", &SystemLogonSession->PrimaryCredentials.DomainName, DnsDomainName));
//
// detect fake updates
//
if (!RtlEqualUnicodeString( &SystemLogonSession->PrimaryCredentials.DomainName, DnsDomainName, TRUE )) { KerbFreeString(&SystemLogonSession->PrimaryCredentials.DomainName); Status = KerbDuplicateString( &SystemLogonSession->PrimaryCredentials.DomainName , DnsDomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbPurgeTicketCache(&SystemLogonSession->PrimaryCredentials.ServerTicketCache); KerbPurgeTicketCache(&SystemLogonSession->PrimaryCredentials.AuthenticationTicketCache); KerbPurgeTicketCache(&SystemLogonSession->PrimaryCredentials.S4UTicketCache); SystemLogonSession->LogonSessionFlags |= KERB_LOGON_DEFERRED; NumOfUpdatedLuids++; }
KerbUnlockLogonSessions(SystemLogonSession);
}
NetworkServiceLogonSession = KerbReferenceLogonSession( &NetworkServiceLogonId, FALSE // don't unlink
);
if (NetworkServiceLogonSession != NULL) { KerbWriteLockLogonSessions(NetworkServiceLogonSession);
D_DebugLog((DEB_TRACE_CRED, "KerbSetDomainName change networkservice domain name from %wZ to %wZ\n", &NetworkServiceLogonSession->PrimaryCredentials.DomainName, DnsDomainName));
//
// detect fake updates
//
if (!RtlEqualUnicodeString( &NetworkServiceLogonSession->PrimaryCredentials.DomainName, DnsDomainName, TRUE )) { KerbFreeString(&NetworkServiceLogonSession->PrimaryCredentials.DomainName); Status = KerbDuplicateString( &NetworkServiceLogonSession->PrimaryCredentials.DomainName , DnsDomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbPurgeTicketCache(&NetworkServiceLogonSession->PrimaryCredentials.ServerTicketCache); KerbPurgeTicketCache(&NetworkServiceLogonSession->PrimaryCredentials.AuthenticationTicketCache); KerbPurgeTicketCache(&NetworkServiceLogonSession->PrimaryCredentials.S4UTicketCache); NetworkServiceLogonSession->LogonSessionFlags |= KERB_LOGON_DEFERRED; NumOfUpdatedLuids++; }
KerbUnlockLogonSessions(NetworkServiceLogonSession); } #endif
Cleanup:
#ifndef WIN32_CHICAGO
if (AcquiredLock) { KerbGlobalReleaseLock(); } #endif // WIN32_CHICAGO
KerbFreeString( &DnsString ); KerbFreeString( &TempDomainName ); KerbFreeString( &TempDnsDomainName ); KerbFreeString( &TempMachineServiceName );
#ifndef WIN32_CHICAGO
KerbFreeKdcName( &TempMitMachineServiceName );
if (TempDomainSid != NULL) { KerbFree(TempDomainSid); } if (SystemLogonSession != NULL) { KerbDereferenceLogonSession(SystemLogonSession); }
if (NetworkServiceLogonSession != NULL) { KerbDereferenceLogonSession(NetworkServiceLogonSession); }
#endif
return(Status);
}
#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
// Function: KerbLoadKdc
//
// Synopsis: Loads kdcsvc.dll and gets the address of important functions
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns: STATUS_SUCCESS if kdcsvc.dll could be loaded and the
// necessary entrypoints found.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbLoadKdc( VOID ) { NTSTATUS Status = STATUS_SUCCESS; KerbKdcHandle = LoadLibraryA("kdcsvc.dll"); if (KerbKdcHandle == NULL) { D_DebugLog((DEB_WARN,"Failed to load kdcsvc.dll: %d\n",GetLastError())); return(STATUS_DLL_NOT_FOUND); }
KerbKdcVerifyPac = (PKDC_VERIFY_PAC_ROUTINE) GetProcAddress( KerbKdcHandle, KDC_VERIFY_PAC_NAME ); if (KerbKdcVerifyPac == NULL) { D_DebugLog((DEB_WARN, "Failed to get proc address for KdcVerifyPac: %d\n", GetLastError())); Status = STATUS_PROCEDURE_NOT_FOUND; goto Cleanup; }
KerbKdcGetTicket = (PKDC_GET_TICKET_ROUTINE) GetProcAddress( KerbKdcHandle, KDC_GET_TICKET_NAME ); if (KerbKdcGetTicket == NULL) { D_DebugLog((DEB_WARN,"Failed to get proc address for KdcGetTicket: %d\n", GetLastError())); Status = STATUS_PROCEDURE_NOT_FOUND; goto Cleanup; }
KerbKdcChangePassword = (PKDC_GET_TICKET_ROUTINE) GetProcAddress( KerbKdcHandle, KDC_CHANGE_PASSWORD_NAME ); if (KerbKdcChangePassword == NULL) { D_DebugLog((DEB_WARN,"Failed to get proc address for KdcChangePassword: %d\n", GetLastError())); Status = STATUS_PROCEDURE_NOT_FOUND; goto Cleanup; }
KerbKdcFreeMemory = (PKDC_FREE_MEMORY_ROUTINE) GetProcAddress( KerbKdcHandle, KDC_FREE_MEMORY_NAME ); if (KerbKdcFreeMemory == NULL) { D_DebugLog((DEB_WARN,"Failed to get proc address for KdcFreeMemory: %d\n", GetLastError())); Status = STATUS_PROCEDURE_NOT_FOUND; goto Cleanup; }
Cleanup:
if (!NT_SUCCESS(Status)) { if (KerbKdcHandle != NULL) { FreeLibrary(KerbKdcHandle); KerbKdcHandle = NULL; } } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbDomainChangeCallback
//
// Synopsis: Function to be called when domain changes
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID NTAPI KerbDomainChangeCallback( IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass ) { NTSTATUS Status = STATUS_SUCCESS; PLSAPR_POLICY_INFORMATION Policy = NULL;
//
// We only care about domain dns information
//
if (ChangedInfoClass != PolicyNotifyDnsDomainInformation) { return; }
//
// Get the new domain information
//
Status = I_LsaIQueryInformationPolicyTrusted( PolicyDnsDomainInformation, &Policy );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to query domain dns information %x - not updating. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// update computer name info.
//
Status = KerbSetComputerName();
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to set computer name: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
Status = KerbSetDomainName( (PUNICODE_STRING) &Policy->PolicyDnsDomainInfo.Name, (PUNICODE_STRING) &Policy->PolicyDnsDomainInfo.DnsDomainName, (PSID) Policy->PolicyDnsDomainInfo.Sid, (GUID) Policy->PolicyDnsDomainInfo.DomainGuid ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to set domain name: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Domain Name has changed. So, the cache in the registry will
// have changed too. And we haven't rebooted yet.
//
KerbSetKdcData(TRUE, FALSE);
Cleanup:
if (Policy != NULL) { I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyDnsDomainInformation, Policy ); } return;
}
//+-------------------------------------------------------------------------
//
// Function: KerbRegisterForDomainChange
//
// Synopsis: Register with the LSA to be notified of domain changes
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbRegisterForDomainChange( VOID ) { NTSTATUS Status = STATUS_SUCCESS; Status = I_LsaIRegisterPolicyChangeNotificationCallback( KerbDomainChangeCallback, PolicyNotifyDnsDomainInformation ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to register for domain change notification: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); } return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbUnregisterForDomainChange
//
// Synopsis: Unregister for domain change notification
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KerbUnregisterForDomainChange( VOID ) { (VOID) I_LsaIUnregisterPolicyChangeNotificationCallback( KerbDomainChangeCallback, PolicyNotifyDnsDomainInformation );
}
//+-------------------------------------------------------------------------
//
// Function: KerbUpdateGlobalAddresses
//
// Synopsis: Updates the global array of addresses with information from
// netlogon.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: Ideally, we should also have called WSAProviderConfigChange to be
// notified of when tcp is added/removed. But, we can take advantage
// of the fact that netlogon is registered for changes in ipaddress
// so, even though, change of ipaddress & xports notifications are
// async, we will get to know of it, so, it suffices to rely on
// netlogon rather than register for a notification change.
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbUpdateGlobalAddresses( IN PSOCKET_ADDRESS NewAddresses, IN ULONG NewAddressCount ) { PSOCKADDR_IN GlobalIpAddresses = NULL; ULONG Index; ULONG AddressCount = 0; WSAPROTOCOL_INFO *lpProtocolBuf = NULL; DWORD dwBufLen = 0; INT protocols[2]; int nRet = 0; BOOLEAN NoTcpInstalled = FALSE;
GlobalIpAddresses = (PSOCKADDR_IN) KerbAllocate(sizeof(SOCKADDR_IN) * NewAddressCount); if (GlobalIpAddresses == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } for (Index = 0; Index < NewAddressCount ; Index++ ) { if ((NewAddresses[Index].iSockaddrLength == sizeof(SOCKADDR)) && (NewAddresses[Index].lpSockaddr->sa_family == AF_INET)) { RtlCopyMemory( &GlobalIpAddresses[AddressCount++], NewAddresses[Index].lpSockaddr, sizeof(SOCKADDR_IN) ); } else { D_DebugLog((DEB_ERROR,"Netlogon handed us a address of length or type %d, %d. %ws, line %d\n", NewAddresses[Index].iSockaddrLength, NewAddresses[Index].lpSockaddr->sa_family, THIS_FILE, __LINE__ )); } }
//
// winsock is already initialized by now, or we would not have
// gotten so far. Check if TCP s an available xport
//
protocols[0] = IPPROTO_TCP; protocols[1] = NULL; nRet = WSAEnumProtocols(protocols, lpProtocolBuf, &dwBufLen); if (nRet == 0) { //
// Tcp is not installed as a xport.
//
D_DebugLog((DEB_TRACE_SOCK,"WSAEnumProtocols returned 0x%x. %ws, line %d\n", nRet, THIS_FILE, __LINE__ )); NoTcpInstalled = TRUE; } //
// Copy them into the global for others to use
//
KerbGlobalWriteLock(); if (KerbGlobalIpAddresses != NULL) { KerbFree(KerbGlobalIpAddresses); } KerbGlobalIpAddresses = GlobalIpAddresses; KerbGlobalIpAddressCount = AddressCount; KerbGlobalIpAddressesInitialized = TRUE; KerbGlobalNoTcpUdp = NoTcpInstalled; KerbGlobalReleaseLock();
return(STATUS_SUCCESS);
}
#ifdef RESTRICTED_TOKEN
//+-------------------------------------------------------------------------
//
// Function: KerbGetTokenInformation
//
// Synopsis: Allocates and returns token information
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetTokenInformation( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS InformationClass, IN OUT PVOID * Buffer ) { NTSTATUS Status = STATUS_SUCCESS; ULONG BufferSize = 0;
//
// First retrieve the restricted sids
//
Status = NtQueryInformationToken( TokenHandle, InformationClass, NULL, 0, &BufferSize ); if (Status != STATUS_BUFFER_TOO_SMALL) { goto Cleanup; }
*Buffer = KerbAllocate(BufferSize); if (*Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
Status = NtQueryInformationToken( TokenHandle, InformationClass, *Buffer, BufferSize, &BufferSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Cleanup:
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbCaptureTokenRestrictions
//
// Synopsis: Captures the restrictions of a restricted token
//
// Effects:
//
// Arguments: TokenHandle - token handle open for TOKEN_QUERY access
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbCaptureTokenRestrictions( IN HANDLE TokenHandle, OUT PKERB_AUTHORIZATION_DATA Restrictions ) { PTOKEN_GROUPS Groups = NULL; PTOKEN_GROUPS RestrictedSids = NULL; PTOKEN_PRIVILEGES Privileges = NULL; PTOKEN_PRIVILEGES DeletePrivileges = NULL; NTSTATUS Status = STATUS_SUCCESS; ULONG BufferSize = 0; ULONG Index,Index2,LastIndex; KERB_TOKEN_RESTRICTIONS TokenRestrictions = {0};
Status = KerbGetTokenInformation( TokenHandle, TokenRestrictedSids, (PVOID *) &RestrictedSids ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = KerbGetTokenInformation( TokenHandle, TokenGroups, (PVOID *) &Groups ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = KerbGetTokenInformation( TokenHandle, TokenPrivileges, (PVOID *) &Privileges ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Now build the list of just the restricted privileges & groups
//
//
// First, find what groups are restricted.
//
LastIndex = 0; for (Index = 0; Index < Groups->GroupCount ; Index++ ) { if ((Groups->Groups[Index].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) != 0) { if (LastIndex != Index) { Groups->Groups[LastIndex].Sid = Groups->Groups[Index].Sid; Groups->Groups[LastIndex].Attributes = 0; } LastIndex++; } } Groups->GroupCount = LastIndex; if (LastIndex != 0) { TokenRestrictions.GroupsToDisable = (PPAC_TOKEN_GROUPS) Groups; TokenRestrictions.Flags |= KERB_TOKEN_RESTRICTION_DISABLE_GROUPS; }
//
// Add the restricted sids
//
if (RestrictedSids->GroupCount != 0) { for (Index = 0; Index < RestrictedSids->GroupCount ; Index++ ) { RestrictedSids->Groups[Index].Attributes = 0; } TokenRestrictions.RestrictedSids = (PPAC_TOKEN_GROUPS) RestrictedSids; TokenRestrictions.Flags |= KERB_TOKEN_RESTRICTION_RESTRICT_SIDS; }
//
// Now make a list of all the privileges that _aren't_ enabled
//
SafeAllocaAllocate(DeletePrivileges, sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES) * (1 + SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE));
if (DeletePrivileges == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
DeletePrivileges->PrivilegeCount = 0;
//
// Find out what privileges haven't been enabled
//
LastIndex = 0; for (Index = SE_MIN_WELL_KNOWN_PRIVILEGE; Index <= SE_MAX_WELL_KNOWN_PRIVILEGE ; Index++ ) { LUID TempLuid; BOOLEAN Found = FALSE; TempLuid = RtlConvertUlongToLuid(Index); for (Index2 = 0; Index2 < Privileges->PrivilegeCount ; Index2++ ) { if (RtlEqualLuid(&Privileges->Privileges[Index2].Luid,&TempLuid) && ((Privileges->Privileges[Index2].Attributes & SE_PRIVILEGE_ENABLED) != 0)) { Found = TRUE; break; } } if (!Found) { DeletePrivileges->Privileges[LastIndex].Luid = TempLuid; DeletePrivileges->Privileges[LastIndex].Attributes = 0; LastIndex++; } } DeletePrivileges->PrivilegeCount = LastIndex; if (LastIndex != 0) { TokenRestrictions.PrivilegesToDelete = (PPAC_TOKEN_PRIVILEGES) DeletePrivileges; TokenRestrictions.Flags |= KERB_TOKEN_RESTRICTION_DELETE_PRIVS; }
Restrictions->value.auth_data_type = KERB_AUTH_DATA_TOKEN_RESTRICTIONS; Status = PAC_EncodeTokenRestrictions( &TokenRestrictions, &Restrictions->value.auth_data.value, &Restrictions->value.auth_data.length );
Cleanup:
if (Groups != NULL) { KerbFree(Groups); }
if (RestrictedSids != NULL) { KerbFree(RestrictedSids); }
if (Privileges != NULL) { KerbFree(Privileges); }
SafeAllocaFree(DeletePrivileges);
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbAddRestrictionsToCredential
//
// Synopsis: Captures client'st token restrictions and sticks them in
// the credential
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbAddRestrictionsToCredential( IN PKERB_LOGON_SESSION LogonSession, IN PKERB_CREDENTIAL Credential ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_AUTHORIZATION_DATA AuthData = NULL; BOOLEAN CrossRealm; PKERB_TICKET_CACHE_ENTRY ExistingTgt = NULL; HANDLE ClientToken = NULL; PKERB_INTERNAL_NAME ServiceName = NULL; UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING; PKERB_KDC_REPLY KdcReply = NULL; PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL; BOOLEAN TicketCacheLocked = FALSE; PKERB_TICKET_CACHE_ENTRY NewTicket = NULL; ULONG CacheFlags = 0; BOOLEAN UseSuppliedCreds = FALSE;
//
// Capture the existing TGT
//
Status = LsaFunctions->ImpersonateClient(); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, // open as self
&ClientToken ); if (!NT_SUCCESS(Status)) { goto Cleanup; } RevertToSelf();
//
// Capture the restrictions for this token
//
AuthData = (PKERB_AUTHORIZATION_DATA) MIDL_user_allocate(sizeof(KERB_AUTHORIZATION_DATA)); if (AuthData == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Status = KerbCaptureTokenRestrictions( ClientToken, AuthData ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to capture token restrictions: 0x%x\n",Status)); goto Cleanup; }
KerbWriteLockLogonSessions(LogonSession); Credential->AuthData = AuthData;
//
// Turn off tgt avail to force us to get a new tgt
//
Credential->CredentialFlags &= ~KERB_CRED_TGT_AVAIL; AuthData = NULL; KerbUnlockLogonSessions(LogonSession);
Cleanup: if (AuthData != NULL) { if (AuthData->value.auth_data.value != NULL) { MIDL_user_free(AuthData->value.auth_data.value); } MIDL_user_free(AuthData); } if (ClientToken != NULL) { NtClose(ClientToken); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbBuildTokenRestrictionAuthData
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: Not called yet - used for restricted tickets
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbBuildEncryptedAuthData( OUT PKERB_ENCRYPTED_DATA EncryptedAuthData, IN PKERB_TICKET_CACHE_ENTRY Ticket, IN PKERB_AUTHORIZATION_DATA PlainAuthData ) { KERBERR KerbErr; NTSTATUS Status = STATUS_SUCCESS; KERB_MESSAGE_BUFFER PackedAuthData = {0};
KerbErr = KerbPackData( &PlainAuthData, PKERB_AUTHORIZATION_DATA_LIST_PDU, &PackedAuthData.BufferSize, &PackedAuthData.Buffer ); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
KerbReadLockTicketCache();
KerbErr = KerbAllocateEncryptionBufferWrapper( Ticket->SessionKey.keytype, PackedAuthData.BufferSize, &EncryptedAuthData->cipher_text.length, &EncryptedAuthData->cipher_text.value ); if (KERB_SUCCESS(KerbErr)) { KerbErr = KerbEncryptDataEx( EncryptedAuthData, PackedAuthData.BufferSize, PackedAuthData.Buffer, KERB_NO_KEY_VERSION, KERB_TGS_REQ_SESSKEY_SALT, // was KERB_NON_KERB_SALT need to check for KERB_TGS_REQ_SUBKEY_SALT also
&Ticket->SessionKey ); } KerbUnlockTicketCache(); if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; }
Cleanup: if (PackedAuthData.Buffer != NULL) { MIDL_user_free(PackedAuthData.Buffer); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbGetRestrictedTgtForCredential
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbGetRestrictedTgtForCredential( IN PKERB_LOGON_SESSION LogonSession, IN PKERB_CREDENTIAL Credential ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_AUTHORIZATION_DATA AuthData = NULL; BOOLEAN CrossRealm; PKERB_TICKET_CACHE_ENTRY ExistingTgt = NULL; PKERB_INTERNAL_NAME ServiceName = NULL; UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING; PKERB_KDC_REPLY KdcReply = NULL; PKERB_ENCRYPTED_KDC_REPLY KdcReplyBody = NULL; BOOLEAN TicketCacheLocked = FALSE; PKERB_TICKET_CACHE_ENTRY NewTicket = NULL; ULONG CacheFlags = 0, RetryFlags = 0; BOOLEAN UseSuppliedCreds = FALSE;
//
// First get an old TGT
//
KerbReadLockLogonSessions(LogonSession);
if (Credential->SuppliedCredentials == NULL) { ULONG Flags;
//
// We don't have supplied creds, but we need them, so copy
// from the logon session.
//
Status = KerbCaptureSuppliedCreds( LogonSession, NULL, // no auth data
NULL, // no principal name
&Credential->SuppliedCredentials, &Flags );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to capture dummy supplied creds: 0x%x\n",Status)); KerbUnlockLogonSessions(LogonSession); goto Cleanup; } AuthData = Credential->AuthData; } else { UseSuppliedCreds = FALSE; }
DsysAssert(Credential->SuppliedCredentials != NULL);
Status = KerbGetTgtForService( LogonSession, (UseSuppliedCreds) ? Credential : NULL, NULL, NULL, // no SuppRealm
&Credential->SuppliedCredentials->DomainName, &ExistingTgt, &CrossRealm ); KerbUnlockLogonSessions(LogonSession);
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Now get a new TGT with this ticket
//
//
// Copy the names out of the input structures so we can
// unlock the structures while going over the network.
//
DsysAssert( !TicketCacheLocked ); KerbReadLockTicketCache(); TicketCacheLocked = TRUE;
//
// If the renew time is not much bigger than the end time, don't bother
// renewing
//
Status = KerbDuplicateString( &ServiceRealm, &ExistingTgt->DomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbDuplicateKdcName( &ServiceName, ExistingTgt->ServiceName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } CacheFlags = ExistingTgt->CacheFlags;
DsysAssert( TicketCacheLocked ); KerbUnlockTicketCache(); TicketCacheLocked = FALSE;
Status = KerbGetTgsTicket( &ServiceRealm, ExistingTgt, ServiceName, FALSE, 0, // no ticket optiosn
0, // no encryption type
AuthData, // no authorization data
NULL, // no tgt reply
NULL, NULL, &KdcReply, &KdcReplyBody, &RetryFlags ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to get restricted tgs ticket: 0x%x\n",Status)); goto Cleanup; }
//
// Now we want to purge the existing ticket cache and add this ticket
//
KerbPurgeTicketCache( &Credential->SuppliedCredentials->AuthenticationTicketCache ); KerbPurgeTicketCache( &Credential->SuppliedCredentials->ServerTicketCache );
KerbReadLockLogonSessions(LogonSession);
Status = KerbCreateTicketCacheEntry( KdcReply, KdcReplyBody, ServiceName, &ServiceRealm, CacheFlags, &Credential->SuppliedCredentials->AuthenticationTicketCache, NULL, // no credential key
&NewTicket );
KerbUnlockLogonSessions(LogonSession);
Cleanup: if (TicketCacheLocked) { KerbUnlockTicketCache(); } if (ExistingTgt != NULL) { KerbDereferenceTicketCacheEntry(ExistingTgt); } if (NewTicket != NULL) { KerbDereferenceTicketCacheEntry(NewTicket); } KerbFreeTgsReply(KdcReply); KerbFreeKdcReplyBody(KdcReplyBody); KerbFreeKdcName(&ServiceName); KerbFreeString(&ServiceRealm); return(Status); } #endif
#endif // WIN32_CHICAGO
|