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.
4157 lines
103 KiB
4157 lines
103 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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
|