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.
9902 lines
266 KiB
9902 lines
266 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
credmgr.cxx
|
|
|
|
Abstract:
|
|
|
|
Credential Manager Interfaces
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (CliffV)
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <lsapch.hxx>
|
|
|
|
extern "C" {
|
|
#include <wincrypt.h>
|
|
#include <windns.h>
|
|
#include <align.h>
|
|
#include <rc4.h>
|
|
#include <des.h>
|
|
#include <modes.h>
|
|
#include <cryptdll.h>
|
|
#include <names.h>
|
|
#include <smbgtpt.h>
|
|
#include <shfolder.h>
|
|
#include <netlibnt.h>
|
|
#include <stddef.h>
|
|
}
|
|
|
|
//
|
|
// Include routines common with netapi32.dll
|
|
//
|
|
|
|
#include <credp.h>
|
|
#include <..\netclient\credapi.c>
|
|
|
|
extern "C"
|
|
{
|
|
BOOLEAN
|
|
LsapIsRunningOnPersonal(
|
|
VOID
|
|
);
|
|
}
|
|
|
|
//
|
|
// Local structures
|
|
//
|
|
|
|
//
|
|
// Structure describing a canonical credential
|
|
//
|
|
// This is the structure of a single credential as stored in memory in the LSA process.
|
|
//
|
|
|
|
typedef struct _CANONICAL_CREDENTIAL {
|
|
|
|
//
|
|
// The Credential itself
|
|
//
|
|
|
|
CREDENTIALW Cred;
|
|
|
|
//
|
|
// The size in bytes of the clear text credential blob
|
|
//
|
|
|
|
ULONG ClearCredentialBlobSize;
|
|
|
|
//
|
|
// Link to next entry in the list of credentials in the credential set.
|
|
// Access serialized by UserCredentialSets->CritSect
|
|
//
|
|
|
|
LIST_ENTRY Next;
|
|
|
|
//
|
|
// UNICODE_STRING form of Cred.TargetName
|
|
//
|
|
|
|
UNICODE_STRING TargetName;
|
|
|
|
//
|
|
// UNICODE_STRING form of Cred.TargetAlias
|
|
//
|
|
|
|
UNICODE_STRING TargetAlias;
|
|
|
|
//
|
|
// UNICODE_STRING form of Cred.UserName
|
|
//
|
|
|
|
UNICODE_STRING UserName;
|
|
|
|
//
|
|
// Describe the wildcard nature of this credential
|
|
//
|
|
WILDCARD_TYPE WildcardType;
|
|
|
|
//
|
|
// TargetName with the wildcard characters removed.
|
|
//
|
|
// The exact value is a function on WildcardType:
|
|
// WcDfsShareName: this is the 'server name' portion of the string
|
|
// WcServerWildcard: this is the 'server name' portion of the string preceeded by a .
|
|
// WcDomainWildcard: this is the 'domain name' portion of the string
|
|
// All Others: This is a copy of TargetName
|
|
//
|
|
// ??? I don't think we use it for WcDfsShare name any more.
|
|
|
|
UNICODE_STRING NonWildcardedTargetName;
|
|
|
|
//
|
|
// Size (in bytes) of this structure and all of the pointed to strings.
|
|
//
|
|
|
|
ULONG AllocatedSize;
|
|
|
|
//
|
|
// True if credential is to be returned to the caller.
|
|
// Access serialized by UserCredentialSets->CritSect
|
|
//
|
|
|
|
BOOLEAN ReturnMe;
|
|
|
|
|
|
} CANONICAL_CREDENTIAL, *PCANONICAL_CREDENTIAL;
|
|
|
|
|
|
//
|
|
// Structure describing when a credential should be prompted for.
|
|
//
|
|
// In general, this structure exists on a per-session basis for each credential that needs prompt
|
|
// data to be stored.
|
|
// In the future, this structure might contain other per-session/per-credential data.
|
|
//
|
|
typedef struct _PROMPT_DATA {
|
|
|
|
//
|
|
// Link to next entry in the list of PROMPT_DATA for this session.
|
|
// Access serialized by UserCredentialSets->CritSect
|
|
//
|
|
|
|
LIST_ENTRY Next;
|
|
|
|
//
|
|
// Target Name of the credential this prompt data is for.
|
|
//
|
|
|
|
UNICODE_STRING TargetName;
|
|
|
|
//
|
|
// Type of the credential this prompt data is for.
|
|
//
|
|
|
|
DWORD Type;
|
|
|
|
//
|
|
// Persistence of the credential this prompt data is for.
|
|
//
|
|
|
|
DWORD Persist;
|
|
|
|
//
|
|
// Boolean indicating if this credential was been written yet in this session.
|
|
//
|
|
|
|
BOOLEAN Written;
|
|
|
|
} PROMPT_DATA, *PPROMPT_DATA;
|
|
|
|
//
|
|
// Structure describing a canonical target info
|
|
//
|
|
|
|
typedef struct _CANONICAL_TARGET_INFO {
|
|
|
|
UNICODE_STRING TargetName;
|
|
UNICODE_STRING NetbiosServerName;
|
|
UNICODE_STRING DnsServerName;
|
|
UNICODE_STRING NetbiosDomainName;
|
|
UNICODE_STRING DnsDomainName;
|
|
UNICODE_STRING DnsTreeName;
|
|
UNICODE_STRING PackageName;
|
|
DWORD Flags;
|
|
DWORD CredTypeCount;
|
|
LPDWORD CredTypes;
|
|
|
|
|
|
//
|
|
// Define how a credential matches a target info.
|
|
//
|
|
// This list is ordered from most specific to least specific
|
|
//
|
|
#define CRED_DFS_SHARE_NAME 0
|
|
#define CRED_DNS_SERVER_NAME 1
|
|
#define CRED_NETBIOS_SERVER_NAME 2
|
|
#define CRED_TARGET_NAME 3
|
|
#define CRED_WILDCARD_SERVER_NAME 4
|
|
#define CRED_DNS_DOMAIN_NAME 5
|
|
#define CRED_NETBIOS_DOMAIN_NAME 6
|
|
#define CRED_UNIVERSAL_SESSION_NAME 7
|
|
#define CRED_UNIVERSAL_NAME 8
|
|
#define CRED_MAX_ALIASES 9
|
|
|
|
|
|
//
|
|
// Link into SessionCredSets->TargetInfoHashTable
|
|
//
|
|
|
|
LIST_ENTRY HashNext;
|
|
|
|
//
|
|
// Link into SessionCredSets->TargetInfoLruList
|
|
//
|
|
|
|
LIST_ENTRY LruNext;
|
|
|
|
} CANONICAL_TARGET_INFO, *PCANONICAL_TARGET_INFO;
|
|
|
|
|
|
//
|
|
// Structure describing an encryptable credential set
|
|
//
|
|
// This is the structure of a credential set as it appears on disk.
|
|
// It appears as a MARSHALED_CREDENTIAL_SET structure followed by a series of
|
|
// MARSHALED_CREDENTIAL structures.
|
|
//
|
|
|
|
typedef struct _MARSHALED_CREDENTIAL_SET {
|
|
|
|
// Version of this structure
|
|
ULONG Version;
|
|
#define MARSHALED_CREDENTIAL_SET_VERSION 1
|
|
|
|
// Size in bytes of the entire credential set.
|
|
ULONG Size;
|
|
|
|
} MARSHALED_CREDENTIAL_SET, *PMARSHALED_CREDENTIAL_SET;
|
|
|
|
typedef struct _MARSHALED_CREDENTIAL {
|
|
|
|
//
|
|
// Size in bytes of the entire credential (including variable length fields)
|
|
//
|
|
ULONG EntrySize;
|
|
|
|
//
|
|
// Fields from the CREDENTIALW structure
|
|
//
|
|
DWORD Flags;
|
|
DWORD Type;
|
|
FILETIME LastWritten;
|
|
DWORD CredentialBlobSize;
|
|
DWORD Persist;
|
|
DWORD AttributeCount;
|
|
DWORD Expansion1; // This field is reserved for expansion
|
|
DWORD Expansion2; // This field is reserved for expansion
|
|
|
|
} MARSHALED_CREDENTIAL, *PMARSHALED_CREDENTIAL;
|
|
|
|
|
|
|
|
//
|
|
// Macro to pick a credential set for a particular peristance.
|
|
//
|
|
|
|
#define CREDENTIAL_FILE_NAME L"Credentials";
|
|
#define CRED_PERSIST_MIN CRED_PERSIST_SESSION
|
|
#define CRED_PERSIST_MAX CRED_PERSIST_ENTERPRISE
|
|
|
|
#define PersistToCredentialSet( _CredentialSets, _Persist ) \
|
|
(((_Persist) == CRED_PERSIST_SESSION) ? \
|
|
(_CredentialSets)->SessionCredSets->SessionCredSet : \
|
|
(((_Persist) == CRED_PERSIST_LOCAL_MACHINE) ? \
|
|
(_CredentialSets)->UserCredentialSets->LocalMachineCredSet : \
|
|
(_CredentialSets)->UserCredentialSets->EnterpriseCredSet ) )
|
|
|
|
//
|
|
// Macro returns TRUE if the CredentialBlob is to be persisted
|
|
//
|
|
// Don't persist PINS passwords. We entrust the PIN to the CSP.
|
|
//
|
|
#define PersistCredBlob( _Credential ) \
|
|
( (_Credential)->Type != CRED_TYPE_DOMAIN_CERTIFICATE )
|
|
|
|
//
|
|
// Macro returns TRUE if the credential is to be prompted for
|
|
//
|
|
// If there is no prompt data,
|
|
// we've never prompted for this credential since logon.
|
|
// If there is prompt data,
|
|
// we can rely on the boolean
|
|
//
|
|
|
|
#define ShouldPromptNow( _PromptData ) \
|
|
((_PromptData) == NULL || !(_PromptData)->Written )
|
|
|
|
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
// List of USER_CREDENTIAL_SETS for each logged on user.
|
|
|
|
RTL_CRITICAL_SECTION CredentialSetListLock;
|
|
LIST_ENTRY CredentialSetList;
|
|
|
|
//
|
|
// Configurable values
|
|
//
|
|
#define CRED_TARGET_INFO_MAX_COUNT 1000;
|
|
ULONG CredTargetInfoMaxCount;
|
|
ULONG CredDisableDomainCreds;
|
|
ULONG CredIsPersonal;
|
|
|
|
//
|
|
// Define the default order for returning credentials from CredReadDomainCredentials
|
|
//
|
|
|
|
ULONG CredTypeDefaultOrder[] =
|
|
{
|
|
CRED_TYPE_DOMAIN_CERTIFICATE,
|
|
CRED_TYPE_DOMAIN_PASSWORD,
|
|
CRED_TYPE_DOMAIN_VISIBLE_PASSWORD
|
|
};
|
|
|
|
//
|
|
// Memory containing key for LSA protected memory.
|
|
//
|
|
|
|
PVOID CredLockedMemory = NULL;
|
|
ULONG CredLockedMemorySize = 0;
|
|
|
|
//
|
|
// DES-X keystate
|
|
//
|
|
|
|
DESXTable *g_pDESXKey = NULL;
|
|
unsigned __int64 g_Feedback;
|
|
|
|
//
|
|
// RC4 keystate
|
|
//
|
|
|
|
PBYTE g_pRandomKey = NULL;
|
|
ULONG g_cbRandomKey = 0;
|
|
|
|
//
|
|
// Forwards
|
|
//
|
|
|
|
NTSTATUS
|
|
CredpWriteCredential(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN OUT PCANONICAL_CREDENTIAL *NewCredential,
|
|
IN BOOLEAN FromPersistedFile,
|
|
IN BOOLEAN WritePinToCsp,
|
|
IN BOOLEAN PromptedFor,
|
|
IN DWORD Flags,
|
|
OUT PCANONICAL_CREDENTIAL *OldCredential OPTIONAL,
|
|
OUT PPROMPT_DATA *OldPromptData OPTIONAL,
|
|
OUT PPROMPT_DATA *NewPromptData OPTIONAL
|
|
);
|
|
|
|
|
|
extern DWORD
|
|
DPAPINotifyPasswordChange(
|
|
IN PUNICODE_STRING NetbiosDomainName,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING OldPassword,
|
|
IN PUNICODE_STRING NewPassword
|
|
);
|
|
|
|
|
|
VOID
|
|
LsaINotifyPasswordChanged(
|
|
IN PUNICODE_STRING NetbiosDomainName OPTIONAL,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DnsDomainName OPTIONAL,
|
|
IN PUNICODE_STRING Upn OPTIONAL,
|
|
IN PUNICODE_STRING OldPassword OPTIONAL,
|
|
IN PUNICODE_STRING NewPassword,
|
|
IN BOOLEAN Impersonating
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback from the MSV authentication package following a password
|
|
change. This routine will update any password caches maintained in the LSA.
|
|
|
|
|
|
Arguments:
|
|
|
|
NetbiosDomainName - Netbios domain name of the user whose password was changed
|
|
OPTIONAL if we're caching MIT realm credentials
|
|
|
|
UserName - User name of the user whose password was changed
|
|
|
|
DnsDomainName - If known, Dns Domain Name of the user whose password was changed
|
|
|
|
Upn - If known, the Upn of the user whose password was changed
|
|
|
|
OldPassword - If known, the previous password for the user.
|
|
|
|
NewPassword - The new password for the user.
|
|
|
|
Impersonating - If TRUE, this routine is called while impersonating the user changing
|
|
the password. That user isn't necessarily UserName.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Notify the credential manager of the change.
|
|
//
|
|
|
|
if ( Impersonating ) {
|
|
CredpNotifyPasswordChange( NetbiosDomainName,
|
|
UserName,
|
|
DnsDomainName,
|
|
Upn,
|
|
NewPassword );
|
|
}
|
|
|
|
//
|
|
// Notify DPAPI of the change.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(NetbiosDomainName)) {
|
|
|
|
DPAPINotifyPasswordChange( NetbiosDomainName,
|
|
UserName,
|
|
OldPassword,
|
|
NewPassword);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LsaEncryptMemory(
|
|
PBYTE pData,
|
|
ULONG cbData,
|
|
int Operation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine encrypts the specified buffer in place with a key that exists until
|
|
the next reboot.
|
|
|
|
The purpose of the routine is to protect sensitive data that will be swapped
|
|
to the page file.
|
|
|
|
Arguments:
|
|
|
|
pData - Pointer to the data to encrypt
|
|
|
|
cbData - Length (in bytes) of the data to encrypt
|
|
|
|
Operation - ENCRYPT or DECRYPT
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
if( pData == NULL || cbData == 0 ) {
|
|
return;
|
|
}
|
|
|
|
DsysAssert( ((DESX_BLOCKLEN % 8) == 0) );
|
|
|
|
if( (cbData & (DESX_BLOCKLEN-1)) == 0 )
|
|
{
|
|
unsigned __int64 feedback;
|
|
ULONG BlockCount;
|
|
|
|
BlockCount = cbData / DESX_BLOCKLEN;
|
|
feedback = g_Feedback;
|
|
|
|
while( BlockCount-- )
|
|
{
|
|
CBC(
|
|
desx, // desx is the cipher routine
|
|
DESX_BLOCKLEN,
|
|
pData, // result buffer.
|
|
pData, // input buffer.
|
|
g_pDESXKey,
|
|
Operation,
|
|
(unsigned char*)&feedback
|
|
);
|
|
|
|
pData += DESX_BLOCKLEN;
|
|
}
|
|
|
|
|
|
} else {
|
|
RC4_KEYSTRUCT rc4key;
|
|
|
|
rc4_key( &rc4key, g_cbRandomKey, g_pRandomKey );
|
|
rc4( &rc4key, cbData, pData );
|
|
|
|
RtlZeroMemory( &rc4key, sizeof(rc4key) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
LsaProtectMemory(
|
|
VOID *pData,
|
|
ULONG cbData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine encrypts the specified buffer in place with a key that exists until
|
|
the next reboot.
|
|
|
|
The purpose of the routine is to protect sensitive data that will be swapped
|
|
to the page file.
|
|
|
|
Arguments:
|
|
|
|
pData - Pointer to the data to encrypt
|
|
|
|
cbData - Length (in bytes) of the data to encrypt
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LsaEncryptMemory( (PBYTE)pData, cbData, ENCRYPT );
|
|
}
|
|
|
|
|
|
VOID
|
|
LsaUnprotectMemory(
|
|
VOID *pData,
|
|
ULONG cbData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrypts the specified buffer in place with a key that exists until
|
|
the next reboot.
|
|
|
|
The purpose of the routine is to un protect sensitive data that was encrypted via
|
|
LsaProtectMemory.
|
|
|
|
Arguments:
|
|
|
|
pData - Pointer to the data to decrypt
|
|
|
|
cbData - Length (in bytes) of the data to decrypt
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LsaEncryptMemory( (PBYTE)pData, cbData, DECRYPT );
|
|
}
|
|
|
|
extern "C"
|
|
VOID
|
|
LsaCleanupProtectedMemory(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up the LsaProtectMemory subsystem
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if( CredLockedMemory ) {
|
|
ZeroMemory( CredLockedMemory, CredLockedMemorySize );
|
|
VirtualFree( CredLockedMemory, 0, MEM_RELEASE );
|
|
CredLockedMemory = NULL;
|
|
}
|
|
}
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
LsaInitializeProtectedMemory(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the LsaProtectMemory subsystem
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Values:
|
|
|
|
Status of the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
//
|
|
// Lock enough memory to contain the maximum size key the algorithm supports.
|
|
//
|
|
|
|
g_cbRandomKey = 256;
|
|
|
|
CredLockedMemorySize = sizeof(DESXTable) + g_cbRandomKey;
|
|
|
|
CredLockedMemory = VirtualAlloc(
|
|
NULL,
|
|
CredLockedMemorySize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE );
|
|
|
|
if ( CredLockedMemory == NULL ) {
|
|
return I_RpcMapWin32Status( GetLastError() );
|
|
}
|
|
|
|
//
|
|
// lock memory.
|
|
//
|
|
|
|
if (!VirtualLock( CredLockedMemory, CredLockedMemorySize )) {
|
|
Status = I_RpcMapWin32Status( GetLastError() );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// setup DESX key.
|
|
//
|
|
|
|
g_pDESXKey = (DESXTable*)CredLockedMemory;
|
|
g_pRandomKey = (PBYTE)( (PBYTE)g_pDESXKey + sizeof(DESXTable) );
|
|
|
|
if ( !RtlGenRandom( g_pRandomKey, DESX_KEYSIZE )) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !RtlGenRandom( (PUCHAR)&g_Feedback, sizeof(g_Feedback) )) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
desxkey( g_pDESXKey, g_pRandomKey );
|
|
|
|
//
|
|
// generate random key in page locked memory.
|
|
//
|
|
|
|
if ( !RtlGenRandom( g_pRandomKey, g_cbRandomKey )) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
LsaCleanupProtectedMemory();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CredpInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the credential manager. It is called once during LSA
|
|
initialization.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Values:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WinStatus;
|
|
ULONG i;
|
|
HKEY LsaKey;
|
|
|
|
|
|
|
|
//
|
|
// Initialize LSA memory protection subsystem
|
|
// (This initialization could have happened earlier, but the cred manager
|
|
// is its first client.)
|
|
//
|
|
Status = LsaInitializeProtectedMemory();
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize credential set globals.
|
|
//
|
|
Status = RtlInitializeCriticalSection( &CredentialSetListLock );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
InitializeListHead( &CredentialSetList );
|
|
|
|
//
|
|
// Domain creds are always disabled on personal
|
|
//
|
|
|
|
if ( LsapIsRunningOnPersonal() ) {
|
|
|
|
CredIsPersonal = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize default values of configurable values.
|
|
//
|
|
CredTargetInfoMaxCount = CRED_TARGET_INFO_MAX_COUNT;
|
|
CredDisableDomainCreds = FALSE;
|
|
|
|
|
|
//
|
|
// Grab registry settings.
|
|
//
|
|
|
|
|
|
WinStatus = RegOpenKeyExA(
|
|
HKEY_LOCAL_MACHINE,
|
|
"System\\CurrentControlSet\\Control\\Lsa",
|
|
0,
|
|
KEY_READ,
|
|
&LsaKey );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
ULONG Value;
|
|
ULONG Type;
|
|
ULONG Size = sizeof(DWORD);
|
|
|
|
//
|
|
// Get the cache size
|
|
//
|
|
WinStatus = RegQueryValueExA(
|
|
LsaKey,
|
|
"TargetInfoCacheSize",
|
|
0,
|
|
&Type,
|
|
(PUCHAR) &Value,
|
|
&Size );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
//
|
|
// Don't allow ridiculously small values.
|
|
//
|
|
|
|
if ( Value == 0 ) {
|
|
Value = 1;
|
|
}
|
|
CredTargetInfoMaxCount = Value;
|
|
}
|
|
|
|
//
|
|
// Get whether domain creds are disabled
|
|
//
|
|
|
|
Size = sizeof(DWORD);
|
|
|
|
WinStatus = RegQueryValueExA(
|
|
LsaKey,
|
|
"DisableDomainCreds",
|
|
0,
|
|
&Type,
|
|
(PUCHAR) &Value,
|
|
&Size );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
CredDisableDomainCreds = Value;
|
|
}
|
|
|
|
RegCloseKey( LsaKey );
|
|
}
|
|
|
|
|
|
// ???: Don't check this in
|
|
// SPMInfoLevel |= DEB_TRACE_CRED;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
CredpHashIndexTargetInfo(
|
|
IN LPWSTR TargetName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine computes the hash index for a particular server.
|
|
|
|
Arguments:
|
|
|
|
TargetName - Name of the server to compute the index for
|
|
|
|
Return Values:
|
|
|
|
Hash index
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OEM_STRING UpcasedString;
|
|
UNICODE_STRING NetbiosServerNameString;
|
|
CHAR StringBuffer[CNLEN+1];
|
|
ULONG i;
|
|
DWORD Value = 0;
|
|
|
|
WCHAR NetbiosServerNameBuffer[CNLEN+1];
|
|
DWORD Size;
|
|
|
|
//
|
|
// Convert the name to a netbios name.
|
|
// (It might already be one.)
|
|
//
|
|
// We want to make sure the queried name falls into the same hash bucket.
|
|
// So, no matter what form the input name is in, make the hash real generic.
|
|
//
|
|
|
|
Size = CNLEN+1;
|
|
if ( DnsHostnameToComputerNameW( TargetName,
|
|
NetbiosServerNameBuffer,
|
|
&Size ) ) {
|
|
|
|
TargetName = NetbiosServerNameBuffer;
|
|
}
|
|
|
|
//
|
|
// Convert the server name to a canonical form
|
|
//
|
|
|
|
RtlInitUnicodeString( &NetbiosServerNameString, TargetName );
|
|
UpcasedString.Buffer = StringBuffer;
|
|
UpcasedString.MaximumLength = sizeof(StringBuffer);
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString(
|
|
&UpcasedString,
|
|
&NetbiosServerNameString,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return 0;
|
|
}
|
|
|
|
for ( i=0; i<UpcasedString.Length; i++ ) {
|
|
Value += UpcasedString.Buffer[i];
|
|
}
|
|
|
|
return (Value & (CRED_TARGET_INFO_HASH_TABLE_SIZE-1));
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CredpCacheTargetInfo(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PCANONICAL_TARGET_INFO TargetInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts the specified TargetInfo into the target info cache.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential sets.
|
|
|
|
TargetInfo - Specifies the target info to insert.
|
|
If this routine returns TRUE, the caller may no longer reference or free
|
|
the passed in TargetInfo.
|
|
|
|
Return Values:
|
|
|
|
TRUE: TargetInfo was inserted.
|
|
FALSE: TargetInfo was not inserted.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index;
|
|
PLIST_ENTRY ListEntry;
|
|
PCANONICAL_TARGET_INFO ExistingTargetInfo;
|
|
|
|
//
|
|
// Require a target name name
|
|
//
|
|
|
|
if ( TargetInfo->TargetName.Length == 0 ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Compute the hash index
|
|
//
|
|
Index = CredpHashIndexTargetInfo( TargetInfo->TargetName.Buffer );
|
|
|
|
//
|
|
// Loop through the existing entries deleting any conflicting entry
|
|
//
|
|
|
|
for ( ListEntry = CredentialSets->SessionCredSets->TargetInfoHashTable[Index].Flink ;
|
|
ListEntry != &CredentialSets->SessionCredSets->TargetInfoHashTable[Index];
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, HashNext );
|
|
|
|
|
|
//
|
|
// If the target names don't match,
|
|
// neither do the entries.
|
|
//
|
|
if ( !RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&ExistingTargetInfo->TargetName,
|
|
TRUE ) ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Remove the existing entry
|
|
//
|
|
|
|
RemoveEntryList( &ExistingTargetInfo->HashNext );
|
|
RemoveEntryList( &ExistingTargetInfo->LruNext );
|
|
LsapFreeLsaHeap( ExistingTargetInfo );
|
|
CredentialSets->SessionCredSets->TargetInfoCount --;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Link the new entry into the list
|
|
//
|
|
|
|
InsertHeadList( &CredentialSets->SessionCredSets->TargetInfoHashTable[Index], &TargetInfo->HashNext );
|
|
InsertHeadList( &CredentialSets->SessionCredSets->TargetInfoLruList, &TargetInfo->LruNext );
|
|
CredentialSets->SessionCredSets->TargetInfoCount ++;
|
|
|
|
//
|
|
// If we now have too many cache entries,
|
|
// ditch the oldest.
|
|
//
|
|
|
|
while ( CredentialSets->SessionCredSets->TargetInfoCount > CredTargetInfoMaxCount ) {
|
|
|
|
ListEntry = RemoveTailList( &CredentialSets->SessionCredSets->TargetInfoLruList );
|
|
|
|
ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, LruNext );
|
|
|
|
RemoveEntryList( &ExistingTargetInfo->HashNext );
|
|
LsapFreeLsaHeap( ExistingTargetInfo );
|
|
CredentialSets->SessionCredSets->TargetInfoCount --;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PCANONICAL_TARGET_INFO
|
|
CredpFindTargetInfo(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN LPWSTR TargetName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds a cached TargetInfo for the named server.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential sets.
|
|
|
|
TargetName - Specifies the server name to find the TargetInfo for
|
|
|
|
Return Values:
|
|
|
|
Returns the requested TargetInfo.
|
|
The returned structure can only be referenced while UserCredentialSets->CritSect remains locked.
|
|
|
|
NULL - No such target info exists
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index;
|
|
PLIST_ENTRY ListEntry;
|
|
PCANONICAL_TARGET_INFO ExistingTargetInfo = NULL;
|
|
UNICODE_STRING TargetNameString;
|
|
|
|
|
|
//
|
|
// Compute the hash index
|
|
//
|
|
|
|
RtlInitUnicodeString( &TargetNameString, TargetName );
|
|
Index = CredpHashIndexTargetInfo( TargetName );
|
|
|
|
//
|
|
// Loop through the entries finding this one.
|
|
//
|
|
|
|
for ( ListEntry = CredentialSets->SessionCredSets->TargetInfoHashTable[Index].Flink ;
|
|
ListEntry != &CredentialSets->SessionCredSets->TargetInfoHashTable[Index];
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, HashNext );
|
|
|
|
if ( RtlEqualUnicodeString( &TargetNameString,
|
|
&ExistingTargetInfo->TargetName,
|
|
TRUE ) ) {
|
|
|
|
break;
|
|
}
|
|
|
|
ExistingTargetInfo = NULL;
|
|
}
|
|
|
|
return ExistingTargetInfo;
|
|
|
|
}
|
|
|
|
PCREDENTIAL_SET
|
|
CredpAllocateCredSet(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes a CREDENTIAL set
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Values:
|
|
|
|
Returns the allocated credential set.
|
|
The caller must dereference this credential set using CredpDereferenceCredSet.
|
|
|
|
Return NULL if the memory cannot be allocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCREDENTIAL_SET TempCredentialSet;
|
|
|
|
//
|
|
// Allocate the credential set.
|
|
//
|
|
|
|
TempCredentialSet = (PCREDENTIAL_SET) LsapAllocateLsaHeap( sizeof(CREDENTIAL_SET) );
|
|
|
|
if ( TempCredentialSet == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Fill it in
|
|
//
|
|
|
|
TempCredentialSet->ReferenceCount = 1;
|
|
InitializeListHead( &TempCredentialSet->Credentials );
|
|
|
|
TempCredentialSet->Dirty = FALSE;
|
|
TempCredentialSet->BeingWritten = FALSE;
|
|
TempCredentialSet->WriteCount = 0;
|
|
|
|
return TempCredentialSet;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CredpCreateCredSets(
|
|
IN PSID UserSid,
|
|
IN PUNICODE_STRING NetbiosDomainName,
|
|
OUT PCREDENTIAL_SETS CredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the credential sets for a user with the specified SID.
|
|
|
|
If a credential set already exists for the specified user, the existing cred set is used.
|
|
|
|
Arguments:
|
|
|
|
UserSid - User sid of the user to create a credential set for.
|
|
|
|
NetbiosDomainName - Specifies the netbios domain name of the user to create the
|
|
credential set for.
|
|
|
|
CredentialSets - Returns a pointer the various credential sets.
|
|
The caller must dereference this credential set using CredpDereferenceCredSets.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG i;
|
|
|
|
WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
DWORD ComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
|
|
UNICODE_STRING ComputerNameString;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
RtlEnterCriticalSection( &CredentialSetListLock );
|
|
RtlZeroMemory( CredentialSets, sizeof(*CredentialSets) );
|
|
|
|
//
|
|
// Allocate a session specific credential structure
|
|
//
|
|
|
|
CredentialSets->SessionCredSets = (PSESSION_CREDENTIAL_SETS) LsapAllocateLsaHeap( sizeof(SESSION_CREDENTIAL_SETS) );
|
|
|
|
if ( CredentialSets->SessionCredSets == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize it
|
|
//
|
|
|
|
CredentialSets->SessionCredSets->ReferenceCount = 1;
|
|
for ( i=0; i<CRED_TARGET_INFO_HASH_TABLE_SIZE; i++ ) {
|
|
InitializeListHead( &CredentialSets->SessionCredSets->TargetInfoHashTable[i] );
|
|
}
|
|
CredentialSets->SessionCredSets->TargetInfoCount = 0;
|
|
InitializeListHead( &CredentialSets->SessionCredSets->TargetInfoLruList );
|
|
InitializeListHead( &CredentialSets->SessionCredSets->PromptData );
|
|
CredentialSets->SessionCredSets->ProfileLoaded = FALSE;
|
|
|
|
//
|
|
// Allocate a session credential set
|
|
//
|
|
|
|
CredentialSets->SessionCredSets->SessionCredSet = CredpAllocateCredSet();
|
|
|
|
if ( CredentialSets->SessionCredSets->SessionCredSet == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Loop through the list of loaded user credential sets trying to find this one
|
|
//
|
|
|
|
for ( ListEntry = CredentialSetList.Flink ;
|
|
ListEntry != &CredentialSetList;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
CredentialSets->UserCredentialSets = CONTAINING_RECORD( ListEntry, USER_CREDENTIAL_SETS, Next );
|
|
|
|
if ( RtlEqualSid( UserSid,
|
|
CredentialSets->UserCredentialSets->UserSid ) ) {
|
|
CredentialSets->UserCredentialSets->ReferenceCount ++;
|
|
break;
|
|
}
|
|
|
|
CredentialSets->UserCredentialSets = NULL;
|
|
}
|
|
|
|
//
|
|
// If we didn't find one,
|
|
// allocate a new one.
|
|
//
|
|
|
|
if ( CredentialSets->UserCredentialSets == NULL ) {
|
|
ULONG UserSidSize;
|
|
LPBYTE Where;
|
|
PUSER_CREDENTIAL_SETS TempUserCredentialSets;
|
|
|
|
|
|
//
|
|
// Allocate a user credential set
|
|
//
|
|
|
|
UserSidSize = RtlLengthSid( UserSid );
|
|
|
|
TempUserCredentialSets = (PUSER_CREDENTIAL_SETS) LsapAllocateLsaHeap(
|
|
sizeof(USER_CREDENTIAL_SETS) + UserSidSize );
|
|
|
|
if ( TempUserCredentialSets == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize constant fields
|
|
//
|
|
|
|
TempUserCredentialSets->ReferenceCount = 1;
|
|
InitializeListHead( &TempUserCredentialSets->Next );
|
|
TempUserCredentialSets->EnterpriseCredSet = NULL;
|
|
TempUserCredentialSets->LocalMachineCredSet = NULL;
|
|
|
|
Where = (LPBYTE)(TempUserCredentialSets+1);
|
|
TempUserCredentialSets->UserSid = Where;
|
|
RtlCopyMemory( Where, UserSid, UserSidSize );
|
|
|
|
//
|
|
// Initialize the crit sect
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSection( &TempUserCredentialSets->CritSect );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
LsapFreeLsaHeap( TempUserCredentialSets );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We've initialized to the point that common cleanup can work.
|
|
//
|
|
|
|
CredentialSets->UserCredentialSets = TempUserCredentialSets;
|
|
|
|
//
|
|
// Allocate the credential sets that hang off this structure
|
|
//
|
|
|
|
TempUserCredentialSets->EnterpriseCredSet = CredpAllocateCredSet();
|
|
|
|
if ( TempUserCredentialSets->EnterpriseCredSet == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TempUserCredentialSets->LocalMachineCredSet = CredpAllocateCredSet();
|
|
|
|
if ( TempUserCredentialSets->LocalMachineCredSet == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Insert the credential set into the global list.
|
|
//
|
|
// Being in the global list doesn't constitute a reference.
|
|
//
|
|
|
|
InsertHeadList( &CredentialSetList, &TempUserCredentialSets->Next );
|
|
}
|
|
|
|
//
|
|
// Determine if the user is logging onto a local account
|
|
//
|
|
|
|
if ( !GetComputerName( ComputerName, &ComputerNameSize ) ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString( &ComputerNameString, ComputerName );
|
|
|
|
if ( RtlEqualUnicodeString( NetbiosDomainName,
|
|
&ComputerNameString,
|
|
TRUE ) ) {
|
|
|
|
CredentialSets->Flags |= CREDSETS_FLAGS_LOCAL_ACCOUNT;
|
|
}
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
Cleanup:
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
CredpDereferenceCredSets( CredentialSets );
|
|
}
|
|
RtlLeaveCriticalSection( &CredentialSetListLock );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CredpReferenceCredSets(
|
|
IN PLUID LogonId,
|
|
OUT PCREDENTIAL_SETS CredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine references a credential sets for the specified LogonId.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredentialSet - Returns a pointer to the referenced credential set.
|
|
The caller must dereference this credential set using CredpDereferenceCredSets.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PLSAP_LOGON_SESSION LogonSession = NULL;
|
|
|
|
//
|
|
// Get the credential set from the logon session.
|
|
//
|
|
|
|
LogonSession = LsapLocateLogonSession( LogonId );
|
|
|
|
if ( LogonSession == NULL ) {
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( LogonSession->CredentialSets.UserCredentialSets == NULL ) {
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Grab a copy of the cred set pointers and reference them
|
|
//
|
|
|
|
RtlEnterCriticalSection( &CredentialSetListLock );
|
|
*CredentialSets = LogonSession->CredentialSets;
|
|
CredentialSets->UserCredentialSets->ReferenceCount ++;
|
|
CredentialSets->SessionCredSets->ReferenceCount ++;
|
|
RtlLeaveCriticalSection( &CredentialSetListLock );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
Cleanup:
|
|
if ( LogonSession != NULL ) {
|
|
LsapReleaseLogonSession( LogonSession );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpDereferenceCredSet(
|
|
IN PCREDENTIAL_SET CredentialSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences a credential set.
|
|
|
|
Arguments:
|
|
|
|
CredentialSet - A pointer to the referenced credential set.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Dereference the credential set.
|
|
//
|
|
RtlEnterCriticalSection( &CredentialSetListLock );
|
|
CredentialSet->ReferenceCount --;
|
|
|
|
if ( CredentialSet->ReferenceCount == 0 ) {
|
|
|
|
//
|
|
// Uninitialize it
|
|
//
|
|
|
|
while ( !IsListEmpty( &CredentialSet->Credentials) ) {
|
|
PLIST_ENTRY ListEntry;
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
|
|
ListEntry = RemoveHeadList( &CredentialSet->Credentials );
|
|
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
LsapFreeLsaHeap( TempCredential );
|
|
}
|
|
|
|
//
|
|
// Free the entry.
|
|
//
|
|
|
|
LsapFreeLsaHeap( CredentialSet );
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &CredentialSetListLock );
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CredpDereferenceUserCredSets(
|
|
IN PUSER_CREDENTIAL_SETS UserCredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences a user credential set.
|
|
|
|
Arguments:
|
|
|
|
UserCredentialSets - A pointer to the referenced user credential set.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Dereference the credential set.
|
|
//
|
|
RtlEnterCriticalSection( &CredentialSetListLock );
|
|
UserCredentialSets->ReferenceCount --;
|
|
|
|
if ( UserCredentialSets->ReferenceCount == 0 ) {
|
|
|
|
//
|
|
// Remove the entry from the global list.
|
|
//
|
|
|
|
RemoveEntryList( &UserCredentialSets->Next );
|
|
|
|
|
|
//
|
|
// Uninitialize it
|
|
//
|
|
|
|
if ( UserCredentialSets->EnterpriseCredSet != NULL ) {
|
|
CredpDereferenceCredSet( UserCredentialSets->EnterpriseCredSet );
|
|
}
|
|
if ( UserCredentialSets->LocalMachineCredSet != NULL ) {
|
|
CredpDereferenceCredSet( UserCredentialSets->LocalMachineCredSet );
|
|
}
|
|
|
|
(VOID) RtlDeleteCriticalSection( &UserCredentialSets->CritSect );
|
|
|
|
|
|
//
|
|
// Free the entry.
|
|
//
|
|
|
|
LsapFreeLsaHeap( UserCredentialSets );
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &CredentialSetListLock );
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CredpDereferenceSessionCredSets(
|
|
IN PSESSION_CREDENTIAL_SETS SessionCredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences a session credential set.
|
|
|
|
Arguments:
|
|
|
|
SessionCredentialSets - A pointer to the referenced session credential set.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// Dereference the credential set.
|
|
//
|
|
RtlEnterCriticalSection( &CredentialSetListLock );
|
|
SessionCredentialSets->ReferenceCount --;
|
|
|
|
if ( SessionCredentialSets->ReferenceCount == 0 ) {
|
|
|
|
//
|
|
// Uninitialize it
|
|
//
|
|
|
|
if ( SessionCredentialSets->SessionCredSet != NULL ) {
|
|
CredpDereferenceCredSet( SessionCredentialSets->SessionCredSet );
|
|
}
|
|
|
|
//
|
|
// Free Target Info Cache
|
|
//
|
|
|
|
while ( SessionCredentialSets->TargetInfoCount > 0 ) {
|
|
PCANONICAL_TARGET_INFO ExistingTargetInfo;
|
|
|
|
ListEntry = RemoveTailList( &SessionCredentialSets->TargetInfoLruList );
|
|
|
|
ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, LruNext );
|
|
|
|
RemoveEntryList( &ExistingTargetInfo->HashNext );
|
|
LsapFreeLsaHeap( ExistingTargetInfo );
|
|
SessionCredentialSets->TargetInfoCount --;
|
|
}
|
|
|
|
//
|
|
// Free the prompt data
|
|
//
|
|
|
|
while ( !IsListEmpty( &SessionCredentialSets->PromptData ) ) {
|
|
PPROMPT_DATA PromptData;
|
|
|
|
ListEntry = RemoveHeadList( &SessionCredentialSets->PromptData );
|
|
|
|
PromptData = CONTAINING_RECORD( ListEntry, PROMPT_DATA, Next );
|
|
LsapFreeLsaHeap( PromptData );
|
|
}
|
|
|
|
|
|
//
|
|
// Free the entry.
|
|
//
|
|
|
|
LsapFreeLsaHeap( SessionCredentialSets );
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &CredentialSetListLock );
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CredpDereferenceCredSets(
|
|
IN PCREDENTIAL_SETS CredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences a set of credential sets.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential sets.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Dereference the User wide credential sets
|
|
//
|
|
|
|
if ( CredentialSets->UserCredentialSets != NULL ) {
|
|
CredpDereferenceUserCredSets( CredentialSets->UserCredentialSets );
|
|
CredentialSets->UserCredentialSets = NULL;
|
|
}
|
|
|
|
//
|
|
// Dereference the session specific credential sets
|
|
//
|
|
|
|
if ( CredentialSets->SessionCredSets != NULL ) {
|
|
CredpDereferenceSessionCredSets( CredentialSets->SessionCredSets );
|
|
CredentialSets->SessionCredSets = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CredpValidateBuffer(
|
|
IN LPBYTE Buffer OPTIONAL,
|
|
IN ULONG BufferSize,
|
|
IN ULONG MaximumSize,
|
|
IN BOOLEAN NullOk,
|
|
IN ULONG Alignment
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates a passed in Buffer
|
|
|
|
Arguments:
|
|
|
|
Buffer - Buffer to validate
|
|
|
|
BufferSize - Size of the buffer in bytes
|
|
|
|
MaximumSize - Maximum size of the buffer (in bytes).
|
|
|
|
NullOk - if TRUE, a NULL Buffer is OK.
|
|
|
|
Alignment - Specifies the alignment requirements of the buffer size.
|
|
|
|
Return Values:
|
|
|
|
TRUE - Buffer is valid.
|
|
|
|
FALSE - Buffer is not valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( Buffer == NULL ) {
|
|
if ( BufferSize != 0 ) {
|
|
return FALSE;
|
|
}
|
|
if ( !NullOk ) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if ( BufferSize == 0 ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( BufferSize > MaximumSize ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( BufferSize != ROUND_UP_COUNT(BufferSize, Alignment) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
CredpCompareCredToTargetInfo(
|
|
IN PCANONICAL_TARGET_INFO TargetInfo,
|
|
IN PCANONICAL_CREDENTIAL Credential,
|
|
OUT PULONG AliasIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the specified credential matches the
|
|
specified target info.
|
|
|
|
Arguments:
|
|
|
|
TargetInfo - A description of the various aliases for a target.
|
|
|
|
Credential - Pointer to the credential to match.
|
|
|
|
AliasIndex - On success, this parameter returns an index
|
|
indicating which target alias matched the credential.
|
|
|
|
Return Values:
|
|
|
|
TRUE if the credential matches the target info.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
BOOLEAN DnsCompareFailed = FALSE;
|
|
|
|
//
|
|
// If the credential isn't a domain credential,
|
|
// it doesn't match;
|
|
//
|
|
|
|
if ( !CredpIsDomainCredential(Credential->Cred.Type) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that UserNameTarget credentials only match
|
|
// UserNameTarget requests (and vice versa)
|
|
//
|
|
|
|
if ( Credential->WildcardType == WcUserName ) {
|
|
if ( (TargetInfo->Flags & CRED_TI_USERNAME_TARGET) == 0 ) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if ( (TargetInfo->Flags & CRED_TI_USERNAME_TARGET) != 0 ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the target info specifies a list of valid cred types,
|
|
// only return a credential that matches the list.
|
|
//
|
|
|
|
if ( TargetInfo->CredTypeCount ) {
|
|
ULONG i;
|
|
|
|
for ( i=0; i<TargetInfo->CredTypeCount; i++ ) {
|
|
|
|
if ( TargetInfo->CredTypes[i] == Credential->Cred.Type ) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the cred doesn't match any of the valid cred types,
|
|
// ignore it.
|
|
//
|
|
if ( i == TargetInfo->CredTypeCount ) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Handle credentials that specify DFS share names
|
|
//
|
|
|
|
switch (Credential->WildcardType ) {
|
|
case WcDfsShareName:
|
|
|
|
//
|
|
// Determine if the target info is for the named dfs share
|
|
//
|
|
|
|
if ( TargetInfo->TargetName.Length != 0 &&
|
|
RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&Credential->TargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_DFS_SHARE_NAME;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// Handle credentials that are for a specific server
|
|
//
|
|
case WcServerName:
|
|
|
|
//
|
|
// Compare the DNS server name
|
|
//
|
|
|
|
if ( TargetInfo->DnsServerName.Length != 0 ) {
|
|
|
|
//
|
|
// Do an exact comparison.
|
|
//
|
|
// ??? NonWildcardedTargetName should probably be TargetName
|
|
if ( RtlEqualUnicodeString( &TargetInfo->DnsServerName,
|
|
&Credential->NonWildcardedTargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_DNS_SERVER_NAME;
|
|
return TRUE;
|
|
}
|
|
|
|
DnsCompareFailed = TRUE;
|
|
|
|
//
|
|
// If a netbios alias is specified,
|
|
// and we don't know the format of the target info name,
|
|
// compare it.
|
|
//
|
|
|
|
if ( Credential->TargetAlias.Length != 0 &&
|
|
(TargetInfo->Flags & CRED_TI_SERVER_FORMAT_UNKNOWN) != 0 ) {
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->DnsServerName,
|
|
&Credential->TargetAlias,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_SERVER_NAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Compare the netbios server name
|
|
//
|
|
|
|
if ( TargetInfo->NetbiosServerName.Length != 0 ) {
|
|
|
|
//
|
|
// If no alias is specified,
|
|
// the TargetName might be the netbios name.
|
|
//
|
|
|
|
if ( Credential->TargetAlias.Length == 0 ) {
|
|
|
|
// ??? NonWildcardedTargetName should probably be TargetName
|
|
if ( RtlEqualUnicodeString( &TargetInfo->NetbiosServerName,
|
|
&Credential->NonWildcardedTargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_SERVER_NAME;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If an alias is specified,
|
|
// it is always the netbios name.
|
|
//
|
|
// (Don't compare the alias if the more specific comparision failed.)
|
|
//
|
|
|
|
} else if ( !DnsCompareFailed ) {
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->NetbiosServerName,
|
|
&Credential->TargetAlias,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_SERVER_NAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Compare the target name if it is different than the Netbios or DNS name already compared
|
|
//
|
|
if ( Credential->WildcardType == WcServerName ) {
|
|
|
|
if ( TargetInfo->TargetName.Length != 0 &&
|
|
!RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&TargetInfo->DnsServerName,
|
|
TRUE ) &&
|
|
!RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&TargetInfo->NetbiosServerName,
|
|
TRUE ) ) {
|
|
|
|
//
|
|
// The TargetName might be the netbios name or DNS name
|
|
//
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&Credential->TargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_TARGET_NAME;
|
|
return TRUE;
|
|
}
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&Credential->TargetAlias,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_TARGET_NAME;
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Handle the server wildcard case
|
|
//
|
|
// If the TargetName is of the form *.xxx.yyy,
|
|
// compare equal if the DnsServerName ends in .xxx.yyy.
|
|
//
|
|
// The comparision includes the . to ensure *.xxx.yyy doesn't match
|
|
// fredxxx.yyy
|
|
//
|
|
|
|
case WcServerWildcard:
|
|
|
|
//
|
|
// Compare the DNS server name
|
|
//
|
|
|
|
if ( TargetInfo->DnsServerName.Length != 0 ) {
|
|
|
|
UNICODE_STRING LocalTargetName = Credential->NonWildcardedTargetName;
|
|
UNICODE_STRING LocalServerName = TargetInfo->DnsServerName;
|
|
|
|
//
|
|
// Build a server name without the leading characters
|
|
//
|
|
|
|
if ( LocalTargetName.Length < LocalServerName.Length ) {
|
|
DWORD TrimAmount = LocalServerName.Length - LocalTargetName.Length;
|
|
|
|
LocalServerName.Length = LocalTargetName.Length;
|
|
LocalServerName.MaximumLength = LocalTargetName.Length;
|
|
LocalServerName.Buffer += TrimAmount/sizeof(WCHAR);
|
|
|
|
//
|
|
// Compare the names
|
|
//
|
|
|
|
if ( RtlEqualUnicodeString( &LocalServerName,
|
|
&LocalTargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_WILDCARD_SERVER_NAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// Handle the domain wildcard case.
|
|
//
|
|
// If the target names is of the form <Domain>\*,
|
|
// compare equal if Netbios or Dns domain name is <Domain>
|
|
//
|
|
|
|
case WcDomainWildcard: {
|
|
UNICODE_STRING LocalTargetName;
|
|
UNICODE_STRING LocalTargetAlias;
|
|
|
|
//
|
|
// Build a target name without the \*
|
|
//
|
|
|
|
LocalTargetName = Credential->NonWildcardedTargetName;
|
|
|
|
LocalTargetAlias = Credential->TargetAlias;
|
|
if ( LocalTargetAlias.Length > 2 * sizeof(WCHAR) &&
|
|
LocalTargetAlias.Buffer[(LocalTargetAlias.Length/sizeof(WCHAR))-1] == L'*' &&
|
|
LocalTargetAlias.Buffer[(LocalTargetAlias.Length/sizeof(WCHAR))-2] == L'\\' ) {
|
|
|
|
LocalTargetAlias.Length -= 2 * sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
//
|
|
// Compare the DNS domain name
|
|
//
|
|
|
|
if ( TargetInfo->DnsDomainName.Length != 0 ) {
|
|
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->DnsDomainName,
|
|
&LocalTargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_DNS_DOMAIN_NAME;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
DnsCompareFailed = TRUE;
|
|
|
|
//
|
|
// If a netbios alias is specified,
|
|
// and we don't know the format of the target info name,
|
|
// compare it.
|
|
//
|
|
|
|
if ( LocalTargetAlias.Length != 0 &&
|
|
(TargetInfo->Flags & CRED_TI_DOMAIN_FORMAT_UNKNOWN) != 0 ) {
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->DnsDomainName,
|
|
&LocalTargetAlias,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Compare the netbios domain name
|
|
//
|
|
|
|
if ( TargetInfo->NetbiosDomainName.Length != 0 ) {
|
|
|
|
//
|
|
// If no alias is specified,
|
|
// the TargetName might be the netbios name.
|
|
//
|
|
|
|
if ( LocalTargetAlias.Length == 0 ) {
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->NetbiosDomainName,
|
|
&LocalTargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If an alias is specified,
|
|
// it is always the netbios name.
|
|
//
|
|
// (Don't compare the alias if the more specific comparision failed.)
|
|
//
|
|
|
|
} else if ( !DnsCompareFailed ) {
|
|
|
|
if ( RtlEqualUnicodeString( &TargetInfo->NetbiosDomainName,
|
|
&LocalTargetAlias,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Handle the * wildcard case.
|
|
//
|
|
|
|
case WcUniversalWildcard:
|
|
|
|
*AliasIndex = CRED_UNIVERSAL_NAME;
|
|
return TRUE;
|
|
|
|
//
|
|
// Handle the * wildcard case.
|
|
//
|
|
|
|
case WcUniversalSessionWildcard:
|
|
|
|
*AliasIndex = CRED_UNIVERSAL_SESSION_NAME;
|
|
return TRUE;
|
|
|
|
//
|
|
// Handles creds that have a user name as the target name
|
|
//
|
|
|
|
case WcUserName:
|
|
|
|
//
|
|
// Determine if the target info is for this username
|
|
//
|
|
|
|
if ( TargetInfo->TargetName.Length != 0 &&
|
|
RtlEqualUnicodeString( &TargetInfo->TargetName,
|
|
&Credential->TargetName,
|
|
TRUE ) ) {
|
|
|
|
*AliasIndex = CRED_TARGET_NAME;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// Catch coding errors
|
|
//
|
|
default:
|
|
ASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CredpLogonCredsMatchTargetInfo(
|
|
IN PLUID LogonId,
|
|
IN PCANONICAL_TARGET_INFO TargetInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the specified credential matches the
|
|
specified target info.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to check
|
|
|
|
TargetInfo - A description of the various aliases for a target.
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS - Logon creds match target info
|
|
STATUS_NO_MATCH - Logon creds don't match target info
|
|
Otherwise, fatal error
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAP_LOGON_SESSION LogonSession = NULL;
|
|
ULONG AliasIndex;
|
|
CANONICAL_CREDENTIAL CanonicalCredential;
|
|
LPWSTR DottedDnsDomainName = NULL;
|
|
PLSAP_DS_NAME_MAP DnsDomainMap = NULL;
|
|
|
|
//
|
|
// If the target machine is a member of a workgroup,
|
|
// never use the *Session cred.
|
|
|
|
if ( TargetInfo->Flags & CRED_TI_WORKGROUP_MEMBER ) {
|
|
DebugLog((DEB_TRACE_CRED,
|
|
"*Session: %wZ: not used on workgroup member.\n",
|
|
&TargetInfo->TargetName ));
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set from the logon session.
|
|
//
|
|
|
|
LogonSession = LsapLocateLogonSession( LogonId );
|
|
|
|
if ( LogonSession == NULL ) {
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the DnsDomainName for the logon session. Do not go
|
|
// off-machine if it's not in the cache.
|
|
//
|
|
|
|
Status = LsapGetNameForLogonSession(
|
|
LogonSession,
|
|
NameDnsDomain,
|
|
&DnsDomainMap,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DnsDomainMap = NULL;
|
|
} else {
|
|
if ( DnsDomainMap->Name.Length == 0 ) {
|
|
LsapDerefDsNameMap( DnsDomainMap );
|
|
DnsDomainMap = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check if a *.<DnsDomainName> credential matches the target info
|
|
//
|
|
|
|
if ( DnsDomainMap != NULL ) {
|
|
|
|
//
|
|
// Clear the cred
|
|
//
|
|
|
|
RtlZeroMemory( &CanonicalCredential, sizeof(CanonicalCredential) );
|
|
CanonicalCredential.Cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
|
|
CanonicalCredential.WildcardType = WcServerWildcard;
|
|
|
|
//
|
|
// Allocate space on the stack
|
|
//
|
|
|
|
SafeAllocaAllocate( DottedDnsDomainName,
|
|
DnsDomainMap->Name.Length + (3*sizeof(WCHAR)) );
|
|
|
|
if ( DottedDnsDomainName == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build *.<DnsDomainName>
|
|
//
|
|
|
|
RtlCopyMemory( DottedDnsDomainName,
|
|
L"*.",
|
|
2*sizeof(WCHAR) );
|
|
|
|
RtlCopyMemory( DottedDnsDomainName+2,
|
|
DnsDomainMap->Name.Buffer,
|
|
DnsDomainMap->Name.Length );
|
|
|
|
DottedDnsDomainName[(DnsDomainMap->Name.Length/sizeof(WCHAR))+2] = L'\0';
|
|
|
|
//
|
|
// Fill in the Canonical cred
|
|
//
|
|
|
|
RtlInitUnicodeString( &CanonicalCredential.TargetName, DottedDnsDomainName );
|
|
|
|
CanonicalCredential.NonWildcardedTargetName.Buffer =
|
|
CanonicalCredential.TargetName.Buffer + 1;
|
|
CanonicalCredential.NonWildcardedTargetName.Length =
|
|
CanonicalCredential.TargetName.Length - sizeof(WCHAR);
|
|
CanonicalCredential.NonWildcardedTargetName.MaximumLength =
|
|
CanonicalCredential.TargetName.MaximumLength - sizeof(WCHAR);
|
|
|
|
|
|
//
|
|
// See if the cred matches the target info
|
|
//
|
|
|
|
if ( CredpCompareCredToTargetInfo(
|
|
TargetInfo,
|
|
&CanonicalCredential,
|
|
&AliasIndex ) ) {
|
|
|
|
DebugLog((DEB_TRACE_CRED,
|
|
"*Session: %wZ: %ws: not used on wildcard server match.\n",
|
|
&TargetInfo->TargetName,
|
|
DottedDnsDomainName ));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check if a <DomainName>\* credential matches the target info
|
|
//
|
|
|
|
if ( LogonSession->AuthorityName.Length != 0 ) {
|
|
|
|
//
|
|
// Clear the cred
|
|
//
|
|
|
|
RtlZeroMemory( &CanonicalCredential, sizeof(CanonicalCredential) );
|
|
CanonicalCredential.Cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
|
|
CanonicalCredential.WildcardType = WcDomainWildcard;
|
|
|
|
//
|
|
// If there is a DNS domain name too,
|
|
// build a cred with an alias
|
|
//
|
|
|
|
if ( DnsDomainMap != NULL ) {
|
|
|
|
//
|
|
// The primary target name is the DNS domain name
|
|
//
|
|
|
|
CanonicalCredential.NonWildcardedTargetName = DnsDomainMap->Name;
|
|
|
|
//
|
|
// The alias is the netbios domain name
|
|
//
|
|
|
|
CanonicalCredential.TargetAlias = LogonSession->AuthorityName;
|
|
|
|
|
|
//
|
|
// If there is no DNS domain name,
|
|
// just use the netbios domain name.
|
|
//
|
|
|
|
} else {
|
|
CanonicalCredential.NonWildcardedTargetName = LogonSession->AuthorityName;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// See if the cred matches the target info
|
|
//
|
|
|
|
if ( CredpCompareCredToTargetInfo(
|
|
TargetInfo,
|
|
&CanonicalCredential,
|
|
&AliasIndex ) ) {
|
|
|
|
DebugLog((DEB_TRACE_CRED,
|
|
"*Session: %wZ: %wZ: not used on wildcard domain match.\n",
|
|
&TargetInfo->TargetName,
|
|
&CanonicalCredential.NonWildcardedTargetName ));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Status = STATUS_NO_MATCH;
|
|
|
|
|
|
Cleanup:
|
|
if ( DnsDomainMap != NULL ) {
|
|
LsapDerefDsNameMap( DnsDomainMap );
|
|
}
|
|
|
|
if ( LogonSession != NULL ) {
|
|
LsapReleaseLogonSession( LogonSession );
|
|
}
|
|
|
|
if ( DottedDnsDomainName != NULL ) {
|
|
SafeAllocaFree( DottedDnsDomainName );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CredpValidateCredential(
|
|
IN ULONG CredFlags,
|
|
IN PCANONICAL_TARGET_INFO TargetInfo OPTIONAL,
|
|
IN PENCRYPTED_CREDENTIALW EncryptedInputCredential,
|
|
OUT PCANONICAL_CREDENTIAL *ValidatedCredential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates a passed in credential structure. It returns
|
|
a canonicalized version of the credential.
|
|
|
|
Arguments:
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
CREDP_FLAGS_IN_PROCESS - Caller is in-process.
|
|
If set, CredentialBlob data is passed in protected via LsapProtectMemory.
|
|
CREDP_FLAGS_CLEAR_PASSWORD - CredentialBlob data is passed in in the clear.
|
|
If neither set, CredentialBlob data is passed in protected via CredpEncodeCredential.
|
|
|
|
TargetInfo - Validated Target information that further describes the
|
|
target of the credential.
|
|
|
|
EncryptedInputCredential - Specifies the credential to validate.
|
|
|
|
ValidatedCredential - Returns a pointer to the canonicalized credential.
|
|
The caller should free this structure by calling LsapFreeLsaHeap.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_INVALID_PARMETER - The input credential is not valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WinStatus;
|
|
PCREDENTIALW InputCredential;
|
|
PCANONICAL_CREDENTIAL ReturnCredential = NULL;
|
|
ULONG TempSize;
|
|
|
|
ULONG TargetNameSize;
|
|
ULONG TempTargetNameSize;
|
|
UNICODE_STRING NonWildcardedTargetName;
|
|
ULONG CommentSize;
|
|
|
|
ULONG TargetAliasSize;
|
|
LPWSTR TargetAlias;
|
|
LPWSTR AllocatedTargetAlias = NULL;
|
|
|
|
ULONG UserNameSize;
|
|
LPWSTR UserName;
|
|
LPWSTR AllocatedUserName = NULL;
|
|
|
|
ULONG CredBlobSizeToAlloc;
|
|
|
|
ULONG VariableAttributeSize;
|
|
ULONG AllocatedSize;
|
|
WILDCARD_TYPE WildcardType;
|
|
TARGET_NAME_TYPE TargetNameType;
|
|
|
|
LPBYTE Where;
|
|
LPBYTE OldWhere;
|
|
|
|
ULONG i;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
RtlInitUnicodeString( &NonWildcardedTargetName, NULL );
|
|
|
|
//
|
|
// Validate the pointer itself.
|
|
//
|
|
|
|
if ( EncryptedInputCredential == NULL ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: credential NULL\n" ));
|
|
goto Cleanup;
|
|
}
|
|
InputCredential = &EncryptedInputCredential->Cred;
|
|
|
|
//
|
|
// Validate flags
|
|
// (Flags may indicate presence of other fields.)
|
|
//
|
|
|
|
if ( (InputCredential->Flags & ~CRED_FLAGS_VALID_FLAGS) != 0 ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid flags: 0x%lx\n", InputCredential->TargetName, InputCredential->Flags ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
InputCredential->Flags &= ~CRED_FLAGS_PROMPT_NOW; // Ignore "prompt now" bit on input
|
|
|
|
|
|
//
|
|
// Ensure flags are consistent with type.
|
|
//
|
|
if ( InputCredential->Flags & CRED_FLAGS_USERNAME_TARGET ) {
|
|
|
|
if ( !CredpIsDomainCredential(InputCredential->Type ) ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: UsernameTarget flag for non domain credentrial: 0x%ld\n", InputCredential->TargetName, InputCredential->Type ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TargetNameType = IsUsernameTarget;
|
|
} else {
|
|
TargetNameType = IsNotUsernameTarget;
|
|
}
|
|
|
|
//
|
|
// Validate the Target Name
|
|
//
|
|
|
|
Status = CredpValidateTargetName( InputCredential->TargetName,
|
|
InputCredential->Type,
|
|
TargetNameType,
|
|
&InputCredential->UserName,
|
|
&InputCredential->Persist,
|
|
&TargetNameSize,
|
|
&WildcardType,
|
|
&NonWildcardedTargetName );
|
|
|
|
if ( !NT_SUCCESS(Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the contained strings.
|
|
//
|
|
|
|
if ( !CredpValidateString( InputCredential->Comment, CRED_MAX_STRING_LENGTH, TRUE, &CommentSize ) ||
|
|
!CredpValidateString( InputCredential->TargetAlias, CRED_MAX_STRING_LENGTH, TRUE, &TargetAliasSize ) ||
|
|
!CredpValidateString( InputCredential->UserName, CRED_MAX_USERNAME_LENGTH, TRUE, &UserNameSize ) ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: Invalid Comment or TargetAlias or UserName\n",
|
|
InputCredential->TargetName ));
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
TargetAlias = InputCredential->TargetAlias;
|
|
UserName = InputCredential->UserName;
|
|
|
|
//
|
|
// Validate the credential blob.
|
|
// The passed in blob size must be the clear text size or the encrypted text size.
|
|
//
|
|
|
|
CredBlobSizeToAlloc = AllocatedCredBlobSize( EncryptedInputCredential->ClearCredentialBlobSize );
|
|
|
|
if ( CredFlags & CREDP_FLAGS_CLEAR_PASSWORD ) {
|
|
if ( InputCredential->CredentialBlobSize != EncryptedInputCredential->ClearCredentialBlobSize ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Bad clear cred blob size %ld %ld\n",
|
|
InputCredential->TargetName,
|
|
InputCredential->CredentialBlobSize,
|
|
EncryptedInputCredential->ClearCredentialBlobSize ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
} else {
|
|
if ( InputCredential->CredentialBlobSize != CredBlobSizeToAlloc ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Bad encrypted cred blob size %ld %ld\n",
|
|
InputCredential->TargetName,
|
|
InputCredential->CredentialBlobSize,
|
|
CredBlobSizeToAlloc ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
if ( !CredpValidateBuffer( InputCredential->CredentialBlob,
|
|
EncryptedInputCredential->ClearCredentialBlobSize,
|
|
CRED_MAX_CREDENTIAL_BLOB_SIZE,
|
|
TRUE,
|
|
InputCredential->Type == CRED_TYPE_GENERIC ?
|
|
ALIGN_BYTE :
|
|
ALIGN_WCHAR ) ) {
|
|
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid credential blob\n", InputCredential->TargetName ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the attribute list.
|
|
// Ensure RPC passed us sane information.
|
|
//
|
|
|
|
if ( InputCredential->AttributeCount != 0 &&
|
|
InputCredential->Attributes == NULL ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid attribute buffer\n", InputCredential->TargetName ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure there aren't too many attributes
|
|
//
|
|
|
|
if ( InputCredential->AttributeCount > CRED_MAX_ATTRIBUTES ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Too many attributes %ld\n", InputCredential->TargetName, InputCredential->AttributeCount ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate each attribute.
|
|
//
|
|
|
|
VariableAttributeSize = 0;
|
|
for ( i=0; i<InputCredential->AttributeCount; i++ ) {
|
|
if ( !CredpValidateString( InputCredential->Attributes[i].Keyword,
|
|
CRED_MAX_STRING_LENGTH,
|
|
FALSE,
|
|
&TempSize ) ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid attribute keyword buffer: %ld\n", InputCredential->TargetName, i ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
VariableAttributeSize += TempSize;
|
|
|
|
if ( !CredpValidateBuffer( InputCredential->Attributes[i].Value,
|
|
InputCredential->Attributes[i].ValueSize,
|
|
CRED_MAX_VALUE_SIZE,
|
|
TRUE,
|
|
ALIGN_BYTE ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: %ws: Invalid attribute value buffer: %ld\n",
|
|
InputCredential->TargetName,
|
|
InputCredential->Attributes[i].Keyword,
|
|
i ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
VariableAttributeSize += InputCredential->Attributes[i].ValueSize;
|
|
}
|
|
|
|
//
|
|
// Validate the credential type
|
|
//
|
|
// Handle generic credentials
|
|
//
|
|
|
|
if ( InputCredential->Type == CRED_TYPE_GENERIC ) {
|
|
/* Drop through */
|
|
|
|
|
|
//
|
|
// Handle domain credentials
|
|
//
|
|
} else if ( CredpIsDomainCredential(InputCredential->Type) ) {
|
|
|
|
LPWSTR RealTargetAlias; // TargetAlias sans wildcard chars
|
|
ULONG RealTargetAliasLength;
|
|
|
|
|
|
//
|
|
//
|
|
// Domain credentials have a specific Username format
|
|
//
|
|
|
|
Status = CredpValidateUserName( UserName, InputCredential->Type, &AllocatedUserName );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Recompute since CredpValidateUserName canonicalized the name
|
|
UserName = AllocatedUserName;
|
|
UserNameSize = (wcslen( AllocatedUserName ) + 1) * sizeof(WCHAR);
|
|
|
|
|
|
|
|
//
|
|
// Handle where the target alias is specified,
|
|
//
|
|
|
|
RealTargetAlias = TargetAlias;
|
|
RealTargetAliasLength = (TargetAliasSize-sizeof(WCHAR))/sizeof(WCHAR);
|
|
if ( TargetAlias != NULL ) {
|
|
|
|
//
|
|
// Process alias as a function of Wildcard Type
|
|
//
|
|
|
|
switch ( WildcardType ) {
|
|
case WcServerName:
|
|
/* Nothing to do here */
|
|
break;
|
|
case WcDfsShareName:
|
|
case WcServerWildcard:
|
|
case WcUniversalWildcard:
|
|
case WcUniversalSessionWildcard:
|
|
case WcUserName:
|
|
//
|
|
// Server credentials of the form *.xxx.yyy aren't allowed with TargetAliases
|
|
// Otherwise a wildcarded DNS name would have a non-wildcarded netbios name.
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: TargetAlias not allowed for server wildcard credential.\n",
|
|
InputCredential->TargetName ));
|
|
goto Cleanup;
|
|
|
|
case WcDomainWildcard:
|
|
//
|
|
// If the TargetName is a domain wildcard, so must the TargetAlias
|
|
//
|
|
|
|
if ( RealTargetAliasLength > 2 &&
|
|
RealTargetAlias[RealTargetAliasLength-1] == L'*' &&
|
|
RealTargetAlias[RealTargetAliasLength-2] == L'\\' ) {
|
|
|
|
//
|
|
// Allocate a buffer for the target alias so we don't have to modify the
|
|
// callers buffer.
|
|
//
|
|
|
|
SafeAllocaAllocate( AllocatedTargetAlias, TargetAliasSize );
|
|
|
|
if ( AllocatedTargetAlias == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( AllocatedTargetAlias, RealTargetAlias, TargetAliasSize );
|
|
RealTargetAlias = AllocatedTargetAlias;
|
|
RealTargetAliasLength -= 2;
|
|
RealTargetAlias[RealTargetAliasLength] = '\0';
|
|
} else {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: %ws: TargetAlias must be wildcard if TargetName is.\n",
|
|
InputCredential->TargetName,
|
|
TargetAlias ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The target alias must be a netbios name.
|
|
//
|
|
if ( !NetpIsDomainNameValid( RealTargetAlias ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: TargetAlias '%ws' must be a netbios name.\n",
|
|
InputCredential->TargetName,
|
|
TargetAlias ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The target name must be a DNS name
|
|
//
|
|
if ( !CredpValidateDnsString( NonWildcardedTargetName.Buffer,
|
|
FALSE,
|
|
DnsNameDomain,
|
|
&TempTargetNameSize ) ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: TargetName for domain or server must be a DNS name if target alias specified.\n",
|
|
InputCredential->TargetName ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: Type %ld not valid\n",
|
|
InputCredential->TargetName,
|
|
InputCredential->Type ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the persistence.
|
|
//
|
|
|
|
switch ( InputCredential->Persist ) {
|
|
case CRED_PERSIST_SESSION:
|
|
case CRED_PERSIST_LOCAL_MACHINE:
|
|
case CRED_PERSIST_ENTERPRISE:
|
|
break;
|
|
default:
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: Invalid persistance: %ld.\n",
|
|
InputCredential->TargetName,
|
|
InputCredential->Persist ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a buffer for the canonicalized credential.
|
|
//
|
|
|
|
AllocatedSize = (ULONG)(ROUND_UP_COUNT( sizeof(CANONICAL_CREDENTIAL), ALIGN_WORST) +
|
|
TargetNameSize +
|
|
(NonWildcardedTargetName.Buffer == NULL ?
|
|
0 :
|
|
NonWildcardedTargetName.MaximumLength ) +
|
|
CommentSize +
|
|
ROUND_UP_COUNT( CredBlobSizeToAlloc, ALIGN_WORST) +
|
|
InputCredential->AttributeCount * sizeof(CREDENTIAL_ATTRIBUTE) +
|
|
VariableAttributeSize +
|
|
TargetAliasSize +
|
|
UserNameSize);
|
|
|
|
ReturnCredential = (PCANONICAL_CREDENTIAL) LsapAllocateLsaHeap( AllocatedSize );
|
|
|
|
|
|
if ( ReturnCredential == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
OldWhere = (PUCHAR)(ReturnCredential+1);
|
|
Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
|
|
RtlZeroMemory( OldWhere, Where-OldWhere );
|
|
|
|
ReturnCredential->AllocatedSize = AllocatedSize;
|
|
ReturnCredential->WildcardType = WildcardType;
|
|
|
|
//
|
|
// Copy the fixed size data
|
|
//
|
|
|
|
ReturnCredential->Cred.Flags = InputCredential->Flags;
|
|
ReturnCredential->Cred.Type = InputCredential->Type;
|
|
ReturnCredential->Cred.Persist = InputCredential->Persist;
|
|
ReturnCredential->Cred.LastWritten = InputCredential->LastWritten;
|
|
|
|
RtlZeroMemory( &ReturnCredential->Next, sizeof(ReturnCredential->Next) );
|
|
|
|
//
|
|
// Copy the 8-byte aligned data.
|
|
// (We don't know the format of the credential blob, but our in-process callers
|
|
// may have alignment requirements.)
|
|
//
|
|
|
|
if ( EncryptedInputCredential->ClearCredentialBlobSize != 0 ) {
|
|
|
|
//
|
|
// Put the credential blob in the buffer.
|
|
//
|
|
ReturnCredential->Cred.CredentialBlob = Where;
|
|
ReturnCredential->Cred.CredentialBlobSize = InputCredential->CredentialBlobSize;
|
|
ReturnCredential->ClearCredentialBlobSize = EncryptedInputCredential->ClearCredentialBlobSize;
|
|
RtlCopyMemory( Where, InputCredential->CredentialBlob, InputCredential->CredentialBlobSize );
|
|
Where += InputCredential->CredentialBlobSize;
|
|
|
|
|
|
//
|
|
// Align the running pointer again
|
|
//
|
|
OldWhere = Where;
|
|
// Always leave an extra trailing byte for encrypting the buffer in place
|
|
Where += CredBlobSizeToAlloc - InputCredential->CredentialBlobSize;
|
|
Where = (PUCHAR) ROUND_UP_POINTER( Where, ALIGN_WORST );
|
|
RtlZeroMemory( OldWhere, Where-OldWhere );
|
|
|
|
//
|
|
// If the blob isn't encrypted correctly for the cache,
|
|
// convert it.
|
|
//
|
|
|
|
if ( (CredFlags & CREDP_FLAGS_IN_PROCESS) == 0 ) {
|
|
|
|
//
|
|
// Only decode data if it is there
|
|
//
|
|
|
|
if ( ReturnCredential->Cred.CredentialBlobSize != 0 ) {
|
|
ULONG PaddingSize;
|
|
|
|
|
|
//
|
|
// If the password isn't already in the clear,
|
|
// convert it to the clear.
|
|
//
|
|
if ( (CredFlags & CREDP_FLAGS_CLEAR_PASSWORD) == 0 ) {
|
|
|
|
// Ensure we can cast between the types
|
|
ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, Cred) == offsetof( _CANONICAL_CREDENTIAL, Cred));
|
|
ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, ClearCredentialBlobSize) == offsetof( _CANONICAL_CREDENTIAL, ClearCredentialBlobSize));
|
|
|
|
if ( !CredpDecodeCredential( (PENCRYPTED_CREDENTIALW)ReturnCredential ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: Cannot decode cred blob\n",
|
|
InputCredential->TargetName ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Obfuscate the sensitive data
|
|
// A large enough space was already pushed above.
|
|
//
|
|
|
|
ReturnCredential->Cred.CredentialBlobSize = CredBlobSizeToAlloc;
|
|
|
|
// Clear the padding at the end to ensure we can compare encrypted blobs
|
|
PaddingSize = CredBlobSizeToAlloc -
|
|
ReturnCredential->ClearCredentialBlobSize;
|
|
|
|
if ( PaddingSize != 0 ) {
|
|
RtlZeroMemory( &ReturnCredential->Cred.CredentialBlob[ReturnCredential->ClearCredentialBlobSize],
|
|
PaddingSize );
|
|
}
|
|
|
|
LsaProtectMemory( ReturnCredential->Cred.CredentialBlob,
|
|
ReturnCredential->Cred.CredentialBlobSize );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the 4 byte aligned data
|
|
//
|
|
|
|
ReturnCredential->Cred.AttributeCount = InputCredential->AttributeCount;
|
|
if ( InputCredential->AttributeCount != 0 ) {
|
|
ReturnCredential->Cred.Attributes = (PCREDENTIAL_ATTRIBUTEW) Where;
|
|
Where += ReturnCredential->Cred.AttributeCount * sizeof(CREDENTIAL_ATTRIBUTE);
|
|
} else {
|
|
ReturnCredential->Cred.Attributes = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the 2 byte aligned data
|
|
//
|
|
|
|
ReturnCredential->Cred.TargetName = (LPWSTR) Where;
|
|
RtlCopyMemory( Where, InputCredential->TargetName, TargetNameSize );
|
|
Where += TargetNameSize;
|
|
ReturnCredential->TargetName.Buffer = ReturnCredential->Cred.TargetName;
|
|
ReturnCredential->TargetName.MaximumLength = (USHORT)TargetNameSize;
|
|
ReturnCredential->TargetName.Length = ReturnCredential->TargetName.MaximumLength - sizeof(WCHAR);
|
|
|
|
if ( NonWildcardedTargetName.Buffer != NULL ) {
|
|
ReturnCredential->NonWildcardedTargetName.Buffer = (LPWSTR)Where;
|
|
|
|
RtlCopyMemory( Where, NonWildcardedTargetName.Buffer, NonWildcardedTargetName.MaximumLength );
|
|
Where += NonWildcardedTargetName.MaximumLength;
|
|
|
|
ReturnCredential->NonWildcardedTargetName.MaximumLength = NonWildcardedTargetName.MaximumLength;
|
|
ReturnCredential->NonWildcardedTargetName.Length = NonWildcardedTargetName.Length;
|
|
} else {
|
|
ReturnCredential->NonWildcardedTargetName = ReturnCredential->TargetName;
|
|
}
|
|
|
|
if ( CommentSize != 0 ) {
|
|
ReturnCredential->Cred.Comment = (LPWSTR) Where;
|
|
RtlCopyMemory( Where, InputCredential->Comment, CommentSize );
|
|
Where += CommentSize;
|
|
} else {
|
|
ReturnCredential->Cred.Comment = NULL;
|
|
}
|
|
|
|
if ( TargetAliasSize != 0 ) {
|
|
ReturnCredential->Cred.TargetAlias = (LPWSTR) Where;
|
|
RtlCopyMemory( Where, TargetAlias, TargetAliasSize );
|
|
Where += TargetAliasSize;
|
|
|
|
ReturnCredential->TargetAlias.Buffer = ReturnCredential->Cred.TargetAlias;
|
|
ReturnCredential->TargetAlias.MaximumLength = (USHORT)TargetAliasSize;
|
|
ReturnCredential->TargetAlias.Length = ReturnCredential->TargetAlias.MaximumLength - sizeof(WCHAR);
|
|
} else {
|
|
ReturnCredential->Cred.TargetAlias = NULL;
|
|
RtlInitUnicodeString( &ReturnCredential->TargetAlias, NULL );
|
|
}
|
|
|
|
if ( UserNameSize != 0 ) {
|
|
ReturnCredential->Cred.UserName = (LPWSTR) Where;
|
|
RtlCopyMemory( Where, UserName, UserNameSize );
|
|
Where += UserNameSize;
|
|
ReturnCredential->UserName.Buffer = ReturnCredential->Cred.UserName;
|
|
ReturnCredential->UserName.MaximumLength = (USHORT)UserNameSize;
|
|
ReturnCredential->UserName.Length = ReturnCredential->UserName.MaximumLength - sizeof(WCHAR);
|
|
} else {
|
|
ReturnCredential->Cred.UserName = NULL;
|
|
RtlInitUnicodeString( &ReturnCredential->UserName, NULL );
|
|
}
|
|
|
|
for ( i=0; i<InputCredential->AttributeCount; i++ ) {
|
|
TempSize = (wcslen( InputCredential->Attributes[i].Keyword ) + 1) * sizeof(WCHAR);
|
|
ReturnCredential->Cred.Attributes[i].Keyword = (LPWSTR) Where;
|
|
RtlCopyMemory( Where, InputCredential->Attributes[i].Keyword, TempSize );
|
|
Where += TempSize;
|
|
|
|
ReturnCredential->Cred.Attributes[i].Flags = InputCredential->Attributes[i].Flags;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the 1 byte aligned data
|
|
//
|
|
|
|
for ( i=0; i<InputCredential->AttributeCount; i++ ) {
|
|
|
|
if ( InputCredential->Attributes[i].ValueSize != 0 ) {
|
|
ReturnCredential->Cred.Attributes[i].ValueSize =
|
|
InputCredential->Attributes[i].ValueSize;
|
|
ReturnCredential->Cred.Attributes[i].Value = Where;
|
|
RtlCopyMemory( Where,
|
|
InputCredential->Attributes[i].Value,
|
|
InputCredential->Attributes[i].ValueSize );
|
|
Where += InputCredential->Attributes[i].ValueSize;
|
|
} else {
|
|
ReturnCredential->Cred.Attributes[i].ValueSize = 0;
|
|
ReturnCredential->Cred.Attributes[i].Value = NULL;
|
|
}
|
|
|
|
ReturnCredential->Cred.Attributes[i].Flags = InputCredential->Attributes[i].Flags;
|
|
}
|
|
|
|
|
|
//
|
|
// If we have target information,
|
|
// use it to do an extra validation.
|
|
//
|
|
|
|
if ( TargetInfo != NULL ) {
|
|
ULONG AliasIndex;
|
|
|
|
if (!CredpCompareCredToTargetInfo( TargetInfo, ReturnCredential, &AliasIndex ) ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateCredential: %ws: TargetInfo doesn't match credential.\n",
|
|
InputCredential->TargetName ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Return the credential to the caller.
|
|
//
|
|
*ValidatedCredential = ReturnCredential;
|
|
ReturnCredential = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
if ( ReturnCredential != NULL ) {
|
|
LsapFreeLsaHeap( ReturnCredential );
|
|
}
|
|
if ( NonWildcardedTargetName.Buffer != NULL ) {
|
|
LsapFreeLsaHeap( NonWildcardedTargetName.Buffer );
|
|
}
|
|
if ( AllocatedTargetAlias != NULL ) {
|
|
SafeAllocaFree( AllocatedTargetAlias );
|
|
}
|
|
if ( AllocatedUserName != NULL ) {
|
|
MIDL_user_free( AllocatedUserName );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CredpValidateNames(
|
|
IN OUT PCREDENTIAL_TARGET_INFORMATION InputTargetInfo,
|
|
IN BOOLEAN DoServer,
|
|
OUT PULONG NetbiosNameSize,
|
|
OUT PULONG DnsNameSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates the server names (or domain names) in a target info structure.
|
|
It handles the CRED_TI_*_FORMAT_UNKNOWN bit. If specified, the routine ensures only the
|
|
"dns" field of the name is specified. Also, the specified name is syntax checked.
|
|
If the specified name only matches one of the name formats, the "FORMAT_UNKNOWN" bit is
|
|
turned off and the appropriate name is set in InputTargetInfo
|
|
|
|
Arguments:
|
|
|
|
InputTargetInfo - Specifies the target info to validate.
|
|
On return, the Flags and name fields my be updated to reflect the true name formats.
|
|
|
|
DoServer - If TRUE the server names are validated.
|
|
If FALSE, the domain names are validated.
|
|
|
|
NetbiosNameSize - Returns the size in bytes of the netbios name
|
|
|
|
DnsNameSize - Returns the size in bytes of the DNS name
|
|
|
|
Return Values:
|
|
|
|
FALSE - if the names are invalid.
|
|
|
|
--*/
|
|
{
|
|
DWORD FlagBit;
|
|
LPWSTR *NetbiosNamePtr;
|
|
LPWSTR *DnsNamePtr;
|
|
DNS_NAME_FORMAT DnsNameFormat;
|
|
|
|
//
|
|
// Set up some locals identifying whether we're doing the server or domain.
|
|
//
|
|
|
|
if ( DoServer ) {
|
|
FlagBit = CRED_TI_SERVER_FORMAT_UNKNOWN;
|
|
NetbiosNamePtr = &InputTargetInfo->NetbiosServerName;
|
|
DnsNamePtr = &InputTargetInfo->DnsServerName;
|
|
DnsNameFormat = DnsNameHostnameFull;
|
|
} else {
|
|
FlagBit = CRED_TI_DOMAIN_FORMAT_UNKNOWN;
|
|
NetbiosNamePtr = &InputTargetInfo->NetbiosDomainName;
|
|
DnsNamePtr = &InputTargetInfo->DnsDomainName;
|
|
DnsNameFormat = DnsNameDomain;
|
|
}
|
|
|
|
|
|
//
|
|
// If the format unknown bit is set,
|
|
// try to determine the formation.
|
|
//
|
|
|
|
if ( InputTargetInfo->Flags & FlagBit ) {
|
|
|
|
BOOLEAN IsNetbios;
|
|
BOOLEAN IsDns;
|
|
ULONG LocalNetbiosNameSize;
|
|
ULONG LocalDnsNameSize;
|
|
|
|
//
|
|
// Caller must pass the unknown name as the DNS name
|
|
//
|
|
|
|
if ( *NetbiosNamePtr != NULL) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpValidateNames: Netbios name must be null.\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine the syntax of the passed in name.
|
|
//
|
|
|
|
IsNetbios = CredpValidateString( *DnsNamePtr, CNLEN, FALSE, &LocalNetbiosNameSize );
|
|
IsDns = CredpValidateDnsString( *DnsNamePtr, FALSE, DnsNameFormat, &LocalDnsNameSize );
|
|
|
|
//
|
|
// If the name simply isn't valid,
|
|
// we're done.
|
|
//
|
|
|
|
if ( !IsNetbios && !IsDns ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpValidateNames: Invalid DNS Buffer: %ws (may not be fatal).\n",
|
|
*DnsNamePtr ));
|
|
return FALSE;
|
|
|
|
//
|
|
// If the name is only a valid DNS name
|
|
// use it as such
|
|
//
|
|
|
|
} else if ( !IsNetbios && IsDns ) {
|
|
|
|
// Turn off the bit since the name is not ambiguous.
|
|
InputTargetInfo->Flags &= ~FlagBit;
|
|
|
|
*NetbiosNameSize = 0;
|
|
*DnsNameSize = LocalDnsNameSize;
|
|
|
|
//
|
|
// If the name is only a valid netbios name
|
|
// use it as such
|
|
//
|
|
|
|
} else if ( IsNetbios && !IsDns ) {
|
|
|
|
// Turn off the bit since the name is not ambiguous.
|
|
InputTargetInfo->Flags &= ~FlagBit;
|
|
|
|
*NetbiosNameSize = LocalNetbiosNameSize;
|
|
*NetbiosNamePtr = *DnsNamePtr;
|
|
|
|
*DnsNamePtr = NULL;
|
|
*DnsNameSize = 0;
|
|
|
|
|
|
//
|
|
// If the name is valid for both formats,
|
|
// leave it ambiguous
|
|
//
|
|
|
|
} else {
|
|
|
|
*NetbiosNameSize = 0;
|
|
*DnsNameSize = LocalDnsNameSize;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
if ( !CredpValidateString( *NetbiosNamePtr, CNLEN, TRUE, NetbiosNameSize ) ||
|
|
!CredpValidateDnsString( *DnsNamePtr, TRUE, DnsNameFormat, DnsNameSize ) ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpValidateNames: Invalid Buffer.\n" ));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
CredpValidateTargetInfo(
|
|
IN PCREDENTIAL_TARGET_INFORMATION InputTargetInfo,
|
|
OUT PCANONICAL_TARGET_INFO *ValidatedTargetInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates a passed in target info structure. It returns
|
|
a canonicalized version of the target info.
|
|
|
|
All DNS names have the trailing . stripped.
|
|
|
|
Arguments:
|
|
|
|
InputTargetInfo - Specifies the target info to validate.
|
|
|
|
ValidatedTargetInfo - Returns a pointer to the canonicalized target info.
|
|
The caller should free this structure by calling LsapFreeLsaHeap.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_INVALID_PARMETER - The input target info is not valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PCANONICAL_TARGET_INFO ReturnTargetInfo = NULL;
|
|
ULONG TempSize;
|
|
|
|
// Local copy of InputTargetInfo that this routine can modify.
|
|
CREDENTIAL_TARGET_INFORMATION LocalTargetInfo;
|
|
|
|
ULONG TargetNameSize;
|
|
|
|
ULONG NetbiosServerNameSize;
|
|
WCHAR NetbiosServerName[CNLEN+1];
|
|
|
|
ULONG DnsServerNameSize;
|
|
ULONG NetbiosDomainNameSize;
|
|
ULONG DnsDomainNameSize;
|
|
ULONG DnsTreeNameSize;
|
|
ULONG PackageNameSize;
|
|
ULONG CredTypeSize;
|
|
ULONG AllocatedSize;
|
|
|
|
BOOLEAN FictitiousServerName = FALSE;
|
|
|
|
|
|
LPBYTE Where;
|
|
LPBYTE OldWhere;
|
|
|
|
ULONG i;
|
|
|
|
//
|
|
// Validate the pointer itself.
|
|
//
|
|
|
|
if ( InputTargetInfo == NULL ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: TargetInfo NULL.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate Flags
|
|
//
|
|
|
|
LocalTargetInfo = *InputTargetInfo;
|
|
if ( (LocalTargetInfo.Flags & ~CRED_TI_VALID_FLAGS) != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: Invalid flags 0x%lx.\n",
|
|
LocalTargetInfo.Flags ));
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// All share level machines are workgroup members
|
|
//
|
|
|
|
if ( LocalTargetInfo.Flags & CRED_TI_ONLY_PASSWORD_REQUIRED ) {
|
|
LocalTargetInfo.Flags |= CRED_TI_WORKGROUP_MEMBER;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the contained strings.
|
|
//
|
|
|
|
if ( !CredpValidateString( LocalTargetInfo.TargetName, CRED_MAX_DOMAIN_TARGET_NAME_LENGTH, TRUE, &TargetNameSize ) ||
|
|
!CredpValidateDnsString( LocalTargetInfo.DnsTreeName, TRUE, DnsNameDomain, &DnsTreeNameSize ) ||
|
|
!CredpValidateString( LocalTargetInfo.PackageName, CRED_MAX_STRING_LENGTH, TRUE, &PackageNameSize ) ) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: Invalid Buffer.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Supply a server name if none is present.
|
|
//
|
|
// pre-NTLM-V2 servers don't supply a server name in the negotiate response. NTLM supplies
|
|
// the TargetName from the SPN. In that case, the TargetName makes a better server name than
|
|
// none at all.
|
|
//
|
|
|
|
if ( (LocalTargetInfo.NetbiosServerName == NULL || *(LocalTargetInfo.NetbiosServerName) == L'\0') &&
|
|
(LocalTargetInfo.DnsServerName == NULL || *(LocalTargetInfo.DnsServerName) == L'\0') &&
|
|
TargetNameSize != 0 ) {
|
|
|
|
LocalTargetInfo.DnsServerName = LocalTargetInfo.TargetName;
|
|
LocalTargetInfo.Flags |= CRED_TI_SERVER_FORMAT_UNKNOWN;
|
|
FictitiousServerName = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Validate the server and domain names.
|
|
//
|
|
|
|
if ( !CredpValidateNames( &LocalTargetInfo, TRUE, &NetbiosServerNameSize, &DnsServerNameSize ) ) {
|
|
|
|
//
|
|
// Don't bail out if we made up the server name
|
|
//
|
|
|
|
if ( FictitiousServerName ) {
|
|
LocalTargetInfo.DnsServerName = NULL;
|
|
LocalTargetInfo.Flags &= ~CRED_TI_SERVER_FORMAT_UNKNOWN;
|
|
FictitiousServerName = FALSE;
|
|
NetbiosServerNameSize = 0;
|
|
DnsServerNameSize = 0;
|
|
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: Invalid server buffer.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
if ( !CredpValidateNames( &LocalTargetInfo, FALSE, &NetbiosDomainNameSize, &DnsDomainNameSize ) ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: Invalid domain buffer.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the attribute list.
|
|
// Ensure RPC passed us sane information.
|
|
//
|
|
|
|
if ( LocalTargetInfo.CredTypeCount != 0 &&
|
|
LocalTargetInfo.CredTypes == NULL ) {
|
|
DebugLog((DEB_TRACE_CRED, "ValidateTargetInfo: Invalid cred type buffer.\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the cred types
|
|
//
|
|
|
|
for ( i=0; i<LocalTargetInfo.CredTypeCount; i ++ ) {
|
|
switch ( LocalTargetInfo.CredTypes[i] ) {
|
|
case CRED_TYPE_GENERIC:
|
|
case CRED_TYPE_DOMAIN_PASSWORD:
|
|
case CRED_TYPE_DOMAIN_CERTIFICATE:
|
|
case CRED_TYPE_DOMAIN_VISIBLE_PASSWORD:
|
|
break;
|
|
default:
|
|
DebugLog((DEB_TRACE_CRED, "ValidateTargetInfo: Invalid cred type %ld %ld.\n", i, LocalTargetInfo.CredTypes[i] ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
CredTypeSize = LocalTargetInfo.CredTypeCount * sizeof(DWORD);
|
|
|
|
//
|
|
// Supply a netbios server name if none is present and DNS server name is known.
|
|
//
|
|
|
|
if ( NetbiosServerNameSize == 0 &&
|
|
DnsServerNameSize != 0 &&
|
|
(LocalTargetInfo.Flags & CRED_TI_SERVER_FORMAT_UNKNOWN) == 0 ) {
|
|
|
|
DWORD Size = CNLEN+1;
|
|
|
|
if ( !DnsHostnameToComputerNameW( LocalTargetInfo.DnsServerName,
|
|
NetbiosServerName,
|
|
&Size ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"ValidateTargetInfo: Cannot DnsHostNameToComputerName: %ld.\n",
|
|
GetLastError() ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalTargetInfo.NetbiosServerName = NetbiosServerName;
|
|
NetbiosServerNameSize = (Size + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
//
|
|
// Supply a target name if none is present.
|
|
//
|
|
// NTLM authentication to Unix servers return target info. However, the callers of SSPI
|
|
// don't supply an SPN. NTLM builds the TargetName from the SPN.
|
|
//
|
|
|
|
if ( TargetNameSize == 0 ) {
|
|
|
|
//
|
|
// If there's a DNS server name,
|
|
// use it.
|
|
//
|
|
|
|
if ( DnsServerNameSize != 0 ) {
|
|
LocalTargetInfo.TargetName = LocalTargetInfo.DnsServerName;
|
|
TargetNameSize = DnsServerNameSize;
|
|
|
|
//
|
|
// If there's a netbios server name,
|
|
// use it.
|
|
|
|
} else if ( NetbiosServerNameSize != 0 ) {
|
|
LocalTargetInfo.TargetName = LocalTargetInfo.NetbiosServerName;
|
|
TargetNameSize = NetbiosServerNameSize;
|
|
|
|
//
|
|
// If there's a DNS Domain name,
|
|
// use it.
|
|
//
|
|
|
|
} else if ( DnsDomainNameSize != 0 ) {
|
|
LocalTargetInfo.TargetName = LocalTargetInfo.DnsDomainName;
|
|
TargetNameSize = DnsDomainNameSize;
|
|
|
|
//
|
|
// If there's a netbios Domain name,
|
|
// use it.
|
|
|
|
} else if ( NetbiosDomainNameSize != 0 ) {
|
|
LocalTargetInfo.TargetName = LocalTargetInfo.NetbiosDomainName;
|
|
TargetNameSize = NetbiosDomainNameSize;
|
|
|
|
//
|
|
// If there's a DNS tree name,
|
|
// use it.
|
|
//
|
|
|
|
} else if ( DnsTreeNameSize != 0 ) {
|
|
LocalTargetInfo.TargetName = LocalTargetInfo.DnsTreeName;
|
|
TargetNameSize = DnsTreeNameSize;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Canonicalize the target info.
|
|
//
|
|
// There are several cases where authentication packages are sent invalid target info.
|
|
// This section clears up those cases.
|
|
//
|
|
|
|
if ( NetbiosServerNameSize != 0 ) {
|
|
UNICODE_STRING NetbiosServerNameString;
|
|
|
|
NetbiosServerNameString.Buffer = LocalTargetInfo.NetbiosServerName;
|
|
NetbiosServerNameString.MaximumLength = (USHORT) NetbiosServerNameSize;
|
|
NetbiosServerNameString.Length = NetbiosServerNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
//
|
|
// Machines that are a member of a workgroup indicate that their NetbiosDomainName
|
|
// equals their NetbiosServerName.
|
|
// Instead, indicate workgroup membership by the lack of a NetbiosDomainName.
|
|
//
|
|
|
|
if ( NetbiosDomainNameSize != 0 ) {
|
|
UNICODE_STRING NetbiosDomainNameString;
|
|
|
|
NetbiosDomainNameString.Buffer = LocalTargetInfo.NetbiosDomainName;
|
|
NetbiosDomainNameString.MaximumLength = (USHORT) NetbiosDomainNameSize;
|
|
NetbiosDomainNameString.Length = NetbiosDomainNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
if ( RtlEqualUnicodeString( &NetbiosServerNameString,
|
|
&NetbiosDomainNameString,
|
|
TRUE ) ) {
|
|
NetbiosDomainNameSize = 0;
|
|
LocalTargetInfo.Flags |= CRED_TI_WORKGROUP_MEMBER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Machines that are a member of an NT 4 domain indicate that their DnsDomainName
|
|
// equals their NetbiosServerName.
|
|
// Instead, zap the DnsDomainName.
|
|
//
|
|
|
|
if ( DnsDomainNameSize != 0 ) {
|
|
UNICODE_STRING DnsDomainNameString;
|
|
|
|
DnsDomainNameString.Buffer = LocalTargetInfo.DnsDomainName;
|
|
DnsDomainNameString.MaximumLength = (USHORT) DnsDomainNameSize;
|
|
DnsDomainNameString.Length = DnsDomainNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
if ( RtlEqualUnicodeString( &NetbiosServerNameString,
|
|
&DnsDomainNameString,
|
|
TRUE ) ) {
|
|
DnsDomainNameSize = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Some machines in a workgroup also return the DnsDomainName set to the DnsServerName.
|
|
// Instead, zap the DnsDomainName.
|
|
|
|
if ( DnsDomainNameSize != 0 && DnsServerNameSize != 0 ) {
|
|
UNICODE_STRING DnsServerNameString;
|
|
UNICODE_STRING DnsDomainNameString;
|
|
|
|
DnsServerNameString.Buffer = LocalTargetInfo.DnsServerName;
|
|
DnsServerNameString.MaximumLength = (USHORT) DnsServerNameSize;
|
|
DnsServerNameString.Length = DnsServerNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
|
|
DnsDomainNameString.Buffer = LocalTargetInfo.DnsDomainName;
|
|
DnsDomainNameString.MaximumLength = (USHORT) DnsDomainNameSize;
|
|
DnsDomainNameString.Length = DnsDomainNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
if ( RtlEqualUnicodeString( &DnsServerNameString,
|
|
&DnsDomainNameString,
|
|
TRUE ) ) {
|
|
DnsDomainNameSize = 0;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a buffer for the canonicalized TargetInfo.
|
|
//
|
|
|
|
AllocatedSize = sizeof(CANONICAL_TARGET_INFO) +
|
|
TargetNameSize +
|
|
NetbiosServerNameSize +
|
|
DnsServerNameSize +
|
|
NetbiosDomainNameSize +
|
|
DnsDomainNameSize +
|
|
DnsTreeNameSize +
|
|
PackageNameSize +
|
|
CredTypeSize;
|
|
|
|
ReturnTargetInfo = (PCANONICAL_TARGET_INFO) LsapAllocateLsaHeap( AllocatedSize );
|
|
|
|
|
|
if ( ReturnTargetInfo == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Where = (PUCHAR)(ReturnTargetInfo+1);
|
|
|
|
//
|
|
// Copy fixed size data
|
|
//
|
|
|
|
ReturnTargetInfo->Flags = LocalTargetInfo.Flags;
|
|
|
|
//
|
|
// Copy the DWORD aligned data
|
|
//
|
|
|
|
ReturnTargetInfo->CredTypeCount = LocalTargetInfo.CredTypeCount;
|
|
if ( ReturnTargetInfo->CredTypeCount != 0 ) {
|
|
|
|
ReturnTargetInfo->CredTypes = (LPDWORD)Where;
|
|
RtlCopyMemory( Where, LocalTargetInfo.CredTypes, CredTypeSize );
|
|
Where += CredTypeSize;
|
|
|
|
} else {
|
|
ReturnTargetInfo->CredTypes = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the 2 byte aligned data
|
|
//
|
|
|
|
if ( TargetNameSize != 0 ) {
|
|
ReturnTargetInfo->TargetName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->TargetName.MaximumLength = (USHORT) TargetNameSize;
|
|
ReturnTargetInfo->TargetName.Length = (USHORT)(ReturnTargetInfo->TargetName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.TargetName, TargetNameSize );
|
|
Where += TargetNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->TargetName, NULL );
|
|
}
|
|
|
|
if ( NetbiosServerNameSize != 0 ) {
|
|
ReturnTargetInfo->NetbiosServerName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->NetbiosServerName.MaximumLength = (USHORT) NetbiosServerNameSize;
|
|
ReturnTargetInfo->NetbiosServerName.Length = (USHORT)(ReturnTargetInfo->NetbiosServerName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.NetbiosServerName, NetbiosServerNameSize );
|
|
Where += NetbiosServerNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->NetbiosServerName, NULL );
|
|
}
|
|
|
|
if ( DnsServerNameSize != 0 ) {
|
|
ReturnTargetInfo->DnsServerName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->DnsServerName.MaximumLength = (USHORT) DnsServerNameSize;
|
|
ReturnTargetInfo->DnsServerName.Length = (USHORT)(ReturnTargetInfo->DnsServerName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.DnsServerName, DnsServerNameSize );
|
|
Where += DnsServerNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->DnsServerName, NULL );
|
|
}
|
|
|
|
if ( NetbiosDomainNameSize != 0 ) {
|
|
ReturnTargetInfo->NetbiosDomainName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->NetbiosDomainName.MaximumLength = (USHORT)(NetbiosDomainNameSize);
|
|
ReturnTargetInfo->NetbiosDomainName.Length = (USHORT)(ReturnTargetInfo->NetbiosDomainName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.NetbiosDomainName, NetbiosDomainNameSize );
|
|
Where += NetbiosDomainNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->NetbiosDomainName, NULL );
|
|
}
|
|
|
|
if ( DnsDomainNameSize != 0 ) {
|
|
ReturnTargetInfo->DnsDomainName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->DnsDomainName.MaximumLength = (USHORT)(DnsDomainNameSize);
|
|
ReturnTargetInfo->DnsDomainName.Length = (USHORT)(ReturnTargetInfo->DnsDomainName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.DnsDomainName, DnsDomainNameSize );
|
|
Where += DnsDomainNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->DnsDomainName, NULL );
|
|
}
|
|
|
|
if ( DnsTreeNameSize != 0 ) {
|
|
ReturnTargetInfo->DnsTreeName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->DnsTreeName.MaximumLength = (USHORT)(DnsTreeNameSize);
|
|
ReturnTargetInfo->DnsTreeName.Length = (USHORT)(ReturnTargetInfo->DnsTreeName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.DnsTreeName, DnsTreeNameSize );
|
|
Where += DnsTreeNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->DnsTreeName, NULL );
|
|
}
|
|
|
|
if ( PackageNameSize != 0 ) {
|
|
ReturnTargetInfo->PackageName.Buffer = (LPWSTR) Where;
|
|
ReturnTargetInfo->PackageName.MaximumLength = (USHORT)(PackageNameSize);
|
|
ReturnTargetInfo->PackageName.Length = (USHORT)(ReturnTargetInfo->PackageName.MaximumLength - sizeof(WCHAR));
|
|
|
|
RtlCopyMemory( Where, LocalTargetInfo.PackageName, PackageNameSize );
|
|
Where += PackageNameSize;
|
|
} else {
|
|
RtlInitUnicodeString( &ReturnTargetInfo->PackageName, NULL );
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Return the TargetInfo to the caller.
|
|
//
|
|
*ValidatedTargetInfo = ReturnTargetInfo;
|
|
ReturnTargetInfo = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
if ( ReturnTargetInfo != NULL ) {
|
|
LsapFreeLsaHeap( ReturnTargetInfo );
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
PPROMPT_DATA
|
|
CredpFindPromptData(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PUNICODE_STRING TargetName,
|
|
IN ULONG Type,
|
|
IN ULONG Persist
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds a the prompt data for a credential in a credential set.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSet - Credential set list to find the credential in.
|
|
|
|
TargetName - Name of the credential whose prompt data is to be found.
|
|
|
|
Type - Type of the credential whose prompt data is to be found.
|
|
|
|
Persist - Peristence of the credential whose prompt data is to be found.
|
|
|
|
Return Values:
|
|
|
|
Returns a pointer to the prompt data. This pointer my be used as long as
|
|
UserCredentialSets->CritSect remains locked.
|
|
|
|
NULL: There is no such credential or there is no prompt data for the credential.
|
|
|
|
--*/
|
|
{
|
|
PPROMPT_DATA PromptData;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// Loop through the list of prompt data trying to find this one.
|
|
//
|
|
|
|
for ( ListEntry = CredentialSets->SessionCredSets->PromptData.Flink ;
|
|
ListEntry != &CredentialSets->SessionCredSets->PromptData;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
PromptData = CONTAINING_RECORD( ListEntry, PROMPT_DATA, Next );
|
|
|
|
if ( Type == PromptData->Type &&
|
|
Persist == PromptData->Persist &&
|
|
RtlEqualUnicodeString( TargetName,
|
|
&PromptData->TargetName,
|
|
TRUE ) ) {
|
|
return PromptData;
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PPROMPT_DATA
|
|
CredpAllocatePromptData(
|
|
IN PCANONICAL_CREDENTIAL Credential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a prompt data structure that corresponds to the passed in
|
|
credential.
|
|
|
|
Arguments:
|
|
|
|
Credential - Specifies the credential to allocate the prompt data structure for.
|
|
|
|
Return Values:
|
|
|
|
Returns a pointer to the prompt data.
|
|
The buffer should be freed via LsapFreeLsaHeap.
|
|
|
|
NULL: Memory could not be allocated.
|
|
|
|
--*/
|
|
{
|
|
PPROMPT_DATA PromptData;
|
|
LPBYTE Where;
|
|
|
|
//
|
|
// Allocate the PromptData structure.
|
|
//
|
|
|
|
PromptData = (PPROMPT_DATA) LsapAllocateLsaHeap(
|
|
sizeof(PROMPT_DATA) +
|
|
Credential->TargetName.Length );
|
|
|
|
if ( PromptData == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
Where = (LPBYTE)(PromptData+1);
|
|
|
|
//
|
|
// Fill it in
|
|
//
|
|
|
|
PromptData->Type = Credential->Cred.Type;
|
|
PromptData->Persist = Credential->Cred.Persist;
|
|
PromptData->Written = FALSE;
|
|
|
|
PromptData->TargetName.Buffer = (LPWSTR)Where;
|
|
PromptData->TargetName.Length = Credential->TargetName.Length;
|
|
PromptData->TargetName.MaximumLength = Credential->TargetName.Length;
|
|
|
|
RtlCopyMemory( Where,
|
|
Credential->TargetName.Buffer,
|
|
PromptData->TargetName.Length );
|
|
|
|
return PromptData;
|
|
}
|
|
|
|
|
|
PENCRYPTED_CREDENTIALW
|
|
CredpCloneCredential(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN ULONG CredFlags,
|
|
IN PCANONICAL_CREDENTIAL InputCredential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a credential suitable for returning out of the
|
|
credential manager.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced redential sets the cloned credential is in.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
|
|
CREDP_FLAGS_USE_MIDL_HEAP - If specified, use MIDL_user_allocate to allocate memory.
|
|
|
|
InputCredential - Specifies the credential to clone
|
|
|
|
Return Values:
|
|
|
|
Credential to return.
|
|
The returned credential may container pointers. All pointer are to addresses within
|
|
the returned allocated block.
|
|
|
|
The caller should free this memory using LsapFreeLsaHeap.
|
|
If CREDP_FLAGS_USE_MIDL_HEAP was specified, use MIDL_user_free.
|
|
|
|
|
|
NULL: Memory could not be allocated
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PCREDENTIAL Credential;
|
|
PENCRYPTED_CREDENTIALW EncryptedCredential;
|
|
DWORD_PTR Offset;
|
|
ULONG i;
|
|
|
|
//
|
|
// Allocate memory for the returned credential
|
|
//
|
|
|
|
if ( CredFlags & CREDP_FLAGS_USE_MIDL_HEAP ) {
|
|
Credential = (PCREDENTIALW) MIDL_user_allocate( InputCredential->AllocatedSize );
|
|
} else {
|
|
Credential = (PCREDENTIALW) LsapAllocateLsaHeap( InputCredential->AllocatedSize );
|
|
}
|
|
|
|
if ( Credential == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
EncryptedCredential = (PENCRYPTED_CREDENTIALW) Credential;
|
|
|
|
//
|
|
// Copy the credential
|
|
// Note that the "fixed size" part of the structure copied below is copying a
|
|
// CANONICAL_CREDENTIAL to a ENCRYPTED_CREDENTIALW. We rely on the following
|
|
// asserted conditions.
|
|
//
|
|
ASSERT( sizeof(ENCRYPTED_CREDENTIALW) <= sizeof(CANONICAL_CREDENTIAL) );
|
|
ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, Cred) == 0 );
|
|
ASSERT( offsetof( _CANONICAL_CREDENTIAL, Cred) == 0 );
|
|
|
|
RtlCopyMemory( Credential, InputCredential, InputCredential->AllocatedSize );
|
|
|
|
//
|
|
// Grab the clear text size of the blob
|
|
//
|
|
|
|
EncryptedCredential->ClearCredentialBlobSize = InputCredential->ClearCredentialBlobSize;
|
|
ASSERT( InputCredential->ClearCredentialBlobSize <= InputCredential->Cred.CredentialBlobSize );
|
|
|
|
|
|
//
|
|
// Relocate any pointers
|
|
//
|
|
#define RELOCATE_ONE( _x, _type ) if ( _x != NULL ) { _x = (_type) ((LPBYTE)(_x) + Offset); }
|
|
|
|
Offset = ((LPBYTE)Credential) - ((LPBYTE)InputCredential);
|
|
|
|
RELOCATE_ONE( Credential->TargetName, LPWSTR );
|
|
RELOCATE_ONE( Credential->Comment, LPWSTR );
|
|
|
|
RELOCATE_ONE( Credential->CredentialBlob, LPBYTE );
|
|
|
|
RELOCATE_ONE( Credential->Attributes, PCREDENTIAL_ATTRIBUTEW );
|
|
for ( i=0; i<Credential->AttributeCount; i++ ) {
|
|
RELOCATE_ONE( Credential->Attributes[i].Keyword, LPWSTR );
|
|
RELOCATE_ONE( Credential->Attributes[i].Value, LPBYTE );
|
|
}
|
|
|
|
RELOCATE_ONE( Credential->TargetAlias, LPWSTR );
|
|
RELOCATE_ONE( Credential->UserName, LPWSTR );
|
|
#undef RELOCATE_ONE
|
|
|
|
//
|
|
// If we're leaving the process,
|
|
// handle the private data.
|
|
//
|
|
|
|
if ( (CredFlags & CREDP_FLAGS_IN_PROCESS) == 0 ) {
|
|
|
|
//
|
|
// Domain passwords or cert pins never leave the process
|
|
//
|
|
// "Visible Password" is for user mode implementation auth package implementations.
|
|
// So allow "Visible Password" credentials out of process.
|
|
//
|
|
|
|
if ( Credential->Type != CRED_TYPE_GENERIC &&
|
|
Credential->Type != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD ) {
|
|
|
|
if ( Credential->CredentialBlob != NULL &&
|
|
Credential->CredentialBlobSize != 0 ) {
|
|
|
|
RtlZeroMemory( Credential->CredentialBlob, Credential->CredentialBlobSize );
|
|
Credential->CredentialBlob = NULL;
|
|
Credential->CredentialBlobSize = 0;
|
|
EncryptedCredential->ClearCredentialBlobSize = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Other credentials should be protected for transit on the wire.
|
|
//
|
|
} else {
|
|
|
|
//
|
|
// Only encode data if it is there
|
|
//
|
|
|
|
if ( Credential->CredentialBlobSize != 0 ) {
|
|
|
|
//
|
|
// First unprotect the memory
|
|
//
|
|
|
|
LsaUnprotectMemory( Credential->CredentialBlob,
|
|
Credential->CredentialBlobSize );
|
|
|
|
|
|
//
|
|
// Encrypt for transit on the wire (always LPC)
|
|
//
|
|
|
|
if ( !CredpEncodeCredential( EncryptedCredential )) {
|
|
|
|
// Clear the possibly clear password
|
|
RtlZeroMemory( Credential->CredentialBlob, Credential->CredentialBlobSize );
|
|
|
|
if ( CredFlags & CREDP_FLAGS_USE_MIDL_HEAP ) {
|
|
MIDL_user_free( Credential );
|
|
} else {
|
|
LsapFreeLsaHeap( Credential );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Return the PromptNow bit if the credential hasn't been refreshed recently.
|
|
//
|
|
|
|
Credential->Flags &= ~CRED_FLAGS_PROMPT_NOW;
|
|
|
|
if ( !PersistCredBlob( Credential ) ) {
|
|
PPROMPT_DATA PromptData;
|
|
|
|
//
|
|
// Get the prompt data for this credential
|
|
//
|
|
|
|
PromptData = CredpFindPromptData( CredentialSets,
|
|
&InputCredential->TargetName,
|
|
Credential->Type,
|
|
Credential->Persist );
|
|
|
|
//
|
|
// If we've never prompted for this credential since logon,
|
|
// Prompt now
|
|
//
|
|
|
|
if ( ShouldPromptNow( PromptData )) {
|
|
Credential->Flags |= CRED_FLAGS_PROMPT_NOW;
|
|
}
|
|
}
|
|
|
|
|
|
return EncryptedCredential;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CredpMarshalCredentials(
|
|
IN PCREDENTIAL_SET CredentialSet,
|
|
OUT LPDWORD BufferSize,
|
|
OUT LPBYTE *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine grabs all of the credentials from a Credential Set and
|
|
marshals them into a single buffer.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSet - Credential set containing the credentials to marshal
|
|
|
|
BufferSize - Returns the size of the marshaled credentials
|
|
Returns zero if there are no credentials.
|
|
|
|
Buffer - Returns a buffer containing the marshaled credentials
|
|
The buffer must be freed using LsapFreeLsaHeap.
|
|
|
|
Return Values:
|
|
|
|
TRUE - Buffer was sucessfully marshaled
|
|
|
|
FALSE - Buffer could not be allocated
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PCANONICAL_CREDENTIAL Credential;
|
|
ULONG ReturnBufferSize;
|
|
PMARSHALED_CREDENTIAL_SET ReturnBuffer;
|
|
ULONG i;
|
|
LPBYTE Where;
|
|
LPBYTE OldWhere;
|
|
|
|
LPBYTE LocalCredBlob = NULL;
|
|
ULONG LocalCredBlobSize = 0;
|
|
|
|
|
|
//
|
|
// Loop through the list of credentials computing the size of the return buffer
|
|
//
|
|
|
|
ReturnBufferSize = 0;
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
ReturnBufferSize += sizeof(MARSHALED_CREDENTIAL);
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
|
|
ReturnBufferSize += sizeof(ULONG) + Credential->TargetName.MaximumLength;
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
|
|
ReturnBufferSize += sizeof(ULONG) + (Credential->Cred.Comment == NULL ? 0 : ((wcslen( Credential->Cred.Comment ) + 1) * sizeof(WCHAR)));
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
|
|
ReturnBufferSize += sizeof(ULONG) + Credential->TargetAlias.MaximumLength;
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
|
|
ReturnBufferSize += sizeof(ULONG) + Credential->UserName.MaximumLength;
|
|
|
|
if ( PersistCredBlob( &Credential->Cred ) ) {
|
|
ReturnBufferSize += sizeof(ULONG) + Credential->ClearCredentialBlobSize;
|
|
LocalCredBlobSize = max( LocalCredBlobSize, Credential->Cred.CredentialBlobSize );
|
|
} else {
|
|
ReturnBufferSize += sizeof(ULONG);
|
|
}
|
|
|
|
|
|
for ( i=0; i<Credential->Cred.AttributeCount; i++ ) {
|
|
|
|
ReturnBufferSize += sizeof(ULONG);
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
|
|
ReturnBufferSize += sizeof(ULONG) + (Credential->Cred.Attributes[i].Keyword == NULL ? 0 : (wcslen( Credential->Cred.Attributes[i].Keyword ) + 1 ) * sizeof(WCHAR) );
|
|
|
|
ReturnBufferSize += sizeof(ULONG) + Credential->Cred.Attributes[i].ValueSize;
|
|
|
|
}
|
|
|
|
ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WORST );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If there is nothing to marshal,
|
|
// we're done.
|
|
//
|
|
|
|
if ( ReturnBufferSize == 0 ) {
|
|
*Buffer = NULL;
|
|
*BufferSize = 0;
|
|
return TRUE;
|
|
}
|
|
//
|
|
// Allocate a buffer to return to the caller.
|
|
//
|
|
|
|
ReturnBufferSize += ROUND_UP_COUNT( sizeof(MARSHALED_CREDENTIAL_SET), ALIGN_WORST );
|
|
ReturnBuffer = (PMARSHALED_CREDENTIAL_SET) LsapAllocateLsaHeap( ReturnBufferSize );
|
|
|
|
if ( ReturnBuffer == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to decrypt the credential blob into
|
|
//
|
|
|
|
SafeAllocaAllocate( LocalCredBlob, LocalCredBlobSize );
|
|
if ( LocalCredBlob == NULL ) {
|
|
LsapFreeLsaHeap( ReturnBuffer );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Create the buffer header.
|
|
//
|
|
|
|
ReturnBuffer->Version = MARSHALED_CREDENTIAL_SET_VERSION;
|
|
ReturnBuffer->Size = ReturnBufferSize;
|
|
|
|
OldWhere = (PUCHAR)(ReturnBuffer+1);
|
|
Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
|
|
RtlZeroMemory( OldWhere, Where-OldWhere );
|
|
|
|
//
|
|
// Copy the individual credentials
|
|
//
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
PMARSHALED_CREDENTIAL CredEntry;
|
|
ULONG CommentSize;
|
|
|
|
Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
CredEntry = (PMARSHALED_CREDENTIAL)Where;
|
|
|
|
//
|
|
// Copy the fixed size fields into the buffer.
|
|
//
|
|
|
|
CredEntry->Flags = Credential->Cred.Flags;
|
|
CredEntry->Type = Credential->Cred.Type;
|
|
CredEntry->LastWritten = Credential->Cred.LastWritten;
|
|
CredEntry->Persist = Credential->Cred.Persist;
|
|
CredEntry->AttributeCount = Credential->Cred.AttributeCount;
|
|
CredEntry->Expansion1 = 0;
|
|
CredEntry->Expansion2 = 0;
|
|
|
|
Where = (LPBYTE)(CredEntry+1);
|
|
|
|
//
|
|
// Copy the strings
|
|
//
|
|
|
|
#define CredpMarshalBytes( _Ptr, _Size, _Align ) \
|
|
OldWhere = Where; \
|
|
Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, _Align ); \
|
|
RtlZeroMemory( OldWhere, Where-OldWhere ); \
|
|
SmbPutUlong( Where, (_Size) ); \
|
|
Where += sizeof(ULONG); \
|
|
if ( _Size != 0 ) { \
|
|
RtlCopyMemory( Where, (_Ptr), (_Size) ); \
|
|
Where += (_Size); \
|
|
}
|
|
|
|
CredpMarshalBytes( Credential->TargetName.Buffer, Credential->TargetName.MaximumLength, ALIGN_WCHAR );
|
|
|
|
CommentSize = Credential->Cred.Comment == NULL ? 0 : (wcslen( Credential->Cred.Comment ) + 1 ) * sizeof(WCHAR);
|
|
CredpMarshalBytes( Credential->Cred.Comment, CommentSize, ALIGN_WCHAR );
|
|
|
|
CredpMarshalBytes( Credential->TargetAlias.Buffer, Credential->TargetAlias.MaximumLength, ALIGN_WCHAR );
|
|
|
|
CredpMarshalBytes( Credential->UserName.Buffer, Credential->UserName.MaximumLength, ALIGN_WCHAR );
|
|
|
|
|
|
//
|
|
// Marshal the (decrypted) credential itself
|
|
//
|
|
|
|
if ( PersistCredBlob( &Credential->Cred ) ) {
|
|
|
|
//
|
|
// Grab a local copy of the cred blob to decrypt into
|
|
//
|
|
|
|
if ( Credential->Cred.CredentialBlobSize != 0 ) {
|
|
RtlCopyMemory( LocalCredBlob,
|
|
Credential->Cred.CredentialBlob,
|
|
Credential->Cred.CredentialBlobSize );
|
|
|
|
LsaUnprotectMemory( LocalCredBlob,
|
|
Credential->Cred.CredentialBlobSize );
|
|
}
|
|
|
|
CredpMarshalBytes( LocalCredBlob, Credential->ClearCredentialBlobSize, ALIGN_BYTE );
|
|
|
|
} else {
|
|
CredpMarshalBytes( NULL, 0, ALIGN_BYTE );
|
|
}
|
|
|
|
//
|
|
// Marshal the attributes
|
|
//
|
|
|
|
for ( i=0; i<CredEntry->AttributeCount; i++ ) {
|
|
ULONG KeywordSize;
|
|
SmbPutUlong( Where, Credential->Cred.Attributes[i].Flags );
|
|
Where += sizeof(ULONG);
|
|
|
|
KeywordSize = Credential->Cred.Attributes[i].Keyword == NULL ? 0 : (wcslen( Credential->Cred.Attributes[i].Keyword ) + 1) * sizeof(WCHAR);
|
|
CredpMarshalBytes( Credential->Cred.Attributes[i].Keyword, KeywordSize, ALIGN_WCHAR );
|
|
|
|
CredpMarshalBytes( Credential->Cred.Attributes[i].Value,
|
|
Credential->Cred.Attributes[i].ValueSize,
|
|
ALIGN_BYTE );
|
|
|
|
}
|
|
|
|
//
|
|
// Zero any padding bytes
|
|
//
|
|
OldWhere = Where;
|
|
Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
|
|
RtlZeroMemory( OldWhere, Where-OldWhere );
|
|
|
|
|
|
//
|
|
// Remember the size of this credential.
|
|
//
|
|
|
|
CredEntry->EntrySize = (ULONG)(Where - ((LPBYTE)CredEntry));
|
|
|
|
}
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
|
|
if ( ReturnBufferSize != (ULONG)(Where - ((LPBYTE)ReturnBuffer))) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpMarshalCredentials: Marshaled %ld bytes into a buffer %ld long.\n",
|
|
Where - ((LPBYTE)ReturnBuffer),
|
|
ReturnBufferSize ));
|
|
}
|
|
|
|
//
|
|
// Return the buffer to the caller
|
|
//
|
|
|
|
*Buffer = (LPBYTE) ReturnBuffer;
|
|
*BufferSize = ReturnBufferSize;
|
|
|
|
//
|
|
// Free the temp buffer
|
|
//
|
|
|
|
if ( LocalCredBlob != NULL ) {
|
|
RtlZeroMemory( LocalCredBlob, LocalCredBlobSize );
|
|
SafeAllocaFree( LocalCredBlob );
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateNestedDirectories(
|
|
IN LPWSTR szFullPath,
|
|
IN LPWSTR szCreationStartPoint // must point in null-terminated range of szFullPath
|
|
)
|
|
/*++
|
|
|
|
Create all subdirectories if they do not exists starting at
|
|
szCreationStartPoint.
|
|
|
|
szCreationStartPoint must point to a character within the null terminated
|
|
buffer specified by the szFullPath parameter.
|
|
|
|
Note that szCreationStartPoint should not point at the first character
|
|
of a drive root, eg:
|
|
|
|
d:\foo\bar\bilge\water
|
|
\\server\share\foo\bar
|
|
\\?\d:\big\path\bilge\water
|
|
|
|
Instead, szCreationStartPoint should point beyond these components, eg:
|
|
|
|
bar\bilge\water
|
|
foo\bar
|
|
big\path\bilge\water
|
|
|
|
This function does not implement logic for adjusting to compensate for these
|
|
inputs because the environment it was design to be used in causes the input
|
|
szCreationStartPoint to point well into the szFullPath input buffer.
|
|
|
|
|
|
This function stolen from crypto api.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
DWORD cchRemaining;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
BOOL fSuccess = FALSE;
|
|
|
|
|
|
if( szCreationStartPoint < szFullPath ||
|
|
szCreationStartPoint > (lstrlenW(szFullPath) + szFullPath)
|
|
)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
cchRemaining = lstrlenW( szCreationStartPoint );
|
|
|
|
//
|
|
// scan from left to right in the szCreationStartPoint string
|
|
// looking for directory delimiter.
|
|
//
|
|
|
|
for ( i = 0 ; i < cchRemaining ; i++ ) {
|
|
WCHAR charReplaced = szCreationStartPoint[ i ];
|
|
|
|
if( charReplaced == L'\\' || charReplaced == L'/' ) {
|
|
|
|
szCreationStartPoint[ i ] = L'\0';
|
|
|
|
fSuccess = CreateDirectoryW( szFullPath, NULL );
|
|
|
|
szCreationStartPoint[ i ] = charReplaced;
|
|
|
|
if( !fSuccess ) {
|
|
LastError = GetLastError();
|
|
if( LastError != ERROR_ALREADY_EXISTS ) {
|
|
|
|
//
|
|
// continue onwards, trying to create specified subdirectories.
|
|
// this is done to address the obscure scenario where
|
|
// the Bypass Traverse Checking Privilege allows the caller
|
|
// to create directories below an existing path where one
|
|
// component denies the user access.
|
|
// we just keep trying and the last CreateDirectory()
|
|
// result is returned to the caller.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
LastError = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// check if the last directory creation actually succeeded.
|
|
// if it did, we need to adjust the file attributes on that directory
|
|
// and its parent.
|
|
//
|
|
|
|
if( fSuccess ) {
|
|
|
|
SetFileAttributesW( szFullPath, FILE_ATTRIBUTE_SYSTEM );
|
|
|
|
//
|
|
// now, scan from right to left looking for the prior directory
|
|
// de-limiter.
|
|
//
|
|
|
|
if( cchRemaining < 2 )
|
|
return LastError;
|
|
|
|
for ( i = (cchRemaining-2) ; i > 0 ; i-- ) {
|
|
WCHAR charReplaced = szCreationStartPoint[ i ];
|
|
|
|
if( charReplaced == L'\\' || charReplaced == L'/' ) {
|
|
|
|
szCreationStartPoint[ i ] = L'\0';
|
|
SetFileAttributesW( szFullPath, FILE_ATTRIBUTE_SYSTEM );
|
|
szCreationStartPoint[ i ] = charReplaced;
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return LastError;
|
|
}
|
|
|
|
DWORD
|
|
GetUserStorageArea(
|
|
IN int csidl,
|
|
IN PSID UserSid,
|
|
OUT LPWSTR *UserStorageArea
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the path to user's roaming profile. The subdirectories are created as needed.
|
|
|
|
Caller must be impersonating the user.
|
|
|
|
Arguments:
|
|
|
|
csidl - Specifies which system directory to use as a root.
|
|
|
|
UserSid - Sid of the user
|
|
|
|
|
|
UserStorageArea - Returns the path of the user storage area
|
|
The buffer must be freed using LsapFreeLsaHeap
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR: Path returned properly
|
|
ERROR_CANTOPEN: Path could not be found
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
NTSTATUS Status;
|
|
|
|
WCHAR szUserStorageRoot[MAX_PATH+1];
|
|
DWORD cbUserStorageRoot;
|
|
|
|
const WCHAR szProductString[] = L"\\Microsoft\\Credentials\\";
|
|
DWORD cbProductString = sizeof(szProductString) - sizeof(WCHAR);
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
PBYTE Where;
|
|
UNICODE_STRING UserSidString;
|
|
|
|
typedef HRESULT (WINAPI *SHGETFOLDERPATHW)(
|
|
HWND hwnd,
|
|
int csidl,
|
|
HANDLE hToken,
|
|
DWORD dwFlags,
|
|
LPWSTR pszPath
|
|
);
|
|
|
|
static SHGETFOLDERPATHW _SHGetFolderPathW;
|
|
HANDLE hToken;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
*UserStorageArea = NULL;
|
|
RtlInitUnicodeString( &UserSidString, NULL );
|
|
|
|
//
|
|
// Load shell32.dll
|
|
//
|
|
if(_SHGetFolderPathW == NULL) {
|
|
|
|
HMODULE hShell32 = LoadLibraryW( L"shell32.dll" );
|
|
if(hShell32 == NULL) {
|
|
WinStatus = ERROR_CANTOPEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
_SHGetFolderPathW = (SHGETFOLDERPATHW)GetProcAddress(hShell32, "SHGetFolderPathW");
|
|
|
|
if( _SHGetFolderPathW == NULL ) {
|
|
WinStatus = ERROR_CANTOPEN;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the path of the "Application Data" folder
|
|
//
|
|
|
|
if( !OpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken )) {
|
|
WinStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
WinStatus = (DWORD)_SHGetFolderPathW( NULL, csidl | CSIDL_FLAG_CREATE, hToken, 0, szUserStorageRoot );
|
|
|
|
CloseHandle( hToken );
|
|
|
|
if( WinStatus != ERROR_SUCCESS ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
cbUserStorageRoot = wcslen( szUserStorageRoot ) * sizeof(WCHAR);
|
|
|
|
//
|
|
// An empty string is not legal as the root component of the per-user
|
|
// storage area.
|
|
//
|
|
|
|
if( cbUserStorageRoot == 0 ) {
|
|
WinStatus = ERROR_CANTOPEN;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure returned string does not have trailing \
|
|
//
|
|
|
|
if( szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] == L'\\' ) {
|
|
|
|
szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] = L'\0';
|
|
cbUserStorageRoot -= sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// Convert the SID to a text string
|
|
//
|
|
|
|
Status = RtlConvertSidToUnicodeString( &UserSidString, UserSid, TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate memory for the return string
|
|
//
|
|
|
|
*UserStorageArea = (LPWSTR)LsapAllocateLsaHeap(
|
|
cbUserStorageRoot +
|
|
cbProductString +
|
|
UserSidString.Length +
|
|
(2 * sizeof(WCHAR)) // trailing slash and NULL
|
|
);
|
|
|
|
if( *UserStorageArea == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Build the name as of the path.
|
|
//
|
|
|
|
Where = (PBYTE)*UserStorageArea;
|
|
|
|
RtlCopyMemory(Where, szUserStorageRoot, cbUserStorageRoot);
|
|
Where += cbUserStorageRoot;
|
|
|
|
RtlCopyMemory(Where, szProductString, cbProductString);
|
|
Where += cbProductString;
|
|
|
|
RtlCopyMemory(Where, UserSidString.Buffer, UserSidString.Length);
|
|
Where += UserSidString.Length; // note: does not include terminal NULL
|
|
|
|
|
|
if( *((LPWSTR)Where - 1) != L'\\' ) {
|
|
*(LPWSTR)Where = L'\\';
|
|
Where += sizeof(WCHAR);
|
|
}
|
|
|
|
*(LPWSTR)Where = L'\0';
|
|
|
|
|
|
//
|
|
// Ensure the directory exists
|
|
//
|
|
|
|
WinStatus = CreateNestedDirectories(
|
|
*UserStorageArea,
|
|
(LPWSTR)((LPBYTE)*UserStorageArea + cbUserStorageRoot + sizeof(WCHAR)) );
|
|
|
|
|
|
Cleanup:
|
|
|
|
RtlFreeUnicodeString( &UserSidString );
|
|
|
|
if( WinStatus != ERROR_SUCCESS && *UserStorageArea ) {
|
|
LsapFreeLsaHeap( *UserStorageArea );
|
|
*UserStorageArea = NULL;
|
|
}
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
OpenFileInStorageArea(
|
|
IN DWORD dwDesiredAccess,
|
|
IN LPCWSTR szUserStorageArea,
|
|
IN LPCWSTR szFileName,
|
|
IN OUT HANDLE *phFile
|
|
)
|
|
// Routine stolen from crypto API
|
|
{
|
|
LPWSTR szFilePath = NULL;
|
|
DWORD cbUserStorageArea;
|
|
DWORD cbFileName;
|
|
DWORD dwShareMode = 0;
|
|
DWORD dwCreationDistribution = OPEN_EXISTING;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
*phFile = INVALID_HANDLE_VALUE;
|
|
|
|
if( dwDesiredAccess & GENERIC_READ ) {
|
|
dwShareMode |= FILE_SHARE_READ;
|
|
dwCreationDistribution = OPEN_EXISTING;
|
|
}
|
|
|
|
if( dwDesiredAccess & GENERIC_WRITE ) {
|
|
dwShareMode = 0;
|
|
dwCreationDistribution = CREATE_ALWAYS;
|
|
}
|
|
|
|
cbUserStorageArea = wcslen( szUserStorageArea ) * sizeof(WCHAR);
|
|
cbFileName = wcslen( szFileName ) * sizeof(WCHAR);
|
|
|
|
SafeAllocaAllocate( szFilePath, cbUserStorageArea + cbFileName + sizeof(WCHAR) );
|
|
|
|
if( szFilePath == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
CopyMemory(szFilePath, szUserStorageArea, cbUserStorageArea);
|
|
CopyMemory((LPBYTE)szFilePath+cbUserStorageArea, szFileName, cbFileName + sizeof(WCHAR));
|
|
|
|
if( LastError == ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// TODO:
|
|
// apply security descriptor to file.
|
|
//
|
|
|
|
*phFile = CreateFileW(
|
|
szFilePath,
|
|
dwDesiredAccess,
|
|
dwShareMode,
|
|
NULL,
|
|
dwCreationDistribution,
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
FILE_ATTRIBUTE_SYSTEM |
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if( *phFile == INVALID_HANDLE_VALUE ) {
|
|
LastError = GetLastError();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if(szFilePath)
|
|
SafeAllocaFree(szFilePath);
|
|
|
|
return LastError;
|
|
}
|
|
|
|
DWORD
|
|
DeleteFileInStorageArea(
|
|
IN LPCWSTR szUserStorageArea,
|
|
IN LPCWSTR szFileName
|
|
)
|
|
// Routine stolen from crypto API
|
|
{
|
|
LPWSTR szFilePath = NULL;
|
|
DWORD cbUserStorageArea;
|
|
DWORD cbFileName;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
cbUserStorageArea = wcslen( szUserStorageArea ) * sizeof(WCHAR);
|
|
cbFileName = wcslen( szFileName ) * sizeof(WCHAR);
|
|
|
|
SafeAllocaAllocate( szFilePath, cbUserStorageArea + cbFileName + sizeof(WCHAR) );
|
|
|
|
if( szFilePath == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
CopyMemory(szFilePath, szUserStorageArea, cbUserStorageArea);
|
|
CopyMemory((LPBYTE)szFilePath+cbUserStorageArea, szFileName, cbFileName + sizeof(WCHAR));
|
|
|
|
if ( !DeleteFileW( szFilePath ) ) {
|
|
LastError = GetLastError();
|
|
}
|
|
|
|
if(szFilePath)
|
|
SafeAllocaFree(szFilePath);
|
|
|
|
return LastError;
|
|
}
|
|
|
|
BOOL
|
|
CredpGetUnicodeString(
|
|
IN LPBYTE BufferEnd,
|
|
IN OUT LPBYTE *Where,
|
|
OUT LPWSTR *String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if a UNICODE string in a message buffer is valid.
|
|
|
|
UNICODE strings always appear at a 2-byte boundary in the message.
|
|
|
|
Arguments:
|
|
|
|
BufferEnd - A pointer to the first byte beyond the end of the buffer.
|
|
|
|
Where - Indirectly points to the current location in the buffer. The
|
|
string at the current location is validated (i.e., checked to ensure
|
|
its length is within the bounds of the message buffer and not too
|
|
long). If the string is valid, this current location is updated
|
|
to point to the byte following the zero byte in the message buffer.
|
|
|
|
String - Returns a pointer to the validated string.
|
|
Pointer is to the buffer *Where points to.
|
|
Returns NULL for empty strings.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - the string is valid.
|
|
|
|
FALSE - the string is invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Size;
|
|
LPWSTR ZeroPtr;
|
|
|
|
//
|
|
// Align the unicode string on a WCHAR boundary.
|
|
//
|
|
|
|
*Where = (LPBYTE) ROUND_UP_POINTER( *Where, ALIGN_WCHAR );
|
|
|
|
if ( (*Where) + sizeof(ULONG) > BufferEnd ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetUnicodeString: String size after buffer end: %lx %lx.\n",
|
|
*Where,
|
|
BufferEnd ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the string size (in bytes)
|
|
//
|
|
|
|
Size = SmbGetUlong( *Where );
|
|
*Where += sizeof(ULONG);
|
|
|
|
if ( Size == 0 ) {
|
|
*String = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if ( *Where >= BufferEnd ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetUnicodeString: String after buffer end: %lx %lx.\n",
|
|
*Where,
|
|
BufferEnd ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure the size is aligned
|
|
//
|
|
|
|
if ( Size != ROUND_UP_COUNT( Size, ALIGN_WCHAR) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetUnicodeString: Size not aligned: %lx.\n",
|
|
Size ));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Limit the string to the number of bytes remaining in the message buffer.
|
|
//
|
|
|
|
if ( Size > (ULONG)(BufferEnd - (*Where)) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetUnicodeString: String too big: %lx %lx %lx.\n",
|
|
*Where,
|
|
BufferEnd,
|
|
Size ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure the trailing zero exists
|
|
//
|
|
|
|
if ( ((LPWSTR)(*Where))[(Size/sizeof(WCHAR))-1] != L'\0' ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetUnicodeString: No trailing zero: %lx.\n",
|
|
Size ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Ensure there aren't extra zero bytes.
|
|
//
|
|
|
|
ZeroPtr = wcschr( (LPWSTR)(*Where), L'\0' );
|
|
|
|
if ( ZeroPtr < &((LPWSTR)(*Where))[(Size/sizeof(WCHAR))-1] ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"Trailing zero in middle of string: %lx.\n",
|
|
Size ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Position 'Where' past the end of the string.
|
|
//
|
|
|
|
*String = (LPWSTR)(*Where);
|
|
*Where += Size;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOL
|
|
CredpGetBytes(
|
|
IN LPBYTE BufferEnd,
|
|
IN OUT LPBYTE *Where,
|
|
OUT LPDWORD BufferSize,
|
|
OUT LPBYTE *Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmarshal an array of bytes from a message buffer.
|
|
|
|
Arguments:
|
|
|
|
BufferEnd - A pointer to the first byte beyond the end of the buffer.
|
|
|
|
Where - Indirectly points to the current location in the buffer. The
|
|
string at the current location is validated (i.e., checked to ensure
|
|
its length is within the bounds of the message buffer and not too
|
|
long). If the string is valid, this current location is updated
|
|
to point to the byte following the data bytes in the message buffer.
|
|
|
|
BufferSize - Returns the size (in bytes) of the data.
|
|
|
|
Buffer - Returns a pointer to the validated data.
|
|
Pointer is to the buffer *Where points to.
|
|
Returns NULL for zero length data.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - the string is valid.
|
|
|
|
FALSE - the string is invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Size;
|
|
|
|
//
|
|
// Get the string size (in bytes)
|
|
//
|
|
|
|
Size = SmbGetUlong( *Where );
|
|
*Where += sizeof(ULONG);
|
|
|
|
if ( Size == 0 ) {
|
|
*Buffer = NULL;
|
|
*BufferSize = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if ( *Where >= BufferEnd ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetBytes: String after buffer end: %lx %lx.\n",
|
|
*Where,
|
|
BufferEnd ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Limit the string to the number of bytes remaining in the message buffer.
|
|
//
|
|
|
|
if ( Size > (ULONG)(BufferEnd - (*Where)) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpGetBytes: String too big: %lx %lx %lx.\n",
|
|
*Where,
|
|
BufferEnd,
|
|
Size ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Position 'Where' past the end of the string.
|
|
//
|
|
|
|
*Buffer = *Where;
|
|
*BufferSize = Size;
|
|
*Where += Size;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpMarkDirty(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN ULONG Persist,
|
|
IN PCANONICAL_CREDENTIAL Credential OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine marks a credential set dirty and updates the last written
|
|
time on a credential.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential set to mark.
|
|
|
|
Persist - Persistence of the credential set to mark
|
|
|
|
Credential - Specifies the modified credential.
|
|
If NULL, no specific credential was modified.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Mark the credential set dirty.
|
|
//
|
|
|
|
PersistToCredentialSet( CredentialSets, Persist )->Dirty = TRUE;
|
|
|
|
//
|
|
// Mark the credential as modified
|
|
//
|
|
|
|
if ( Credential != NULL ) {
|
|
LsapQuerySystemTime( &Credential->Cred.LastWritten );
|
|
}
|
|
|
|
}
|
|
|
|
DWORD
|
|
CredpReadCredSet(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN ULONG Persist
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine reads a credential set from disk
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential sets to read into
|
|
|
|
Persist - Persistence of the credential set to read
|
|
|
|
Return Values:
|
|
|
|
NO_ERROR - Credential set read successfully
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
PMARSHALED_CREDENTIAL_SET CredSetBuffer = NULL;
|
|
ULONG CredSetBufferSize = 0;
|
|
LPBYTE CredSetBufferEnd;
|
|
PMARSHALED_CREDENTIAL CredEntry;
|
|
LIST_ENTRY CredentialList;
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
|
|
LPWSTR FilePath = NULL;
|
|
LPWSTR FileName = CREDENTIAL_FILE_NAME;
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE;
|
|
PCREDENTIAL_ATTRIBUTEW Attributes = NULL;
|
|
BOOLEAN CritSectLocked = TRUE;
|
|
|
|
ULONG i;
|
|
ULONG BytesRead;
|
|
|
|
PVOID EncryptedBlob = NULL;
|
|
ULONG EncryptedBlobSize;
|
|
|
|
LPBYTE LocalCredBlob = NULL;
|
|
ULONG LocalCredBlobSize = 0;
|
|
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
LPBYTE Where;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
InitializeListHead( &CredentialList );
|
|
|
|
|
|
//
|
|
// Ignore non-persistent credential sets
|
|
//
|
|
|
|
if ( Persist == CRED_PERSIST_SESSION ) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
|
|
|
|
|
|
//
|
|
// Drop the lock while we're doing the read. DPAPI accesses network resources
|
|
// while encrypting. We don't want to hold up cred set access just because the
|
|
// network is slow.
|
|
//
|
|
|
|
RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
CritSectLocked = FALSE;
|
|
|
|
//
|
|
// Get the name of the path to read the cred set from
|
|
//
|
|
|
|
WinStatus = GetUserStorageArea(
|
|
(Persist == CRED_PERSIST_ENTERPRISE ? CSIDL_APPDATA : CSIDL_LOCAL_APPDATA),
|
|
CredentialSets->UserCredentialSets->UserSid,
|
|
&FilePath );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
if ( WinStatus == ERROR_CANTOPEN ) {
|
|
WinStatus = NO_ERROR;
|
|
} else {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot determine path to profile: %ld.\n",
|
|
WinStatus ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
WinStatus = OpenFileInStorageArea(
|
|
GENERIC_READ,
|
|
FilePath,
|
|
FileName,
|
|
&FileHandle );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
if ( WinStatus == ERROR_FILE_NOT_FOUND ||
|
|
WinStatus == ERROR_PATH_NOT_FOUND ) {
|
|
WinStatus = NO_ERROR;
|
|
} else {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot open file %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
WinStatus ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the size of the file.
|
|
//
|
|
|
|
EncryptedBlobSize = GetFileSize( FileHandle, NULL );
|
|
|
|
if ( EncryptedBlobSize == 0xFFFFFFFF ) {
|
|
|
|
WinStatus = GetLastError();
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot GetFileSize %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
WinStatus ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( EncryptedBlobSize < 1 ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Size too small %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
EncryptedBlobSize ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a buffer to read the file into.
|
|
//
|
|
|
|
SafeAllocaAllocate( EncryptedBlob, EncryptedBlobSize );
|
|
|
|
if ( EncryptedBlob == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Read the file into the buffer.
|
|
//
|
|
|
|
if ( !ReadFile( FileHandle,
|
|
EncryptedBlob,
|
|
EncryptedBlobSize,
|
|
&BytesRead,
|
|
NULL ) ) { // Not Overlapped
|
|
|
|
WinStatus = GetLastError();
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot ReadFile %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
WinStatus ));
|
|
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( BytesRead != EncryptedBlobSize ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot read entire file %ls\\%ls: %ld %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
BytesRead,
|
|
EncryptedBlobSize ));
|
|
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Decrypt the data
|
|
//
|
|
|
|
if ( !LsaICryptUnprotectData(
|
|
EncryptedBlob,
|
|
EncryptedBlobSize,
|
|
NULL, // No additional entropy
|
|
0,
|
|
NULL, // Must be NULL
|
|
NULL, // Must be NULL
|
|
CRYPTPROTECT_SYSTEM | // Cannot be decrypted by usermode app
|
|
CRYPTPROTECT_VERIFY_PROTECTION | // Tell us if the encryption algorithm needs to change
|
|
CRYPTPROTECT_UI_FORBIDDEN, // No UI allowed
|
|
NULL, // No description of the data
|
|
(PVOID *)&CredSetBuffer,
|
|
&CredSetBufferSize ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Cannot CryptUnprotectData: 0x%lx.\n",
|
|
WinStatus ));
|
|
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the encryption algorithm changed,
|
|
// encrypt the data with the new algorithm
|
|
//
|
|
|
|
WinStatus = GetLastError();
|
|
if ( WinStatus == CRYPT_I_NEW_PROTECTION_REQUIRED ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Need to change encryption algorithm: 0x%lx.\n",
|
|
WinStatus ));
|
|
|
|
// The easiest way to do that is to simply mark the cred set as dirty.
|
|
CredpMarkDirty( CredentialSets, Persist, NULL );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Validate the returned data.
|
|
//
|
|
|
|
if ( CredSetBufferSize < sizeof(MARSHALED_CREDENTIAL_SET) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Size too small %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
CredSetBufferSize ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CredSetBuffer->Version != MARSHALED_CREDENTIAL_SET_VERSION ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Version wrong %ls\\%ls: %ld %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
CredSetBuffer->Version,
|
|
MARSHALED_CREDENTIAL_SET_VERSION ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CredSetBuffer->Size != CredSetBufferSize ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Size wrong %ls\\%ls: %ld %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
CredSetBuffer->Size,
|
|
CredSetBufferSize ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Loop through each log entry.
|
|
//
|
|
|
|
CredSetBufferEnd = ((LPBYTE)CredSetBuffer) + CredSetBufferSize;
|
|
CredEntry = (PMARSHALED_CREDENTIAL)ROUND_UP_POINTER( (CredSetBuffer + 1), ALIGN_WORST );
|
|
|
|
while ( (LPBYTE)(CredEntry+1) <= CredSetBufferEnd ) {
|
|
LPBYTE CredEntryEnd;
|
|
ENCRYPTED_CREDENTIALW LocalCredential;
|
|
|
|
CredEntryEnd = ((LPBYTE)CredEntry) + CredEntry->EntrySize;
|
|
|
|
//
|
|
// Cleanup from a previous iteration.
|
|
//
|
|
|
|
if ( Attributes != NULL ) {
|
|
LsapFreeLsaHeap( Attributes );
|
|
Attributes = NULL;
|
|
}
|
|
|
|
//
|
|
// Ensure this entry is entirely within the allocated buffer.
|
|
//
|
|
|
|
if ( CredEntryEnd > CredSetBufferEnd ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Entry too big %ls\\%ls: %ld %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
((LPBYTE)CredEntry)-((LPBYTE)CredSetBuffer),
|
|
CredEntry->EntrySize ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the entry
|
|
//
|
|
|
|
if ( !COUNT_IS_ALIGNED(CredEntry->EntrySize, ALIGN_WORST) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: EntrySize not aligned %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
CredEntry->EntrySize ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Grab the Position past the fixed size data for the entry.
|
|
//
|
|
|
|
Where = (LPBYTE) (CredEntry+1);
|
|
if ( Where >= CredEntryEnd ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Data after record missing %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the fixed size fields into the buffer.
|
|
//
|
|
|
|
|
|
RtlZeroMemory( &LocalCredential, sizeof(LocalCredential) );
|
|
LocalCredential.Cred.Flags = CredEntry->Flags;
|
|
LocalCredential.Cred.Type = CredEntry->Type;
|
|
LocalCredential.Cred.LastWritten = CredEntry->LastWritten;
|
|
LocalCredential.Cred.Persist = Persist;
|
|
LocalCredential.Cred.AttributeCount = CredEntry->AttributeCount;
|
|
|
|
//
|
|
// Copy the strings
|
|
//
|
|
|
|
if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.TargetName ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: TargetName string broken %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.Comment ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Comment string broken %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.TargetAlias ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: TargetAlias string broken %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.UserName ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: UserName string broken %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Unmarshal the clear credential itself
|
|
//
|
|
|
|
if ( !CredpGetBytes( CredEntryEnd,
|
|
&Where,
|
|
&LocalCredential.Cred.CredentialBlobSize,
|
|
&LocalCredential.Cred.CredentialBlob ) ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Credential broken %ls\\%ls: %lx %lx.\n",
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
// Clear cred have both buffers the same size.
|
|
LocalCredential.ClearCredentialBlobSize = LocalCredential.Cred.CredentialBlobSize;
|
|
|
|
|
|
//
|
|
// Marshal the attributes
|
|
//
|
|
|
|
if ( CredEntry->AttributeCount != 0) {
|
|
//
|
|
// Allocate an array to point to the aliases.
|
|
//
|
|
|
|
Attributes = (PCREDENTIAL_ATTRIBUTEW) LsapAllocateLsaHeap(
|
|
CredEntry->AttributeCount * sizeof(CREDENTIAL_ATTRIBUTEW) );
|
|
|
|
if ( Attributes == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalCredential.Cred.Attributes = Attributes;
|
|
|
|
//
|
|
// Loop unmarshaling the aliases.
|
|
//
|
|
|
|
for ( i=0; i<CredEntry->AttributeCount; i++ ) {
|
|
|
|
//
|
|
// Get the flags
|
|
//
|
|
LocalCredential.Cred.Attributes[i].Flags = SmbGetUlong( Where );
|
|
Where += sizeof(ULONG);
|
|
|
|
//
|
|
// Get the keyword
|
|
//
|
|
if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.Attributes[i].Keyword ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Keyword %ld broken %ls\\%ls: %lx %lx.\n",
|
|
i,
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the value
|
|
//
|
|
|
|
if ( !CredpGetBytes( CredEntryEnd,
|
|
&Where,
|
|
&LocalCredential.Cred.Attributes[i].ValueSize,
|
|
&LocalCredential.Cred.Attributes[i].Value ) ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpReadCredSet: Value %ld broken %ls\\%ls: %lx %lx.\n",
|
|
i,
|
|
FilePath,
|
|
FileName,
|
|
Where,
|
|
CredEntryEnd ));
|
|
WinStatus = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Canonicalize the credential.
|
|
//
|
|
|
|
Status = CredpValidateCredential(
|
|
CREDP_FLAGS_CLEAR_PASSWORD,
|
|
NULL, // No target info
|
|
&LocalCredential,
|
|
&TempCredential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
if ( Status == ERROR_INVALID_PARAMETER ) {
|
|
// This isn't fatal. Just ignore this one credential
|
|
} else {
|
|
WinStatus = RtlNtStatusToDosError( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Put it on a local list until we finish reading the entire log file
|
|
//
|
|
|
|
} else {
|
|
InsertTailList( &CredentialList, &TempCredential->Next );
|
|
}
|
|
|
|
|
|
//
|
|
// Move to the next entry.
|
|
//
|
|
|
|
CredEntry = (PMARSHALED_CREDENTIAL)(((LPBYTE)CredEntry) + CredEntry->EntrySize);
|
|
}
|
|
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
|
|
//
|
|
// Be tidy.
|
|
//
|
|
Cleanup:
|
|
|
|
if ( !CritSectLocked ) {
|
|
RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
}
|
|
|
|
//
|
|
// Free the temp buffer
|
|
//
|
|
|
|
if ( LocalCredBlob != NULL ) {
|
|
RtlZeroMemory( LocalCredBlob, LocalCredBlobSize );
|
|
SafeAllocaFree( LocalCredBlob );
|
|
}
|
|
|
|
//
|
|
// If the file cannot be read,
|
|
// that's not really an error.
|
|
//
|
|
|
|
if ( WinStatus == ERROR_INVALID_DATA ) {
|
|
// Leave it lying around to allow debugging.
|
|
WinStatus = NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If we're successful so far,
|
|
// add the temporary credential list to the in-memory credential set.
|
|
//
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
|
|
|
|
//
|
|
// Loop through the credentials adding them to the in-memory credential set.
|
|
//
|
|
|
|
while ( !IsListEmpty( &CredentialList ) ) {
|
|
|
|
ListEntry = RemoveHeadList( &CredentialList );
|
|
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
//
|
|
// Write it to the credential set
|
|
//
|
|
|
|
Status = CredpWriteCredential( CredentialSets,
|
|
&TempCredential,
|
|
TRUE, // Creds are from persisted file
|
|
FALSE, // Don't update pin in CSP
|
|
FALSE, // Creds have not been prompted for
|
|
0, // No flags
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
LsapFreeLsaHeap( TempCredential );
|
|
|
|
//
|
|
// Remember the status. But continue on.
|
|
//
|
|
WinStatus = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup temporary credential list
|
|
//
|
|
while ( !IsListEmpty( &CredentialList ) ) {
|
|
|
|
ListEntry = RemoveHeadList( &CredentialList );
|
|
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
LsapFreeLsaHeap( TempCredential );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Clear the buffer with clear text passwords in it
|
|
//
|
|
|
|
if ( CredSetBuffer != NULL ) {
|
|
RtlZeroMemory( CredSetBuffer, CredSetBufferSize );
|
|
LocalFree( CredSetBuffer );
|
|
}
|
|
|
|
if ( EncryptedBlob != NULL ) {
|
|
SafeAllocaFree( EncryptedBlob );
|
|
}
|
|
|
|
if ( FilePath != NULL ) {
|
|
LsapFreeLsaHeap( FilePath );
|
|
}
|
|
if ( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
CloseHandle( FileHandle );
|
|
}
|
|
|
|
if ( Attributes != NULL ) {
|
|
LsapFreeLsaHeap( Attributes );
|
|
Attributes = NULL;
|
|
}
|
|
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpLockCredSets(
|
|
IN PCREDENTIAL_SETS CredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locks a set of credential sets.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential set to lock
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
//
|
|
// Lock the credential set.
|
|
//
|
|
// There's one crit sect the proctects all of the cred sets for the user.
|
|
// We could introduce one crit sect per credential set, but the user-wide crit
|
|
// sect would always be locked along with one of the session crit sects.
|
|
// So, the session crit sect would be wasted.
|
|
//
|
|
|
|
RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpUnlockAndFlushCredSets(
|
|
IN PCREDENTIAL_SETS CredentialSets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unlocks a set of credential sets and flushes it to disk if dirty.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential set to lock
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCREDENTIAL_SET CredentialSet;
|
|
PCANONICAL_CREDENTIAL Credential;
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG Persist;
|
|
|
|
//
|
|
// Loop through the list of credential sets persisting each
|
|
//
|
|
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
//
|
|
// Ignore non-persistent credential sets
|
|
//
|
|
|
|
if ( Persist == CRED_PERSIST_SESSION ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
|
|
|
|
|
|
|
|
//
|
|
// If the credential set is dirty,
|
|
// flush it.
|
|
//
|
|
|
|
if ( CredentialSet->Dirty ) {
|
|
|
|
//
|
|
// Indicate that the credential set is no longer dirty.
|
|
// But increment that count of times that it has been dirty.
|
|
//
|
|
|
|
CredentialSet->Dirty = FALSE;
|
|
CredentialSet->WriteCount ++;
|
|
|
|
//
|
|
// If no other thread is already writing it,
|
|
// we'll take on that responsibility.
|
|
//
|
|
// Otherwise, let that thread write it again.
|
|
//
|
|
|
|
if ( !CredentialSet->BeingWritten ) {
|
|
|
|
ULONG WriteCount;
|
|
|
|
//
|
|
// Tell other threads that we're writing
|
|
//
|
|
|
|
CredentialSet->BeingWritten = TRUE;
|
|
|
|
//
|
|
// Loop writing the credentials
|
|
//
|
|
|
|
do {
|
|
LPBYTE CredSetBuffer;
|
|
ULONG CredSetBufferSize;
|
|
LPWSTR FilePath;
|
|
LPWSTR FileName = CREDENTIAL_FILE_NAME;
|
|
DWORD WinStatus;
|
|
|
|
//
|
|
// Remember which snapshot we're writing.
|
|
//
|
|
WriteCount = CredentialSet->WriteCount;
|
|
FilePath = NULL;
|
|
|
|
//
|
|
// Grab a marshaled copy of the credential set
|
|
//
|
|
|
|
if ( !CredpMarshalCredentials( CredentialSet,
|
|
&CredSetBufferSize,
|
|
&CredSetBuffer ) ) {
|
|
//
|
|
// If we can't, mark them dirty for the next caller to flush
|
|
//
|
|
CredentialSet->Dirty = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Drop the lock while we're doing the write. DPAPI accesses network resources
|
|
// while encrypting. We don't want to hold up cred set access just because the
|
|
// network is slow.
|
|
//
|
|
|
|
RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
|
|
//
|
|
// Get the name of the path to write the cred set to
|
|
//
|
|
|
|
WinStatus = GetUserStorageArea(
|
|
(Persist == CRED_PERSIST_ENTERPRISE ? CSIDL_APPDATA : CSIDL_LOCAL_APPDATA),
|
|
CredentialSets->UserCredentialSets->UserSid,
|
|
&FilePath );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUnlockAndFlushCredSets: Cannot determine path to profile: %ld.\n",
|
|
WinStatus ));
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the buffer is zero length,
|
|
// just delete the file
|
|
//
|
|
|
|
if ( CredSetBufferSize == 0 ) {
|
|
|
|
WinStatus = DeleteFileInStorageArea( FilePath, FileName );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
if ( WinStatus == ERROR_FILE_NOT_FOUND ||
|
|
WinStatus == ERROR_PATH_NOT_FOUND ) {
|
|
WinStatus = NO_ERROR;
|
|
} else {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUnlockAndFlushCredSets: Cannot delete %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
WinStatus ));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, write data to the file.
|
|
//
|
|
} else {
|
|
PVOID EncryptedBlob;
|
|
ULONG EncryptedBlobSize;
|
|
UNICODE_STRING DescriptionString;
|
|
|
|
//
|
|
// Encrypt the data
|
|
//
|
|
|
|
RtlInitUnicodeString( &DescriptionString,
|
|
Persist == CRED_PERSIST_ENTERPRISE ?
|
|
L"Enterprise Credential Set" :
|
|
L"Local Credential Set" );
|
|
|
|
if ( !LsaICryptProtectData(
|
|
CredSetBuffer,
|
|
CredSetBufferSize,
|
|
&DescriptionString,
|
|
NULL, // No additional entropy
|
|
0,
|
|
NULL, // Must be NULL
|
|
NULL, // Must be NULL
|
|
CRYPTPROTECT_SYSTEM | // Cannot be decrypted by usermode app
|
|
CRYPTPROTECT_UI_FORBIDDEN, // No UI allowed
|
|
&EncryptedBlob,
|
|
&EncryptedBlobSize ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUnlockAndFlushCredSets: Cannot CryptProtectData: 0x%lx.\n",
|
|
WinStatus ));
|
|
|
|
|
|
} else {
|
|
|
|
HANDLE FileHandle;
|
|
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
WinStatus = OpenFileInStorageArea(
|
|
GENERIC_WRITE,
|
|
FilePath,
|
|
FileName,
|
|
&FileHandle );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
ULONG BytesWritten;
|
|
|
|
//
|
|
// Write the file
|
|
//
|
|
|
|
if ( !WriteFile( FileHandle,
|
|
EncryptedBlob,
|
|
EncryptedBlobSize,
|
|
&BytesWritten,
|
|
NULL ) ) { // Not Overlapped
|
|
|
|
WinStatus = GetLastError();
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUnlockAndFlushCredSets: Cannot write %ls\\%ls: %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
WinStatus ));
|
|
} else {
|
|
if ( BytesWritten != EncryptedBlobSize ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUnlockAndFlushCredSets: Cannot write all of %ls\\%ls: %ld %ld.\n",
|
|
FilePath,
|
|
FileName,
|
|
EncryptedBlobSize,
|
|
BytesWritten ));
|
|
|
|
WinStatus = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
|
|
CloseHandle( FileHandle );
|
|
|
|
}
|
|
|
|
LocalFree( EncryptedBlob );
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If we failed to write for any reason,
|
|
// ensure the next caller does the flush.
|
|
//
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
CredentialSet->Dirty = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Clear the buffer with clear text passwords in it
|
|
//
|
|
|
|
if ( CredSetBufferSize != 0 ) {
|
|
RtlZeroMemory( CredSetBuffer, CredSetBufferSize );
|
|
LsapFreeLsaHeap( CredSetBuffer );
|
|
}
|
|
|
|
//
|
|
// Free any other resources
|
|
//
|
|
|
|
if ( FilePath != NULL ) {
|
|
LsapFreeLsaHeap( FilePath );
|
|
}
|
|
|
|
//
|
|
// Grab the lock again to see if we need to write again
|
|
//
|
|
|
|
RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
|
|
} while ( CredentialSet->WriteCount != WriteCount );
|
|
|
|
//
|
|
// Tell other threads that we're no longer writing
|
|
//
|
|
|
|
CredentialSet->BeingWritten = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unlock the credential set.
|
|
//
|
|
|
|
RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
|
|
}
|
|
|
|
|
|
PCANONICAL_CREDENTIAL
|
|
CredpFindCredential(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PUNICODE_STRING TargetName,
|
|
IN ULONG Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds a named credential in a credential set.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSet - Credential set list to find the credential in.
|
|
|
|
TargetName - Name of the credential to find.
|
|
|
|
Type - Type of the credential to find.
|
|
|
|
Return Values:
|
|
|
|
Returns a pointer to the named credential. This pointer my be used as long as
|
|
UserCredentialSets->CritSect remains locked.
|
|
|
|
NULL: There is no such credential.
|
|
|
|
--*/
|
|
{
|
|
PCREDENTIAL_SET CredentialSet;
|
|
PCANONICAL_CREDENTIAL Credential;
|
|
PLIST_ENTRY ListEntry;
|
|
ULONG Persist;
|
|
|
|
//
|
|
// Ignore queries for unsupported cred types
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
CredpIsDomainCredential( Type ) ) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Loop through the list of credentials trying to find this one.
|
|
//
|
|
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
//
|
|
// If the profile has not yet been loaded by this session,
|
|
// ignore any credentials loaded by another session.
|
|
//
|
|
|
|
if ( Persist != CRED_PERSIST_SESSION &&
|
|
!CredentialSets->SessionCredSets->ProfileLoaded ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
if ( Type == Credential->Cred.Type &&
|
|
RtlEqualUnicodeString( TargetName,
|
|
&Credential->TargetName,
|
|
TRUE ) ) {
|
|
return Credential;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
CredpWritePinToCsp(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PCANONICAL_CREDENTIAL Credential
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine write a PIN to the CSP. CSPs implement the rules for the lifetime of
|
|
the PIN. Cred manager therefore doesn't hold onto the PIN, but rather gives it to
|
|
the CSP to manage.
|
|
|
|
The caller must be impersonating the logged on user owning the cred set.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential set the credential is in.
|
|
|
|
Credential - Specifies the credential whose PIN is to be written.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_SUCCESS - The PIN was stored successfully.
|
|
Or the credential isn't a cert credential.
|
|
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
|
|
existing credential. If such a field does not match the value
|
|
specified in the existing credential, this error is returned.
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WinStatus;
|
|
|
|
CRED_MARSHAL_TYPE CredType;
|
|
PCERT_CREDENTIAL_INFO CredInfo;
|
|
|
|
CRYPT_HASH_BLOB HashBlob;
|
|
|
|
HCERTSTORE CertStoreHandle = NULL;
|
|
PCCERT_CONTEXT CertContext = NULL;
|
|
|
|
ULONG ProviderInfoSize;
|
|
PCRYPT_KEY_PROV_INFO ProviderInfo = NULL;
|
|
|
|
HCRYPTPROV ProviderHandle = NULL;
|
|
|
|
UNICODE_STRING UnicodePin;
|
|
ANSI_STRING AnsiPin;
|
|
BOOLEAN ClearPin = FALSE;
|
|
|
|
CERT_KEY_CONTEXT CertKeyContext;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
RtlInitAnsiString( &AnsiPin, NULL );
|
|
|
|
//
|
|
// Ignore credentials that aren't certificate credentials
|
|
//
|
|
|
|
if ( Credential->Cred.Type != CRED_TYPE_DOMAIN_CERTIFICATE ) {
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ignore pins of zero length.
|
|
//
|
|
// Either the caller is writing the cred and doesn't know the PIN yet.
|
|
// Or this cert has no PIN.
|
|
//
|
|
|
|
if ( Credential->Cred.CredentialBlobSize == 0 ) {
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the profile hasn't been loaded,
|
|
// fail now.
|
|
//
|
|
// The "My" store open will fail below and it'd be better to give a good error code.
|
|
//
|
|
|
|
ClearPin = TRUE;
|
|
if ( !CredentialSets->SessionCredSets->ProfileLoaded ) {
|
|
|
|
Status = SCARD_E_NO_SUCH_CERTIFICATE;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: Cannot write PIN to cert since profile isn't loaded: 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
Status ));
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Convert the UserName of the credential to a hash of the cert
|
|
//
|
|
if (!CredUnmarshalCredentialW(
|
|
Credential->Cred.UserName,
|
|
&CredType,
|
|
(PVOID *)&CredInfo ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
if ( WinStatus == ERROR_INVALID_PARAMETER ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
Status = NetpApiStatusToNtStatus(WinStatus);
|
|
}
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: Cannot unmarshal user name of cert cred: 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
Status ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CredType != CertCredential ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: cred isn't cert cred: %ld.\n",
|
|
Credential->Cred.UserName,
|
|
CredType ));
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open a cert store
|
|
//
|
|
|
|
CertStoreHandle = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
0,
|
|
0,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"MY");
|
|
|
|
if ( CertStoreHandle == NULL ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
|
|
Status = WinStatus;
|
|
} else {
|
|
Status = NetpApiStatusToNtStatus(WinStatus);
|
|
}
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: cannot open cert store: %ld 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
WinStatus,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Find the cert in the store which meets this hash
|
|
//
|
|
|
|
HashBlob.cbData = sizeof(CredInfo->rgbHashOfCert);
|
|
HashBlob.pbData = CredInfo->rgbHashOfCert;
|
|
|
|
CertContext = CertFindCertificateInStore(
|
|
CertStoreHandle,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_HASH,
|
|
&HashBlob,
|
|
NULL );
|
|
|
|
if ( CertContext == NULL ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
|
|
Status = WinStatus;
|
|
} else if ( WinStatus == CRYPT_E_NOT_FOUND ) {
|
|
Status = SCARD_E_NO_SUCH_CERTIFICATE;
|
|
} else {
|
|
Status = NetpApiStatusToNtStatus(WinStatus);
|
|
}
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: cannot find cert: %ld 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
WinStatus,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get a handle to the private key
|
|
//
|
|
|
|
if ( !CryptAcquireCertificatePrivateKey(
|
|
CertContext,
|
|
CRYPT_ACQUIRE_SILENT_FLAG, // Ensure it doesn't prompt
|
|
NULL, // reserved
|
|
&ProviderHandle,
|
|
NULL,
|
|
NULL ) ) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
|
|
Status = WinStatus;
|
|
|
|
} else if ( WinStatus == NTE_BAD_KEYSET ) {
|
|
// Some CSPs (Schlumberger Cryptoflex) return the wrong status
|
|
Status = SCARD_W_REMOVED_CARD;
|
|
|
|
} else {
|
|
Status = NetpApiStatusToNtStatus(WinStatus);
|
|
}
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: cannot CryptAcquireCertificatePrivateKey: %ld 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
WinStatus,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Convert the pin to ANSI
|
|
//
|
|
|
|
LsaUnprotectMemory( Credential->Cred.CredentialBlob,
|
|
Credential->Cred.CredentialBlobSize );
|
|
|
|
UnicodePin.Buffer = (LPWSTR) Credential->Cred.CredentialBlob;
|
|
UnicodePin.Length = (USHORT) Credential->ClearCredentialBlobSize;
|
|
UnicodePin.MaximumLength = (USHORT) Credential->ClearCredentialBlobSize;
|
|
|
|
Status = RtlUnicodeStringToAnsiString( &AnsiPin,
|
|
&UnicodePin,
|
|
TRUE );
|
|
|
|
LsaProtectMemory( Credential->Cred.CredentialBlob,
|
|
Credential->Cred.CredentialBlobSize );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: Cannot convert PIN '%wZ' to ANSI: 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
&UnicodePin,
|
|
Status ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the pin in the provider
|
|
//
|
|
if (!CryptSetProvParam(
|
|
ProviderHandle,
|
|
PP_KEYEXCHANGE_PIN,
|
|
(LPBYTE)AnsiPin.Buffer,
|
|
0 )) {
|
|
|
|
WinStatus = GetLastError();
|
|
|
|
//
|
|
// Some certs don't require a PIN
|
|
//
|
|
|
|
if ( WinStatus == NTE_BAD_TYPE ) {
|
|
|
|
//
|
|
// If the caller didn't pass us a PIN,
|
|
// we're fine.
|
|
//
|
|
|
|
if ( AnsiPin.Length == 0 ) {
|
|
WinStatus = NO_ERROR;
|
|
|
|
//
|
|
// If the caller did pass us a PIN,
|
|
// tell him.
|
|
//
|
|
} else {
|
|
|
|
WinStatus = ERROR_INVALID_PASSWORD;
|
|
}
|
|
}
|
|
|
|
if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
|
|
Status = WinStatus;
|
|
|
|
} else if ( WinStatus == ERROR_ACCOUNT_DISABLED ) {
|
|
// Some CSPs (Schlumberger Cryptoflex) return the wrong status
|
|
Status = SCARD_W_CHV_BLOCKED;
|
|
|
|
} else {
|
|
Status = NetpApiStatusToNtStatus(WinStatus);
|
|
}
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWritePinToCsp: %ws: cannot CryptSetProvParam: %ld 0x%lx.\n",
|
|
Credential->Cred.UserName,
|
|
WinStatus,
|
|
WinStatus ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Free any locally used resources
|
|
//
|
|
Cleanup:
|
|
//
|
|
// The whole reason we wrote the PIN to the CSP was so that cred man wouldn't store it.
|
|
// So clear it.
|
|
//
|
|
if ( ClearPin ) {
|
|
if ( Credential->Cred.CredentialBlob != NULL &&
|
|
Credential->Cred.CredentialBlobSize != 0 ) {
|
|
RtlZeroMemory( Credential->Cred.CredentialBlob,
|
|
Credential->Cred.CredentialBlobSize );
|
|
|
|
}
|
|
|
|
Credential->Cred.CredentialBlob = NULL;
|
|
Credential->Cred.CredentialBlobSize = 0;
|
|
Credential->ClearCredentialBlobSize = 0;
|
|
// Cred it already marked dirty
|
|
}
|
|
|
|
if ( AnsiPin.Buffer != NULL ) {
|
|
RtlZeroMemory( AnsiPin.Buffer, AnsiPin.Length );
|
|
RtlFreeAnsiString( &AnsiPin );
|
|
}
|
|
|
|
if ( ProviderHandle != NULL ) {
|
|
CryptReleaseContext( ProviderHandle, 0 );
|
|
}
|
|
|
|
if (NULL != ProviderInfo) {
|
|
LsapFreeLsaHeap( ProviderInfo );
|
|
}
|
|
|
|
if ( CertContext != NULL ) {
|
|
CertFreeCertificateContext( CertContext );
|
|
}
|
|
|
|
if ( CertStoreHandle != NULL ) {
|
|
CertCloseStore( CertStoreHandle, 0 );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CredpWriteCredential(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN OUT PCANONICAL_CREDENTIAL *NewCredential,
|
|
IN BOOLEAN FromPersistedFile,
|
|
IN BOOLEAN WritePinToCsp,
|
|
IN BOOLEAN PromptedFor,
|
|
IN DWORD Flags,
|
|
OUT PCANONICAL_CREDENTIAL *OldCredential OPTIONAL,
|
|
OUT PPROMPT_DATA *OldPromptData OPTIONAL,
|
|
OUT PPROMPT_DATA *NewPromptData OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine writes a credential to a credential set. If the credential
|
|
replaces an existing credential, the existing credential is delinked and
|
|
returned to the caller.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked. (This design ensures
|
|
Credential is valid upon return from the routine.)
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - Credential set to write the credential in.
|
|
|
|
NewCredential - Specifies the credential to be written.
|
|
If CRED_PRESERVE_CREDENTIAL_BLOB is specified, the Credential returned in this
|
|
parameter will contain the preserved credential blob. The original credential will be
|
|
deleted.
|
|
|
|
If CRED_PRESERVE_CREDENTIAL_BLOB is not specified, this field will not be modified.
|
|
|
|
FromPersistedFile - True if the credential is from a persisted file.
|
|
Less validation is done on the credential.
|
|
|
|
WritePinToCsp - True if the password is explicitly being set.
|
|
If true, the PIN of certificate credentials is to be written to the CSP.
|
|
Callers that are simply updating the in-memory credential will set this false.
|
|
|
|
PromptedFor - Specifies whether Credential has already been prompted for
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
The following flags are defined:
|
|
|
|
CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
|
|
already existing credential with the same credential name and credential type.
|
|
|
|
OldCredential - If specified, returns a pointer to the old credential
|
|
Returns NULL if there was no old credential.
|
|
OldCredential should be freed using LsapFreeLsaHeap.
|
|
|
|
OldPromptData - If specified, returns a pointer to the old prompt data for the credential
|
|
Returns NULL if there is no old prompt data.
|
|
OldPromptData should be freed using LsapFreeLsaHeap.
|
|
|
|
NewPromptData - If specified, returns a pointer to the new prompt data for the credential
|
|
Returns NULL if there is no new prompt data.
|
|
NewPromptData is properly linked into the prompt data list and should only be freed
|
|
(using LsapFreeLsaHeap) if it is delinked by the caller.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
|
|
existing credential. If such a field does not match the value
|
|
specified in the existing credential, this error is returned.
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PCANONICAL_CREDENTIAL Credential = *NewCredential;
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
PPROMPT_DATA TempPromptData;
|
|
PPROMPT_DATA PromptData = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
if ( OldCredential != NULL ) {
|
|
*OldCredential = NULL;
|
|
}
|
|
if ( OldPromptData != NULL ) {
|
|
*OldPromptData = NULL;
|
|
}
|
|
if ( NewPromptData != NULL ) {
|
|
*NewPromptData = NULL;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the profile hasn't been loaded yet,
|
|
// don't allow writing to persistent credential sets.
|
|
//
|
|
|
|
if ( Credential->Cred.Persist != CRED_PERSIST_SESSION &&
|
|
!FromPersistedFile &&
|
|
!CredentialSets->SessionCredSets->ProfileLoaded ) {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: Cannot write persistent cred set until profile loaded: %ld.\n",
|
|
Credential->Cred.Persist ));
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If domain creds have been disabled,
|
|
// don't allow them to be written.
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
!FromPersistedFile &&
|
|
CredpIsDomainCredential( Credential->Cred.Type ) ) {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: Cannot write domain cred when feature is disabled: %ld.\n",
|
|
Credential->Cred.Type ));
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a personal system,
|
|
// and this is a domain credential other than the *Session cred,
|
|
// don't allow them to be written.
|
|
//
|
|
|
|
if ( CredIsPersonal &&
|
|
CredpIsDomainCredential( Credential->Cred.Type ) &&
|
|
Credential->Cred.Type != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD &&
|
|
Credential->WildcardType != WcUniversalSessionWildcard ) {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: Cannot write domain cred on personal: %ld.\n",
|
|
Credential->Cred.Type ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Don't allow * credential if the user is logged onto a domain account.
|
|
// In that case, the system uses the logon session credential for several purposes
|
|
// and the * credential masks that.
|
|
//
|
|
|
|
if ( Credential->WildcardType == WcUniversalWildcard &&
|
|
(CredentialSets->Flags & CREDSETS_FLAGS_LOCAL_ACCOUNT) == 0 ) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: Cannot write '*' cred when logged onto domain account.\n" ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate PromptData if user is writing an always prompt credential
|
|
// Assume that the user only writes such a credential because he was prompted for a password.
|
|
//
|
|
|
|
if ( !FromPersistedFile &&
|
|
!PersistCredBlob( &Credential->Cred ) ) {
|
|
|
|
PromptData = CredpAllocatePromptData( Credential );
|
|
|
|
if ( PromptData == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Find any existing credential by the same name.
|
|
//
|
|
|
|
TempCredential = CredpFindCredential(
|
|
CredentialSets,
|
|
&Credential->TargetName,
|
|
Credential->Cred.Type );
|
|
|
|
|
|
//
|
|
// Find any prompt data for the existing credential
|
|
//
|
|
// There are cases where there is prompt data and no credential. That'd be the case
|
|
// if a machine (or enterprise) credential is deleted from another session.
|
|
//
|
|
|
|
TempPromptData = CredpFindPromptData(
|
|
CredentialSets,
|
|
&Credential->TargetName,
|
|
Credential->Cred.Type,
|
|
TempCredential == NULL ?
|
|
Credential->Cred.Persist :
|
|
TempCredential->Cred.Persist );
|
|
|
|
|
|
|
|
//
|
|
// Preserve the credential blob from this credential by the same name.
|
|
//
|
|
|
|
if ( Flags & CRED_PRESERVE_CREDENTIAL_BLOB ) {
|
|
|
|
ENCRYPTED_CREDENTIALW LocalCredential;
|
|
PCANONICAL_CREDENTIAL CompleteCredential;
|
|
|
|
//
|
|
// Don't allow the caller to specify a credential blob if he asked us to preserve the existing one
|
|
|
|
if ( Credential->Cred.CredentialBlobSize != 0 ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: %ws: Trying to preserve credential blob AND specify a new one.\n",
|
|
Credential->Cred.TargetName ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If we need to preserve the blob,
|
|
// fail if there is no blob to preserve.
|
|
//
|
|
|
|
if ( TempCredential == NULL ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: %ws: Cannot write credential with preserved blob.\n",
|
|
Credential->Cred.TargetName ));
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Since we're preserving the existing credential blob,
|
|
// we know better than the caller whether the blob has been prompted for.
|
|
//
|
|
// If the existing credential has a persisted credential blob,
|
|
// then it has been prompted for.
|
|
//
|
|
// Otherwise, check whether we've already cached the credential in memory.
|
|
//
|
|
// (Use the PromptData for that to prevent leakage from another logon session.)
|
|
//
|
|
|
|
if ( PersistCredBlob( &TempCredential->Cred ) ) {
|
|
PromptedFor = TRUE;
|
|
} else {
|
|
PromptedFor = !ShouldPromptNow( TempPromptData );
|
|
}
|
|
|
|
//
|
|
// If the new credential isn't an always prompt credential, and
|
|
// there isn't a existing password on the credential,
|
|
// then the caller should have prompted for the credential and not asked us to preserve the old one.
|
|
//
|
|
|
|
if ( PromptData == NULL && !PromptedFor ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpWriteCredential: %ws: Trying to preserve credential blob AND it hasn't been prompted for.\n",
|
|
Credential->Cred.TargetName ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Build a new credential with the preserved credential blob in it.
|
|
//
|
|
|
|
LocalCredential.Cred = Credential->Cred;
|
|
LocalCredential.Cred.CredentialBlob = TempCredential->Cred.CredentialBlob;
|
|
LocalCredential.Cred.CredentialBlobSize = TempCredential->Cred.CredentialBlobSize;
|
|
LocalCredential.ClearCredentialBlobSize = TempCredential->ClearCredentialBlobSize;
|
|
|
|
//
|
|
// Get a marshaled copy of the new credential
|
|
//
|
|
|
|
Status = CredpValidateCredential(
|
|
CREDP_FLAGS_IN_PROCESS,
|
|
NULL, // Don't need to use TargetInfo again
|
|
&LocalCredential,
|
|
&CompleteCredential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Use this new credential for the rest of the operation
|
|
//
|
|
|
|
LsapFreeLsaHeap( Credential );
|
|
Credential = CompleteCredential;
|
|
*NewCredential = Credential;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a cert credential,
|
|
// write the pin into the CSP.
|
|
//
|
|
|
|
if ( WritePinToCsp ) {
|
|
Status = CredpWritePinToCsp( CredentialSets, Credential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Delink the existing credential.
|
|
//
|
|
|
|
if ( TempCredential != NULL ) {
|
|
|
|
RemoveEntryList( &TempCredential->Next );
|
|
CredpMarkDirty( CredentialSets, TempCredential->Cred.Persist, NULL );
|
|
if ( OldCredential != NULL ) {
|
|
*OldCredential = TempCredential;
|
|
} else {
|
|
LsapFreeLsaHeap( TempCredential );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Delink existing prompt data
|
|
//
|
|
if ( TempPromptData != NULL ) {
|
|
|
|
RemoveEntryList( &TempPromptData->Next );
|
|
if ( OldPromptData != NULL ) {
|
|
*OldPromptData = TempPromptData;
|
|
} else {
|
|
LsapFreeLsaHeap( TempPromptData );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the credential as modified.
|
|
// Don't change the LastWritten date on restored credentials
|
|
//
|
|
|
|
CredpMarkDirty( CredentialSets,
|
|
Credential->Cred.Persist,
|
|
FromPersistedFile ? NULL : Credential );
|
|
|
|
//
|
|
// Link the prompt data
|
|
//
|
|
|
|
if ( PromptData != NULL ) {
|
|
InsertHeadList( &CredentialSets->SessionCredSets->PromptData,
|
|
&PromptData->Next );
|
|
|
|
if ( NewPromptData != NULL ) {
|
|
*NewPromptData = PromptData;
|
|
}
|
|
|
|
PromptData->Written = PromptedFor;
|
|
PromptData = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Link the new credential into the cred set.
|
|
//
|
|
|
|
if ( FromPersistedFile ) {
|
|
// Preserve the order of the credentials in the file.
|
|
InsertTailList(
|
|
&PersistToCredentialSet( CredentialSets, Credential->Cred.Persist )->Credentials,
|
|
&Credential->Next );
|
|
} else {
|
|
// Put new credentials early in the list where they can be found quickly
|
|
InsertHeadList(
|
|
&PersistToCredentialSet( CredentialSets, Credential->Cred.Persist )->Credentials,
|
|
&Credential->Next );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( PromptData != NULL ) {
|
|
LsapFreeLsaHeap( PromptData );
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CredpWriteMorphedCredential(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN BOOLEAN PromptedFor,
|
|
IN PCANONICAL_TARGET_INFO TargetInfo OPTIONAL,
|
|
IN PENCRYPTED_CREDENTIALW MorphedCredential,
|
|
OUT PCANONICAL_CREDENTIAL *WrittenCredential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine write a morphed credential to the credential set.
|
|
|
|
Any credential by the same target name and type is delinked and deallocated.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential set.
|
|
|
|
PromptedFor - Specifies whether the MorphedCredential should be considered
|
|
as having been prompted for.
|
|
|
|
TargetInfo - A description of the various aliases for a target.
|
|
|
|
MorphedCredential - Pointer to a credential to add to the credential set.
|
|
|
|
WrittenCredential - On success, returns a pointer to the written credential.
|
|
|
|
Return Values:
|
|
|
|
Sundry credential write status codes.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PCANONICAL_CREDENTIAL ValidatedCredential;
|
|
|
|
//
|
|
// Get a marshaled copy of the new credential
|
|
//
|
|
|
|
Status = CredpValidateCredential(
|
|
CREDP_FLAGS_IN_PROCESS,
|
|
TargetInfo,
|
|
MorphedCredential,
|
|
&ValidatedCredential );
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// Write it to the credential set
|
|
//
|
|
|
|
Status = CredpWriteCredential( CredentialSets,
|
|
&ValidatedCredential,
|
|
FALSE, // Creds are not from persisted file
|
|
FALSE, // Don't update pin in CSP
|
|
PromptedFor, // Caller knows if cred has been prompted for
|
|
0, // No flags
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
*WrittenCredential = ValidatedCredential;
|
|
} else {
|
|
LsapFreeLsaHeap( ValidatedCredential );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CredpUpdatePassword2(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password,
|
|
IN DWORD CredType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the password in all credentials for UserName.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential set.
|
|
|
|
UserName - The UserName of the user.
|
|
Same format as in credential.
|
|
|
|
Password - New password for the user.
|
|
This password should be passed in hidden via LsapProtectMemory.
|
|
Length - Clear text length of the password
|
|
MaximumLength - Encrypted length of the password
|
|
|
|
CredType - Specifies the credential type to change the password on.
|
|
This should be one of CRED_TYPE_DOMAIN_*
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Persist;
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
PCANONICAL_CREDENTIAL WrittenCredential;
|
|
PLIST_ENTRY ListEntry;
|
|
ENCRYPTED_CREDENTIALW LocalCredential;
|
|
|
|
//
|
|
// Ignore writes for unsupported cred types
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
CredpIsDomainCredential( CredType ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Loop through the credentials finding those that match.
|
|
//
|
|
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
PCREDENTIAL_SET CredentialSet;
|
|
|
|
//
|
|
// If the profile has not yet been loaded by this session,
|
|
// ignore any credentials loaded by another session.
|
|
//
|
|
|
|
if ( Persist != CRED_PERSIST_SESSION &&
|
|
!CredentialSets->SessionCredSets->ProfileLoaded ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
) {
|
|
|
|
|
|
// Grab a pointer to the next entry since this one may be delinked
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
|
|
//
|
|
// Only do credentials of the type requested
|
|
//
|
|
if ( TempCredential->Cred.Type != CredType ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Only do credentials with identical user names.
|
|
//
|
|
|
|
if ( TempCredential->UserName.Length == 0 ||
|
|
!RtlEqualUnicodeString( UserName,
|
|
&TempCredential->UserName,
|
|
TRUE ) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Only do credentials if the password doesn't match already
|
|
//
|
|
|
|
if ( Password->MaximumLength == TempCredential->Cred.CredentialBlobSize &&
|
|
Password->Length == TempCredential->ClearCredentialBlobSize &&
|
|
(Password->MaximumLength == 0 ||
|
|
RtlEqualMemory( Password->Buffer,
|
|
TempCredential->Cred.CredentialBlob,
|
|
Password->MaximumLength ) ) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Finally, update the credential to match the new one.
|
|
//
|
|
|
|
LocalCredential.Cred = TempCredential->Cred;
|
|
LocalCredential.Cred.CredentialBlob = (LPBYTE)Password->Buffer;
|
|
LocalCredential.Cred.CredentialBlobSize = Password->MaximumLength;
|
|
LocalCredential.ClearCredentialBlobSize = Password->Length;
|
|
|
|
//
|
|
// Get a marshaled copy of the new credential
|
|
//
|
|
|
|
Status = CredpWriteMorphedCredential(
|
|
CredentialSets,
|
|
TRUE, // Cred has been prompted for
|
|
NULL, // No Target info since cred not for this target
|
|
&LocalCredential,
|
|
&WrittenCredential );
|
|
|
|
|
|
//
|
|
// Simply note success
|
|
//
|
|
if ( NT_SUCCESS(Status) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpUpdatePassword: %ws: Updated password for '%ws'.\n",
|
|
WrittenCredential->TargetName.Buffer,
|
|
UserName->Buffer ));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpUpdatePassword(
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PCANONICAL_CREDENTIAL NewCredential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the password in all credentials to match that of the new credential.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
CredentialSets - A pointer to the referenced credential set.
|
|
|
|
NewCredential - Pointer to a credential that was just added to the
|
|
credential set.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING Password;
|
|
|
|
|
|
//
|
|
// If the credential isn't a domain credential,
|
|
// we're done.
|
|
//
|
|
|
|
if ( !CredpIsDomainCredential( NewCredential->Cred.Type ) ||
|
|
NewCredential->UserName.Length == 0 ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the password on all matching credentials.
|
|
//
|
|
|
|
Password.Buffer = (LPWSTR)NewCredential->Cred.CredentialBlob;
|
|
Password.Length = (USHORT)NewCredential->ClearCredentialBlobSize;
|
|
Password.MaximumLength = (USHORT)NewCredential->Cred.CredentialBlobSize;
|
|
|
|
CredpUpdatePassword2( CredentialSets,
|
|
&NewCredential->UserName,
|
|
&Password,
|
|
NewCredential->Cred.Type );
|
|
|
|
}
|
|
|
|
VOID
|
|
CredpNotifyPasswordChange(
|
|
IN PUNICODE_STRING NetbiosDomainName OPTIONAL,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DnsDomainName OPTIONAL,
|
|
IN PUNICODE_STRING Upn OPTIONAL,
|
|
IN PUNICODE_STRING NewPassword
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the password of a particular user in the credential manager.
|
|
|
|
This routine is called while impersonating the user changing the password. That
|
|
user isn't necessarily UserName.
|
|
|
|
Arguments:
|
|
|
|
NetbiosDomainName - Netbios domain name of the user whose password was changed
|
|
|
|
UserName - User name of the user whose password was changed
|
|
|
|
DnsDomainName - If known, Dns Domain Name of the user whose password was changed
|
|
|
|
Upn - If known, the Upn of the user whose password was changed
|
|
|
|
NewPassword - The new password for the user.
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HANDLE ClientToken;
|
|
LUID LogonId;
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = { NULL };
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
UNICODE_STRING LocalUserName;
|
|
UNICODE_STRING EncryptedNewPassword;
|
|
|
|
//
|
|
// Got to have at least Netbios DomainName OR DNSDomainName
|
|
//
|
|
if (!ARGUMENT_PRESENT(NetbiosDomainName) &&
|
|
!ARGUMENT_PRESENT(DnsDomainName)) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
LocalUserName.Buffer = NULL;
|
|
EncryptedNewPassword.Buffer = NULL;
|
|
|
|
//
|
|
// The password needs to be protected when we put it on the credential
|
|
//
|
|
|
|
EncryptedNewPassword.MaximumLength = AllocatedCredBlobSize( NewPassword->Length );
|
|
EncryptedNewPassword.Length = NewPassword->Length;
|
|
|
|
SafeAllocaAllocate( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
|
|
|
|
if ( EncryptedNewPassword.Buffer == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
|
|
|
|
RtlCopyMemory( EncryptedNewPassword.Buffer,
|
|
NewPassword->Buffer,
|
|
NewPassword->Length );
|
|
|
|
LsaProtectMemory( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
|
|
|
|
|
|
//
|
|
// Get the logon id from the token
|
|
//
|
|
|
|
Status = NtOpenThreadToken( NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE,
|
|
&ClientToken );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto Cleanup;
|
|
} else {
|
|
TOKEN_STATISTICS TokenStats;
|
|
ULONG ReturnedSize;
|
|
|
|
//
|
|
// Get the LogonId
|
|
//
|
|
|
|
Status = NtQueryInformationToken( ClientToken,
|
|
TokenStatistics,
|
|
&TokenStats,
|
|
sizeof( TokenStats ),
|
|
&ReturnedSize );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Save the logon id
|
|
//
|
|
|
|
LogonId = TokenStats.AuthenticationId;
|
|
}
|
|
|
|
NtClose( ClientToken );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( &LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Update the password for all <NetbiosDomainName>\<UserName> credentials
|
|
//
|
|
|
|
if ( NetbiosDomainName != NULL ) {
|
|
|
|
|
|
LocalUserName.Length = NetbiosDomainName->Length +
|
|
sizeof(WCHAR) +
|
|
UserName->Length;
|
|
LocalUserName.MaximumLength = LocalUserName.Length;
|
|
SafeAllocaAllocate( LocalUserName.Buffer, LocalUserName.MaximumLength );
|
|
|
|
if ( LocalUserName.Buffer == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( LocalUserName.Buffer, NetbiosDomainName->Buffer, NetbiosDomainName->Length );
|
|
LocalUserName.Buffer[NetbiosDomainName->Length/sizeof(WCHAR)] = '\\';
|
|
RtlCopyMemory( &LocalUserName.Buffer[(NetbiosDomainName->Length/sizeof(WCHAR)) + 1],
|
|
UserName->Buffer,
|
|
UserName->Length );
|
|
|
|
|
|
CredpUpdatePassword2( &CredentialSets,
|
|
&LocalUserName,
|
|
&EncryptedNewPassword,
|
|
CRED_TYPE_DOMAIN_PASSWORD );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Update the password for all <DnsDomainName>\<UserName> credentials
|
|
//
|
|
|
|
if ( DnsDomainName != NULL ) {
|
|
|
|
if ( LocalUserName.Buffer != NULL ) {
|
|
SafeAllocaFree( LocalUserName.Buffer );
|
|
}
|
|
|
|
LocalUserName.Length = DnsDomainName->Length +
|
|
sizeof(WCHAR) +
|
|
UserName->Length;
|
|
LocalUserName.MaximumLength = LocalUserName.Length;
|
|
SafeAllocaAllocate( LocalUserName.Buffer, LocalUserName.MaximumLength );
|
|
|
|
if ( LocalUserName.Buffer == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( LocalUserName.Buffer, DnsDomainName->Buffer, DnsDomainName->Length );
|
|
LocalUserName.Buffer[DnsDomainName->Length/sizeof(WCHAR)] = '\\';
|
|
RtlCopyMemory( &LocalUserName.Buffer[(DnsDomainName->Length/sizeof(WCHAR)) + 1],
|
|
UserName->Buffer,
|
|
UserName->Length );
|
|
|
|
|
|
CredpUpdatePassword2( &CredentialSets,
|
|
&LocalUserName,
|
|
&EncryptedNewPassword,
|
|
CRED_TYPE_DOMAIN_PASSWORD );
|
|
}
|
|
|
|
|
|
//
|
|
// Update the password for all <Upn> credentials
|
|
//
|
|
|
|
if ( Upn != NULL ) {
|
|
|
|
CredpUpdatePassword2( &CredentialSets,
|
|
Upn,
|
|
&EncryptedNewPassword,
|
|
CRED_TYPE_DOMAIN_PASSWORD );
|
|
}
|
|
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( LocalUserName.Buffer != NULL ) {
|
|
SafeAllocaFree( LocalUserName.Buffer );
|
|
}
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
if ( EncryptedNewPassword.Buffer != NULL ) {
|
|
SafeAllocaFree( EncryptedNewPassword.Buffer );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CredpFindBestCredentials(
|
|
IN PLUID LogonId,
|
|
IN PCREDENTIAL_SETS CredentialSets,
|
|
IN PCANONICAL_TARGET_INFO TargetInfo,
|
|
OUT PCANONICAL_CREDENTIAL *BestCredentials,
|
|
OUT PULONG BestCredentialCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the best credentials given a description of the target.
|
|
|
|
On entry, UserCredentialSets->CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredentialSets - A pointer to the referenced credential set.
|
|
|
|
TargetInfo - A description of the various aliases for a target.
|
|
|
|
BestCredentials - Returns the best credential for each credential type.
|
|
|
|
BestCredentialCount - Returns the number of elements returned in BestCredentials.
|
|
|
|
Return Values:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PCANONICAL_CREDENTIAL Credential;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
ULONG TypeIndex;
|
|
ULONG AliasIndex;
|
|
|
|
ULONG Persist;
|
|
|
|
|
|
//
|
|
// This routine finds the best credentials for each credtype that match the TargetInfo.
|
|
// This array contains the pointers to the found credentials.
|
|
//
|
|
|
|
PCANONICAL_CREDENTIAL SavedCredentials[CRED_TYPE_MAXIMUM];
|
|
ULONG AliasIndices[CRED_TYPE_MAXIMUM];
|
|
|
|
ULONG CredTypeCount;
|
|
LPDWORD CredTypes;
|
|
|
|
//
|
|
// Clear our list of remembered credentials
|
|
//
|
|
|
|
RtlZeroMemory( &SavedCredentials, sizeof(SavedCredentials) );
|
|
for ( TypeIndex=0; TypeIndex<CRED_TYPE_MAXIMUM; TypeIndex++ ) {
|
|
AliasIndices[TypeIndex] = CRED_MAX_ALIASES;
|
|
}
|
|
|
|
//
|
|
// Loop through the list of credentials finding all that match the TargetInfo
|
|
//
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
|
|
//
|
|
// If the profile has not yet been loaded by this session,
|
|
// ignore any credentials loaded by another session.
|
|
//
|
|
|
|
if ( Persist != CRED_PERSIST_SESSION &&
|
|
!CredentialSets->SessionCredSets->ProfileLoaded ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
//
|
|
// Ignore unsupported cred types
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
CredpIsDomainCredential( Credential->Cred.Type ) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the credential matches,
|
|
// save it.
|
|
//
|
|
|
|
if ( CredpCompareCredToTargetInfo( TargetInfo,
|
|
Credential,
|
|
&AliasIndex ) ) {
|
|
|
|
//
|
|
// Compute indices into the array of saved credentials.
|
|
//
|
|
// Rely on the following:
|
|
// CompareCred* validated that the cred is a domain cred
|
|
// CompareCred* only returns TRUE for well known CredType values.
|
|
|
|
TypeIndex = Credential->Cred.Type;
|
|
|
|
|
|
//
|
|
// If we've already found a credential in this category,
|
|
// keep the "better" one.
|
|
//
|
|
|
|
if ( SavedCredentials[TypeIndex] != NULL ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: %ws: Two credentials match same target info criterias.\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer,
|
|
Credential->TargetName.Buffer ));
|
|
|
|
//
|
|
// If the existing credential is more specific than this one,
|
|
// Keep the existing.
|
|
//
|
|
|
|
if ( AliasIndices[TypeIndex] < AliasIndex ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (AI1).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
/* nothing to do here */
|
|
|
|
//
|
|
// If this credential is more specific than the saved one,
|
|
// save this one.
|
|
//
|
|
|
|
} else if ( AliasIndices[TypeIndex] > AliasIndex ) {
|
|
SavedCredentials[TypeIndex] = Credential;
|
|
AliasIndices[TypeIndex] = AliasIndex;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (AI1).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
|
|
//
|
|
// If the existing credential has a more specific wildcard,
|
|
// keep the existing.
|
|
//
|
|
|
|
} else if ( AliasIndex == CRED_WILDCARD_SERVER_NAME &&
|
|
SavedCredentials[TypeIndex]->TargetName.Length >
|
|
Credential->TargetName.Length ) {
|
|
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (WC1).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
/* nothing to do here */
|
|
|
|
//
|
|
// If the existing credential has a less specific wildcard,
|
|
// save this one.
|
|
//
|
|
|
|
} else if ( AliasIndex == CRED_WILDCARD_SERVER_NAME &&
|
|
SavedCredentials[TypeIndex]->TargetName.Length <
|
|
Credential->TargetName.Length ) {
|
|
|
|
SavedCredentials[TypeIndex] = Credential;
|
|
AliasIndices[TypeIndex] = AliasIndex;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (WC2).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
/* nothing to do here */
|
|
|
|
//
|
|
// If one credential has a target alias and the other doesn't,
|
|
// keep the one without an alias,
|
|
// this is the case when the target info passes in a netbios name
|
|
// and there is both a netbios credential and a DNS credential.
|
|
// Prefer the netbios credential.
|
|
//
|
|
|
|
} else if ( SavedCredentials[TypeIndex]->TargetAlias.Length == 0 ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (AL).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
/* nothing to do here */
|
|
|
|
//
|
|
// If one credential is session specific,
|
|
// keep it (allowing the user to override persistent credentials).
|
|
//
|
|
|
|
} else if ( SavedCredentials[TypeIndex]->Cred.Persist == CRED_PERSIST_SESSION ) {
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (PE).\n",
|
|
SavedCredentials[TypeIndex]->TargetName.Buffer ));
|
|
/* nothing to do here */
|
|
|
|
//
|
|
// Otherwise, just save this one
|
|
//
|
|
|
|
} else {
|
|
SavedCredentials[TypeIndex] = Credential;
|
|
AliasIndices[TypeIndex] = AliasIndex;
|
|
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CredpFindBestCredentials: %ws: Use this one (default).\n",
|
|
Credential->TargetName.Buffer ));
|
|
}
|
|
|
|
//
|
|
// Otherwise, just save this one
|
|
//
|
|
|
|
} else {
|
|
SavedCredentials[TypeIndex] = Credential;
|
|
AliasIndices[TypeIndex] = AliasIndex;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Don't return a *Session credential if our logon creds should work
|
|
//
|
|
// The *Session cred is a hack to force us to always use RAS dial creds on all
|
|
// connections to the dialed up network. However, we don't know what network a
|
|
// server is on. We still want to be able to use logon creds on the LAN. Our
|
|
// best bet is to use logon creds if the server is in the same domain as our logon
|
|
// creds.
|
|
//
|
|
// If we found a password cred,
|
|
// and that cred is the *Session cred,
|
|
// and the logon creds should work,
|
|
// ignore the *Session cred.
|
|
|
|
//
|
|
|
|
if ( SavedCredentials[CRED_TYPE_DOMAIN_PASSWORD] != NULL &&
|
|
AliasIndices[CRED_TYPE_DOMAIN_PASSWORD] == CRED_UNIVERSAL_SESSION_NAME ) {
|
|
|
|
Status = CredpLogonCredsMatchTargetInfo( LogonId, TargetInfo );
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
SavedCredentials[CRED_TYPE_DOMAIN_PASSWORD] = NULL;
|
|
} else if ( Status != STATUS_NO_MATCH ) {
|
|
return Status;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Decide the order to return the credentials in
|
|
//
|
|
|
|
if ( TargetInfo->CredTypeCount != 0 ) {
|
|
|
|
// Use the order specified by the auth package
|
|
CredTypeCount = TargetInfo->CredTypeCount;
|
|
CredTypes = TargetInfo->CredTypes;
|
|
|
|
} else {
|
|
|
|
// Use the default order
|
|
CredTypeCount = sizeof(CredTypeDefaultOrder)/sizeof(CredTypeDefaultOrder[0]);
|
|
CredTypes = CredTypeDefaultOrder;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return the credentials to the caller.
|
|
// Only return the ones requested by the caller
|
|
//
|
|
|
|
*BestCredentialCount = 0;
|
|
|
|
for ( TypeIndex=0; TypeIndex<CredTypeCount; TypeIndex++ ) {
|
|
|
|
ASSERT( CredTypes[TypeIndex] < CRED_TYPE_MAXIMUM );
|
|
if ( CredTypes[TypeIndex] < CRED_TYPE_MAXIMUM ) {
|
|
|
|
//
|
|
// If we have a saved credential,
|
|
// return it.
|
|
//
|
|
|
|
if ( SavedCredentials[CredTypes[TypeIndex]] != NULL ) {
|
|
BestCredentials[*BestCredentialCount] = SavedCredentials[CredTypes[TypeIndex]];
|
|
*BestCredentialCount += 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediWrite(
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN PENCRYPTED_CREDENTIALW Credential,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredWrite API creates a new credential or modifies an existing
|
|
credential in the user's credential set. The new credential is
|
|
associated with the logon session of the current token. The token
|
|
must not have the user's SID disabled.
|
|
|
|
The CredWrite API creates a credential if none already exists by the
|
|
specified TargetName. If the specified TargetName already exists, the
|
|
specified credential replaces the existing one.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
Reserved. Must be zero.
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
Credential - Specifies the credential to be written.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
The following flags are defined:
|
|
|
|
CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
|
|
already existing credential with the same credential name and credential type.
|
|
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
|
|
existing credential. If such a field does not match the value
|
|
specified in the existing credential, this error is returned.
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = { NULL };
|
|
PCANONICAL_CREDENTIAL PassedCredential = NULL;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
#define CREDP_WRITE_VALID_FLAGS CRED_PRESERVE_CREDENTIAL_BLOB
|
|
|
|
if ( (Flags & ~CREDP_WRITE_VALID_FLAGS) != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the passed in credential
|
|
//
|
|
|
|
Status = CredpValidateCredential( CredFlags,
|
|
NULL, // No TargetInfo
|
|
Credential,
|
|
&PassedCredential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Write the credential to the credential set.
|
|
//
|
|
|
|
Status = CredpWriteCredential( &CredentialSets,
|
|
&PassedCredential,
|
|
FALSE, // Creds are not from persisted file
|
|
TRUE, // Update pin in CSP
|
|
TRUE, // Cred has been prompted for
|
|
Flags,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Update the password for this user on all credentials.
|
|
//
|
|
|
|
CredpUpdatePassword( &CredentialSets,
|
|
PassedCredential );
|
|
|
|
|
|
PassedCredential = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
if ( PassedCredential != NULL ) {
|
|
LsapFreeLsaHeap( PassedCredential );
|
|
}
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediRead (
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN LPWSTR TargetName,
|
|
IN ULONG Type,
|
|
IN ULONG Flags,
|
|
OUT PENCRYPTED_CREDENTIALW *Credential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredRead API reads a credential from the user's credential set.
|
|
The credential set used is the one associated with the logon session
|
|
of the current token. The token must not have the user's SID disabled.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
|
|
CREDP_FLAGS_USE_MIDL_HEAP - If specified, use MIDL_user_allocate to allocate memory.
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
TargetName - Specifies the name of the credential to read.
|
|
|
|
Type - Specifies the Type of the credential to find.
|
|
One of the CRED_TYPE_* values should be specified.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
Reserved. Must be zero.
|
|
|
|
Credential - Returns a pointer to the credential. The returned buffer
|
|
must be freed by calling LsapFreeLsaHeap.
|
|
If CREDP_FLAGS_USE_MIDL_HEAP was specified, use MIDL_user_free.
|
|
|
|
Return Values:
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = { NULL };
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
UNICODE_STRING TargetNameString;
|
|
DWORD TargetNameSize;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( Flags != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the input parameters
|
|
//
|
|
|
|
Status = CredpValidateTargetName( TargetName,
|
|
Type,
|
|
MightBeUsernameTarget,
|
|
NULL, // Don't know user name
|
|
NULL, // Don't know persist
|
|
&TargetNameSize,
|
|
NULL, // Don't care about name type
|
|
NULL ); // Don't care about non-wilcarded form of name
|
|
|
|
if ( !NT_SUCCESS(Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the credential
|
|
//
|
|
|
|
TargetNameString.Buffer = TargetName;
|
|
TargetNameString.MaximumLength = (USHORT) TargetNameSize;
|
|
TargetNameString.Length = TargetNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
TempCredential = CredpFindCredential(
|
|
&CredentialSets,
|
|
&TargetNameString,
|
|
Type );
|
|
|
|
if ( TempCredential == NULL ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Grab a copy of the credential to return to the caller.
|
|
//
|
|
|
|
*Credential = CredpCloneCredential( &CredentialSets, CredFlags, TempCredential );
|
|
|
|
if ( *Credential == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
extern "C"
|
|
VOID
|
|
CrediFreeCredentials (
|
|
IN ULONG Count,
|
|
IN PENCRYPTED_CREDENTIALW *Credentials OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the buffers allocated by CrediEnumerate or
|
|
CrediReadDomainCredentials.
|
|
|
|
Arguments:
|
|
|
|
Count - Specifies the number of credentials in Credentials.
|
|
|
|
Credentials - A pointer to an array of pointers to credentials.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
if ( Credentials != NULL ) {
|
|
for ( i=0; i<Count; i++ ) {
|
|
if ( Credentials[i] != NULL ) {
|
|
MIDL_user_free( Credentials[i] );
|
|
}
|
|
}
|
|
MIDL_user_free( Credentials );
|
|
}
|
|
}
|
|
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediEnumerate (
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN LPWSTR Filter,
|
|
IN ULONG Flags,
|
|
OUT PULONG Count,
|
|
OUT PENCRYPTED_CREDENTIALW **Credentials
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredEnumerate API enumerates the credentials from the user's credential set.
|
|
The credential set used is the one associated with the logon session
|
|
of the current token. The token must not have the user's SID disabled.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
Filter - Specifies a filter for the returned credentials. Only credentials
|
|
with a TargetName matching the filter will be returned. The filter specifies
|
|
a name prefix followed by an asterisk. For instance, the filter "FRED*" will
|
|
return all credentials with a TargetName beginning with the string "FRED".
|
|
|
|
If NULL is specified, all credentials will be returned.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
Reserved. Must be zero.
|
|
|
|
Count - Returns a count of the number of credentials returned in Credentials.
|
|
|
|
Credentials - Returns a pointer to an array of pointers to credentials.
|
|
The returned buffer must be freed by calling CrediFreeCredentials
|
|
|
|
Return Values:
|
|
|
|
On success, TRUE is returned. On failure, FALSE is returned.
|
|
GetLastError() may be called to get a more specific status code.
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NOT_FOUND - There is no credentials matching the specified Filter.
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = { NULL };
|
|
ULONG Persist;
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
PENCRYPTED_CREDENTIALW *TempCredentials = NULL;
|
|
DWORD FilterSize;
|
|
ULONG CredentialCount = 0;
|
|
ULONG CredentialIndex;
|
|
PLIST_ENTRY ListEntry;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
UNICODE_STRING PassedFilter;
|
|
BOOLEAN Wildcarded = FALSE;
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( Flags != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the input parameters
|
|
//
|
|
|
|
if ( !CredpValidateString( Filter,
|
|
max(CRED_MAX_GENERIC_TARGET_NAME_LENGTH, CRED_MAX_DOMAIN_TARGET_NAME_LENGTH),
|
|
TRUE, // NULL is OK
|
|
&FilterSize ) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CrediEnumerate: Invalid Filter buffer.\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Canonicalize the filter
|
|
//
|
|
|
|
if ( FilterSize != 0 ) {
|
|
PassedFilter.Buffer = Filter;
|
|
PassedFilter.Length = (USHORT)(FilterSize - sizeof(WCHAR));
|
|
PassedFilter.MaximumLength = (USHORT)FilterSize;
|
|
|
|
if ( Filter[(PassedFilter.Length-sizeof(WCHAR))/sizeof(WCHAR)] == '*' ) {
|
|
PassedFilter.Length -= sizeof(WCHAR);
|
|
PassedFilter.MaximumLength -= sizeof(WCHAR);
|
|
Wildcarded = TRUE;
|
|
}
|
|
} else {
|
|
RtlInitUnicodeString( &PassedFilter, NULL );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Count the number of credentials the match the filter
|
|
//
|
|
|
|
CredentialCount = 0;
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
//
|
|
// Walk each credential set
|
|
//
|
|
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
|
|
//
|
|
// If the profile has not yet been loaded by this session,
|
|
// ignore any credentials loaded by another session.
|
|
//
|
|
|
|
if ( Persist != CRED_PERSIST_SESSION &&
|
|
!CredentialSets.SessionCredSets->ProfileLoaded ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
UNICODE_STRING TempTargetName;
|
|
|
|
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
//
|
|
// Ignore unsupported cred types
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
CredpIsDomainCredential( TempCredential->Cred.Type ) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// If wildcarding,
|
|
// compare the filter to the prefix of the target name.
|
|
//
|
|
|
|
TempTargetName = TempCredential->TargetName;
|
|
if ( Wildcarded && TempTargetName.Length > PassedFilter.Length ) {
|
|
|
|
TempTargetName.Length = PassedFilter.Length;
|
|
TempTargetName.MaximumLength = PassedFilter.MaximumLength;
|
|
}
|
|
|
|
if ( FilterSize == 0 ||
|
|
RtlEqualUnicodeString( &PassedFilter,
|
|
&TempTargetName,
|
|
TRUE ) ) {
|
|
CredentialCount++;
|
|
TempCredential->ReturnMe = TRUE;
|
|
} else {
|
|
TempCredential->ReturnMe = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( CredentialCount == 0 ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to return the credentials in
|
|
//
|
|
|
|
TempCredentials = (PENCRYPTED_CREDENTIALW *) MIDL_user_allocate( CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
|
|
|
|
if ( TempCredentials == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory( TempCredentials, CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
|
|
|
|
|
|
//
|
|
// Grab a copy of each of the credentials to return to the caller.
|
|
//
|
|
|
|
CredentialIndex = 0;
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
|
|
//
|
|
// If the profile has not yet been loaded by this session,
|
|
// ignore any credentials loaded by another session.
|
|
//
|
|
|
|
if ( Persist != CRED_PERSIST_SESSION &&
|
|
!CredentialSets.SessionCredSets->ProfileLoaded ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
|
|
|
|
for ( ListEntry = CredentialSet->Credentials.Flink ;
|
|
ListEntry != &CredentialSet->Credentials;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
|
|
|
|
//
|
|
// Ignore unsupported cred types
|
|
//
|
|
|
|
if ( CredDisableDomainCreds &&
|
|
CredpIsDomainCredential( TempCredential->Cred.Type ) ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( TempCredential->ReturnMe ) {
|
|
TempCredentials[CredentialIndex] =
|
|
CredpCloneCredential( &CredentialSets,
|
|
CredFlags | CREDP_FLAGS_USE_MIDL_HEAP,
|
|
TempCredential );
|
|
|
|
if ( TempCredentials[CredentialIndex] == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CredentialIndex ++;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*Count = CredentialCount;
|
|
*Credentials = TempCredentials;
|
|
TempCredentials = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
if ( TempCredentials != NULL ) {
|
|
CrediFreeCredentials( CredentialCount, TempCredentials );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediWriteDomainCredentials (
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN PCREDENTIAL_TARGET_INFORMATIONW TargetInfo,
|
|
IN PENCRYPTED_CREDENTIALW Credential,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredWriteDomainCredentials API writes a new domain
|
|
credential to the user's credential set. The new credential is
|
|
associated with the logon session of the current token. The token
|
|
must not have the user's SID disabled.
|
|
|
|
CredWriteDomainCredentials differs from CredWrite in that it handles
|
|
the idiosyncrasies of domain (CRED_TYPE_DOMAIN_*)
|
|
credentials. Domain credentials contain more than one target field.
|
|
|
|
At least one of the naming parameters must be specified: NetbiosServerName,
|
|
DnsServerName, NetbiosDomainName, DnsDomainName or DnsTreeName.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
No flags are currently defined.
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
TargetInfo - Specifies the target information identifying the target server.
|
|
|
|
Credential - Specifies the credential to be written.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
The following flags are defined:
|
|
|
|
CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
|
|
already existing credential with the same credential name and credential type.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
|
|
existing credential. If such a field does not match the value
|
|
specified in the existing credential, this error is returned.
|
|
|
|
STATUS_INVALID_PARAMETER - None of the naming parameters were specified
|
|
or the credential specified did not have the Type field set to
|
|
CRED_TYPE_DOMAIN_PASSWORD, CRED_TYPE_DOMAIN_CERTIFICATE or CRED_TYPE_DOMAIN_VISIBLE_PASSWORD.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
PCANONICAL_CREDENTIAL OldCredential = NULL;
|
|
PPROMPT_DATA OldPromptData = NULL;
|
|
PPROMPT_DATA NewPromptData = NULL;
|
|
PCANONICAL_CREDENTIAL PassedCredential = NULL;
|
|
PCANONICAL_TARGET_INFO CanonicalTargetInfo = NULL;
|
|
ULONG AliasIndex;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
ULONG CredentialCount = 0;
|
|
PCANONICAL_CREDENTIAL BestCredentials[CRED_TYPE_MAXIMUM];
|
|
ULONG CredentialIndex;
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
#define CREDP_WRITE_DOM_VALID_FLAGS CRED_PRESERVE_CREDENTIAL_BLOB
|
|
|
|
if ( (Flags & ~CREDP_WRITE_DOM_VALID_FLAGS) != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the TargetInfo
|
|
//
|
|
|
|
Status = CredpValidateTargetInfo( TargetInfo,
|
|
&CanonicalTargetInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the passed in credential
|
|
//
|
|
|
|
Status = CredpValidateCredential( CredFlags,
|
|
CanonicalTargetInfo,
|
|
Credential,
|
|
&PassedCredential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !CredpIsDomainCredential(PassedCredential->Cred.Type) ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CrediWriteDomainCredential: Only allow for domain credentials.\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Write the credential to the credential set.
|
|
//
|
|
|
|
Status = CredpWriteCredential( &CredentialSets,
|
|
&PassedCredential,
|
|
FALSE, // Creds are not from persisted file
|
|
TRUE, // Update pin in CSP
|
|
TRUE, // Cred has been prompted for
|
|
Flags,
|
|
&OldCredential,
|
|
&OldPromptData,
|
|
&NewPromptData );
|
|
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure the written credential is the best credential for this target info
|
|
//
|
|
|
|
Status = CredpFindBestCredentials( LogonId,
|
|
&CredentialSets,
|
|
CanonicalTargetInfo,
|
|
BestCredentials,
|
|
&CredentialCount );
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
for ( CredentialIndex = 0; CredentialIndex<CredentialCount; CredentialIndex++ ) {
|
|
|
|
if ( PassedCredential == BestCredentials[CredentialIndex] ) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( CredentialIndex >= CredentialCount ) {
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CrediWriteDomainCredential: Credential isn't best credential for target info.\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// Remove the new credential from the cred set.
|
|
//
|
|
|
|
RemoveEntryList( &PassedCredential->Next );
|
|
|
|
//
|
|
// Remove the new prompt data from the cred set.
|
|
//
|
|
|
|
if ( NewPromptData != NULL ) {
|
|
RemoveEntryList( &NewPromptData->Next );
|
|
LsapFreeLsaHeap( NewPromptData );
|
|
}
|
|
|
|
//
|
|
// Put the old credential back in the cred set.
|
|
//
|
|
|
|
if ( OldCredential != NULL ) {
|
|
InsertHeadList(
|
|
&PersistToCredentialSet( &CredentialSets, OldCredential->Cred.Persist )->Credentials,
|
|
&OldCredential->Next );
|
|
OldCredential = NULL;
|
|
|
|
//
|
|
// Put the old prompt data back in the cred set.
|
|
//
|
|
|
|
if ( OldPromptData != NULL ) {
|
|
InsertHeadList(
|
|
&CredentialSets.SessionCredSets->PromptData,
|
|
&OldPromptData->Next );
|
|
OldPromptData = NULL;
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Update the password for this user on all credentials.
|
|
//
|
|
|
|
CredpUpdatePassword( &CredentialSets,
|
|
PassedCredential );
|
|
|
|
|
|
PassedCredential = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
if ( PassedCredential != NULL ) {
|
|
LsapFreeLsaHeap( PassedCredential );
|
|
}
|
|
if ( OldCredential != NULL ) {
|
|
LsapFreeLsaHeap( OldCredential );
|
|
}
|
|
if ( OldPromptData != NULL ) {
|
|
LsapFreeLsaHeap( OldPromptData );
|
|
}
|
|
if ( CanonicalTargetInfo != NULL ) {
|
|
LsapFreeLsaHeap( CanonicalTargetInfo );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediReadDomainCredentials (
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN PCREDENTIAL_TARGET_INFORMATIONW TargetInfo,
|
|
IN ULONG Flags,
|
|
OUT PULONG Count,
|
|
OUT PENCRYPTED_CREDENTIALW **Credentials
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredReadDomainCredentials API reads the domain credentials from the user's credential set.
|
|
The credential set used is the one associated with the logon session
|
|
of the current token. The token must not have the user's SID disabled.
|
|
|
|
CredReadDomainCredentials differs from CredRead in that it handles the
|
|
idiosyncrasies of domain (CRED_TYPE_DOMAIN_*)
|
|
credentials. Domain credentials contain more than one target field.
|
|
|
|
At least one of the naming parameters must be specified: NetbiosServerName,
|
|
DnsServerName, NetbiosDomainName, DnsDomainName or DnsTreeName. This API returns
|
|
the most specific credentials that match the naming parameters. That is, if there
|
|
is a credential that matches the target server name and a credential that matches
|
|
the target domain name, only the server specific credential is returned. This is
|
|
the credential that would be used.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
|
|
CREDP_FLAGS_DONT_CACHE_TI - TargetInformation shouldn't be cached for CredGetTargetInfo
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
TargetInfo - Specifies the target information identifying the target server.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
Reserved. Must be zero.
|
|
|
|
Count - Returns a count of the number of credentials returned in Credentials.
|
|
|
|
Credentials - Returns a pointer to an array of pointers to credentials.
|
|
The returned buffer must be freed by calling CrediFreeCredentials
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_INVALID_PARAMETER - None of the naming parameters were specified.
|
|
|
|
STATUS_NOT_FOUND - There are no credentials matching the specified naming parameters.
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
PENCRYPTED_CREDENTIALW *TempCredentials = NULL;
|
|
PCANONICAL_TARGET_INFO CanonicalTargetInfo = NULL;
|
|
ULONG CredentialCount = 0;
|
|
PCANONICAL_CREDENTIAL BestCredentials[CRED_TYPE_MAXIMUM];
|
|
ULONG CredentialIndex;
|
|
PLIST_ENTRY ListEntry;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( Flags != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the TargetInfo
|
|
//
|
|
|
|
Status = CredpValidateTargetInfo( TargetInfo,
|
|
&CanonicalTargetInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Determine the best credential of each type.
|
|
//
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
Status = CredpFindBestCredentials( LogonId,
|
|
&CredentialSets,
|
|
CanonicalTargetInfo,
|
|
BestCredentials,
|
|
&CredentialCount );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CredentialCount == 0 ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to return the credentials in
|
|
//
|
|
|
|
TempCredentials = (PENCRYPTED_CREDENTIALW *) MIDL_user_allocate( CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
|
|
|
|
if ( TempCredentials == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory( TempCredentials, CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
|
|
|
|
|
|
//
|
|
// Grab a copy of each of the credentials to return to the caller.
|
|
//
|
|
|
|
for ( CredentialIndex = 0; CredentialIndex<CredentialCount; CredentialIndex++ ) {
|
|
|
|
TempCredentials[CredentialIndex] =
|
|
CredpCloneCredential( &CredentialSets,
|
|
CredFlags | CREDP_FLAGS_USE_MIDL_HEAP,
|
|
BestCredentials[CredentialIndex] );
|
|
|
|
if ( TempCredentials[CredentialIndex] == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
*Count = CredentialCount;
|
|
*Credentials = TempCredentials;
|
|
TempCredentials = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
|
|
if ( CanonicalTargetInfo != NULL ) {
|
|
|
|
//
|
|
// Cache this target info
|
|
//
|
|
if ( CritSectLocked &&
|
|
(CredFlags & CREDP_FLAGS_DONT_CACHE_TI) == 0 &&
|
|
(CanonicalTargetInfo->Flags & CRED_TI_USERNAME_TARGET) == 0 ) {
|
|
|
|
if ( CredpCacheTargetInfo( &CredentialSets, CanonicalTargetInfo )) {
|
|
CanonicalTargetInfo = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the target info if it is still around
|
|
//
|
|
|
|
if ( CanonicalTargetInfo != NULL ) {
|
|
LsapFreeLsaHeap( CanonicalTargetInfo );
|
|
}
|
|
}
|
|
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
if ( TempCredentials != NULL ) {
|
|
CrediFreeCredentials( CredentialCount, TempCredentials );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediDelete (
|
|
IN PLUID LogonId,
|
|
IN ULONG CredFlags,
|
|
IN LPWSTR TargetName,
|
|
IN ULONG Type,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredDelete API deletes a credential from the user's credential set.
|
|
The credential set used is the one associated with the logon session
|
|
of the current token. The token must not have the user's SID disabled.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
CredFlags - Flags changing the behavior of the routine:
|
|
Must be zero.
|
|
|
|
Note: The CredFlags parameter is internal to the LSA process.
|
|
The Flags parameter is external to the process.
|
|
|
|
TargetName - Specifies the name of the credential to delete.
|
|
|
|
Type - Specifies the Type of the credential to find.
|
|
One of the CRED_TYPE_* values should be specified.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
Reserved. Must be zero.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
PCANONICAL_CREDENTIAL TempCredential;
|
|
PPROMPT_DATA PromptData;
|
|
UNICODE_STRING TargetNameString;
|
|
DWORD TargetNameSize;
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( Flags != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the input parameters
|
|
//
|
|
|
|
Status = CredpValidateTargetName( TargetName,
|
|
Type,
|
|
MightBeUsernameTarget,
|
|
NULL, // Don't know user name
|
|
NULL, // Don't know persist
|
|
&TargetNameSize,
|
|
NULL, // Don't care about name type
|
|
NULL ); // Don't care about non-wilcarded form of name
|
|
|
|
if ( !NT_SUCCESS(Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the credential
|
|
//
|
|
|
|
TargetNameString.Buffer = TargetName;
|
|
TargetNameString.MaximumLength = (USHORT) TargetNameSize;
|
|
TargetNameString.Length = TargetNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
TempCredential = CredpFindCredential(
|
|
&CredentialSets,
|
|
&TargetNameString,
|
|
Type );
|
|
|
|
if ( TempCredential == NULL ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the prompt data (if any)
|
|
//
|
|
|
|
PromptData = CredpFindPromptData(
|
|
&CredentialSets,
|
|
&TargetNameString,
|
|
Type,
|
|
TempCredential->Cred.Persist );
|
|
|
|
//
|
|
// Mark the credential set as modified.
|
|
//
|
|
|
|
CredpMarkDirty( &CredentialSets, TempCredential->Cred.Persist, NULL );
|
|
|
|
//
|
|
// Delink and delete the credential.
|
|
//
|
|
|
|
RemoveEntryList( &TempCredential->Next );
|
|
LsapFreeLsaHeap( TempCredential );
|
|
|
|
//
|
|
// Delink and delete the prompt data
|
|
//
|
|
|
|
if ( PromptData != NULL ) {
|
|
RemoveEntryList( &PromptData->Next );
|
|
LsapFreeLsaHeap( PromptData );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CrediRename (
|
|
IN PLUID LogonId,
|
|
IN LPWSTR OldTargetName,
|
|
IN LPWSTR NewTargetName,
|
|
IN ULONG Type,
|
|
IN ULONG Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredRename API renames a credential in the user's credential set.
|
|
The credential set used is the one associated with the logon session
|
|
of the current token. The token must not have the user's SID disabled.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
OldTargetName - Specifies the current name of the credential to rename.
|
|
|
|
NewTargetName - Specifies the new name of the credential.
|
|
|
|
Type - Specifies the Type of the credential to rename
|
|
One of the CRED_TYPE_* values should be specified.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
Reserved. Must be zero.
|
|
|
|
Return Values:
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified OldTargetName.
|
|
|
|
STATUS_OBJECT_NAME_COLLISION - There is already a credential named NewTargetName.
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
PPROMPT_DATA OldPromptData;
|
|
UNICODE_STRING OldTargetNameString;
|
|
DWORD OldTargetNameSize;
|
|
PCANONICAL_CREDENTIAL OldCredential;
|
|
|
|
UNICODE_STRING NewTargetNameString;
|
|
DWORD NewTargetNameSize;
|
|
PCANONICAL_CREDENTIAL NewCredential;
|
|
|
|
ENCRYPTED_CREDENTIALW LocalCredential;
|
|
PCANONICAL_CREDENTIAL WrittenCredential;
|
|
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( Flags != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
|
|
//
|
|
// Find the old credential
|
|
//
|
|
|
|
Status = CredpValidateTargetName( OldTargetName,
|
|
Type,
|
|
MightBeUsernameTarget,
|
|
NULL, // Don't know user name
|
|
NULL, // Don't know persist
|
|
&OldTargetNameSize,
|
|
NULL, // Don't care about name type
|
|
NULL ); // Don't care about non-wilcarded form of name
|
|
|
|
if ( !NT_SUCCESS(Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
OldTargetNameString.Buffer = OldTargetName;
|
|
OldTargetNameString.MaximumLength = (USHORT) OldTargetNameSize;
|
|
OldTargetNameString.Length = OldTargetNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
OldCredential = CredpFindCredential(
|
|
&CredentialSets,
|
|
&OldTargetNameString,
|
|
Type );
|
|
|
|
if ( OldCredential == NULL ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Find the New credential
|
|
//
|
|
|
|
Status = CredpValidateTargetName( NewTargetName,
|
|
Type,
|
|
(OldCredential->Cred.Flags & CRED_FLAGS_USERNAME_TARGET) ?
|
|
IsUsernameTarget :
|
|
IsNotUsernameTarget,
|
|
&OldCredential->Cred.UserName,
|
|
&OldCredential->Cred.Persist,
|
|
&NewTargetNameSize,
|
|
NULL, // Don't care about name type
|
|
NULL ); // Don't care about non-wilcarded form of name
|
|
|
|
if ( !NT_SUCCESS(Status ) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
NewTargetNameString.Buffer = NewTargetName;
|
|
NewTargetNameString.MaximumLength = (USHORT) NewTargetNameSize;
|
|
NewTargetNameString.Length = NewTargetNameString.MaximumLength - sizeof(WCHAR);
|
|
|
|
NewCredential = CredpFindCredential(
|
|
&CredentialSets,
|
|
&NewTargetNameString,
|
|
Type );
|
|
|
|
if ( NewCredential != NULL ) {
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the prompt data (if any)
|
|
//
|
|
|
|
OldPromptData = CredpFindPromptData(
|
|
&CredentialSets,
|
|
&OldTargetNameString,
|
|
Type,
|
|
OldCredential->Cred.Persist );
|
|
|
|
|
|
//
|
|
// Write the new credential
|
|
//
|
|
// Clear the TargetAlias. It'll either be syntactically invalid and fail. Or
|
|
// worse, it'll be syntactically valid but semantic nonsense.
|
|
//
|
|
|
|
LocalCredential.Cred = OldCredential->Cred;
|
|
LocalCredential.ClearCredentialBlobSize = OldCredential->ClearCredentialBlobSize;
|
|
LocalCredential.Cred.TargetName = NewTargetName;
|
|
LocalCredential.Cred.TargetAlias = NULL;
|
|
|
|
Status = CredpWriteMorphedCredential(
|
|
&CredentialSets,
|
|
!ShouldPromptNow( OldPromptData ), // Preserve the prompted for state
|
|
NULL, // No Target info
|
|
&LocalCredential,
|
|
&WrittenCredential );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Mark the credential set as modified.
|
|
//
|
|
|
|
CredpMarkDirty( &CredentialSets, OldCredential->Cred.Persist, NULL );
|
|
|
|
//
|
|
// Delink and delete the old credential.
|
|
//
|
|
|
|
RemoveEntryList( &OldCredential->Next );
|
|
LsapFreeLsaHeap( OldCredential );
|
|
|
|
//
|
|
// Delink and delete the prompt data
|
|
//
|
|
|
|
if ( OldPromptData != NULL ) {
|
|
RemoveEntryList( &OldPromptData->Next );
|
|
LsapFreeLsaHeap( OldPromptData );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
extern "C"
|
|
NTSTATUS
|
|
CrediGetTargetInfo (
|
|
IN PLUID LogonId,
|
|
IN LPWSTR TargetServerName,
|
|
IN ULONG Flags,
|
|
OUT PCREDENTIAL_TARGET_INFORMATIONW *TargetInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredGetTargetInfo API gets all of the known target name information
|
|
for the named target machine. This API is executed locally
|
|
and does not need any particular privilege. The information returned is expected
|
|
to be passed to the CredReadDomainCredentials and CredWriteDomainCredentials APIs.
|
|
The information should not be used for any other purpose.
|
|
|
|
Authentication packages compute TargetInfo when attempting to authenticate to
|
|
ServerName. The authentication packages cache this target information to make it
|
|
available to CredGetTargetInfo. Therefore, the target information will only be
|
|
available if we've recently attempted to authenticate to ServerName.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session the target info is associated with
|
|
|
|
TargetServerName - This parameter specifies the name of the machine to get the information
|
|
for.
|
|
|
|
Flags - Specifies flags to control the operation of the API.
|
|
|
|
CRED_ALLOW_NAME_RESOLUTION - Specifies that if no target info can be found for
|
|
TargetName, then name resolution should be done on TargetName to convert it
|
|
to other forms. If target info exists for any of those other forms, that
|
|
target info is returned. Currently only DNS name resolution is done.
|
|
|
|
This bit is useful if the application doesn't call the authentication package
|
|
directly. The application might pass the TargetName to another layer of software
|
|
to authenticate to the server. That layer of software might resolve the name and
|
|
pass the resolved name to the authentication package. As such, there will be no
|
|
target info for the original TargetName.
|
|
|
|
TargetInfo - Returns a pointer to the target information.
|
|
At least one of the returned fields of TargetInfo will be non-NULL.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NO_MEMORY - There isn't enough memory to complete the operation.
|
|
|
|
STATUS_NOT_FOUND - There is no credential with the specified TargetName.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WinStatus;
|
|
PCANONICAL_TARGET_INFO CanonicalTargetInfo;
|
|
PCREDENTIAL_TARGET_INFORMATIONW LocalTargetInfo = NULL;
|
|
ULONG TargetServerNameSize;
|
|
LPBYTE Where;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
PDNS_RECORD DnsARecords = NULL;
|
|
|
|
#define CREDP_GTI_VALID_FLAGS CRED_ALLOW_NAME_RESOLUTION
|
|
|
|
//
|
|
// Validate the flags
|
|
//
|
|
|
|
if ( (Flags & ~CREDP_GTI_VALID_FLAGS) != 0 ) {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the input parameters
|
|
//
|
|
|
|
if ( !CredpValidateString( TargetServerName,
|
|
CRED_MAX_DOMAIN_TARGET_NAME_LENGTH,
|
|
FALSE, // NULL not OK
|
|
&TargetServerNameSize ) ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DebugLog(( DEB_TRACE_CRED,
|
|
"CrediGetTargetInfo: Invalid TargetServerName buffer.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Lock the credential set.
|
|
//
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
|
|
//
|
|
// See if the named server can be found directly
|
|
//
|
|
|
|
CanonicalTargetInfo = CredpFindTargetInfo( &CredentialSets, TargetServerName );
|
|
|
|
if ( CanonicalTargetInfo == NULL ) {
|
|
PDNS_RECORD DnsRecord;
|
|
|
|
//
|
|
// If the caller doesn't wants us to try name resolution in an attempt for find the target info
|
|
// we're done.
|
|
//
|
|
|
|
if ( (Flags & CRED_ALLOW_NAME_RESOLUTION) == 0 ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Query DNS for the name
|
|
//
|
|
|
|
WinStatus = DnsQuery_W(
|
|
TargetServerName,
|
|
DNS_TYPE_A,
|
|
0, // No special flags
|
|
NULL, // No special DNS servers
|
|
&DnsARecords,
|
|
NULL );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
// Don't confuse the caller with a more specific status
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Find the first A or AAAA record
|
|
//
|
|
// Only look up the first name returned from DNS. That's the name the redir used when
|
|
// creating the SPN that the authentication packages pass us in the TargetName.
|
|
//
|
|
|
|
for ( DnsRecord = DnsARecords;
|
|
DnsRecord != NULL;
|
|
DnsRecord = DnsRecord->pNext ) {
|
|
|
|
if ( DnsRecord->wType == DNS_TYPE_A ||
|
|
DnsRecord->wType == DNS_TYPE_AAAA ) {
|
|
|
|
//
|
|
// See if the named server can be found directly
|
|
//
|
|
|
|
CanonicalTargetInfo = CredpFindTargetInfo( &CredentialSets, DnsRecord->pName );
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( CanonicalTargetInfo == NULL ) {
|
|
Status = STATUS_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a structure to return to the caller
|
|
//
|
|
|
|
LocalTargetInfo = (PCREDENTIAL_TARGET_INFORMATIONW) MIDL_user_allocate(
|
|
sizeof(*LocalTargetInfo) +
|
|
CanonicalTargetInfo->CredTypeCount * sizeof(DWORD) +
|
|
CanonicalTargetInfo->TargetName.MaximumLength +
|
|
CanonicalTargetInfo->NetbiosServerName.MaximumLength +
|
|
CanonicalTargetInfo->DnsServerName.MaximumLength +
|
|
CanonicalTargetInfo->NetbiosDomainName.MaximumLength +
|
|
CanonicalTargetInfo->DnsDomainName.MaximumLength +
|
|
CanonicalTargetInfo->DnsTreeName.MaximumLength +
|
|
CanonicalTargetInfo->PackageName.MaximumLength );
|
|
|
|
if ( LocalTargetInfo == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory( LocalTargetInfo, sizeof(*LocalTargetInfo) );
|
|
Where = (LPBYTE) (LocalTargetInfo+1);
|
|
|
|
//
|
|
// Copy the constant size data
|
|
//
|
|
|
|
LocalTargetInfo->Flags = CanonicalTargetInfo->Flags;
|
|
|
|
//
|
|
// Copy the DWORD aligned data
|
|
//
|
|
|
|
LocalTargetInfo->CredTypeCount = CanonicalTargetInfo->CredTypeCount;
|
|
if ( LocalTargetInfo->CredTypeCount != 0 ) {
|
|
LocalTargetInfo->CredTypes = (LPDWORD)Where;
|
|
RtlCopyMemory( Where, CanonicalTargetInfo->CredTypes, CanonicalTargetInfo->CredTypeCount * sizeof(DWORD) );
|
|
Where += CanonicalTargetInfo->CredTypeCount * sizeof(DWORD);
|
|
} else {
|
|
LocalTargetInfo->CredTypes = NULL;
|
|
}
|
|
|
|
//
|
|
// Copy the 2-byte aligned data
|
|
//
|
|
|
|
if ( CanonicalTargetInfo->TargetName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->TargetName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->TargetName,
|
|
CanonicalTargetInfo->TargetName.Buffer,
|
|
CanonicalTargetInfo->TargetName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->TargetName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->NetbiosServerName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->NetbiosServerName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->NetbiosServerName,
|
|
CanonicalTargetInfo->NetbiosServerName.Buffer,
|
|
CanonicalTargetInfo->NetbiosServerName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->NetbiosServerName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->DnsServerName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->DnsServerName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->DnsServerName,
|
|
CanonicalTargetInfo->DnsServerName.Buffer,
|
|
CanonicalTargetInfo->DnsServerName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->DnsServerName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->NetbiosDomainName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->NetbiosDomainName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->NetbiosDomainName,
|
|
CanonicalTargetInfo->NetbiosDomainName.Buffer,
|
|
CanonicalTargetInfo->NetbiosDomainName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->NetbiosDomainName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->DnsDomainName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->DnsDomainName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->DnsDomainName,
|
|
CanonicalTargetInfo->DnsDomainName.Buffer,
|
|
CanonicalTargetInfo->DnsDomainName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->DnsDomainName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->DnsTreeName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->DnsTreeName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->DnsTreeName,
|
|
CanonicalTargetInfo->DnsTreeName.Buffer,
|
|
CanonicalTargetInfo->DnsTreeName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->DnsTreeName.MaximumLength;
|
|
}
|
|
|
|
if ( CanonicalTargetInfo->PackageName.Length != 0 ) {
|
|
|
|
LocalTargetInfo->PackageName = (LPWSTR) Where;
|
|
|
|
RtlCopyMemory( LocalTargetInfo->PackageName,
|
|
CanonicalTargetInfo->PackageName.Buffer,
|
|
CanonicalTargetInfo->PackageName.MaximumLength );
|
|
|
|
Where += CanonicalTargetInfo->PackageName.MaximumLength;
|
|
}
|
|
|
|
//
|
|
// Return the information to the caller
|
|
//
|
|
|
|
*TargetInfo = LocalTargetInfo;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
if ( DnsARecords != NULL ) {
|
|
DnsRecordListFree( DnsARecords, DnsFreeRecordListDeep );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CrediGetSessionTypes (
|
|
IN PLUID LogonId,
|
|
IN DWORD MaximumPersistCount,
|
|
OUT LPDWORD MaximumPersist
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
CredGetSessionTypes returns the maximum persistence supported by the current logon
|
|
session.
|
|
|
|
For whistler, CRED_PERSIST_LOCAL_MACHINE and CRED_PERSIST_ENTERPRISE credentials can not
|
|
be stored for sessions where the profile is not loaded. If future releases, credentials
|
|
might not be associated with the user's profile.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
MaximumPersistCount - Specifies the number of elements in the MaximumPersist array.
|
|
The caller should specify CRED_TYPE_MAXIMUM for this parameter.
|
|
|
|
MaximumPersist - Returns the maximum persistance supported by the current logon session for
|
|
each credential type. Index into the array with one of the CRED_TYPE_* defines.
|
|
Returns CRED_PERSIST_NONE if no credential of this type can be stored.
|
|
Returns CRED_PERSIST_SESSION if only session specific credential may be stored.
|
|
Returns CRED_PERSIST_LOCAL_MACHINE if session specific and machine specific credentials
|
|
may be stored.
|
|
Returns CRED_PERSIST_ENTERPRISE if any credential may be stored.
|
|
|
|
Return Values:
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
ULONG i;
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
ULONG Persist;
|
|
|
|
//
|
|
// Validate the parameters
|
|
//
|
|
|
|
if ( MaximumPersistCount != 0 && MaximumPersist == NULL ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Avoid huge buffers
|
|
// Allow some leeway to allow new applications to run on old OSes.
|
|
//
|
|
if ( MaximumPersistCount > CRED_TYPE_MAXIMUM+1000 ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Lock the credential set.
|
|
//
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
//
|
|
// Loop through the list filling in each element
|
|
//
|
|
|
|
for ( i=0; i<MaximumPersistCount; i++ ) {
|
|
|
|
//
|
|
// The default value depends on whether the profile is loaded.
|
|
//
|
|
|
|
if ( CredentialSets.SessionCredSets->ProfileLoaded ) {
|
|
MaximumPersist[i] = CRED_PERSIST_ENTERPRISE;
|
|
} else {
|
|
MaximumPersist[i] = CRED_PERSIST_SESSION;
|
|
}
|
|
|
|
//
|
|
// Some types can be disabled
|
|
//
|
|
|
|
if ( i == CRED_TYPE_GENERIC ) {
|
|
/* Nothing to do here */
|
|
|
|
|
|
//
|
|
// Disable domain credentials based on policy
|
|
//
|
|
} else if ( CredpIsDomainCredential(i) ) {
|
|
if ( CredDisableDomainCreds ) {
|
|
|
|
MaximumPersist[i] = CRED_PERSIST_NONE;
|
|
|
|
} else if ( CredIsPersonal &&
|
|
i != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD ) {
|
|
|
|
MaximumPersist[i] = CRED_PERSIST_NONE;
|
|
}
|
|
|
|
//
|
|
// Disable all types we don't understand
|
|
//
|
|
} else {
|
|
MaximumPersist[i] = CRED_PERSIST_NONE;
|
|
}
|
|
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CrediProfileLoaded (
|
|
IN PLUID LogonId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The CredProfileLoaded API is a private API used by LoadUserProfile to notify the
|
|
credential manager that the profile for the current user has been loaded.
|
|
|
|
The caller must be impersonating the logged on user.
|
|
|
|
Arguments:
|
|
|
|
LogonId - LogonId of the session to associate the credential with.
|
|
|
|
Return Values:
|
|
|
|
The following status codes may be returned:
|
|
|
|
STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
|
|
there is no credential set associated with this logon session.
|
|
Network logon sessions do not have an associated credential set.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WinStatus;
|
|
CREDENTIAL_SETS CredentialSets = {NULL};
|
|
BOOLEAN CritSectLocked = FALSE;
|
|
|
|
PCREDENTIAL_SET CredentialSet;
|
|
ULONG Persist;
|
|
|
|
//
|
|
// Get the credential set.
|
|
//
|
|
|
|
Status = CredpReferenceCredSets( LogonId, &CredentialSets );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// This is a notification API. Just because there are no credentials, doesn't
|
|
// mean the profile load should fail. Indeed, it is already finished.
|
|
//
|
|
if ( Status == STATUS_NO_SUCH_LOGON_SESSION ) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Lock the credential set.
|
|
//
|
|
|
|
CredpLockCredSets( &CredentialSets );
|
|
CritSectLocked = TRUE;
|
|
|
|
//
|
|
// Mark it that the profile is now loaded.
|
|
//
|
|
|
|
CredentialSets.SessionCredSets->ProfileLoaded = TRUE;
|
|
|
|
|
|
//
|
|
// Loop through the list of credential sets reading each
|
|
//
|
|
|
|
for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
|
|
|
|
//
|
|
// Ignore non-persistent credential sets
|
|
//
|
|
|
|
if ( Persist == CRED_PERSIST_SESSION ) {
|
|
continue;
|
|
}
|
|
|
|
CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
|
|
|
|
//
|
|
// If this cred set hasn't been read yet,
|
|
// read it now.
|
|
//
|
|
|
|
if ( !CredentialSet->FileRead ) {
|
|
WinStatus = CredpReadCredSet( &CredentialSets, Persist );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
CredentialSet->FileRead = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
Cleanup:
|
|
if ( CritSectLocked ) {
|
|
CredpUnlockAndFlushCredSets( &CredentialSets );
|
|
}
|
|
|
|
CredpDereferenceCredSets( &CredentialSets );
|
|
|
|
return Status;
|
|
}
|