mirror of https://github.com/tongzx/nt5src
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.
1940 lines
49 KiB
1940 lines
49 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: logonses.cxx
|
|
//
|
|
// Contents: Code for managing the global list of logon sessions
|
|
//
|
|
//
|
|
// History: 16-April-1996 Created MikeSw
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include <kerb.hxx>
|
|
#define LOGONSES_ALLOCATE
|
|
#include <kerbp.h>
|
|
|
|
|
|
#ifdef DEBUG_SUPPORT
|
|
static TCHAR THIS_FILE[]=TEXT(__FILE__);
|
|
#endif
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitLogonSessionList
|
|
//
|
|
// Synopsis: Initializes logon session list
|
|
//
|
|
// Effects: allocates a resources
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success, other error codes
|
|
// on failure
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbInitLogonSessionList(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
|
|
Status = KerbInitializeList( &KerbLogonSessionList );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
KerberosLogonSessionsInitialized = TRUE;
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KerbFreeList( &KerbLogonSessionList);
|
|
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// Function: KerbInitNetworkServiceLoopbackDetection
|
|
//
|
|
// Synopsis: Initialize the network service session loopback detection
|
|
//
|
|
// Effects: Allocates a resources
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success, other error codes on failure
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbInitNetworkServiceLoopbackDetection(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
InitializeListHead(&KerbNetworkServiceSKeyList);
|
|
|
|
#if DBG
|
|
|
|
KerbcSKeyEntries = 0;
|
|
|
|
#endif
|
|
|
|
KerbhSKeyTimerQueue = NULL;
|
|
|
|
__try
|
|
{
|
|
RtlInitializeResource(&KerbNetworkServiceSKeyLock);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeNetworkServiceSKeyListAndLock
|
|
//
|
|
// Synopsis: Free the network service session key list and its lock
|
|
//
|
|
// Effects: Frees a resources
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeNetworkServiceSKeyListAndLock(
|
|
VOID
|
|
)
|
|
{
|
|
if (RtlAcquireResourceExclusive(&KerbNetworkServiceSKeyLock, TRUE))
|
|
{
|
|
for (LIST_ENTRY* pListEntry = KerbNetworkServiceSKeyList.Flink;
|
|
pListEntry != &KerbNetworkServiceSKeyList;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
|
|
|
|
KerbFreeSKeyEntry(pSKeyEntry);
|
|
}
|
|
RtlReleaseResource(&KerbNetworkServiceSKeyLock);
|
|
}
|
|
|
|
RtlDeleteResource(&KerbNetworkServiceSKeyLock);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeLogonSessionList
|
|
//
|
|
// Synopsis: Frees the logon session list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeLogonSessionList(
|
|
VOID
|
|
)
|
|
{
|
|
PKERB_LOGON_SESSION LogonSession;
|
|
|
|
|
|
if (KerberosLogonSessionsInitialized)
|
|
{
|
|
|
|
KerbLockList(&KerbLogonSessionList);
|
|
|
|
//
|
|
// Go through the list of logon sessions and dereferences them all
|
|
//
|
|
|
|
while (!IsListEmpty(&KerbLogonSessionList.List))
|
|
{
|
|
LogonSession = CONTAINING_RECORD(
|
|
KerbLogonSessionList.List.Flink,
|
|
KERB_LOGON_SESSION,
|
|
ListEntry.Next
|
|
);
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbLogonSessionList,
|
|
&LogonSession->ListEntry,
|
|
TRUE
|
|
);
|
|
|
|
KerbDereferenceLogonSession(LogonSession);
|
|
|
|
}
|
|
|
|
KerbFreeList(&KerbLogonSessionList);
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbAllocateLogonSession
|
|
//
|
|
// Synopsis: Allocates a logon session structure
|
|
//
|
|
// Effects: Allocates a logon session, but does not add it to the
|
|
// list of logon sessions
|
|
//
|
|
// Arguments: NewLogonSession - receives a new logon session allocated
|
|
// with KerbAllocate
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS on success
|
|
// STATUS_INSUFFICIENT_RESOURCES if the allocation fails
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
KerbAllocateLogonSession(
|
|
PKERB_LOGON_SESSION * NewLogonSession
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_LOGON_SESSION LogonSession;
|
|
|
|
LogonSession = (PKERB_LOGON_SESSION) KerbAllocate(
|
|
sizeof(KERB_LOGON_SESSION)
|
|
);
|
|
if (LogonSession == NULL)
|
|
{
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Set the references to 1 since we are returning a pointer to the
|
|
// logon session
|
|
//
|
|
|
|
KerbInitializeListEntry(
|
|
&LogonSession->ListEntry
|
|
);
|
|
|
|
InitializeListHead(&LogonSession->SspCredentials);
|
|
|
|
//
|
|
// Initialize the ticket caches
|
|
//
|
|
|
|
KerbInitTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
|
|
KerbInitTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
|
|
KerbInitTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);
|
|
|
|
Status = RtlInitializeCriticalSection(&LogonSession->Lock);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KerbFree(LogonSession);
|
|
}
|
|
else
|
|
{
|
|
*NewLogonSession = LogonSession;
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInsertLogonSession
|
|
//
|
|
// Synopsis: Inserts a logon session into the list of logon sessions
|
|
//
|
|
// Effects: bumps reference count on logon session
|
|
//
|
|
// Arguments: LogonSession - LogonSession to insert
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS always
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbInsertLogonSession(
|
|
IN PKERB_LOGON_SESSION LogonSession
|
|
)
|
|
{
|
|
KerbInsertListEntry(
|
|
&LogonSession->ListEntry,
|
|
&KerbLogonSessionList
|
|
);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbReferenceLogonSession
|
|
//
|
|
// Synopsis: Locates a logon session from the logon ID and references it
|
|
//
|
|
// Effects: Increments reference count and possible unlinks it from list
|
|
//
|
|
// Arguments: LogonId - LogonId of logon session to locate
|
|
// RemoveFromList - If TRUE, logon session will be delinked
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
PKERB_LOGON_SESSION
|
|
KerbReferenceLogonSession(
|
|
IN PLUID LogonId,
|
|
IN BOOLEAN RemoveFromList
|
|
)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PKERB_LOGON_SESSION LogonSession = NULL;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
KerbLockList(&KerbLogonSessionList);
|
|
|
|
//
|
|
// Go through the list of logon sessions looking for the correct
|
|
// LUID
|
|
//
|
|
|
|
for (ListEntry = KerbLogonSessionList.List.Flink ;
|
|
ListEntry != &KerbLogonSessionList.List ;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
LogonSession = CONTAINING_RECORD(ListEntry, KERB_LOGON_SESSION, ListEntry.Next);
|
|
if (RtlEqualLuid(
|
|
&LogonSession->LogonId,
|
|
LogonId
|
|
) )
|
|
{
|
|
D_DebugLog((DEB_TRACE_LSESS,"Referencing session 0x%x:0x%x, Remove=%d\n",
|
|
LogonSession->LogonId.HighPart,
|
|
LogonSession->LogonId.LowPart,
|
|
RemoveFromList
|
|
));
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbLogonSessionList,
|
|
&LogonSession->ListEntry,
|
|
RemoveFromList
|
|
);
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (!Found)
|
|
{
|
|
LogonSession = NULL;
|
|
}
|
|
KerbUnlockList(&KerbLogonSessionList);
|
|
return(LogonSession);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeLogonSession
|
|
//
|
|
// Synopsis: Frees a logon session and all associated data
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: LogonSession
|
|
//
|
|
// Requires: the logon session must already be unlinked
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbFreeLogonSession(
|
|
IN PKERB_LOGON_SESSION LogonSession
|
|
)
|
|
{
|
|
DsysAssert((LogonSession->ListEntry.Next.Flink == NULL) &&
|
|
(LogonSession->ListEntry.Next.Blink == NULL));
|
|
|
|
// Don't purge creds, as there isn't a ref-count for the credential in
|
|
// the logon session list,and there might be outstanding handles to your
|
|
// credentials in a local system process.
|
|
//KerbPurgeCredentials(&LogonSession->SspCredentials);
|
|
KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
|
|
KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
|
|
|
|
if (LogonSession->PrimaryCredentials.Passwords != NULL)
|
|
{
|
|
KerbFreeStoredCred(LogonSession->PrimaryCredentials.Passwords);
|
|
}
|
|
|
|
if (LogonSession->PrimaryCredentials.OldPasswords != NULL)
|
|
{
|
|
KerbFreeStoredCred(LogonSession->PrimaryCredentials.OldPasswords);
|
|
}
|
|
|
|
KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
|
|
KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);
|
|
|
|
if (LogonSession->PrimaryCredentials.ClearPassword.Buffer != NULL)
|
|
{
|
|
RtlZeroMemory(
|
|
LogonSession->PrimaryCredentials.ClearPassword.Buffer,
|
|
LogonSession->PrimaryCredentials.ClearPassword.Length
|
|
);
|
|
KerbFreeString(&LogonSession->PrimaryCredentials.ClearPassword);
|
|
}
|
|
|
|
if (LogonSession->PrimaryCredentials.PublicKeyCreds != NULL)
|
|
{
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
KerbReleasePkCreds(
|
|
LogonSession,
|
|
NULL
|
|
);
|
|
#endif // WIN32_CHICAGO
|
|
}
|
|
|
|
if ((LogonSession->LogonSessionFlags & KERB_LOGON_CREDMAN_INITIALIZED) != 0)
|
|
{
|
|
RtlDeleteCriticalSection(&LogonSession->CredmanCredentials.Lock);
|
|
}
|
|
|
|
|
|
RtlDeleteCriticalSection(&LogonSession->Lock);
|
|
KerbFree(LogonSession);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbDereferenceLogonSession
|
|
//
|
|
// Synopsis: Dereferences a logon session - if reference count goes
|
|
// to zero it frees the logon session
|
|
//
|
|
// Effects: decrements reference count
|
|
//
|
|
// Arguments: LogonSession - Logon session to dereference
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: none
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbDereferenceLogonSession(
|
|
IN PKERB_LOGON_SESSION LogonSession
|
|
)
|
|
{
|
|
if (KerbDereferenceListEntry(
|
|
&LogonSession->ListEntry,
|
|
&KerbLogonSessionList
|
|
) )
|
|
{
|
|
D_DebugLog((DEB_TRACE_LSESS,"Dereferencing and freeing logon session 0x%x:0x%x\n",
|
|
LogonSession->LogonId.HighPart,
|
|
LogonSession->LogonId.HighPart
|
|
));
|
|
KerbFreeLogonSession( LogonSession );
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbReferenceLogonSessionByPointer
|
|
//
|
|
// Synopsis: References a LogonSession by the LogonSession pointer itself.
|
|
//
|
|
// Effects: Increments reference count and possible unlinks it from list
|
|
//
|
|
// Arguments: LogonSession - The LogonSession to reference.
|
|
// RemoveFromList - If TRUE, LogonSession will be delinked
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
VOID
|
|
KerbReferenceLogonSessionByPointer(
|
|
IN PKERB_LOGON_SESSION LogonSession,
|
|
IN BOOLEAN RemoveFromList
|
|
)
|
|
{
|
|
|
|
KerbLockList(&KerbLogonSessionList);
|
|
|
|
KerbReferenceListEntry(
|
|
&KerbLogonSessionList,
|
|
&LogonSession->ListEntry,
|
|
RemoveFromList
|
|
);
|
|
|
|
KerbUnlockList(&KerbLogonSessionList);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbGetSaltForEtype
|
|
//
|
|
// Synopsis: Looks in the list of salt for an etype & returns it if found
|
|
//
|
|
// Effects: Allocate the output string
|
|
//
|
|
// Arguments: EncryptionType - etype searched for
|
|
// EtypeInfo - List of etypes
|
|
// DefaultSalt - salt to use if none provided
|
|
// Salt - receives the salt to use. On error, no key should be
|
|
// generated.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbGetSaltForEtype(
|
|
IN ULONG EncryptionType,
|
|
IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
|
|
IN OPTIONAL PKERB_STORED_CREDENTIAL PasswordList,
|
|
IN PUNICODE_STRING DefaultSalt,
|
|
OUT PUNICODE_STRING SaltToUse
|
|
)
|
|
{
|
|
PKERB_ETYPE_INFO ListEntry;
|
|
STRING TempString;
|
|
|
|
//
|
|
// If there is no etype, just use the default
|
|
//
|
|
|
|
if (EtypeInfo == NULL)
|
|
{
|
|
//
|
|
// If we have a password list, get the salt from that.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(PasswordList))
|
|
{
|
|
ULONG Index;
|
|
for (Index = 0; Index < PasswordList->CredentialCount ; Index++ )
|
|
{
|
|
if (PasswordList->Credentials[Index].Key.keytype == (int) EncryptionType)
|
|
{
|
|
if (PasswordList->Credentials[Index].Salt.Buffer != NULL)
|
|
{
|
|
return(KerbDuplicateString(
|
|
SaltToUse,
|
|
&PasswordList->Credentials[Index].Salt
|
|
));
|
|
}
|
|
else if (PasswordList->DefaultSalt.Buffer != NULL)
|
|
{
|
|
return(KerbDuplicateString(
|
|
SaltToUse,
|
|
&PasswordList->DefaultSalt
|
|
));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// otherise return the default
|
|
//
|
|
|
|
return(KerbDuplicateString(
|
|
SaltToUse,
|
|
DefaultSalt
|
|
));
|
|
}
|
|
|
|
//
|
|
// Otherwise, only return salt if the etype is in the list.
|
|
//
|
|
|
|
for (ListEntry = EtypeInfo; ListEntry != NULL ; ListEntry = ListEntry->next )
|
|
{
|
|
//
|
|
// First check for the encryption type we want.
|
|
//
|
|
|
|
if (ListEntry->value.encryption_type == (int) EncryptionType)
|
|
{
|
|
|
|
//
|
|
// if it has salt, return that.
|
|
//
|
|
|
|
if ((ListEntry->value.bit_mask & salt_present) != 0)
|
|
{
|
|
KERBERR KerbErr;
|
|
TempString.Buffer = (PCHAR) ListEntry->value.salt.value;
|
|
TempString.Length = (USHORT) ListEntry->value.salt.length;
|
|
TempString.MaximumLength = (USHORT) ListEntry->value.salt.length;
|
|
|
|
KerbErr = KerbStringToUnicodeString(
|
|
SaltToUse,
|
|
&TempString
|
|
);
|
|
return(KerbMapKerbError(KerbErr));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise return the default
|
|
//
|
|
|
|
return(KerbDuplicateString(
|
|
SaltToUse,
|
|
DefaultSalt
|
|
));
|
|
}
|
|
}
|
|
}
|
|
return(STATUS_OBJECT_NAME_NOT_FOUND);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbFreeStoredCred
|
|
//
|
|
// Synopsis: Frees a KERB_STORED_CREDENTIAL
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KerbFreeStoredCred(
|
|
IN PKERB_STORED_CREDENTIAL StoredCred
|
|
)
|
|
{
|
|
USHORT Index;
|
|
for (Index = 0; Index < StoredCred->CredentialCount + StoredCred->OldCredentialCount ; Index++ )
|
|
{
|
|
if (StoredCred->Credentials[Index].Salt.Buffer != NULL)
|
|
{
|
|
KerbFreeString(&StoredCred->Credentials[Index].Salt);
|
|
}
|
|
}
|
|
KerbFree(StoredCred);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbBuildPasswordList
|
|
//
|
|
// Synopsis: Builds a list of passwords for a logged on user
|
|
//
|
|
// Effects: allocates memory
|
|
//
|
|
// Arguments: Password - clear or OWF password
|
|
// PasswordFlags - Indicates whether the password is clear or OWF
|
|
// PasswordList - Receives new password list
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbBuildPasswordList(
|
|
IN PUNICODE_STRING Password,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PKERB_ETYPE_INFO SuppliedSalt,
|
|
IN PKERB_STORED_CREDENTIAL OldPasswords,
|
|
IN OPTIONAL PUNICODE_STRING PrincipalName,
|
|
IN KERB_ACCOUNT_TYPE AccountType,
|
|
IN ULONG PasswordFlags,
|
|
OUT PKERB_STORED_CREDENTIAL * PasswordList
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
|
|
ULONG CryptCount = 0 ;
|
|
PKERB_STORED_CREDENTIAL Credentials = NULL;
|
|
UNICODE_STRING KeySalt = {0};
|
|
UNICODE_STRING DefaultSalt = {0};
|
|
ULONG CredentialSize = 0;
|
|
ULONG CredentialCount = 0;
|
|
PCRYPTO_SYSTEM CryptoSystem;
|
|
ULONG Index, CredentialIndex = 0;
|
|
PUCHAR Base;
|
|
ULONG Offset;
|
|
KERBERR KerbErr;
|
|
|
|
*PasswordList = NULL;
|
|
|
|
//
|
|
// If we were passed an OWF, then there is just one password
|
|
//
|
|
|
|
if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
|
|
{
|
|
CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
|
|
CredentialCount++;
|
|
#ifndef DONT_SUPPORT_OLD_TYPES
|
|
CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
|
|
CredentialCount++;
|
|
#endif
|
|
}
|
|
else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
|
|
{
|
|
|
|
//
|
|
// Build the key salt.
|
|
//
|
|
|
|
KerbErr = KerbBuildKeySalt(
|
|
DomainName,
|
|
(ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? PrincipalName : UserName,
|
|
(ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? UnknownAccount : AccountType,
|
|
&DefaultSalt
|
|
);
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Can't build salt. Might as well fail here\n"));
|
|
Status = KerbMapKerbError(KerbErr);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// For a cleartext password, build a list of encryption types and
|
|
// create a key for each one
|
|
//
|
|
|
|
Status = CDBuildIntegrityVect(
|
|
&CryptCount,
|
|
CryptTypes
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Can't build a list of encryption types: 0x%x.\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
|
|
|
|
//
|
|
// Now find the size of the key for each crypto system
|
|
//
|
|
|
|
for (Index = 0; Index < CryptCount; Index++ )
|
|
{
|
|
Status = CDLocateCSystem(
|
|
CryptTypes[Index],
|
|
&CryptoSystem
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",CryptTypes[Index],Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) == 0) ||
|
|
(DefaultSalt.Buffer != NULL ))
|
|
{
|
|
CredentialSize += sizeof(KERB_KEY_DATA) + CryptoSystem->KeySize;
|
|
CredentialCount++;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No flags set, so nothing we can do.
|
|
//
|
|
D_DebugLog((DEB_WARN,"Password passed but no flags set\n"));
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
#ifdef notdef
|
|
//
|
|
// Add the space for the salt
|
|
//
|
|
|
|
CredentialSize += DefaultSalt.Length;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Add in the size of the base structure
|
|
//
|
|
|
|
CredentialSize += FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials);
|
|
Credentials = (PKERB_STORED_CREDENTIAL) KerbAllocate(CredentialSize);
|
|
if (Credentials == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Fill in the base structure
|
|
//
|
|
|
|
Credentials->Revision = KERB_PRIMARY_CRED_REVISION;
|
|
Credentials->Flags = 0;
|
|
Credentials->OldCredentialCount = 0;
|
|
|
|
|
|
//
|
|
// Now fill in the individual keys
|
|
//
|
|
|
|
Base = (PUCHAR) Credentials;
|
|
Offset = FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials) +
|
|
CredentialCount * sizeof(KERB_KEY_DATA);
|
|
|
|
#ifdef notdef
|
|
//
|
|
// Add the default salt
|
|
//
|
|
|
|
Credentials->DefaultSalt = DefaultSalt;
|
|
Credentials->DefaultSalt.Buffer = (LPWSTR) Base+Offset;
|
|
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
DefaultSalt.Buffer,
|
|
DefaultSalt.Length
|
|
);
|
|
Offset += Credentials->DefaultSalt.Length;
|
|
|
|
#endif
|
|
|
|
if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
|
|
{
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
Password->Buffer,
|
|
Password->Length
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
|
|
&Credentials->Credentials[CredentialIndex].Key,
|
|
Base + Offset,
|
|
Password->Length,
|
|
KERB_ETYPE_RC4_HMAC_NT
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
Base += Password->Length;
|
|
CredentialIndex++;
|
|
#ifndef DONT_SUPPORT_OLD_TYPES
|
|
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
Password->Buffer,
|
|
Password->Length
|
|
);
|
|
|
|
if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
|
|
&Credentials->Credentials[CredentialIndex].Key,
|
|
Base + Offset,
|
|
Password->Length,
|
|
KERB_ETYPE_RC4_HMAC_OLD
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
Base += Password->Length;
|
|
CredentialIndex++;
|
|
#endif
|
|
}
|
|
else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
|
|
{
|
|
KERB_ENCRYPTION_KEY TempKey = {0};
|
|
|
|
//
|
|
// Now find the size of the key for each crypto system
|
|
//
|
|
|
|
for (Index = 0; Index < CryptCount; Index++ )
|
|
{
|
|
CryptoSystem = NULL;
|
|
Status = CDLocateCSystem(
|
|
CryptTypes[Index],
|
|
&CryptoSystem
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
Status = KerbGetSaltForEtype(
|
|
CryptTypes[Index],
|
|
SuppliedSalt,
|
|
OldPasswords,
|
|
&DefaultSalt,
|
|
&KeySalt
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// This probably means that the etype wasn't supported by
|
|
// the kdc
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we don't have salt, skip this crypt system
|
|
//
|
|
|
|
if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) != 0) &&
|
|
(KeySalt.Buffer == NULL ))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
KerbErr = KerbHashPasswordEx(
|
|
Password,
|
|
&KeySalt,
|
|
CryptTypes[Index],
|
|
&TempKey
|
|
);
|
|
|
|
|
|
if (!KERB_SUCCESS(KerbErr))
|
|
{
|
|
//
|
|
// It is possible that the password can't be used for every
|
|
// encryption scheme, so skip failures
|
|
//
|
|
|
|
D_DebugLog((DEB_WARN, "Failed to hash pasword %wZ with type 0x%x\n",
|
|
Password,CryptTypes[Index] ));
|
|
KerbFreeString(&KeySalt);
|
|
continue;
|
|
}
|
|
|
|
|
|
DsysAssert(CryptoSystem->KeySize >= TempKey.keyvalue.length);
|
|
|
|
|
|
//
|
|
// Copy the salt and key data into the credentials
|
|
//
|
|
|
|
Credentials->Credentials[CredentialIndex].Salt = KeySalt;
|
|
|
|
RtlInitUnicodeString(
|
|
&KeySalt,
|
|
0
|
|
);
|
|
Credentials->Credentials[CredentialIndex].Key = TempKey;
|
|
Credentials->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset;
|
|
|
|
RtlCopyMemory(
|
|
Base + Offset,
|
|
TempKey.keyvalue.value,
|
|
TempKey.keyvalue.length
|
|
);
|
|
|
|
Offset += TempKey.keyvalue.length;
|
|
|
|
KerbFreeKey(
|
|
&TempKey
|
|
);
|
|
|
|
|
|
CredentialIndex++;
|
|
}
|
|
|
|
}
|
|
Credentials->CredentialCount = (USHORT) CredentialIndex;
|
|
*PasswordList = Credentials;
|
|
Credentials = NULL;
|
|
|
|
Cleanup:
|
|
if (Credentials != NULL)
|
|
{
|
|
KerbFreeStoredCred(Credentials);
|
|
}
|
|
KerbFreeString(&KeySalt);
|
|
KerbFreeString(&DefaultSalt);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreatePrimaryCredentials
|
|
//
|
|
// Synopsis: Fills in a new primary credentials structure
|
|
//
|
|
// Effects: allocates space for the user name and domain name
|
|
//
|
|
// Arguments: AccountName - Account name of this user
|
|
// DomainName - domain name of this user
|
|
// Password - Optionally contains a kereros hash of the password
|
|
// Note: if present, it is used and zeroed out.
|
|
// PrimaryCredentials - contains structure to fill in.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbCreatePrimaryCredentials(
|
|
IN PUNICODE_STRING AccountName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN OPTIONAL PUNICODE_STRING Password,
|
|
IN OPTIONAL PUNICODE_STRING OldPassword,
|
|
IN ULONG PasswordFlags,
|
|
IN PLUID LogonId,
|
|
OUT PKERB_PRIMARY_CREDENTIAL PrimaryCredentials
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
LUID SystemLogonId = SYSTEM_LUID;
|
|
BOOLEAN IsSystemLogon = FALSE;
|
|
BOOLEAN IsPersonal = KerbRunningPersonal();
|
|
|
|
|
|
//
|
|
// We can only accept account name / service name / pwds of max_unicode_length
|
|
// -1, because we NULL terminate these for later DES derivation, and the
|
|
// input buffers from LogonUser may not be NULL terminated.
|
|
//
|
|
if ((AccountName->Length > KERB_MAX_UNICODE_STRING) ||
|
|
(DomainName->Length > KERB_MAX_UNICODE_STRING))
|
|
|
|
{
|
|
return (STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
if ((ARGUMENT_PRESENT(Password) && (Password->Length > KERB_MAX_UNICODE_STRING)) ||
|
|
(ARGUMENT_PRESENT(OldPassword) && (OldPassword->Length > KERB_MAX_UNICODE_STRING)))
|
|
{
|
|
return (STATUS_NAME_TOO_LONG);
|
|
}
|
|
|
|
if (RtlEqualLuid(
|
|
&SystemLogonId,
|
|
LogonId))
|
|
{
|
|
IsSystemLogon = TRUE;
|
|
}
|
|
|
|
Status = KerbDuplicateString(
|
|
&PrimaryCredentials->UserName,
|
|
AccountName
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The system logon always comes in uppercase, so lowercase it.
|
|
//
|
|
|
|
if (IsSystemLogon)
|
|
{
|
|
RtlDowncaseUnicodeString(
|
|
&PrimaryCredentials->UserName,
|
|
&PrimaryCredentials->UserName,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
Status = KerbDuplicateString(
|
|
&PrimaryCredentials->DomainName,
|
|
DomainName
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Neuter personal so it can't act as a server,
|
|
// even if someone has hacked in a machine pwd.
|
|
//
|
|
if (IsSystemLogon && IsPersonal)
|
|
{
|
|
PrimaryCredentials->Passwords = NULL;
|
|
PrimaryCredentials->OldPasswords = NULL;
|
|
D_DebugLog((DEB_WARN, "Running personal - No kerberos for SYSTEM LUID\n"));
|
|
}
|
|
else
|
|
{
|
|
|
|
Status = KerbBuildPasswordList(
|
|
Password,
|
|
&PrimaryCredentials->UserName,
|
|
&PrimaryCredentials->DomainName,
|
|
NULL, // no supplied salt
|
|
NULL, // no old paswords
|
|
NULL, // no principal name
|
|
IsSystemLogon ? MachineAccount : UserAccount,
|
|
PasswordFlags,
|
|
&PrimaryCredentials->Passwords
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(OldPassword) && (OldPassword->Buffer != NULL))
|
|
{
|
|
Status = KerbBuildPasswordList(
|
|
OldPassword,
|
|
&PrimaryCredentials->UserName,
|
|
&PrimaryCredentials->DomainName,
|
|
NULL, // no supplied salt
|
|
PrimaryCredentials->Passwords,
|
|
NULL, // no principal name
|
|
IsSystemLogon ? MachineAccount : UserAccount,
|
|
PasswordFlags,
|
|
&PrimaryCredentials->OldPasswords
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store the clear password if necessary
|
|
//
|
|
|
|
if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
|
|
{
|
|
Status = KerbDuplicatePassword(
|
|
&PrimaryCredentials->ClearPassword,
|
|
Password
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
KerbHidePassword(
|
|
&PrimaryCredentials->ClearPassword
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
KerbFreeString(&PrimaryCredentials->UserName);
|
|
KerbFreeString(&PrimaryCredentials->DomainName);
|
|
if (PrimaryCredentials->ClearPassword.Buffer != NULL)
|
|
{
|
|
RtlZeroMemory(
|
|
PrimaryCredentials->ClearPassword.Buffer,
|
|
PrimaryCredentials->ClearPassword.Length
|
|
);
|
|
KerbFreeString(&PrimaryCredentials->ClearPassword);
|
|
}
|
|
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbChangeCredentialsPassword
|
|
//
|
|
// Synopsis: Changes the password for a KERB_PRIMARY_CREDENTIALS -
|
|
// copies the current password into the old password field
|
|
// and then sets the new pasword as the primary password.
|
|
// If no new password is provided, it just fixes up the salt.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbChangeCredentialsPassword(
|
|
IN PKERB_PRIMARY_CREDENTIAL PrimaryCredentials,
|
|
IN OPTIONAL PUNICODE_STRING NewPassword,
|
|
IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
|
|
IN KERB_ACCOUNT_TYPE AccountType,
|
|
IN ULONG PasswordFlags
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PKERB_STORED_CREDENTIAL Passwords = NULL;
|
|
|
|
|
|
//
|
|
// LogonSession no password was supplied, use the cleartext password
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(NewPassword) && (PrimaryCredentials->ClearPassword.Buffer == NULL))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Can't change password without new password\n"));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
if (PrimaryCredentials->ClearPassword.Buffer != NULL)
|
|
{
|
|
KerbRevealPassword(&PrimaryCredentials->ClearPassword);
|
|
}
|
|
|
|
Status = KerbBuildPasswordList(
|
|
(ARGUMENT_PRESENT(NewPassword) ? NewPassword : &PrimaryCredentials->ClearPassword),
|
|
&PrimaryCredentials->UserName,
|
|
&PrimaryCredentials->DomainName,
|
|
EtypeInfo,
|
|
PrimaryCredentials->Passwords,
|
|
NULL, // no principal name
|
|
AccountType,
|
|
PasswordFlags,
|
|
&Passwords
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (PrimaryCredentials->ClearPassword.Buffer != NULL)
|
|
{
|
|
KerbHidePassword(&PrimaryCredentials->ClearPassword);
|
|
}
|
|
}
|
|
else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
|
|
{
|
|
KerbFreeString(&PrimaryCredentials->ClearPassword);
|
|
|
|
Status = KerbDuplicatePassword(
|
|
&PrimaryCredentials->ClearPassword,
|
|
NewPassword
|
|
);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
KerbHidePassword(
|
|
&PrimaryCredentials->ClearPassword
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KerbHidePassword(
|
|
&PrimaryCredentials->ClearPassword
|
|
);
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Move the current password to the old password
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(NewPassword))
|
|
{
|
|
if (PrimaryCredentials->OldPasswords != NULL)
|
|
{
|
|
KerbFreeStoredCred(PrimaryCredentials->OldPasswords);
|
|
}
|
|
PrimaryCredentials->OldPasswords = PrimaryCredentials->Passwords;
|
|
}
|
|
else
|
|
{
|
|
KerbFreeStoredCred(PrimaryCredentials->Passwords);
|
|
}
|
|
|
|
PrimaryCredentials->Passwords = Passwords;
|
|
Passwords = NULL;
|
|
|
|
Cleanup:
|
|
if (Passwords != NULL)
|
|
{
|
|
MIDL_user_free(Passwords);
|
|
}
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateLogonSession
|
|
//
|
|
// Synopsis: Allocates a logon session, fills in the various fields,
|
|
// and inserts it on the logon session list
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: LogonId - LogonId for new logon session
|
|
// AccountName - Account name of user
|
|
// Domain Name - Domain name of user
|
|
// Password - password for user
|
|
// OldPassword - Old password for user, if present
|
|
// LogonType - Type of logon
|
|
// NewLogonSession - Receives new logon session (referenced)
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbCreateLogonSession(
|
|
IN PLUID LogonId,
|
|
IN PUNICODE_STRING AccountName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN OPTIONAL PUNICODE_STRING Password,
|
|
IN OPTIONAL PUNICODE_STRING OldPassword,
|
|
IN ULONG PasswordFlags,
|
|
IN SECURITY_LOGON_TYPE LogonType,
|
|
OUT PKERB_LOGON_SESSION * NewLogonSession
|
|
)
|
|
{
|
|
PKERB_LOGON_SESSION LogonSession = NULL;
|
|
NTSTATUS Status;
|
|
|
|
D_DebugLog((DEB_TRACE_LSESS,"Creating logon session for 0x%x:0x%x\n",
|
|
LogonId->HighPart,LogonId->LowPart));
|
|
|
|
|
|
//
|
|
// Check for a logon session with the same id
|
|
//
|
|
|
|
LogonSession = KerbReferenceLogonSession(
|
|
LogonId,
|
|
FALSE // don't unlink
|
|
);
|
|
if (LogonSession != NULL)
|
|
{
|
|
//
|
|
// We already have this logon session, so don't create another one.
|
|
//
|
|
KerbDereferenceLogonSession(LogonSession);
|
|
|
|
return(STATUS_OBJECT_NAME_EXISTS);
|
|
}
|
|
//
|
|
// Allocate the new logon session
|
|
//
|
|
|
|
Status = KerbAllocateLogonSession( &LogonSession );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Fill in the logon session components
|
|
//
|
|
|
|
LogonSession->LogonId = *LogonId;
|
|
LogonSession->Lifetime = KerbGlobalWillNeverTime;
|
|
|
|
//
|
|
// All logons are deferred until proven otherwise
|
|
//
|
|
|
|
LogonSession->LogonSessionFlags = KERB_LOGON_DEFERRED;
|
|
|
|
//
|
|
// If the domain name is equal to the computer name then the logon was
|
|
// local.
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
KerbGlobalReadLock();
|
|
|
|
if (RtlEqualDomainName(
|
|
DomainName,
|
|
&KerbGlobalMachineName
|
|
))
|
|
{
|
|
LogonSession->LogonSessionFlags |= KERB_LOGON_LOCAL_ONLY;
|
|
}
|
|
|
|
KerbGlobalReleaseLock();
|
|
#endif // WIN32_CHICAGO
|
|
|
|
Status = KerbCreatePrimaryCredentials(
|
|
AccountName,
|
|
DomainName,
|
|
Password,
|
|
OldPassword,
|
|
PasswordFlags,
|
|
LogonId,
|
|
&LogonSession->PrimaryCredentials
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbInitializeList(
|
|
&LogonSession->CredmanCredentials
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
LogonSession->LogonSessionFlags |= KERB_LOGON_CREDMAN_INITIALIZED;
|
|
|
|
|
|
|
|
if ((LogonSession->PrimaryCredentials.Passwords == NULL) ||
|
|
(LogonSession->PrimaryCredentials.Passwords->CredentialCount == 0))
|
|
{
|
|
LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
|
|
}
|
|
|
|
//
|
|
// S4ULogon (any reason to keep this??? maybe for debug..)
|
|
//
|
|
if ((PasswordFlags & KERB_LOGON_S4U_SESSION) != 0)
|
|
{
|
|
LogonSession->LogonSessionFlags |= KERB_LOGON_S4U_SESSION;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Now that the logon session structure is filled out insert it
|
|
// into the list. After this you need to hold the logon session lock
|
|
// to read or write this logon session.
|
|
//
|
|
|
|
Status = KerbInsertLogonSession(LogonSession);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
*NewLogonSession = LogonSession;
|
|
|
|
Cleanup:
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (NULL != LogonSession)
|
|
{
|
|
KerbFreeLogonSession(LogonSession);
|
|
LogonSession = NULL;
|
|
}
|
|
}
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbCreateLogonSessionFromKerbCred
|
|
//
|
|
// Synopsis: Creates a logon session from the delegation information
|
|
// in a KERB_CRED structure. If a logon session is supplied,
|
|
// it is updated with the supplied information.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: LogonId - Logon id for the logon session
|
|
// Ticket - Ticket from the AP request containing the client's
|
|
// name and realm.
|
|
// KerbCred - KERB_CRED containing the delegation tickets
|
|
// EncryptedCred - Structure containing information about the
|
|
// tickets, such as session keys, flags, etc.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS
|
|
KerbCreateLogonSessionFromKerbCred(
|
|
IN OPTIONAL PLUID LogonId,
|
|
IN PKERB_ENCRYPTED_TICKET Ticket,
|
|
IN PKERB_CRED KerbCred,
|
|
IN PKERB_ENCRYPTED_CRED EncryptedCred,
|
|
IN OUT PKERB_LOGON_SESSION *OldLogonSession
|
|
)
|
|
{
|
|
|
|
PKERB_LOGON_SESSION LogonSession = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING AccountName;
|
|
UNICODE_STRING ShortAccountName;
|
|
UNICODE_STRING DomainName;
|
|
KERB_ENCRYPTED_KDC_REPLY FakeReplyBody;
|
|
KERB_KDC_REPLY FakeReply;
|
|
PKERB_CRED_INFO CredInfo;
|
|
PKERB_CRED_TICKET_LIST TicketList;
|
|
PKERB_CRED_INFO_LIST CredInfoList;
|
|
PKERB_TICKET_CACHE_ENTRY TicketCacheEntry;
|
|
ULONG NameType;
|
|
LPWSTR LastSlash;
|
|
BOOLEAN LogonSessionLocked = FALSE;
|
|
BOOLEAN CreatedLogonSession = TRUE;
|
|
PKERB_TICKET_CACHE TicketCache = NULL;
|
|
ULONG TgtFlags = KERB_TICKET_CACHE_PRIMARY_TGT;
|
|
ULONG CacheFlags = 0;
|
|
|
|
AccountName.Buffer = NULL;
|
|
DomainName.Buffer = NULL;
|
|
if (ARGUMENT_PRESENT(LogonId))
|
|
{
|
|
D_DebugLog((DEB_TRACE_LSESS,"Creating logon session for 0x%x:0x%x\n",
|
|
LogonId->HighPart,LogonId->LowPart));
|
|
}
|
|
|
|
|
|
if (!KERB_SUCCESS(KerbConvertPrincipalNameToString(
|
|
&AccountName,
|
|
&NameType,
|
|
&Ticket->client_name
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We need to strip off everything before the last '\' in case there
|
|
// was a domain name.
|
|
//
|
|
|
|
LastSlash = wcsrchr(AccountName.Buffer, L'\\');
|
|
if (LastSlash != NULL)
|
|
{
|
|
ShortAccountName.Buffer = LastSlash+1;
|
|
RtlInitUnicodeString(
|
|
&ShortAccountName,
|
|
ShortAccountName.Buffer
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ShortAccountName = AccountName;
|
|
}
|
|
if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
|
|
&DomainName,
|
|
&Ticket->client_realm
|
|
)))
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
D_DebugLog((DEB_TRACE, "Creating delegation logon session for %wZ \\ %wZ\n",
|
|
&DomainName, &ShortAccountName ));
|
|
|
|
if ((*OldLogonSession) == NULL)
|
|
{
|
|
//
|
|
// Allocate the new logon session
|
|
//
|
|
|
|
Status = KerbAllocateLogonSession( &LogonSession );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Fill in the logon session components
|
|
//
|
|
|
|
LogonSession->LogonId = *LogonId;
|
|
LogonSession->Lifetime = KerbGlobalWillNeverTime;
|
|
|
|
|
|
Status = KerbCreatePrimaryCredentials(
|
|
&ShortAccountName,
|
|
&DomainName,
|
|
NULL, // no password
|
|
NULL, // no old password
|
|
0, // no flags
|
|
LogonId,
|
|
&LogonSession->PrimaryCredentials
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
|
|
}
|
|
else
|
|
{
|
|
CreatedLogonSession = FALSE;
|
|
KerbWriteLockLogonSessions(*OldLogonSession);
|
|
LogonSessionLocked = TRUE;
|
|
LogonSession = *OldLogonSession;
|
|
|
|
//
|
|
// If the user name & domain name are blank, update them from the
|
|
// ticket.
|
|
//
|
|
|
|
if (LogonSession->PrimaryCredentials.UserName.Length == 0)
|
|
{
|
|
KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
|
|
LogonSession->PrimaryCredentials.UserName = AccountName;
|
|
AccountName.Buffer = NULL;
|
|
}
|
|
if (LogonSession->PrimaryCredentials.DomainName.Length == 0)
|
|
{
|
|
KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);
|
|
LogonSession->PrimaryCredentials.DomainName = DomainName;
|
|
DomainName.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now stick the ticket into the ticket cache. First build up a fake
|
|
// KDC reply message from the encryped cred info.
|
|
//
|
|
|
|
TicketList = KerbCred->tickets;
|
|
CredInfoList = EncryptedCred->ticket_info;
|
|
|
|
while (TicketList != NULL)
|
|
{
|
|
TimeStamp Endtime = {0};
|
|
|
|
if (CredInfoList == NULL)
|
|
{
|
|
|
|
D_DebugLog((DEB_ERROR, "No ticket info in encrypted cred. %ws, line %d\n", THIS_FILE, __LINE__));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
CredInfo = &CredInfoList->value;
|
|
|
|
//
|
|
// Set the lifetime to the end or renew_until of the longest lived ticket
|
|
//
|
|
|
|
if ((CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present) != 0)
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Endtime,
|
|
&CredInfo->KERB_CRED_INFO_renew_until,
|
|
0 // no usec
|
|
);
|
|
}
|
|
else if ((CredInfo->bit_mask & endtime_present) != 0)
|
|
{
|
|
KerbConvertGeneralizedTimeToLargeInt(
|
|
&Endtime,
|
|
&CredInfo->endtime,
|
|
0 // no usec
|
|
);
|
|
}
|
|
|
|
if (Endtime.QuadPart != 0)
|
|
{
|
|
if (LogonSession->Lifetime.QuadPart == KerbGlobalWillNeverTime.QuadPart)
|
|
{
|
|
LogonSession->Lifetime.QuadPart = Endtime.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
LogonSession->Lifetime.QuadPart = max(LogonSession->Lifetime.QuadPart,Endtime.QuadPart);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
RtlZeroMemory(
|
|
&FakeReplyBody,
|
|
sizeof(KERB_ENCRYPTED_KDC_REPLY)
|
|
);
|
|
|
|
FakeReplyBody.session_key = CredInfo->key;
|
|
FakeReplyBody.nonce = 0;
|
|
|
|
//
|
|
// Set the ticket flags
|
|
//
|
|
|
|
if (CredInfo->bit_mask & flags_present)
|
|
{
|
|
FakeReplyBody.flags = CredInfo->flags;
|
|
}
|
|
else
|
|
{
|
|
FakeReplyBody.flags.length = 0;
|
|
FakeReplyBody.flags.value = NULL;
|
|
}
|
|
|
|
FakeReplyBody.authtime = Ticket->authtime;
|
|
|
|
if (CredInfo->bit_mask & KERB_CRED_INFO_starttime_present)
|
|
{
|
|
FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_starttime =
|
|
CredInfo->KERB_CRED_INFO_starttime;
|
|
FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_starttime_present;
|
|
}
|
|
|
|
//
|
|
// If an end time was sent, use it, otherwise assume the ticket
|
|
// lasts forever
|
|
//
|
|
|
|
if (CredInfo->bit_mask & endtime_present)
|
|
{
|
|
FakeReplyBody.endtime =
|
|
CredInfo->endtime;
|
|
}
|
|
else
|
|
{
|
|
KerbConvertLargeIntToGeneralizedTime(
|
|
&FakeReplyBody.endtime,
|
|
NULL,
|
|
&KerbGlobalWillNeverTime
|
|
);
|
|
}
|
|
|
|
if (CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present)
|
|
{
|
|
FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_renew_until =
|
|
CredInfo->KERB_CRED_INFO_renew_until;
|
|
FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_renew_until_present;
|
|
}
|
|
|
|
FakeReplyBody.server_name = TicketList->value.server_name;
|
|
FakeReplyBody.server_realm = TicketList->value.realm;
|
|
|
|
//
|
|
// Determine which ticket cache to use
|
|
//
|
|
|
|
if ((FakeReplyBody.server_name.name_string != NULL) &&
|
|
_stricmp(
|
|
FakeReplyBody.server_name.name_string->value,
|
|
KDC_PRINCIPAL_NAME_A) == 0)
|
|
{
|
|
TicketCache = &LogonSession->PrimaryCredentials.AuthenticationTicketCache;
|
|
//
|
|
// We only want to use the primary_tgt flag the first time through
|
|
//
|
|
|
|
CacheFlags = TgtFlags;
|
|
TgtFlags = 0;
|
|
D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to authentication ticket cache\n"));
|
|
}
|
|
else
|
|
{
|
|
TicketCache = &LogonSession->PrimaryCredentials.ServerTicketCache;
|
|
CacheFlags = 0;
|
|
D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to server ticket cache\n"));
|
|
}
|
|
|
|
|
|
FakeReply.client_name = Ticket->client_name;
|
|
FakeReply.ticket = TicketList->value;
|
|
FakeReply.client_realm = Ticket->client_realm;
|
|
Status = KerbCacheTicket(
|
|
TicketCache,
|
|
&FakeReply,
|
|
&FakeReplyBody,
|
|
NULL, // no target name
|
|
NULL,
|
|
CacheFlags,
|
|
TRUE, // link
|
|
&TicketCacheEntry
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to cache ticket: 0x%x. %ws, line %d\n",
|
|
Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
LogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
|
|
|
|
KerbDereferenceTicketCacheEntry(TicketCacheEntry);
|
|
|
|
CredInfoList = CredInfoList->next;
|
|
TicketList = TicketList->next;
|
|
}
|
|
|
|
//
|
|
// Now that the logon session structure is filled out insert it
|
|
// into the list. After this you need to hold the logon session lock
|
|
// to read or write this logon session.
|
|
//
|
|
|
|
if (*OldLogonSession == NULL)
|
|
{
|
|
Status = KerbInsertLogonSession(LogonSession);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
*OldLogonSession = LogonSession;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
|
|
if (LogonSessionLocked)
|
|
{
|
|
KerbUnlockLogonSessions(LogonSession);
|
|
}
|
|
|
|
if (CreatedLogonSession)
|
|
{
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (LogonSession != NULL)
|
|
{
|
|
KerbFreeLogonSession(LogonSession);
|
|
}
|
|
}
|
|
}
|
|
KerbFreeString(&AccountName);
|
|
KerbFreeString(&DomainName);
|
|
return(Status);
|
|
|
|
}
|
|
|
|
#endif // WIN32_CHICAGO
|
|
|