Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1119 lines
27 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
credhand.c
Abstract:
API and support routines for handling credential handles.
Author:
Cliff Van Dyke (CliffV) 26-Jun-1993
Revision History:
--*/
//
// Common include files.
//
#include <ntlmcomn.h> // Common definitions for DLL and SERVICE
#include <ntlmsspi.h> // Data private to the common routines
#include <align.h> // ALIGN_WHCAR
//
// Crit Sect to protect various globals in this module.
//
CRITICAL_SECTION SspCredentialCritSect;
LIST_ENTRY SspCredentialList;
SECURITY_STATUS
SspGetUnicodeStringFromClient(
IN PSSP_CLIENT_CONNECTION ClientConnection,
IN LPWSTR String,
IN ULONG StringSize,
IN ULONG MaximumLength,
OUT PUNICODE_STRING OutputString
)
/*++
Routine Description:
This routine copies the InputMessage into the local address space.
This routine then validates the message header.
Arguments:
ClientConnection - Describes the client process.
String - Address of the string in the client process (must include
trailing zero character).
StringSize - Size of the string (in bytes).
MaximumLength - Maximum length of the string (in characters) (not including
the trailing zero characer).
OutputString - Returns a UNICODE_STRING with an allocated buffer that
contains the string. The buffer should be freed using LocalFree.
The field will be set NULL if the input string is NULL.
Return Value:
STATUS_SUCCESS - Call completed successfully
SEC_E_INVALID_TOKEN -- Message improperly formatted
SEC_E_UNKNOWN_CREDENTIALS -- Credentials are improperly formed
--*/
{
SECURITY_STATUS SecStatus;
LPWSTR AllocatedString;
//
// If the caller didn't pass a string,
// just indicate so.
//
if ( String == NULL && StringSize == 0 ) {
RtlInitUnicodeString(
OutputString,
NULL
);
return STATUS_SUCCESS;
}
//
// Allocate a local buffer for the message.
//
if ( !COUNT_IS_ALIGNED(StringSize, ALIGN_WCHAR) ||
StringSize > (MaximumLength+1) * sizeof(WCHAR) ) {
return SEC_E_UNKNOWN_CREDENTIALS;
}
AllocatedString = LocalAlloc( 0, StringSize );
if ( AllocatedString == NULL ) {
return SEC_E_INSUFFICIENT_MEMORY;
}
//
// Copy the message into the buffer
//
SecStatus = SspLpcCopyFromClientBuffer (
ClientConnection,
StringSize,
AllocatedString,
String );
if ( !NT_SUCCESS(SecStatus) ) {
(VOID) LocalFree( AllocatedString );
return SecStatus;
}
//
// Ensure the string is trailing zero terminated.
//
if ( AllocatedString[(StringSize/sizeof(WCHAR))-1] != L'\0' ) {
(VOID) LocalFree( AllocatedString );
return SEC_E_UNKNOWN_CREDENTIALS;
}
OutputString->Buffer = AllocatedString;
OutputString->MaximumLength = (USHORT) StringSize;
OutputString->Length = (USHORT) (StringSize - sizeof(WCHAR));
return STATUS_SUCCESS;
}
PSSP_CREDENTIAL
SspCredentialReferenceCredential(
IN PCredHandle CredentialHandle,
IN PSSP_CLIENT_CONNECTION ClientConnection,
IN BOOLEAN DereferenceCredential,
IN BOOLEAN ForceRemoveCredential
)
/*++
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.
ClientConnection - Points to the client connection of the client
referencing the handle. (NULL means an internal reference.)
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.
ForceRemoveCredential - This boolean value indicates whether the caller
wants the logon process's Credential to be removed from the list
of Credentials. TRUE indicates the Credential is to be removed.
FALSE indicates the Credential is not to be removed.
Return Value:
NULL - the Credential was not found.
Otherwise - returns a pointer to the referenced credential.
--*/
{
PLIST_ENTRY ListEntry;
PSSP_CREDENTIAL Credential;
//
// Sanity check
//
if ( CredentialHandle->dwLower != SspCommonSecHandleValue ) {
return NULL;
}
//
// Make sure that nobody tries to force removal without also
// trying to dereference the credential.
//
ASSERT(!(ForceRemoveCredential && !DereferenceCredential));
//
// Acquire exclusive access to the Credential list
//
EnterCriticalSection( &SspCredentialCritSect );
//
// Now walk the list of Credentials looking for a match.
//
for ( ListEntry = SspCredentialList.Flink;
ListEntry != &SspCredentialList;
ListEntry = ListEntry->Flink ) {
Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next );
//
// Found a match ... reference this Credential
// (if the Credential is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
if ( Credential == (PSSP_CREDENTIAL) CredentialHandle->dwUpper &&
(ClientConnection == NULL ||
ClientConnection == Credential->ClientConnection )) {
if (!DereferenceCredential) {
Credential->References += 1;
} else {
//
// Decremenent the credential references, indicating
// that a call to free
Credential->CredentialReferences--;
if (ForceRemoveCredential || (Credential->CredentialReferences == 0)) {
RemoveEntryList( &Credential->Next );
RemoveEntryList( &Credential->NextForThisClient );
Credential->Unlinked = TRUE;
//
// If we are forcing removal, get rid of the appropriate
// number of references from all the other instances.
// This is used when the client connection is dropped.
//
if (ForceRemoveCredential) {
Credential->References -= Credential->CredentialReferences;
}
Credential->CredentialReferences = 0;
SspPrint(( SSP_API_MORE, "Delinked Credential 0x%lx\n",
Credential ));
}
}
LeaveCriticalSection( &SspCredentialCritSect );
return Credential;
}
}
//
// No match found
//
SspPrint(( SSP_API, "Tried to reference unknown Credential 0x%lx\n",
CredentialHandle->dwUpper ));
LeaveCriticalSection( &SspCredentialCritSect );
return NULL;
}
SECURITY_STATUS
SspCredentialGetPassword(
IN PSSP_CREDENTIAL Credential,
OUT PUNICODE_STRING Password
)
/*++
Routine Description:
This routine copies the password out of credential. It requires locking
the credential list because other threads may be hiding/revealing the
password and we need exclusive access to do that.
Arguments:
Credential - Credential record to retrieve the password from.
Password - UNICODE_STRING to store the password in.
Return Value:
SEC_E_INSUFFICIENT_MEMORY - there was not enough memory to copy
the password.
--*/
{
SECURITY_STATUS SecStatus = SEC_E_OK;
EnterCriticalSection(&SspCredentialCritSect);
SspRevealPassword(&Credential->Password);
if ( Credential->Password.Buffer != NULL ) {
SecStatus = SspDuplicateUnicodeString(
Password,
&Credential->Password
);
} else {
RtlInitUnicodeString(
Password,
NULL
);
}
if (NT_SUCCESS(SecStatus)) {
SspHidePassword(Password);
}
SspHidePassword(&Credential->Password);
LeaveCriticalSection(&SspCredentialCritSect);
return(SecStatus);
}
PSSP_CREDENTIAL
SspCredentialLookupSupplementalCredential(
IN PSSP_CLIENT_CONNECTION ClientConnection,
IN PLUID LogonId,
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:
ClientConnection - Points to the client connection of the client
referencing the handle.
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.
--*/
{
PLIST_ENTRY ListEntry;
PSSP_CREDENTIAL Credential;
PLIST_ENTRY ListHead;
//
// Acquire exclusive access to the Credential list
//
EnterCriticalSection( &SspCredentialCritSect );
if (ClientConnection == NULL) {
ListHead = &SspCredentialList;
} else {
ListHead = &ClientConnection->CredentialHead;
}
//
// Now walk the list of Credentials looking for a match.
//
for ( ListEntry = ListHead->Flink;
ListEntry != ListHead;
ListEntry = ListEntry->Flink ) {
if (ClientConnection != NULL) {
Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, NextForThisClient );
} else {
Credential = CONTAINING_RECORD( ListEntry, SSP_CREDENTIAL, Next );
}
//
// We are only looking for outbound credentials.
//
if ((Credential->CredentialUseFlags & SECPKG_CRED_OUTBOUND) == 0) {
continue;
}
//
// Check for a match
//
if ( RtlEqualUnicodeString(
UserName,
&Credential->UserName,
FALSE
) &&
RtlEqualUnicodeString(
DomainName,
&Credential->DomainName,
FALSE
) &&
RtlEqualLuid(
LogonId,
&Credential->LogonId
)) {
SspRevealPassword(&Credential->Password);
if (RtlEqualUnicodeString(
Password,
&Credential->Password,
FALSE
)) {
//
// Found a match - reference the credential
//
SspHidePassword(&Credential->Password);
//
// Reference the credential and indicate that
// it is in use as two different handles to the caller
// (who may call FreeCredentialsHandle twice)
//
Credential->References++;
Credential->CredentialReferences++;
LeaveCriticalSection( &SspCredentialCritSect );
return Credential;
}
SspHidePassword(&Credential->Password);
}
}
//
// No match found
//
SspPrint(( SSP_API, "Tried to reference unknown Credential\n" ));
LeaveCriticalSection( &SspCredentialCritSect );
return NULL;
}
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.
--*/
{
ULONG References;
//
// Decrement the reference count
//
EnterCriticalSection( &SspCredentialCritSect );
ASSERT( Credential->References >= 1 );
References = -- Credential->References;
LeaveCriticalSection( &SspCredentialCritSect );
//
// If the count dropped to zero, then run-down the Credential
//
if ( References == 0) {
SspPrint(( SSP_API_MORE, "Deleting Credential 0x%lx\n",
Credential ));
if ( Credential->DomainName.Buffer != NULL ) {
(VOID) LocalFree( Credential->DomainName.Buffer );
}
if ( Credential->UserName.Buffer != NULL ) {
(VOID) LocalFree( Credential->UserName.Buffer );
}
if ( Credential->Password.Buffer != NULL ) {
(VOID) LocalFree( Credential->Password.Buffer );
}
if (!Credential->Unlinked) {
RemoveEntryList( &Credential->Next );
RemoveEntryList( &Credential->NextForThisClient );
}
if (Credential->ClientTokenHandle != NULL) {
(VOID) NtClose(Credential->ClientTokenHandle);
}
(VOID) LocalFree( Credential );
}
return;
}
VOID
SspCredentialClientConnectionDropped(
PSSP_CLIENT_CONNECTION ClientConnection
)
/*++
Routine Description:
This routine is called when the ClientConnection is dropped to allow
us to remove any Credentials for the ClientConnection.
Arguments:
ClientConnection - Pointer to the ClientConnection that has been dropped.
Return Value:
None.
--*/
{
//
// Drop any lingering Credentials
//
EnterCriticalSection( &SspCredentialCritSect );
while ( !IsListEmpty( &ClientConnection->CredentialHead ) ) {
CredHandle CredentialHandle;
PSSP_CREDENTIAL Credential;
CredentialHandle.dwUpper =
(LONG) CONTAINING_RECORD( ClientConnection->CredentialHead.Flink,
SSP_CREDENTIAL,
NextForThisClient );
CredentialHandle.dwLower = SspCommonSecHandleValue;
LeaveCriticalSection( &SspCredentialCritSect );
Credential = SspCredentialReferenceCredential(
&CredentialHandle,
ClientConnection,
TRUE,
TRUE); // Remove Credential
if ( Credential != NULL ) {
SspCredentialDereferenceCredential(Credential);
}
EnterCriticalSection( &SspCredentialCritSect );
}
LeaveCriticalSection( &SspCredentialCritSect );
}
SECURITY_STATUS
SsprAcquireCredentialHandle(
IN PSSP_CLIENT_CONNECTION ClientConnection,
IN PHANDLE ClientTokenHandle,
IN PLUID LogonId,
IN ULONG CredentialUseFlags,
OUT PCredHandle CredentialHandle,
OUT PTimeStamp Lifetime,
IN LPWSTR DomainName,
IN ULONG DomainNameSize,
IN LPWSTR UserName,
IN ULONG UserNameSize,
IN LPWSTR Password,
IN ULONG PasswordSize
)
/*++
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:
ClientConnection - Describes the client process.
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
SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
--*/
{
SECURITY_STATUS SecStatus;
NTSTATUS Status;
PSSP_CREDENTIAL Credential = NULL;
UNICODE_STRING LocalDomainName;
UNICODE_STRING LocalUserName;
UNICODE_STRING LocalPassword;
TOKEN_STATISTICS TokenStatisticsInfo;
ULONG TokenStatisticsInfoSize = sizeof(TOKEN_STATISTICS);
//
// Initialization
//
RtlInitUnicodeString(
&LocalDomainName,
NULL
);
RtlInitUnicodeString(
&LocalUserName,
NULL
);
RtlInitUnicodeString(
&LocalPassword,
NULL
);
SspPrint(( SSP_API, "SsprAcquireCredentialHandle Entered\n" ));
//
// Ensure at least one Credential use bit is set.
//
if ( (CredentialUseFlags & (SECPKG_CRED_INBOUND|SECPKG_CRED_OUTBOUND)) == 0 ) {
SspPrint(( SSP_API,
"SsprAcquireCredentialHandle: invalid credential use.\n" ));
SecStatus = SEC_E_INVALID_CREDENTIAL_USE;
goto Cleanup;
}
//
// Copy the default credentials to the credential block
//
SecStatus = SspGetUnicodeStringFromClient(
ClientConnection,
DomainName,
DomainNameSize,
DNLEN,
&LocalDomainName );
if ( !NT_SUCCESS(SecStatus) ) {
SspPrint(( SSP_API, "Cannot copy domain name.\n" ));
goto Cleanup;
}
SecStatus = SspGetUnicodeStringFromClient(
ClientConnection,
UserName,
UserNameSize,
UNLEN,
&LocalUserName );
if ( !NT_SUCCESS(SecStatus) ) {
SspPrint(( SSP_API, "Cannot copy user name.\n" ));
goto Cleanup;
}
SecStatus = SspGetUnicodeStringFromClient(
ClientConnection,
Password,
PasswordSize,
PWLEN,
&LocalPassword );
if ( !NT_SUCCESS(SecStatus) ) {
SspPrint(( SSP_API, "Cannot copy password.\n" ));
goto Cleanup;
}
Status = NtQueryInformationToken(
*ClientTokenHandle,
TokenStatistics,
&TokenStatisticsInfo,
TokenStatisticsInfoSize,
&TokenStatisticsInfoSize );
if (!NT_SUCCESS(Status)) {
SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_IMPERSONATION );
goto Cleanup;
}
//
// If this is an outbound credential, and supplemental credentials
// were supplied, 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.
//
if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) {
Credential = SspCredentialLookupSupplementalCredential(
ClientConnection,
&TokenStatisticsInfo.AuthenticationId,
&LocalUserName,
&LocalDomainName,
&LocalPassword
);
}
//
// If we didn't just find a credential, create one now.
//
if (Credential == NULL) {
//
// Allocate a credential block and initialize it.
//
Credential = LocalAlloc( 0, sizeof(SSP_CREDENTIAL) );
if ( Credential == NULL ) {
SspPrint(( SSP_API, "Cannot allocate credential.\n" ));
SecStatus = SEC_E_INSUFFICIENT_MEMORY;
goto Cleanup;
}
//
// Actually its on both a global credential list and a per client connection
// list, but we link/delink from both lists at the same time so a single
// reference count handles both.
//
Credential->References = 1;
Credential->CredentialReferences = 1;
Credential->ClientConnection = ClientConnection;
Credential->CredentialUseFlags = CredentialUseFlags;
Credential->Unlinked = FALSE;
//
// Stick the token and logon ID in the credential
//
Credential->ClientTokenHandle = *ClientTokenHandle,
*ClientTokenHandle = NULL;
Credential->LogonId = *LogonId;
//
// Stick the supplemental credentials into the credential.
//
Credential->UserName = LocalUserName;
LocalUserName.Buffer = NULL;
Credential->DomainName = LocalDomainName;
LocalDomainName.Buffer = NULL;
SspHidePassword(&LocalPassword);
Credential->Password = LocalPassword;
LocalPassword.Buffer = NULL;
//
// Add it to the list of valid credential handles.
//
EnterCriticalSection( &SspCredentialCritSect );
InsertHeadList( &SspCredentialList, &Credential->Next );
if ( ClientConnection != NULL ) {
InsertHeadList( &ClientConnection->CredentialHead,
&Credential->NextForThisClient );
} else {
InitializeListHead( &Credential->NextForThisClient );
}
LeaveCriticalSection( &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->dwUpper = (DWORD) Credential;
CredentialHandle->dwLower = SspCommonSecHandleValue;
*Lifetime = SspGlobalForever;
SecStatus = STATUS_SUCCESS;
//
// Free and locally used resources.
//
Cleanup:
if ( !NT_SUCCESS(SecStatus) ) {
if ( Credential != NULL ) {
(VOID)LocalFree( Credential );
}
}
if (LocalUserName.Buffer != NULL) {
(VOID)LocalFree(LocalUserName.Buffer);
}
if (LocalDomainName.Buffer != NULL) {
(VOID)LocalFree(LocalDomainName.Buffer);
}
if (LocalPassword.Buffer != NULL) {
(VOID)LocalFree(LocalPassword.Buffer);
}
SspPrint(( SSP_API, "SspAcquireCredentialHandle returns 0x%lx\n", SecStatus ));
return SecStatus;
}
SECURITY_STATUS
SsprFreeCredentialHandle(
IN PSSP_CLIENT_CONNECTION ClientConnection,
IN PCredHandle 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:
ClientConnection - Describes the client process.
CredentialHandle - Credential Handle obtained through
AcquireCredentialHandle.
Return Value:
STATUS_SUCCESS -- Call completed successfully
SEC_E_NO_SPM -- Security Support Provider is not running
SEC_E_INVALID_HANDLE -- Credential Handle is invalid
--*/
{
SECURITY_STATUS SecStatus;
PSSP_CREDENTIAL Credential;
//
// Initialization
//
SspPrint(( SSP_API, "SspFreeCredentialHandle Entered\n" ));
//
// Find the referenced credential and delink it.
//
Credential = SspCredentialReferenceCredential(
CredentialHandle,
ClientConnection,
TRUE, // remove the instance of the credential
FALSE);
if ( Credential == NULL ) {
SecStatus = SEC_E_INVALID_HANDLE;
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 );
SecStatus = STATUS_SUCCESS;
//
// Free and locally used resources.
//
Cleanup:
SspPrint(( SSP_API, "SspFreeCredentialHandle returns 0x%lx\n", SecStatus ));
return SecStatus;
}
NTSTATUS
SspCredentialInitialize(
VOID
)
/*++
Routine Description:
This function initializes this module.
Arguments:
None.
Return Value:
Status of the operation.
--*/
{
//
// Initialize the Credential list to be empty.
//
InitializeCriticalSection(&SspCredentialCritSect);
InitializeListHead( &SspCredentialList );
return STATUS_SUCCESS;
}
VOID
SspCredentialTerminate(
VOID
)
/*++
Routine Description:
This function cleans up any dangling credentials.
Arguments:
None.
Return Value:
Status of the operation.
--*/
{
//
// Drop any lingering Credentials
//
EnterCriticalSection( &SspCredentialCritSect );
while ( !IsListEmpty( &SspCredentialList ) ) {
CredHandle CredentialHandle;
PSSP_CREDENTIAL Credential;
CredentialHandle.dwUpper =
(LONG) CONTAINING_RECORD( SspCredentialList.Flink,
SSP_CREDENTIAL,
Next );
CredentialHandle.dwLower = SspCommonSecHandleValue;
LeaveCriticalSection( &SspCredentialCritSect );
Credential = SspCredentialReferenceCredential(
&CredentialHandle,
NULL, // Don't know the Connection
TRUE,
TRUE); // Remove Credential
if ( Credential != NULL ) {
SspCredentialDereferenceCredential(Credential);
}
EnterCriticalSection( &SspCredentialCritSect );
}
LeaveCriticalSection( &SspCredentialCritSect );
//
// Delete the critical section
//
DeleteCriticalSection(&SspCredentialCritSect);
return;
}