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.
1248 lines
31 KiB
1248 lines
31 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
credhand.cxx
|
|
|
|
Abstract:
|
|
|
|
API and support routines for handling credential handles.
|
|
|
|
Author:
|
|
|
|
|
|
Cliff Van Dyke (CliffV) 26-Jun-1993
|
|
Revision History:
|
|
ChandanS 03-Aug-1996 Stolen from net\svcdlls\ntlmssp\common\credhand.c
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include <global.h>
|
|
#include <align.h> // ALIGN_WCHAR
|
|
|
|
extern "C"
|
|
{
|
|
|
|
#include <nlp.h>
|
|
|
|
}
|
|
|
|
//
|
|
// Crit Sect to protect various globals in this module.
|
|
//
|
|
|
|
RTL_RESOURCE SspCredentialCritSect;
|
|
LIST_ENTRY SspCredentialList;
|
|
|
|
// This is the definition of a null session string.
|
|
// Change this if the definition changes
|
|
|
|
#define IsNullSessionString(x) (((x)->Length == 0) && \
|
|
((x)->Buffer != NULL))
|
|
|
|
|
|
BOOLEAN
|
|
AlterRtlEqualUnicodeString(
|
|
IN PUNICODE_STRING String1,
|
|
IN PUNICODE_STRING String2,
|
|
IN BOOLEAN CaseInSensitive
|
|
)
|
|
/*++
|
|
This is here to catch cases that RtlEqualUnicodeString does not.
|
|
For e.g, if String1 is (NULL,0,0) and String2 is ("",0,2),
|
|
RtlEqualUnicodeString returned TRUE but we really want it to return FALSE
|
|
--*/
|
|
{
|
|
BOOL fRet = RtlEqualUnicodeString(String1, String2, CaseInSensitive);
|
|
|
|
if (fRet && (IsNullSessionString(String1) != IsNullSessionString(String2)))
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
return (fRet != 0);
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SspCredentialReferenceCredential(
|
|
IN ULONG_PTR CredentialHandle,
|
|
IN BOOLEAN DereferenceCredential,
|
|
OUT PSSP_CREDENTIAL * UserCredential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the Credential is from a currently
|
|
active client, and references the Credential if it is valid.
|
|
|
|
The caller may optionally request that the client's Credential be
|
|
removed from the list of valid Credentials - preventing future
|
|
requests from finding this Credential.
|
|
|
|
For a client's Credential to be valid, the Credential value
|
|
must be on our list of active Credentials.
|
|
|
|
|
|
Arguments:
|
|
|
|
CredentialHandle - Points to the CredentialHandle of the Credential
|
|
to be referenced.
|
|
|
|
DereferenceCredential - This boolean value indicates that that a call
|
|
a single instance of this credential handle should be freed. If there
|
|
are multiple instances, they should still continue to work.
|
|
|
|
|
|
Return Value:
|
|
|
|
NULL - the Credential was not found.
|
|
|
|
Otherwise - returns a pointer to the referenced credential.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSSP_CREDENTIAL Credential = NULL;
|
|
SECPKG_CALL_INFO CallInfo;
|
|
ULONG DereferenceCount;
|
|
|
|
*UserCredential = NULL ;
|
|
|
|
|
|
if (LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
DereferenceCount = CallInfo.CallCount;
|
|
} else {
|
|
ASSERT( (STATUS_INTERNAL_ERROR == STATUS_SUCCESS) );
|
|
return STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
|
|
if( CallInfo.Attributes & SECPKG_CALL_CLEANUP )
|
|
{
|
|
CallInfo.Attributes |= SECPKG_CALL_IS_TCB;
|
|
|
|
SspPrint(( SSP_LEAK_TRACK, "SspCredentialReferenceCredential: pid: 0x%lx handle: %p refcount: %lu\n",
|
|
CallInfo.ProcessId, CredentialHandle, DereferenceCount));
|
|
}
|
|
|
|
|
|
//
|
|
// Acquire exclusive access to the Credential list
|
|
//
|
|
|
|
RtlAcquireResourceShared( &SspCredentialCritSect, TRUE );
|
|
|
|
|
|
__try {
|
|
|
|
Credential = (PSSP_CREDENTIAL)CredentialHandle;
|
|
|
|
while( Credential->CredentialTag == SSP_CREDENTIAL_TAG_ACTIVE )
|
|
{
|
|
|
|
// Make sure we have the privilege of accessing
|
|
// this handle
|
|
|
|
if (((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0) &&
|
|
(Credential->ClientProcessID != CallInfo.ProcessId)
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!DereferenceCredential) {
|
|
InterlockedIncrement( (PLONG)&Credential->References );
|
|
} else {
|
|
|
|
LONG References;
|
|
|
|
//
|
|
// Decremenent the credential references, indicating
|
|
// that a call to free
|
|
|
|
ASSERT((DereferenceCount > 0));
|
|
|
|
//
|
|
// NOTE: subtract one off the deref count,
|
|
// avoids an extra interlocked operation, since DerefCred will
|
|
// decrement and check for refcnt == 0.
|
|
//
|
|
|
|
DereferenceCount--;
|
|
|
|
if( DereferenceCount == 1 )
|
|
{
|
|
References = InterlockedDecrement( (PLONG)&Credential->References );
|
|
|
|
ASSERT( (References > 0) );
|
|
} else if( DereferenceCount > 1 )
|
|
{
|
|
|
|
//
|
|
// there is no equivalent to InterlockedSubtract.
|
|
// so, turn it into an Add with some signed magic.
|
|
//
|
|
|
|
LONG DecrementToIncrement = 0 - DereferenceCount;
|
|
|
|
References = InterlockedExchangeAdd( (PLONG)&Credential->References, DecrementToIncrement );
|
|
|
|
ASSERT( ((References+DecrementToIncrement) > 0) );
|
|
}
|
|
}
|
|
|
|
*UserCredential = Credential ;
|
|
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
return STATUS_SUCCESS ;
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SspPrint(( SSP_CRITICAL, "Tried to reference invalid Credential %p\n",
|
|
Credential ));
|
|
|
|
}
|
|
|
|
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
//
|
|
// No match found
|
|
//
|
|
SspPrint(( SSP_API_MORE, "Tried to reference unknown Credential %p\n",
|
|
CredentialHandle ));
|
|
|
|
|
|
return STATUS_INVALID_HANDLE ;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SspCredentialGetPassword(
|
|
IN PSSP_CREDENTIAL Credential,
|
|
OUT PUNICODE_STRING Password
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the password out of credential.
|
|
|
|
NOTE: Locking is no longer required, because the caller is expected
|
|
to NtLmDuplicateUnicodeString() the cipher text Password prior to
|
|
passing it to this routine. This change allows the following advantages:
|
|
|
|
1. Avoid taking Credential list lock.
|
|
2. Avoid having to avoid having to Re-hide the password after reveal.
|
|
3. Avoid having to take locks elsewhere associated with hiding/revealing.
|
|
|
|
|
|
Arguments:
|
|
|
|
Credential - Credential record to retrieve the password from.
|
|
|
|
Password - UNICODE_STRING to store the password in.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_NO_MEMORY - there was not enough memory to copy
|
|
the password.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if ( Credential->Password.Buffer != NULL ) {
|
|
Status = NtLmDuplicatePassword(
|
|
Password,
|
|
&Credential->Password
|
|
);
|
|
} else {
|
|
RtlInitUnicodeString(
|
|
Password,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
PSSP_CREDENTIAL
|
|
SspCredentialLookupCredential(
|
|
IN PLUID LogonId,
|
|
IN ULONG CredentialUseFlags,
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING Password
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks the list of credentials for this client looking
|
|
for one that has the same supplemental credentials as those passed
|
|
in. If it is found, its reference count is increased and a pointer
|
|
to it is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
UserName - User name to match.
|
|
|
|
DomainName - Domain name to match.
|
|
|
|
Password - Password to match.
|
|
|
|
|
|
Return Value:
|
|
|
|
NULL - the Credential was not found.
|
|
|
|
Otherwise - returns a pointer to the referenced credential.
|
|
|
|
--*/
|
|
|
|
{
|
|
SspPrint((SSP_API_MORE, "Entering SspCredentialLookupCredential\n"));
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListEntry;
|
|
PSSP_CREDENTIAL Credential = NULL;
|
|
PSSP_CREDENTIAL CredentialResult = NULL;
|
|
PLIST_ENTRY ListHead;
|
|
SECPKG_CALL_INFO CallInfo ;
|
|
|
|
UNICODE_STRING EncryptedPassword;
|
|
|
|
if ( !LsaFunctions->GetCallInfo( &CallInfo ) )
|
|
{
|
|
SspPrint(( SSP_CRITICAL, "SspCredentialLookupCredential: GetCallInfo returned FALSE\n" ));
|
|
return NULL ;
|
|
}
|
|
|
|
ZeroMemory(&EncryptedPassword, sizeof(EncryptedPassword));
|
|
|
|
Status = NtLmDuplicatePassword(&EncryptedPassword, Password);
|
|
if(!NT_SUCCESS( Status ))
|
|
{
|
|
SspPrint(( SSP_CRITICAL, "SspCredentialLookupCredential: DuplicatePassword failed\n" ));
|
|
return NULL;
|
|
}
|
|
|
|
SspHidePassword( &EncryptedPassword );
|
|
|
|
//
|
|
// Acquire exclusive access to the Credential list
|
|
//
|
|
|
|
RtlAcquireResourceShared( &SspCredentialCritSect, TRUE );
|
|
|
|
ListHead = &SspCredentialList;
|
|
|
|
//
|
|
// Now walk the list of Credentials looking for a match.
|
|
//
|
|
|
|
for ( ListEntry = ListHead->Flink;
|
|
ListEntry != ListHead;
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
|
|
Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next );
|
|
|
|
//
|
|
// we now allow matching and pooling of INBOUND creds, too.
|
|
//
|
|
|
|
|
|
//
|
|
// We only want credentials from the same caller
|
|
//
|
|
if (Credential->ClientProcessID != CallInfo.ProcessId) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// don't share creds across impersonation levels.
|
|
//
|
|
|
|
if (Credential->ImpersonationLevel != ImpersonationLevel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// if the caller is from kernel mode, only return creds
|
|
// granted to kernel mode
|
|
//
|
|
|
|
if ( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) != 0 )
|
|
{
|
|
if ( !Credential->KernelClient )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for a match
|
|
//
|
|
|
|
// The credential use check was added because null session
|
|
// credentials were being returned when default credentials
|
|
// were being asked. This happened becuase RtlEqualUnicodeString
|
|
// for NULL,0,0 and "",0,2 is TRUE
|
|
|
|
if ( (CredentialUseFlags != Credential->CredentialUseFlags) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(!RtlEqualLuid(
|
|
LogonId,
|
|
&Credential->LogonId
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(!AlterRtlEqualUnicodeString(
|
|
UserName,
|
|
&Credential->UserName,
|
|
FALSE
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(!AlterRtlEqualUnicodeString(
|
|
DomainName,
|
|
&Credential->DomainName,
|
|
FALSE
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// password is stored encrypted in list -- we're comparing
|
|
// a one-time encrypted version of candidate. advantages:
|
|
// 1. passwords not revealed in memory.
|
|
// 2. only need encrypt candidate single time.
|
|
//
|
|
|
|
if(!AlterRtlEqualUnicodeString(
|
|
&Credential->Password,
|
|
&EncryptedPassword,
|
|
FALSE
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Found a match - reference the credential
|
|
//
|
|
|
|
|
|
//
|
|
// Reference the credential and indicate that
|
|
// it is in use as two different handles to the caller
|
|
// (who may call FreeCredentialsHandle twice)
|
|
//
|
|
|
|
InterlockedIncrement( (PLONG)&Credential->References );
|
|
|
|
CredentialResult = Credential;
|
|
break;
|
|
}
|
|
|
|
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
if( EncryptedPassword.Buffer != NULL ) {
|
|
ZeroMemory( EncryptedPassword.Buffer, EncryptedPassword.Length );
|
|
NtLmFree( EncryptedPassword.Buffer );
|
|
}
|
|
|
|
|
|
if( CredentialResult == NULL )
|
|
{
|
|
SspPrint(( SSP_API_MORE, "Tried to reference unknown Credential\n" ));
|
|
}
|
|
|
|
SspPrint((SSP_API_MORE, "Leaving SspCredentialLookupCredential\n"));
|
|
|
|
return CredentialResult;
|
|
}
|
|
|
|
|
|
VOID
|
|
SspCredentialDereferenceCredential(
|
|
IN PSSP_CREDENTIAL Credential
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the specified Credential's reference count.
|
|
If the reference count drops to zero, then the Credential is deleted
|
|
|
|
Arguments:
|
|
|
|
Credential - Points to the Credential to be dereferenced.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG References;
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
|
|
References = InterlockedDecrement( (PLONG)&Credential->References );
|
|
|
|
ASSERT( References >= 0 );
|
|
|
|
//
|
|
// If the count dropped to zero, then run-down the Credential
|
|
//
|
|
|
|
if ( References == 0 )
|
|
{
|
|
|
|
if (!Credential->Unlinked) {
|
|
|
|
RtlAcquireResourceExclusive(&SspCredentialCritSect, TRUE);
|
|
|
|
if( Credential->References != 0 )
|
|
{
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
return;
|
|
}
|
|
|
|
RemoveEntryList( &Credential->Next );
|
|
Credential->Unlinked = TRUE;
|
|
Credential->CredentialTag = SSP_CREDENTIAL_TAG_DELETE;
|
|
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
}
|
|
|
|
SspPrint(( SSP_API_MORE, "Deleting Credential 0x%lx\n",
|
|
Credential ));
|
|
|
|
if ( Credential->Password.Buffer ) {
|
|
ZeroMemory( Credential->Password.Buffer, Credential->Password.MaximumLength );
|
|
(VOID) NtLmFree( Credential->Password.Buffer );
|
|
}
|
|
|
|
if ( Credential->DomainName.Buffer ) {
|
|
(VOID) NtLmFree( Credential->DomainName.Buffer );
|
|
}
|
|
|
|
if ( Credential->UserName.Buffer ) {
|
|
(VOID) NtLmFree( Credential->UserName.Buffer );
|
|
}
|
|
|
|
|
|
ZeroMemory( Credential, sizeof(SSP_CREDENTIAL) );
|
|
(VOID) NtLmFree( Credential );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
SsprCheckMachineLogon(
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING Password,
|
|
IN OUT PLUID pLogonId,
|
|
IN OUT ULONG *pCredFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the input credential matches a special
|
|
machine account logon over-ride.
|
|
|
|
This routine also checks if the caller is the NetworkService account,
|
|
specifying default credentials, in a downlevel domain. This will cause
|
|
a credential over-ride to LocalSystem, for backwards compatibility.
|
|
NT4 did not understand machine account authentication, so outbound authentication
|
|
from networkservice as machine account cannot succeed -- downgrade to the old
|
|
LocalSystem default.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - the intput credential was the special machine account logon over-ride.
|
|
the pLogonId is updated to utilize the machine credential.
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING MachineAccountName;
|
|
static LUID LogonIdAnonymous = ANONYMOUS_LOGON_LUID;
|
|
static LUID LogonIdSystem = SYSTEM_LUID;
|
|
static LUID LogonIdNetworkService = NETWORKSERVICE_LUID;
|
|
BOOLEAN fMachineLogon = FALSE;
|
|
|
|
MachineAccountName.Buffer = NULL;
|
|
|
|
//
|
|
// if caller is NetworkService with default cred, for downlevel domains
|
|
// use anonymous
|
|
//
|
|
|
|
if (RtlEqualLuid( pLogonId, &LogonIdNetworkService ))
|
|
{
|
|
if ( UserName->Buffer == NULL &&
|
|
DomainName->Buffer == NULL &&
|
|
Password->Buffer == NULL )
|
|
{
|
|
BOOL MixedMode = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
if ( !NlpNetlogonInitialized )
|
|
{
|
|
Status = NlWaitForNetlogon( NETLOGON_STARTUP_TIME );
|
|
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
NlpNetlogonInitialized = TRUE;
|
|
}
|
|
}
|
|
|
|
if (NlpNetlogonInitialized)
|
|
{
|
|
ASSERT(NlpNetLogonMixedDomain && L"NlpNetLogonMixedDomain must be non null");
|
|
|
|
Status = (*NlpNetLogonMixedDomain)(&MixedMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SsprCheckMachineLogon call to I_NetLogonMixedDomain failed %#x\n", Status));
|
|
MixedMode = FALSE;
|
|
}
|
|
}
|
|
|
|
if (MixedMode)
|
|
{
|
|
SspPrint((SSP_WARNING, "SsprCheckMachineLogon using anonymous connection for networkservices\n"));
|
|
*pLogonId = LogonIdAnonymous; // use anonymous
|
|
*pCredFlags |= SSP_CREDENTIAL_FLAG_WAS_NETWORK_SERVICE;
|
|
|
|
// return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// check if caller was system, and requested machine credential
|
|
// eg: user=computername$, domain=NULL, password=NULL
|
|
//
|
|
|
|
if ( !RtlEqualLuid( pLogonId, &LogonIdSystem ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( UserName->Buffer == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( DomainName->Buffer != NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if( Password->Buffer != NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
RtlAcquireResourceShared (&NtLmGlobalCritSect, TRUE);
|
|
|
|
MachineAccountName.Length = NtLmGlobalUnicodeComputerNameString.Length + sizeof(WCHAR);
|
|
|
|
if( MachineAccountName.Length == UserName->Length )
|
|
{
|
|
MachineAccountName.MaximumLength = MachineAccountName.Length;
|
|
MachineAccountName.Buffer = (PWSTR)NtLmAllocate( MachineAccountName.Length );
|
|
|
|
if( MachineAccountName.Buffer != NULL )
|
|
{
|
|
RtlCopyMemory( MachineAccountName.Buffer,
|
|
NtLmGlobalUnicodeComputerNameString.Buffer,
|
|
NtLmGlobalUnicodeComputerNameString.Length
|
|
);
|
|
|
|
MachineAccountName.Buffer[ (MachineAccountName.Length / sizeof(WCHAR)) - 1 ] = L'$';
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource (&NtLmGlobalCritSect);
|
|
|
|
|
|
if( MachineAccountName.Buffer == NULL )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( RtlEqualUnicodeString( &MachineAccountName, UserName, TRUE ) )
|
|
{
|
|
//
|
|
// yes, it's a machine account logon request, update the
|
|
// requested LogonId to match our mapped logon session.
|
|
//
|
|
|
|
*pLogonId = NtLmGlobalLuidMachineLogon;
|
|
fMachineLogon = TRUE;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if( MachineAccountName.Buffer )
|
|
{
|
|
NtLmFree( MachineAccountName.Buffer );
|
|
}
|
|
|
|
return fMachineLogon;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SsprAcquireCredentialHandle(
|
|
IN PLUID LogonId,
|
|
IN PSECPKG_CLIENT_INFO ClientInfo,
|
|
IN ULONG CredentialUseFlags,
|
|
OUT PLSA_SEC_HANDLE CredentialHandle,
|
|
OUT PTimeStamp Lifetime,
|
|
IN OPTIONAL PUNICODE_STRING DomainName,
|
|
IN OPTIONAL PUNICODE_STRING UserName,
|
|
IN OPTIONAL PUNICODE_STRING Password
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API allows applications to acquire a handle to pre-existing
|
|
credentials associated with the user on whose behalf the call is made
|
|
i.e. under the identity this application is running. These pre-existing
|
|
credentials have been established through a system logon not described
|
|
here. Note that this is different from "login to the network" and does
|
|
not imply gathering of credentials.
|
|
|
|
|
|
This API returns a handle to the credentials of a principal (user, client)
|
|
as used by a specific security package. This handle can then be used
|
|
in subsequent calls to the Context APIs. This API will not let a
|
|
process obtain a handle to credentials that are not related to the
|
|
process; i.e. we won't allow a process to grab the credentials of
|
|
another user logged into the same machine. There is no way for us
|
|
to determine if a process is a trojan horse or not, if it is executed
|
|
by the user.
|
|
|
|
Arguments:
|
|
|
|
CredentialUseFlags - Flags indicating the way with which these
|
|
credentials will be used.
|
|
|
|
#define CRED_INBOUND 0x00000001
|
|
#define CRED_OUTBOUND 0x00000002
|
|
#define CRED_BOTH 0x00000003
|
|
|
|
The credentials created with CRED_INBOUND option can only be used
|
|
for (validating incoming calls and can not be used for making accesses.
|
|
|
|
CredentialHandle - Returned credential handle.
|
|
|
|
Lifetime - Time that these credentials expire. The value returned in
|
|
this field depends on the security package.
|
|
|
|
DomainName, DomainNameSize, UserName, UserNameSize, Password, PasswordSize -
|
|
Optional credentials for this user.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -- Call completed successfully
|
|
|
|
SEC_E_PRINCIPAL_UNKNOWN -- No such principal
|
|
SEC_E_NOT_OWNER -- caller does not own the specified credentials
|
|
STATUS_NO_MEMORY -- Not enough memory
|
|
|
|
--*/
|
|
|
|
{
|
|
SspPrint((SSP_API_MORE, "Entering SsprAcquireCredentialHandle\n"));
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSSP_CREDENTIAL Credential = NULL;
|
|
ULONG CredFlags = 0; // ntlm specific cred use flags
|
|
|
|
|
|
if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) {
|
|
|
|
//
|
|
// check if machine account logon over-ride.
|
|
// this has the side-effect of updating LogonId if over-ride was
|
|
// requsted.
|
|
//
|
|
|
|
SsprCheckMachineLogon(
|
|
UserName,
|
|
DomainName,
|
|
Password,
|
|
LogonId,
|
|
&CredFlags
|
|
);
|
|
}
|
|
|
|
//
|
|
// Look to see if we have already created one with this set of credentials.
|
|
// Note - this leaves the credential referenced, so if we fail further down
|
|
// we need to dereference the credential.
|
|
//
|
|
|
|
Credential = SspCredentialLookupCredential(
|
|
LogonId,
|
|
CredentialUseFlags,
|
|
ClientInfo->ImpersonationLevel,
|
|
UserName,
|
|
DomainName,
|
|
Password
|
|
);
|
|
|
|
//
|
|
// If we're using a common set of data, free the captured stuff
|
|
//
|
|
|
|
if ( Credential )
|
|
{
|
|
if ( (UserName) && (UserName->Buffer) )
|
|
{
|
|
NtLmFree( UserName->Buffer );
|
|
UserName->Buffer = NULL ;
|
|
}
|
|
|
|
if ( ( DomainName ) && (DomainName->Buffer) )
|
|
{
|
|
NtLmFree( DomainName->Buffer );
|
|
DomainName->Buffer = NULL ;
|
|
}
|
|
|
|
if ( ( Password ) && ( Password->Buffer ) )
|
|
{
|
|
ZeroMemory( Password->Buffer, Password->Length );
|
|
NtLmFree( Password->Buffer );
|
|
Password->Buffer = NULL ;
|
|
}
|
|
|
|
Credential->MutableCredFlags = CredFlags; // atomic
|
|
}
|
|
|
|
//
|
|
// If we didn't just find a credential, create one now.
|
|
//
|
|
|
|
if (Credential == NULL) {
|
|
|
|
SECPKG_CALL_INFO CallInfo ;
|
|
|
|
if ( !LsaFunctions->GetCallInfo( &CallInfo ) )
|
|
{
|
|
SspPrint((SSP_CRITICAL, "SsprAcquireCredentialHandle failed to GetCallInfo\n"));
|
|
Status = STATUS_UNSUCCESSFUL ;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a credential block and initialize it.
|
|
//
|
|
|
|
Credential = (PSSP_CREDENTIAL)NtLmAllocate(sizeof(SSP_CREDENTIAL) );
|
|
|
|
if ( Credential == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
SspPrint((SSP_CRITICAL, "Error from NtLmAllocate 0x%lx\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
ZeroMemory( Credential, sizeof(*Credential) );
|
|
|
|
Credential->References = 1;
|
|
Credential->ClientProcessID = ClientInfo->ProcessID;
|
|
Credential->CredentialUseFlags = CredentialUseFlags;
|
|
Credential->MutableCredFlags = CredFlags;
|
|
Credential->ImpersonationLevel = ClientInfo->ImpersonationLevel;
|
|
Credential->Unlinked = TRUE;
|
|
|
|
if ( ( CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) != 0 )
|
|
{
|
|
Credential->KernelClient = TRUE ;
|
|
}
|
|
else
|
|
{
|
|
Credential->KernelClient = FALSE ;
|
|
}
|
|
|
|
|
|
//
|
|
// Stick the logon ID in the credential
|
|
//
|
|
|
|
Credential->LogonId = *LogonId;
|
|
|
|
//
|
|
// Stick the supplemental credentials into the credential.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(DomainName))
|
|
{
|
|
Credential->DomainName = *DomainName;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(UserName))
|
|
{
|
|
Credential->UserName = *UserName;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Password))
|
|
{
|
|
SspHidePassword(Password);
|
|
Credential->Password = *Password;
|
|
}
|
|
|
|
//
|
|
// Add it to the list of valid credential handles.
|
|
//
|
|
|
|
Credential->Unlinked = FALSE;
|
|
Credential->CredentialTag = SSP_CREDENTIAL_TAG_ACTIVE;
|
|
|
|
RtlAcquireResourceExclusive( &SspCredentialCritSect, TRUE );
|
|
InsertHeadList( &SspCredentialList, &Credential->Next );
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
SspPrint((SSP_API_MORE, "Added Credential 0x%lx\n", Credential ));
|
|
|
|
//
|
|
// Don't bother dereferencing because we already set the
|
|
// reference count to 1.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Return output parameters to the caller.
|
|
//
|
|
|
|
*CredentialHandle = (LSA_SEC_HANDLE) Credential;
|
|
|
|
*Lifetime = NtLmGlobalForever;
|
|
|
|
Cleanup:
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
if ( Credential != NULL ) {
|
|
(VOID)NtLmFree( Credential );
|
|
}
|
|
|
|
}
|
|
|
|
SspPrint((SSP_API_MORE, "Leaving SsprAcquireCredentialHandle\n"));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SsprFreeCredentialHandle(
|
|
IN ULONG_PTR CredentialHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API is used to notify the security system that the credentials are
|
|
no longer needed and allows the application to free the handle acquired
|
|
in the call described above. When all references to this credential
|
|
set has been removed then the credentials may themselves be removed.
|
|
|
|
Arguments:
|
|
|
|
|
|
CredentialHandle - Credential Handle obtained through
|
|
AcquireCredentialHandle.
|
|
|
|
Return Value:
|
|
|
|
|
|
STATUS_SUCCESS -- Call completed successfully
|
|
|
|
SEC_E_NO_SPM -- Security Support Provider is not running
|
|
STATUS_INVALID_HANDLE -- Credential Handle is invalid
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSSP_CREDENTIAL Credential;
|
|
|
|
SspPrint(( SSP_API_MORE, "SspFreeCredentialHandle Entered\n" ));
|
|
|
|
//
|
|
// Find the referenced credential and delink it.
|
|
//
|
|
|
|
Status = SspCredentialReferenceCredential(
|
|
CredentialHandle,
|
|
TRUE, // remove the instance of the credential
|
|
&Credential );
|
|
|
|
if ( !NT_SUCCESS( Status ) )
|
|
{
|
|
goto Cleanup ;
|
|
}
|
|
|
|
//
|
|
// Dereferencing the Credential will remove the client's reference
|
|
// to it, causing it to be rundown if nobody else is using it.
|
|
//
|
|
|
|
SspCredentialDereferenceCredential( Credential );
|
|
|
|
//
|
|
// Free and locally used resources.
|
|
//
|
|
Cleanup:
|
|
|
|
|
|
SspPrint(( SSP_API_MORE, "SspFreeCredentialHandle returns 0x%lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SspCredentialInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes this module.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Initialize the Credential list to be empty.
|
|
//
|
|
|
|
__try
|
|
{
|
|
RtlInitializeResource(&SspCredentialCritSect);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeListHead( &SspCredentialList );
|
|
|
|
Cleanup:
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
SspCredentialTerminate(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function cleans up any dangling credentials.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if 0
|
|
NTSTATUS Status ;
|
|
|
|
//
|
|
// Drop any lingering Credentials
|
|
//
|
|
|
|
RtlAcquireResourceShared( &SspCredentialCritSect, TRUE );
|
|
while ( !IsListEmpty( &SspCredentialList ) ) {
|
|
ULONG_PTR CredentialHandle;
|
|
PSSP_CREDENTIAL Credential;
|
|
|
|
CredentialHandle =
|
|
(LSA_SEC_HANDLE) CONTAINING_RECORD( SspCredentialList.Flink,
|
|
SSP_CREDENTIAL,
|
|
Next );
|
|
|
|
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
Status = SspCredentialReferenceCredential(
|
|
CredentialHandle,
|
|
TRUE,
|
|
TRUE,
|
|
&Credential ); // Remove Credential
|
|
|
|
if ( Credential != NULL ) {
|
|
SspCredentialDereferenceCredential(Credential);
|
|
}
|
|
|
|
RtlAcquireResourceShared( &SspCredentialCritSect, TRUE );
|
|
}
|
|
RtlReleaseResource( &SspCredentialCritSect );
|
|
|
|
|
|
//
|
|
// Delete the critical section
|
|
//
|
|
|
|
RtlDeleteResource(&SspCredentialCritSect);
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
BOOL
|
|
SspEnableAllPrivilegesToken(
|
|
IN HANDLE ClientTokenHandle
|
|
)
|
|
{
|
|
PTOKEN_PRIVILEGES pPrivileges;
|
|
BYTE FastBuffer[ 512 ];
|
|
PBYTE SlowBuffer = NULL;
|
|
DWORD cbPrivileges;
|
|
BOOL fSuccess;
|
|
|
|
pPrivileges = (PTOKEN_PRIVILEGES)FastBuffer;
|
|
cbPrivileges = sizeof( FastBuffer );
|
|
|
|
fSuccess = GetTokenInformation(
|
|
ClientTokenHandle,
|
|
TokenPrivileges,
|
|
pPrivileges,
|
|
cbPrivileges,
|
|
&cbPrivileges
|
|
);
|
|
|
|
if( !fSuccess ) {
|
|
|
|
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
|
return FALSE;
|
|
|
|
SlowBuffer = (PBYTE)NtLmAllocate( cbPrivileges );
|
|
if( SlowBuffer == NULL )
|
|
return FALSE;
|
|
|
|
pPrivileges = (PTOKEN_PRIVILEGES)SlowBuffer;
|
|
|
|
fSuccess = GetTokenInformation(
|
|
ClientTokenHandle,
|
|
TokenPrivileges,
|
|
pPrivileges,
|
|
cbPrivileges,
|
|
&cbPrivileges
|
|
);
|
|
}
|
|
|
|
|
|
if( fSuccess && pPrivileges->PrivilegeCount != 0 ) {
|
|
DWORD indexPrivilege;
|
|
|
|
for( indexPrivilege = 0 ;
|
|
indexPrivilege < pPrivileges->PrivilegeCount ;
|
|
indexPrivilege ++ )
|
|
{
|
|
pPrivileges->Privileges[ indexPrivilege ].Attributes |=
|
|
SE_PRIVILEGE_ENABLED;
|
|
}
|
|
|
|
fSuccess = AdjustTokenPrivileges(
|
|
ClientTokenHandle,
|
|
FALSE,
|
|
pPrivileges,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if( fSuccess && GetLastError() != ERROR_SUCCESS )
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
if( SlowBuffer )
|
|
NtLmFree( SlowBuffer );
|
|
|
|
return fSuccess;
|
|
|
|
}
|