Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2093 lines
62 KiB

//+-----------------------------------------------------------------------
//
// 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));
}