|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: credapi.cxx
//
// Contents: Code for credentials APIs for the Kerberos package
//
//
// History: 16-April-1996 Created MikeSw
// 26-Sep-1998 ChandanS
// Added more debugging support etc.
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif
#define FILENO FILENO_CREDAPI
//+-------------------------------------------------------------------------
//
// Function: KerbCopyClientString
//
// Synopsis: Copies a string from the client and if necessary converts
// from ansi to unicode
//
// Effects: allocates output with either KerbAllocate (unicode)
// or RtlAnsiStringToUnicodeString
//
// Arguments: StringPointer - address of string in client process
// StringLength - Lenght (in characters) of string
// AnsiString - if TRUE, string is ansi
// LocalString - receives allocated string
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbCopyClientString( IN PVOID StringPointer, IN ULONG StringLength, IN BOOLEAN AnsiString, OUT PUNICODE_STRING LocalString, IN ULONG MaxLength ) { NTSTATUS Status = STATUS_SUCCESS; PVOID LocalBuffer = NULL; ULONG CharSize = sizeof(WCHAR);
if (AnsiString) { CharSize = sizeof(CHAR); }
if (StringLength > MaxLength) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
LocalBuffer = KerbAllocate(StringLength * CharSize); if (LocalBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
Status = LsaFunctions->CopyFromClientBuffer( NULL, StringLength * CharSize, LocalBuffer, StringPointer ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
if (AnsiString) { ANSI_STRING TempString; UNICODE_STRING TempOutputString = {0};
TempString.Buffer = (PCHAR) LocalBuffer; TempString.MaximumLength = TempString.Length = (USHORT) StringLength;
Status = RtlAnsiStringToUnicodeString( &TempOutputString, &TempString, TRUE // allocate destination
); if (!NT_SUCCESS(Status)) { goto Cleanup; } *LocalString = TempOutputString; } else { LocalString->Buffer = (LPWSTR) LocalBuffer; LocalString->Length = (USHORT) StringLength * sizeof(WCHAR); LocalString->MaximumLength = LocalString->Length; LocalBuffer = NULL; }
Cleanup: if (LocalBuffer) { KerbFree(LocalBuffer); } return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbInitPrimaryCreds
//
// Synopsis: Allocates and initializes a PKERB_PRIMARY_CREDENTIAL
// structure.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbInitPrimaryCreds( IN PKERB_LOGON_SESSION LogonSession, IN PUNICODE_STRING UserString, IN PUNICODE_STRING DomainString, IN OPTIONAL PUNICODE_STRING PrincipalName, IN PUNICODE_STRING PasswordString, // either the password or if pin
IN BOOLEAN PubKeyCreds, IN OPTIONAL PCERT_CONTEXT pCertContext, OUT PKERB_PRIMARY_CREDENTIAL * PrimaryCreds ) { NTSTATUS Status = STATUS_SUCCESS; PUCHAR Where; PKERB_PRIMARY_CREDENTIAL NewCreds = NULL; KERBEROS_MACHINE_ROLE Role;
// allocate the primary cred structure
NewCreds = (PKERB_PRIMARY_CREDENTIAL) KerbAllocate(sizeof(KERB_PRIMARY_CREDENTIAL)); if (NewCreds == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
KerbInitTicketCache( &NewCreds->ServerTicketCache ); KerbInitTicketCache( &NewCreds->AuthenticationTicketCache );
KerbInitTicketCache( &NewCreds->S4UTicketCache );
//
// Fill in the fields
//
Status = KerbDuplicateString( &NewCreds->UserName, UserString ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = KerbDuplicateString( &NewCreds->OldUserName, UserString ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = KerbDuplicateString( &NewCreds->DomainName, DomainString ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = KerbDuplicateString( &NewCreds->OldDomainName, DomainString ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
if (!PubKeyCreds) { if (PasswordString->Buffer != NULL) { Status = KerbDuplicatePassword( &NewCreds->ClearPassword, PasswordString ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KerbHidePassword( &NewCreds->ClearPassword );
RtlCalculateNtOwfPassword( &NewCreds->ClearPassword, &NewCreds->OldHashPassword ); }
if (PasswordString->Buffer != NULL) { Status = KerbBuildPasswordList( PasswordString, UserString, DomainString, NULL, // no supplied salt
NULL, // no old password list
PrincipalName, UserAccount, PRIMARY_CRED_CLEAR_PASSWORD, &NewCreds->Passwords ); if (!NT_SUCCESS(Status)) { goto Cleanup; } } else { ULONG PasswordSize; ULONG Index;
//
// Compute the size of the passwords, which are assumed to be
// marshalled in order.
//
if (LogonSession->PrimaryCredentials.Passwords != NULL) { PasswordSize = sizeof(KERB_STORED_CREDENTIAL) - sizeof(KERB_KEY_DATA) * ANYSIZE_ARRAY + LogonSession->PrimaryCredentials.Passwords->CredentialCount * sizeof(KERB_KEY_DATA);
for (Index = 0; Index < LogonSession->PrimaryCredentials.Passwords->CredentialCount ; Index++ ) { PasswordSize += LogonSession->PrimaryCredentials.Passwords->Credentials[Index].Key.keyvalue.length; }
NewCreds->Passwords = (PKERB_STORED_CREDENTIAL) KerbAllocate(PasswordSize);
if (NewCreds->Passwords == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } NewCreds->Passwords->Revision = KERB_PRIMARY_CRED_REVISION; NewCreds->Passwords->Flags = 0; NewCreds->Passwords->OldCredentialCount = 0;
//
// Zero the salt so we don't accidentally re-use it.
//
RtlInitUnicodeString( &NewCreds->Passwords->DefaultSalt, NULL );
NewCreds->Passwords->CredentialCount = LogonSession->PrimaryCredentials.Passwords->CredentialCount;
Where = (PUCHAR) &NewCreds->Passwords->Credentials[NewCreds->Passwords->CredentialCount];
//
// Copy all the old passwords.
//
for (Index = 0; Index < (USHORT) (NewCreds->Passwords->CredentialCount) ; Index++ ) { RtlInitUnicodeString( &NewCreds->Passwords->Credentials[Index].Salt, NULL ); NewCreds->Passwords->Credentials[Index].Key = LogonSession->PrimaryCredentials.Passwords->Credentials[Index].Key; NewCreds->Passwords->Credentials[Index].Key.keyvalue.value = Where; RtlCopyMemory( Where, LogonSession->PrimaryCredentials.Passwords->Credentials[Index].Key.keyvalue.value, LogonSession->PrimaryCredentials.Passwords->Credentials[Index].Key.keyvalue.length ); Where += LogonSession->PrimaryCredentials.Passwords->Credentials[Index].Key.keyvalue.length;
}
} else { D_DebugLog((DEB_ERROR,"Didn't supply enough credentials - no password available. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; } } } else { // allocate the memory for the public key creds
NewCreds->PublicKeyCreds = (PKERB_PUBLIC_KEY_CREDENTIALS) KerbAllocate(sizeof(KERB_PUBLIC_KEY_CREDENTIALS)); if (NULL == NewCreds->PublicKeyCreds) { D_DebugLog((DEB_ERROR,"Couldn't allocate public key creds\n")); Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
// the password is now considered the pin for the smartcard
if (NULL == pCertContext) { D_DebugLog((DEB_ERROR,"Didn't supply enough credentials - no cert context available. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; }
(NewCreds->PublicKeyCreds)->CertContext = CertDuplicateCertificateContext(pCertContext); if (NULL == (NewCreds->PublicKeyCreds)->CertContext) { Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
if (PasswordString->Buffer != NULL) { Status = KerbDuplicatePassword( &NewCreds->PublicKeyCreds->Pin, PasswordString );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
KerbHidePassword( &NewCreds->PublicKeyCreds->Pin ); }
//
// For non-joined machines, we have to make an assumption about the "best"
// domain to try when we're getting a TGT. Normally, UPNs are cracked at
// the workstation's realm. In this case, we formulate a guess from the DN
// portion of the subject name.
//
// W/o this information, we should fail right here and log an event.
//
Role = KerbGetGlobalRole();
if (Role == KerbRoleRealmlessWksta) { if (!KerbRetrieveDomainFromDn( pCertContext, &((NewCreds->PublicKeyCreds)->AlternateDomainName) )) { Status = STATUS_INVALID_ACCOUNT_NAME; goto Cleanup; }
} }
*PrimaryCreds = NewCreds; NewCreds = NULL; Status = STATUS_SUCCESS;
Cleanup: if (NewCreds != NULL) { KerbFreePrimaryCredentials( NewCreds, TRUE ); }
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KerbCaptureSuppliedCreds
//
// Synopsis: Captures a SEC_WINNT_AUTH_IDENTITY structure from
// the client
//
// Effects:
//
// Arguments: LogonSession - Logon session that supplies the missing
// elements of the supplied creds.
// AuthorizationData - Client address of auth data
// SuppliedCreds - Returns constructed credentials, NULL for
// null session credentials.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbCaptureSuppliedCreds( IN PKERB_LOGON_SESSION LogonSession, IN OPTIONAL PVOID AuthorizationData, IN OPTIONAL PUNICODE_STRING PrincipalName, OUT PKERB_PRIMARY_CREDENTIAL * SuppliedCreds, OUT PULONG Flags ) { NTSTATUS Status = STATUS_SUCCESS; PSEC_WINNT_AUTH_IDENTITY_EXW IdentityEx = NULL; SEC_WINNT_AUTH_IDENTITY_W LocalIdentity = {0}; PSEC_WINNT_AUTH_IDENTITY_W AuthIdentity = NULL; PSEC_WINNT_AUTH_IDENTITY_W Credentials = NULL; BOOLEAN LogonSessionsLocked = FALSE; UNICODE_STRING UserString = {0}; UNICODE_STRING DomainString = {0}; UNICODE_STRING PasswordString = {0}; BOOLEAN AnsiCreds = FALSE; BOOLEAN Marshalled = FALSE; ULONG CredSize; ULONG CredentialSize = 0; ULONG Offset = 0; PCERT_CONTEXT CertContext = NULL; BOOLEAN fSuppliedCertCred = FALSE; LUID SystemLogonId = SYSTEM_LUID;
// WOW64
SEC_WINNT_AUTH_IDENTITY32 Cred32 = {0}; SEC_WINNT_AUTH_IDENTITY_EX32 CredEx32 = {0}; HANDLE TokenHandle = NULL; ULONG CallInfoAttributes = 0;
#if _WIN64
SECPKG_CALL_INFO CallInfo;
LsaFunctions->GetCallInfo( &CallInfo ); CallInfoAttributes = CallInfo.Attributes; #endif
*SuppliedCreds = NULL; *Flags = 0;
if (ARGUMENT_PRESENT(AuthorizationData)) { SafeAllocaAllocate(IdentityEx, sizeof(SEC_WINNT_AUTH_IDENTITY_EXW));
if (IdentityEx != NULL) { // We're being called from a WOW client! Wow!
if (CallInfoAttributes & SECPKG_CALL_WOWCLIENT) { Status = LsaFunctions->CopyFromClientBuffer( NULL, sizeof(Cred32), IdentityEx, AuthorizationData );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to capture WOW64 supplied cred structure - %x\n", Status)); goto Cleanup; } else { RtlCopyMemory(&Cred32, IdentityEx, sizeof(Cred32)); } } else { Status = LsaFunctions->CopyFromClientBuffer( NULL, sizeof(SEC_WINNT_AUTH_IDENTITY), IdentityEx, AuthorizationData );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to copy auth data from %p client address: 0x%x. KLIN(%x)\n", AuthorizationData, Status, KLIN(FILENO, __LINE__ ))); goto Cleanup; } } } else { Status = STATUS_INSUFFICIENT_RESOURCES; D_DebugLog((DEB_ERROR, "KLIN(%x) - Failed allocate\n", KLIN(FILENO, __LINE__))); goto Cleanup; }
//
// Check for extended structures
//
if (IdentityEx->Version == SEC_WINNT_AUTH_IDENTITY_VERSION) { if (CallInfoAttributes & SECPKG_CALL_WOWCLIENT) { Status = LsaFunctions->CopyFromClientBuffer( NULL, sizeof(CredEx32), &CredEx32, AuthorizationData );
if (NT_SUCCESS(Status)) { IdentityEx->Version = CredEx32.Version; IdentityEx->Length = (CredEx32.Length < sizeof(SEC_WINNT_AUTH_IDENTITY_EX) ? (ULONG) sizeof(SEC_WINNT_AUTH_IDENTITY_EX) : CredEx32.Length);
IdentityEx->UserLength = CredEx32.UserLength; IdentityEx->User = (PWSTR) UlongToPtr(CredEx32.User); IdentityEx->DomainLength = CredEx32.DomainLength ; IdentityEx->Domain = (PWSTR) UlongToPtr( CredEx32.Domain ); IdentityEx->PasswordLength = CredEx32.PasswordLength ; IdentityEx->Password = (PWSTR) UlongToPtr( CredEx32.Password ); IdentityEx->Flags = CredEx32.Flags ; IdentityEx->PackageListLength = CredEx32.PackageListLength ; IdentityEx->PackageList = (PWSTR) UlongToPtr( CredEx32.PackageList ); } else { D_DebugLog((DEB_ERROR, "Failed to capture WOW64 supplied credEX structure - %x\n", Status)); goto Cleanup; } } else // not WOW64
{
Status = LsaFunctions->CopyFromClientBuffer( NULL, sizeof(SEC_WINNT_AUTH_IDENTITY_EXW), IdentityEx, AuthorizationData );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to capture supplied EX structure - %x\n", Status)); goto Cleanup; } }
AuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) &IdentityEx->User ; CredSize = IdentityEx->Length ; Offset = FIELD_OFFSET(SEC_WINNT_AUTH_IDENTITY_EXW, User); } else // not Extended version
{ AuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) IdentityEx ; if (CallInfoAttributes & SECPKG_CALL_WOWCLIENT) { AuthIdentity->User = (PWSTR) UlongToPtr(Cred32.User); AuthIdentity->UserLength = Cred32.UserLength; AuthIdentity->Domain = (PWSTR) UlongToPtr(Cred32.Domain); AuthIdentity->DomainLength = Cred32.DomainLength ; AuthIdentity->Password = (PWSTR) UlongToPtr(Cred32.Password); AuthIdentity->PasswordLength = Cred32.PasswordLength ; AuthIdentity->Flags = Cred32.Flags ; } CredSize = sizeof(SEC_WINNT_AUTH_IDENTITY_W); }
//
// Check for the no-pac flag
//
if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ONLY) != 0) { //
// This may mean that we need to get a new TGT even though
// we really just need to drop the PAC from an existing one.
// This could cause problems in the smart card case
// MMS 6/1/98
//
*Flags |= KERB_CRED_NO_PAC; }
//
// Check for ANSI structures.
//
if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0) { AnsiCreds = TRUE;
//
// Turn off the marshalled flag because we don't support marshalling
// with ansi.
//
AuthIdentity->Flags &= ~SEC_WINNT_AUTH_IDENTITY_MARSHALLED; } else if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) == 0) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Check to see if this is a null session request.
//
if ((AuthIdentity->UserLength == 0) && (AuthIdentity->DomainLength == 0) && (AuthIdentity->PasswordLength == 0) ) { if ((AuthIdentity->User != NULL) && (AuthIdentity->Domain != NULL) && (AuthIdentity->Password != NULL) ) { //
// Return NULL credentials in this case.
//
*Flags |= KERB_CRED_NULL_SESSION; Status = STATUS_SUCCESS; goto Cleanup; }
if ((AuthIdentity->User == NULL) && (AuthIdentity->Domain == NULL) && (AuthIdentity->Password == NULL) && (*Flags == 0)) { //
// Use default credentials
//
Status = STATUS_SUCCESS; D_DebugLog((DEB_TRACE_CRED, "Using default credentials\n")); goto Cleanup; } }
//
// If the identity is marshalled, copy it all at once.
//
if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_MARSHALLED) != 0) { ULONG_PTR EndOfCreds; Marshalled = TRUE;
//
// Check for validity of the sizes.
//
if( // may be a marshalled username cred > UNLEN
(AuthIdentity->UserLength > 0xFFFC) || // MAX_USHORT - NULL
(AuthIdentity->DomainLength > DNS_MAX_NAME_LENGTH) || (AuthIdentity->PasswordLength > PWLEN)) { D_DebugLog((DEB_ERROR, "Either UserLength, DomainLength pr PasswordLength in supplied credentials has invalid length. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// The callers can set the length of field to n chars, but they
// will really occupy n+1 chars (null-terminator).
//
CredentialSize = CredSize + ( AuthIdentity->UserLength + AuthIdentity->DomainLength + AuthIdentity->PasswordLength + (((AuthIdentity->User != NULL) ? 1 : 0) + ((AuthIdentity->Domain != NULL) ? 1 : 0) + ((AuthIdentity->Password != NULL) ? 1 : 0)) ) * (ULONG) sizeof(WCHAR);
EndOfCreds = (ULONG_PTR) AuthorizationData + CredentialSize;
//
// Verify that all the offsets are valid and no overflow will happen
//
ULONG_PTR TmpUser = (ULONG_PTR) AuthIdentity->User;
if ((TmpUser != NULL) && ( (TmpUser < (ULONG_PTR) AuthorizationData) || (TmpUser > EndOfCreds) || ((TmpUser + (AuthIdentity->UserLength) * sizeof(WCHAR)) > EndOfCreds ) || ((TmpUser + (AuthIdentity->UserLength * sizeof(WCHAR))) < TmpUser))) { D_DebugLog((DEB_ERROR, "Username in supplied credentials has invalid pointer or length. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
ULONG_PTR TmpDomain = (ULONG_PTR) AuthIdentity->Domain;
if ((TmpDomain != NULL) && ( (TmpDomain < (ULONG_PTR) AuthorizationData) || (TmpDomain > EndOfCreds) || ((TmpDomain + (AuthIdentity->DomainLength) * sizeof(WCHAR)) > EndOfCreds ) || ((TmpDomain + (AuthIdentity->DomainLength * sizeof(WCHAR))) < TmpDomain))) { D_DebugLog((DEB_ERROR, "Domainname in supplied credentials has invalid pointer or length. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
ULONG_PTR TmpPassword = (ULONG_PTR) AuthIdentity->Password;
if ((TmpPassword != NULL) && ( (TmpPassword < (ULONG_PTR) AuthorizationData) || (TmpPassword > EndOfCreds) || ((TmpPassword + (AuthIdentity->PasswordLength) * sizeof(WCHAR)) > EndOfCreds ) || ((TmpPassword + (AuthIdentity->PasswordLength * sizeof(WCHAR))) < TmpPassword))) { D_DebugLog((DEB_ERROR, "Password in supplied credentials has invalid pointer or length. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Allocate a chunk of memory for the credentials
//
SafeAllocaAllocate(Credentials, CredentialSize - Offset);
if (Credentials == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
RtlCopyMemory( Credentials, AuthIdentity, sizeof(SEC_WINNT_AUTH_IDENTITY_W) );
//
// Copy the credentials from the client
//
Status = LsaFunctions->CopyFromClientBuffer( NULL, CredentialSize - (Offset + sizeof(SEC_WINNT_AUTH_IDENTITY_W)), (PUCHAR) Credentials + sizeof(SEC_WINNT_AUTH_IDENTITY_W), (PUCHAR) AuthorizationData + Offset + sizeof(SEC_WINNT_AUTH_IDENTITY_W) ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to copy whole auth identity: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Now convert all the offsets to pointers.
//
if (Credentials->User != NULL) { Credentials->User = (LPWSTR) RtlOffsetToPointer( Credentials->User, (PUCHAR) Credentials - (PUCHAR) AuthorizationData - Offset ); UserString.Buffer = Credentials->User; UserString.Length = UserString.MaximumLength = (USHORT) Credentials->UserLength * sizeof(WCHAR); }
if (Credentials->Domain != NULL) { Credentials->Domain = (LPWSTR) RtlOffsetToPointer( Credentials->Domain, (PUCHAR) Credentials - (PUCHAR) AuthorizationData - Offset ); DomainString.Buffer = Credentials->Domain; DomainString.Length = DomainString.MaximumLength = (USHORT) Credentials->DomainLength * sizeof(WCHAR); }
if (Credentials->Password != NULL) { Credentials->Password = (LPWSTR) RtlOffsetToPointer( Credentials->Password, (PUCHAR) Credentials - (PUCHAR) AuthorizationData - Offset ); PasswordString.Buffer = Credentials->Password; PasswordString.Length = PasswordString.MaximumLength = (USHORT) Credentials->PasswordLength * sizeof(WCHAR);
}
} else { //
// Here we need to copy the pointer individually
//
if (AuthIdentity->User != NULL) { Status = KerbCopyClientString( AuthIdentity->User, AuthIdentity->UserLength, AnsiCreds, &UserString, AuthIdentity->UserLength // may be marshalled username > UNLEN
);
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to copy client user name. %ws, line %d\n", THIS_FILE, __LINE__)); goto Cleanup; } }
if (AuthIdentity->Domain != NULL) { Status = KerbCopyClientString( AuthIdentity->Domain, AuthIdentity->DomainLength, AnsiCreds, &DomainString, DNS_MAX_NAME_LENGTH ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to copy client Domain name. %ws, line %d\n", THIS_FILE, __LINE__)); goto Cleanup; } }
if (AuthIdentity->Password != NULL) { Status = KerbCopyClientString( AuthIdentity->Password, AuthIdentity->PasswordLength, AnsiCreds, &PasswordString, PWLEN );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to copy client Password name. %ws, line %d\n", THIS_FILE, __LINE__)); goto Cleanup; } }
Credentials = AuthIdentity; }
} else { Credentials = &LocalIdentity; RtlZeroMemory( Credentials, sizeof(SEC_WINNT_AUTH_IDENTITY_W) );
}
//
// Now build the supplied credentials.
//
DsysAssert( !LogonSessionsLocked ); KerbReadLockLogonSessions(LogonSession); LogonSessionsLocked = TRUE;
//
// Compute the size of the new credentials
//
//
// If a field is not present, use the field from the logon session
//
if (Credentials->User == NULL) { UserString = LogonSession->PrimaryCredentials.UserName; }
D_DebugLog((DEB_TRACE_CRED, "Using user %wZ\n", &UserString));
if (Credentials->Domain == NULL) { ULONG Index; BOOLEAN Upn = FALSE;
//
// if it's a UPN and domain was NULL, supply an empty domain
// rather than filling in the default.
//
for( Index = 0 ; Index < (UserString.Length/sizeof(WCHAR)) ; Index++ ) { if( UserString.Buffer[ Index ] == L'@' ) { Upn = TRUE; break; } }
if( !Upn ) { DomainString = LogonSession->PrimaryCredentials.DomainName; } else { RtlInitUnicodeString( &DomainString, L"" ); } } else { if ((DomainString.Length > sizeof(WCHAR)) && (DomainString.Buffer[-1 + DomainString.Length / sizeof(WCHAR)] == L'.') ) { DomainString.Length -= sizeof(WCHAR); } } D_DebugLog((DEB_TRACE_CRED, "Using domain %wZ\n", &DomainString));
//
// Special case when no password is supplied, so its copied from
// the logon session.
//
if (Credentials->Password == NULL) { //
// The password stored in the logon session is not a string
// so don't copy it here.
//
PasswordString.Buffer = NULL; PasswordString.Length = 0;
//
// Special case hack for some apps using NTLM hack of machinename$.
// This allows us to use default creds after they've been changed, and
// is a one-off piece of functionality for .Net - make a better sol'n
// with credential reworking in Longhorn.
//
if ( RtlEqualLuid( &LogonSession->LogonId, &SystemLogonId ) && RtlEqualUnicodeString( &KerbGlobalMachineServiceName, &UserString, TRUE )) { D_DebugLog((DEB_TRACE_CRED, "Getting cred handle for LS$ special case\n")); *Flags |= KERB_CRED_LS_DEFAULT; } }
//
// Check if the user name holds a cert context thumbprint
//
Status = KerbCheckUserNameForCert( &LogonSession->LogonId, FALSE, &UserString, &CertContext );
if (NT_SUCCESS(Status)) { if (NULL != CertContext) { fSuppliedCertCred = TRUE; } } else { goto Cleanup; }
if (fSuppliedCertCred) { //
// Generate the PK credentials for a smart card cert
//
Status = KerbAddCertCredToPrimaryCredential( LogonSession, PrincipalName, CertContext, &PasswordString, CONTEXT_INITIALIZED_WITH_ACH, SuppliedCreds ); if (NT_SUCCESS(Status)) { goto Cleanup; } } else { // setup the primary creds structure
Status = KerbInitPrimaryCreds( LogonSession, &UserString, &DomainString, PrincipalName, &PasswordString, FALSE, NULL, SuppliedCreds); if (!NT_SUCCESS(Status)) { goto Cleanup; }
}
Cleanup: if (NULL != CertContext) { CertFreeCertificateContext(CertContext); }
if (LogonSessionsLocked) { KerbUnlockLogonSessions(LogonSession); }
//
// Zero the password
//
if (PasswordString.Buffer != NULL) { RtlSecureZeroMemory( PasswordString.Buffer, PasswordString.Length );
} if (AuthIdentity != NULL) { if (AnsiCreds) { if ((AuthIdentity->Password != NULL) && (PasswordString.Buffer != NULL)) { RtlSecureZeroMemory( PasswordString.Buffer, PasswordString.Length ); RtlFreeUnicodeString(&PasswordString); } if ((AuthIdentity->User != NULL) && (UserString.Buffer != NULL)) { RtlFreeUnicodeString(&UserString); } if ((AuthIdentity->Domain != NULL) && (DomainString.Buffer != NULL)) { RtlFreeUnicodeString(&DomainString); } } else if (!Marshalled) { if ((AuthIdentity->Password != NULL) && (PasswordString.Buffer != NULL)) { KerbFree(PasswordString.Buffer); } if ((AuthIdentity->User != NULL) && (UserString.Buffer != NULL)) { KerbFree(UserString.Buffer); } if ((AuthIdentity->Domain != NULL) && (DomainString.Buffer != NULL)) { KerbFree(DomainString.Buffer); }
} }
if ((Credentials != NULL) && (Credentials != AuthIdentity) && (Credentials != &LocalIdentity)) { SafeAllocaFree(Credentials); }
SafeAllocaFree(IdentityEx);
if ( TokenHandle != NULL ) { CloseHandle( TokenHandle ); }
return(Status); }
//+-------------------------------------------------------------------------
//
// Function: SpAcceptCredentials
//
// Synopsis: This routine is called after another package has logged
// a user on. The other package provides a user name and
// password and the Kerberos package will create a logon
// session for this user.
//
// Effects: Creates a logon session
//
// Arguments: LogonType - Type of logon, such as network or interactive
// Accountname - Name of the account that logged on
// PrimaryCredentials - Primary Credentials for the account,
// containing a domain name, password, SID, etc.
// SupplementalCredentials - Kerberos-Specific blob of
// supplemental Credentials.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI SpAcceptCredentials( IN SECURITY_LOGON_TYPE LogonType, IN PUNICODE_STRING AccountName, IN PSECPKG_PRIMARY_CRED PrimaryCredentials, IN PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_LOGON_SESSION LogonSession = NULL; KERBERR KerbErr = KDC_ERR_NONE; PUNICODE_STRING RealmName; PUNICODE_STRING UserName; UNICODE_STRING TempRealm = {0}; UNICODE_STRING TempUser = {0}; PKERB_MIT_REALM MitRealm = NULL; BOOLEAN UsedAlternateName = FALSE; LUID SystemLogonId = SYSTEM_LUID; LUID NetworkServiceLogonId = NETWORKSERVICE_LUID;
D_DebugLog((DEB_TRACE_API, "SpAcceptCredentials called\n"));
if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; goto Cleanup; }
D_DebugLog((DEB_TRACE_CRED, "SpAcceptCredentials accepting credentials for %#x:%#x, flag %#x, " "logon type %#x, %wZ\\%wZ or %wZ at %wZ\n", PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart, PrimaryCredentials->Flags, LogonType, &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, &PrimaryCredentials->Upn, &PrimaryCredentials->DnsDomainName));
LogonSession = KerbReferenceLogonSession( &PrimaryCredentials->LogonId, FALSE // don't unlink
);
//
// If this is an update, locate the credentials & update the password
//
if ((PrimaryCredentials->Flags & PRIMARY_CRED_UPDATE) != 0) { KERB_ACCOUNT_TYPE AccountType; LUID SystemLuid = SYSTEM_LUID;
if (LogonSession == NULL) { goto Cleanup; }
if(RtlEqualLuid(&PrimaryCredentials->LogonId, &SystemLuid)) { AccountType = MachineAccount; } else { AccountType = UserAccount; }
KerbWriteLockLogonSessions(LogonSession); Status = KerbChangeCredentialsPassword( &LogonSession->PrimaryCredentials, &PrimaryCredentials->Password, NULL, // no etype info
AccountType, PrimaryCredentials->Flags );
if(NT_SUCCESS(Status)) { if( AccountType == MachineAccount ) { LogonSession->LogonSessionFlags &= ~(KERB_LOGON_LOCAL_ONLY | KERB_LOGON_NO_PASSWORD); } }
KerbUnlockLogonSessions(LogonSession);
goto Cleanup;
}
//
// This is not an update. If we got a Logon session back from the
// reference call, bail out now. This is an extra call because we
// are doing an MIT logon
//
if ( LogonSession ) { if (RtlEqualLuid(&PrimaryCredentials->LogonId, &SystemLogonId)) { D_DebugLog(( DEB_ERROR, "Somebody created a logon session for machine account\n")); }
D_DebugLog(( DEB_TRACE_CRED, "Skipping AcceptCred for %wZ\\%wZ (%x:%x)\n", &PrimaryCredentials->DomainName, &PrimaryCredentials->DownlevelName, PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart ));
goto Cleanup;
}
//
// Check to see if the domain is an alias for another realm.
//
if (RtlEqualLuid(&PrimaryCredentials->LogonId, &SystemLogonId) || RtlEqualLuid(&PrimaryCredentials->LogonId, &NetworkServiceLogonId)) { KerbGlobalReadLock(); if (KerbLookupMitRealm( &KerbGlobalDnsDomainName, &MitRealm, &UsedAlternateName)) {
KerbErr = KerbConvertKdcNameToString( &TempUser, KerbGlobalMitMachineServiceName, NULL ); KerbGlobalReleaseLock();
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } UserName = &TempUser;
RealmName = &MitRealm->RealmName; } else { KerbGlobalReleaseLock(); UserName = &PrimaryCredentials->DownlevelName; Status = KerbGetOurDomainName( &TempRealm ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (TempRealm.Length != 0) { RealmName = &TempRealm; } else { RealmName = &PrimaryCredentials->DomainName; } } } else { if (PrimaryCredentials->Upn.Length != 0) { // UPNs can't have a realm in credential.
RealmName = &TempRealm; UserName = &PrimaryCredentials->Upn; } else { RealmName = &PrimaryCredentials->DomainName; UserName = &PrimaryCredentials->DownlevelName; } }
Status = KerbCreateLogonSession( &PrimaryCredentials->LogonId, UserName, RealmName, &PrimaryCredentials->Password, &PrimaryCredentials->OldPassword, PrimaryCredentials->Flags, LogonType == NewCredentials ? KERB_LOGON_NEW_CREDENTIALS : 0, FALSE, &LogonSession );
if (!NT_SUCCESS(Status)) { //
// If we know about the logon session, that is o.k. because we
// probably handled the logon.
//
if (Status == STATUS_OBJECT_NAME_EXISTS) { Status = STATUS_SUCCESS; if (RtlEqualLuid(&PrimaryCredentials->LogonId,&SystemLogonId)) { D_DebugLog(( DEB_ERROR, "Somebody called AcquireCredentialsHandle before AcceptCredentials completed.\n")); } } goto Cleanup; }
Cleanup:
if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); } KerbFreeString(&TempRealm); KerbFreeString(&TempUser);
D_DebugLog((DEB_TRACE_API, "SpAcceptCredentials returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status)); }
//+-------------------------------------------------------------------------
//
// Function: SpAcquireCredentialsHandle
//
// Synopsis: Contains Kerberos Code for AcquireCredentialsHandle which
// creates a Credential associated with a logon session.
//
// Effects: Creates a KERB_CREDENTIAL
//
// Arguments: PrincipalName - Name of logon session for which to create credential
// CredentialUseFlags - Flags indicating whether the Credentials
// is for inbound or outbound use.
// LogonId - The logon ID of logon session for which to create
// a credential.
// AuthorizationData - Unused blob of Kerberos-specific data
// GetKeyFunction - Unused function to retrieve a session key
// GetKeyArgument - Argument for GetKeyFunction
// CredentialHandle - Receives handle to new credential
// ExpirationTime - Receives expiration time for credential
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI SpAcquireCredentialsHandle( IN OPTIONAL PUNICODE_STRING PrincipalName, IN ULONG CredentialUseFlags, IN OPTIONAL PLUID LogonId, IN PVOID AuthorizationData, IN PVOID GetKeyFunction, IN PVOID GetKeyArgument, OUT PLSA_SEC_HANDLE CredentialHandle, OUT PTimeStamp ExpirationTime ) { NTSTATUS Status = STATUS_SUCCESS; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonIdToUse = NULL; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDENTIAL Credential = NULL; PKERB_PRIMARY_CREDENTIAL SuppliedCreds = NULL; UNICODE_STRING CapturedPrincipalName = {0}; ULONG CredentialFlags = 0; LUID SystemLogonId = SYSTEM_LUID; LUID AnonymousLogonId = ANONYMOUS_LOGON_LUID; LUID LocalServiceLogonId = LOCALSERVICE_LUID; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; BOOLEAN Impersonating = FALSE; HANDLE hProcess = NULL;
if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; ClientInfo.ProcessID = 0; goto Cleanup; }
//
// Kerberos does not support acquiring Credentials handle by name
// so first locate the logon session to use.
//
//
// First get information about the caller.
//
Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR, "Failed to get client information: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } else if ((ClientInfo.ClientFlags & SECPKG_CLIENT_THREAD_TERMINATED) != 0) { Status = STATUS_ACCESS_DENIED; goto Cleanup; }
//
// If the caller supplied a logon ID they must have the TCB privilege
//
if (ARGUMENT_PRESENT(LogonId) && ((LogonId->LowPart != 0) || (LogonId->HighPart != 0))) { if (!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } LogonIdToUse = LogonId; } else { //
// Use the callers logon id.
//
LogonIdToUse = &ClientInfo.LogonId;
}
ImpersonationLevel = ClientInfo.ImpersonationLevel; Impersonating = ClientInfo.Impersonating; hProcess = (HANDLE) LongToHandle(ClientInfo.ProcessID);
D_DebugLog((DEB_TRACE_API, "SpAcquireCredentialsHandle for pid 0x%x, luid (%x:%x) called\n", ClientInfo.ProcessID, ((LogonIdToUse==NULL) ? 0xffffffff : LogonIdToUse->HighPart), ((LogonIdToUse==NULL) ? 0xffffffff : LogonIdToUse->LowPart)));
//
// Now try to reference the logon session with this logon id.
//
LogonSession = KerbReferenceLogonSession( LogonIdToUse, FALSE // don't unlink
);
if (LogonSession == NULL) {
//
// Enforce that the caller has > Impersonation level token.
// This is because we don't have an S4U Logon session, which is
// the only way we can try for S4UProxy w/o having TCB.
//
if (ClientInfo.ImpersonationLevel <= SecurityIdentification) { DebugLog((DEB_ERROR, "Trying to acquire credentials with an token no better than SecurityIdentification\n")); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; }
//
// If we're getting called from anonymous, local service, or deferred
// local system logon, fail here. Additionally, if we're missing a given
// logon session, && we're not impersonating, then there's something
// wrong as processes need to have a logon session that we should have
// been informed of.
//
if ((( RtlEqualLuid(LogonIdToUse,&SystemLogonId) ) || ( RtlEqualLuid(LogonIdToUse, &AnonymousLogonId) ) || ( RtlEqualLuid(LogonIdToUse, &LocalServiceLogonId) ) || !Impersonating ) && AuthorizationData == NULL)
{ D_DebugLog((DEB_TRACE_CRED, "SpAcquireCredentialsHandle called for unknown logon session %#x:%#x, Impersonating ? %s, AuthorizationData %p\n", LogonIdToUse->HighPart, LogonIdToUse->LowPart, Impersonating ? "true" : "false", AuthorizationData)); Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// This is a logon session that we don't know about - rather than creating
// a dummy logon session for "every ASC", we let the lsa keep track,
// then we create a new one, if needed.
//
Status = KerbCreateDummyLogonSession( LogonIdToUse, &LogonSession, ImpersonationLevel, Impersonating, hProcess );
if (!NT_SUCCESS( Status )) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } } else if (ClientInfo.ImpersonationLevel <= SecurityIdentification) { D_DebugLog((DEB_TRACE_CRED, "Got identify token on ls %p\n", LogonSession));
//
// If we have supplied authorization data, fail the request in the "old"
// way - e.g. we only allow logon sessions originating from S4USelf to
// be used with an identify level token. This is the way things behaved
// in previous releases.
//
KerbReadLockLogonSessions(LogonSession); if ( AuthorizationData != NULL || (( LogonSession->LogonSessionFlags & KERB_LOGON_S4U_SESSION ) == 0)) { DebugLog((DEB_ERROR, "ACH called w identify + !s4u or w pvauth\n")); KerbUnlockLogonSessions(LogonSession); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; } KerbUnlockLogonSessions(LogonSession); }
#if DBG
KerbReadLockLogonSessions(LogonSession); D_DebugLog((DEB_TRACE_CTXT, "SpAcquireCredHandle: Acquiring creds for %wZ\\%wZ\n", &LogonSession->PrimaryCredentials.DomainName, &LogonSession->PrimaryCredentials.UserName )); KerbUnlockLogonSessions(LogonSession); #endif
//
// Check for supplied Credentials
//
if (ARGUMENT_PRESENT(AuthorizationData)) { Status = KerbCaptureSuppliedCreds( LogonSession, AuthorizationData, PrincipalName, &SuppliedCreds, &CredentialFlags ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "Failed to capture auth data: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__)); goto Cleanup; }
if ((SuppliedCreds == NULL) && RtlEqualLuid(LogonIdToUse, &AnonymousLogonId)) { D_DebugLog((DEB_ERROR, "SpAcquireCredentialsHandle can not have anonymous with no explicit creds: %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } }
//
// if there was a supplied principal name, put it into the credential
//
if (ARGUMENT_PRESENT(PrincipalName) && (PrincipalName->Length != 0)) { Status = KerbDuplicateString( &CapturedPrincipalName, PrincipalName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } }
//
// We found the logon session. Good. Now create a new Credentials.
//
Status = KerbCreateCredential( LogonIdToUse, LogonSession, CredentialUseFlags, &SuppliedCreds, CredentialFlags, &CapturedPrincipalName, &Credential, ExpirationTime ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_WARN,"Failed to create credential: 0x%x\n",Status)); goto Cleanup; }
CapturedPrincipalName.Buffer = NULL;
//
// If the client is a restricted token, observe that here
// Note: This has been punted to Blackcomb
#ifdef RESTRICTED_TOKEN
if (ClientInfo.Restricted) { Credential->CredentialFlags |= KERB_CRED_RESTRICTED; D_DebugLog((DEB_TRACE_API,"Adding token restrictions\n"));
//
// We don't let restricted processes accept connections
//
if ((CredentialUseFlags & SECPKG_CRED_INBOUND) != 0) { DebugLog((DEB_ERROR,"Restricted token trying to acquire inbound credentials - denied\n")); Status = STATUS_ACCESS_DENIED; goto Cleanup; }
Status = KerbAddRestrictionsToCredential( LogonSession, Credential );
if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to add restrictions to credential: 0x%x\n",Status)); goto Cleanup; } }
#endif
*CredentialHandle = KerbGetCredentialHandle(Credential);
KerbUtcTimeToLocalTime( ExpirationTime, ExpirationTime );
D_DebugLog((DEB_TRACE_API, "SpAcquireCredentialsHandle returning success, handle = 0x%x\n",*CredentialHandle));
Cleanup: if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); } if (Credential != NULL) { KerbDereferenceCredential(Credential); } if (SuppliedCreds != NULL) { KerbFreePrimaryCredentials( SuppliedCreds, TRUE ); }
KerbFreeString(&CapturedPrincipalName);
D_DebugLog((DEB_TRACE_API, "SpAcquireCredentialsHandle for pid 0x%x, luid (%x:%x) returned 0x%x\n", ClientInfo.ProcessID, ((LogonIdToUse==NULL) ? 0xffffffff : LogonIdToUse->HighPart), ((LogonIdToUse == NULL) ? 0xffffffff : LogonIdToUse->LowPart), KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status)); }
//+-------------------------------------------------------------------------
//
// Function: SpFreeCredentialsHandle
//
// Synopsis: Frees a credential created by AcquireCredentialsHandle.
//
// Effects: Unlinks the credential from the global list and the list
// for this client.
//
// Arguments: CredentialHandle - Handle to the credential to free
//
// Requires:
//
// Returns: STATUS_SUCCESS on success,
// SEC_E_INVALID_HANDLE if the handle is not valid
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI SpFreeCredentialsHandle( IN LSA_SEC_HANDLE CredentialHandle ) { NTSTATUS Status; PKERB_CREDENTIAL Credential;
D_DebugLog((DEB_TRACE_API,"SpFreeCredentialsHandle 0x%x called\n",CredentialHandle));
if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; goto Cleanup; }
Status = KerbReferenceCredential( CredentialHandle, 0, // no flags
TRUE, // unlink handle
&Credential );
if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"SpFreeCredentialsHandle: Failed to reference credential 0x%0x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Now dereference the credential. If nobody else is using this credential
// currently it will be freed.
//
KerbDereferenceCredential(Credential); Status = STATUS_SUCCESS;
Cleanup:
D_DebugLog((DEB_TRACE_API, "SpFreeCredentialsHandle returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status)); }
//+-------------------------------------------------------------------------
//
// Function: SpQueryCredentialsAttributes
//
// Synopsis: Returns attributes of a credential
//
// Effects: allocate memory in client address space
//
// Arguments: CredentialHandle - handle to query
// CredentialAttribute - Attribute to query:
// SECPKG_CRED_ATTR_NAMES - returns credential name
// Buffer - points to structure in client's address space
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI SpQueryCredentialsAttributes( IN LSA_SEC_HANDLE CredentialHandle, IN ULONG CredentialAttribute, IN OUT PVOID Buffer ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_CREDENTIAL Credential = NULL; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_PRIMARY_CREDENTIAL PrimaryCredentials; LUID LogonId; UNICODE_STRING FullServiceName = { 0 } ; SecPkgCredentials_NamesW Names;
#if _WIN64
SECPKG_CALL_INFO CallInfo; #endif
Names.sUserName = NULL;
if (!KerbGlobalInitialized) { Status = STATUS_INVALID_SERVER_STATE; goto Cleanup; }
D_DebugLog((DEB_TRACE_API,"SpQueryCredentialsAttributes Called\n"));
Status = KerbReferenceCredential( CredentialHandle, 0, // no flags
FALSE, // don't unlink
&Credential ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
#if _WIN64
if(!LsaFunctions->GetCallInfo( &CallInfo )) { Status = STATUS_INTERNAL_ERROR; DebugLog((DEB_ERROR, "SpQueryCredentialsAttributes, failed to get callinfo 0x%lx\n", Status)); goto Cleanup; } #endif
//
// The logon id of the credential is constant, so it is o.k.
// to use it without locking the credential
//
LogonId = Credential->LogonId;
//
// Get the associated logon session to get the name
//
LogonSession = KerbReferenceLogonSession( &LogonId, FALSE // don't unlink
); if (LogonSession == NULL) { DebugLog((DEB_ERROR,"Failed to locate logon session for Credential. %ws, line %d\n", THIS_FILE, __LINE__)); Status = SEC_E_NO_CREDENTIALS; goto Cleanup;
}
if (CredentialAttribute != SECPKG_CRED_ATTR_NAMES) { D_DebugLog((DEB_WARN, "Asked for illegal info level in QueryCredAttr: %d\n", CredentialAttribute)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
KerbReadLockLogonSessions(LogonSession);
//
// Figure out which credentials to use
//
if (Credential->SuppliedCredentials != NULL) { PrimaryCredentials = Credential->SuppliedCredentials; } else { PrimaryCredentials = &LogonSession->PrimaryCredentials; } //
// Build the full service name
//
if (!KERB_SUCCESS(KerbBuildEmailName( &PrimaryCredentials->DomainName, &PrimaryCredentials->UserName, &FullServiceName ))) { Status = STATUS_INSUFFICIENT_RESOURCES; }
KerbUnlockLogonSessions(LogonSession);
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Allocate memory in the client's address space
//
Status = LsaFunctions->AllocateClientBuffer( NULL, FullServiceName.MaximumLength, (PVOID *) &Names.sUserName ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Copy the string there
//
Status = LsaFunctions->CopyToClientBuffer( NULL, FullServiceName.MaximumLength, Names.sUserName, FullServiceName.Buffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Now copy the address of the string there
//
#if _WIN64
if( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) { Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(ULONG), Buffer, &Names ); } else {
Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(Names), Buffer, &Names ); }
#else
Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(Names), Buffer, &Names ); #endif
if (!NT_SUCCESS(Status)) { goto Cleanup; }
Cleanup:
if (Credential != NULL) { KerbDereferenceCredential(Credential); } if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); } KerbFreeString( &FullServiceName );
if (!NT_SUCCESS(Status)) { if (Names.sUserName != NULL) { (VOID) LsaFunctions->FreeClientBuffer( NULL, Names.sUserName ); } }
D_DebugLog((DEB_TRACE_API, "SpQueryCredentialsAttribute returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status)); }
NTSTATUS NTAPI SpSaveCredentials( IN LSA_SEC_HANDLE CredentialHandle, IN PSecBuffer Credentials ) { NTSTATUS Status = STATUS_NOT_SUPPORTED; D_DebugLog((DEB_TRACE_API,"SpSaveCredentials Called\n")); D_DebugLog((DEB_TRACE_API,"SpSaveCredentials returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); }
NTSTATUS NTAPI SpGetCredentials( IN LSA_SEC_HANDLE CredentialHandle, IN OUT PSecBuffer Credentials ) { NTSTATUS Status = STATUS_NOT_SUPPORTED; D_DebugLog((DEB_TRACE_API,"SpGetCredentials Called\n")); D_DebugLog((DEB_TRACE_API,"SpGetCredentials returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); }
NTSTATUS NTAPI SpDeleteCredentials( IN LSA_SEC_HANDLE CredentialHandle, IN PSecBuffer Key ) { NTSTATUS Status = STATUS_NOT_SUPPORTED; D_DebugLog((DEB_TRACE_API,"SpDeleteCredentials Called\n")); D_DebugLog((DEB_TRACE_API,"SpDeleteCredentials returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status))); return(KerbMapKerbNtStatusToNtStatus(Status)); }
|