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.
 
 
 
 
 
 

3121 lines
80 KiB

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: pkauth.cxx
//
// Contents: Routines for supporting public-key authentication
//
//
// History: 14-October-1997 Created MikeSw
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
//#ifndef WIN32_CHICAGO
extern "C"
{
#include <stdlib.h>
#include <cryptdll.h>
}
//#endif // WIN32_CHICAGO
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__);
#endif
KERB_OBJECT_ID KerbSignatureAlg[10];
#define KERB_SCLOGON_DOMAIN_SUFFIX L"-sclogon"
#define KERB_SCLOGON_DOMAIN_SUFFIX_SIZE (sizeof(KERB_SCLOGON_DOMAIN_SUFFIX) - sizeof(WCHAR))
#ifndef SHA1DIGESTLEN
#define SHA1DIGESTLEN 20
#endif
NTSTATUS
KerbInitializeHProvFromCert(
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds
);
//+-------------------------------------------------------------------------
//
// Function: KerbComparePublicKeyCreds
//
// Synopsis: Verfies a certificate is valid for the specified usage
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
BOOL
KerbComparePublicKeyCreds(
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds1,
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds2
)
{
return CertCompareCertificate(
X509_ASN_ENCODING,
PkCreds1->CertContext->pCertInfo,
PkCreds2->CertContext->pCertInfo
);
// more later?
//return (fRet);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCheckCertificate
//
// Synopsis: Verfies a certificate is valid for the specified usage
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCheckCertificate(
IN PCCERT_CONTEXT CertContext,
IN LPSTR Usage,
IN BOOLEAN LocalLogon // AllowRevocationCheckFailure
)
{
NTSTATUS Status = STATUS_SUCCESS;
CERT_CHAIN_PARA ChainParameters = {0};
PCCERT_CHAIN_CONTEXT ChainContext = NULL;
ChainParameters.cbSize = sizeof(CERT_CHAIN_PARA);
ChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
ChainParameters.RequestedUsage.Usage.cUsageIdentifier = 1;
ChainParameters.RequestedUsage.Usage.rgpszUsageIdentifier = &Usage;
if (!CertGetCertificateChain(
HCCE_LOCAL_MACHINE,
CertContext,
NULL, // evaluate at current time
NULL, // no additional stores
&ChainParameters,
(LocalLogon?
CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY|CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT:
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT),
NULL, // reserved
&ChainContext
))
{
DebugLog((DEB_WARN,"Failed to verify certificate chain: %0x%x\n",GetLastError()));
Status = STATUS_PKINIT_FAILURE;
}
else
{
CERT_CHAIN_POLICY_PARA ChainPolicy;
CERT_CHAIN_POLICY_STATUS PolicyStatus;
ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
ChainPolicy.cbSize = sizeof(ChainPolicy);
if (LocalLogon)
{
ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
}
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
PolicyStatus.cbSize = sizeof(PolicyStatus);
PolicyStatus.lChainIndex = -1;
PolicyStatus.lElementIndex = -1;
if (!CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_BASE,
ChainContext,
&ChainPolicy,
&PolicyStatus))
{
DebugLog((DEB_WARN,"CertVerifyCertificateChainPolicy failure: %0x%x\n", GetLastError()));
Status = STATUS_PKINIT_FAILURE;
}
if(PolicyStatus.dwError != S_OK)
{
DebugLog((DEB_WARN,"CertVerifyCertificateChainPolicy - Chain Status failure: %0x%x\n",PolicyStatus.dwError));
KerbReportPkinitError(
PolicyStatus.dwError,
CertContext
);
Status = KerbMapCertChainError(PolicyStatus.dwError, FALSE);
}
}
if (ChainContext != NULL)
{
CertFreeCertificateChain(ChainContext);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbVerifyPkAsReply
//
// Synopsis: Verifies the reply from the KDC and retrieves the
// ticket encryption key
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbVerifyPkAsReply(
IN PKERB_PA_DATA_LIST InputPaData,
IN PKERB_PRIMARY_CREDENTIAL Credentials,
IN ULONG Nonce,
OUT PKERB_ENCRYPTION_KEY EncryptionKey,
OUT PBOOLEAN Done
)
{
NTSTATUS Status = STATUS_SUCCESS;
KERBERR KerbErr = KDC_ERR_NONE;
PKERB_PA_PK_AS_REP Reply = NULL;
PCCERT_CONTEXT KdcCertContext = NULL;
PBYTE EncodedKeyPackage = NULL;
ULONG KeyPackageSize = 0;
PKERB_SIGNED_REPLY_KEY_PACKAGE KeyPackage = NULL;
PKERB_REPLY_KEY_PACKAGE ReplyKeyPackage = NULL;
PBYTE PackedKeyPack = NULL;
ULONG PackedKeyPackSize = 0;
HCRYPTKEY PrivateKey = NULL;
PKERB_ENCRYPTION_KEY TempKey = NULL;
HCRYPTPROV KdcProvider = NULL;
BOOLEAN InitializedPkCreds = FALSE;
NTSTATUS TokenStatus = STATUS_SUCCESS;
HANDLE ImpersonationToken = NULL;
*Done = TRUE;
//
// Unpack the request
//
KerbErr = KerbUnpackData(
InputPaData->value.preauth_data.value,
InputPaData->value.preauth_data.length,
KERB_PA_PK_AS_REP_PDU,
(PVOID *) &Reply
);
if (!KERB_SUCCESS(KerbErr))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
if (Reply->choice != key_package_chosen)
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Now we need to verify the signature on the message
//
//
// Make sure the csp data is available
//
if ((Credentials->PublicKeyCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
Status = KerbInitializePkCreds(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
InitializedPkCreds = TRUE;
}
else if ((Credentials->PublicKeyCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS) != 0)
{
// need to set the PIN and this function does that
Status = KerbInitializeHProvFromCert(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
//
// Decode the contents as an encrypted data buffer
//
Status = __ScHelperDecryptMessage(
&Credentials->PublicKeyCreds->Pin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
Credentials->PublicKeyCreds->CertContext,
Reply->u.key_package.value,
Reply->u.key_package.length,
EncodedKeyPackage,
&KeyPackageSize
);
if ((Status != STATUS_BUFFER_TOO_SMALL) && (Status != STATUS_SUCCESS))
{
DebugLog((DEB_ERROR,"Failed to decrypt pkcs message: %x\n",Status));
goto Cleanup;
}
SafeAllocaAllocate(EncodedKeyPackage, KeyPackageSize);
if (EncodedKeyPackage == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Status = __ScHelperDecryptMessage(
&Credentials->PublicKeyCreds->Pin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
Credentials->PublicKeyCreds->CertContext,
Reply->u.key_package.value,
Reply->u.key_package.length,
EncodedKeyPackage,
&KeyPackageSize
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to decrypt pkcs message: %x\n",Status));
goto Cleanup;
}
//
// Verify the signature
//
Status = ScHelperVerifyPkcsMessage(
Credentials->PublicKeyCreds->CspData,
NULL, // we don't care which CSP is used for the verification
EncodedKeyPackage,
KeyPackageSize,
PackedKeyPack,
&PackedKeyPackSize,
NULL // don't return certificate context
);
if ((Status != STATUS_BUFFER_TOO_SMALL) && (Status != STATUS_SUCCESS))
{
DebugLog((DEB_ERROR,"Failed to verify message: %x\n",Status));
goto Cleanup;
}
PackedKeyPack = (PBYTE) MIDL_user_allocate(PackedKeyPackSize);
if (PackedKeyPack == NULL)
{
KerbErr = KRB_ERR_GENERIC;
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Status = ScHelperVerifyPkcsMessage(
Credentials->PublicKeyCreds->CspData,
NULL, // we don't care which CSP is used for the verification
EncodedKeyPackage,
KeyPackageSize,
PackedKeyPack,
&PackedKeyPackSize,
&KdcCertContext
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to verify message: %x\n",Status));
goto Cleanup;
}
KerbErr = KerbUnpackData(
PackedKeyPack,
PackedKeyPackSize,
KERB_REPLY_KEY_PACKAGE_PDU,
(PVOID *) &ReplyKeyPackage
);
if (!KERB_SUCCESS(KerbErr))
{
D_DebugLog((DEB_ERROR,"Failed to unpack reply key package\n"));
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
if (Nonce != (ULONG) ReplyKeyPackage->nonce)
{
D_DebugLog((DEB_ERROR,"Returned nonce is not correct: 0x%x instead of 0x%x. %ws, line %d\n",
ReplyKeyPackage->nonce, Nonce, THIS_FILE, __LINE__ ));
Status = STATUS_LOGON_FAILURE;
goto Cleanup;
}
//
// Finally, copy the encryption key out and return it.
//
if (!KERB_SUCCESS(KerbDuplicateKey(
EncryptionKey,
&ReplyKeyPackage->reply_key
)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Verify the certificate
//
// If we're impersonating, revert, and save off old token. This keeps us from
// going recursive.
//
// Are we impersonating?
//
TokenStatus = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
TRUE,
&ImpersonationToken
);
if( NT_SUCCESS(TokenStatus) )
{
RevertToSelf();
}
else if (TokenStatus != STATUS_NO_TOKEN)
{
Status = TokenStatus;
goto Cleanup;
}
Status = KerbCheckCertificate(
KdcCertContext,
KERB_PKINIT_KDC_CERT_TYPE,
FALSE // don't allow revocation failures
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to verify KDC certificate: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
Cleanup:
//
// re-impersonate
//
if( ImpersonationToken != NULL ) {
//
// put the thread token back if we were impersonating.
//
SetThreadToken( NULL, ImpersonationToken );
NtClose( ImpersonationToken );
}
//
// If we initialized these, reset them
//
if (InitializedPkCreds)
{
KerbReleasePkCreds(
NULL,
Credentials->PublicKeyCreds,
FALSE
);
}
if (Reply != NULL)
{
KerbFreeData(
KERB_PA_PK_AS_REP_PDU,
Reply
);
}
if (KdcCertContext != NULL)
{
CertFreeCertificateContext(KdcCertContext);
}
if (KeyPackage != NULL)
{
KerbFreeData(
KERB_SIGNED_REPLY_KEY_PACKAGE_PDU,
KeyPackage
);
}
if (ReplyKeyPackage != NULL)
{
KerbFreeData(
KERB_REPLY_KEY_PACKAGE_PDU,
ReplyKeyPackage
);
}
if (PackedKeyPack != NULL)
{
MIDL_user_free(PackedKeyPack);
}
if (PrivateKey != NULL)
{
CryptDestroyKey(PrivateKey);
}
if (TempKey != NULL)
{
KerbFreeData(
KERB_ENCRYPTION_KEY_PDU,
TempKey
);
}
if (KdcProvider != NULL)
{
CryptReleaseContext(
KdcProvider,
0 // no flags
);
}
SafeAllocaFree(EncodedKeyPackage);
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbFreePKCreds
//
// Synopsis: Frees the public key creds
//
// Effects:
//
// Arguments: OkForReuse - Allows us to release info which may not be valid,
// but that we can reuse to reaquire a good ScHelperHandle.
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
KerbFreePKCreds(
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds,
IN BOOLEAN OkForReuse
)
{
if ( NULL != PkCreds )
{
if ((( PkCreds->InitializationInfo & CSP_DATA_INITIALIZED ) != 0) &&
(( PkCreds->InitializationInfo &
(CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS | CONTEXT_INITIALIZED_WITH_ACH) ) == 0))
{
__ScHelperRelease(
PkCreds->CspData
);
PkCreds->InitializationInfo &= ~CSP_DATA_INITIALIZED;
}
if ( PkCreds->KerbHProv != NULL )
{
__ScHelper_CryptReleaseContext( PkCreds->KerbHProv );
PkCreds->KerbHProv = NULL;
}
if ( PkCreds->CertContext != NULL )
{
CertFreeCertificateContext( PkCreds->CertContext );
PkCreds->CertContext = NULL;
}
if ( !OkForReuse )
{
RtlSecureZeroMemory(PkCreds->Pin.Buffer, PkCreds->Pin.MaximumLength);
KerbFreeString( &PkCreds->Pin );
KerbFree( PkCreds );
}
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbInitializeHProvFromCert
//
// Synopsis: Initializes the out parameter phProv by getting the key
// prov info from the cert context and acquiring a CSP context
// given this information.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbInitializeHProvFromCert(
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds
)
{
ULONG cPin;
LPWSTR pwszPin = NULL;
LPSTR pszPin = NULL;
NTSTATUS Status = STATUS_SUCCESS;
DWORD dw;
if (PkCreds->KerbHProv != NULL)
{
goto Cleanup;
}
//
// This function validates that the cert matches the key on the smartcard.
//
Status = __ScHelper_CryptAcquireCertificatePrivateKey(
PkCreds->CertContext,
&PkCreds->KerbHProv,
&dw);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,
"CryptAcquireCertificatePrivateKey failed - %x\n",
Status));
goto Cleanup;
}
//
// Convert the pin to ANSI, but only for creds acquired by ACH, as the
// credman isn't "allowed" to cache pins anymore..
//
if (( PkCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_ACH ) != 0)
{
if (0 == PkCreds->Pin.Length)
{
Status = STATUS_LOGON_FAILURE;
goto Cleanup;
}
SafeAllocaAllocate(pwszPin, PkCreds->Pin.Length + sizeof(WCHAR));
if (NULL == pwszPin)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlCopyMemory(pwszPin, PkCreds->Pin.Buffer, PkCreds->Pin.Length);
pwszPin[PkCreds->Pin.Length / sizeof(WCHAR)] = L'\0';
cPin = WideCharToMultiByte(
GetACP(),
0,
pwszPin,
-1,
NULL,
0,
NULL,
NULL);
SafeAllocaAllocate(pszPin, (cPin + 1) * sizeof(CHAR));
if (NULL == pszPin)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
cPin = WideCharToMultiByte(
GetACP(),
0,
pwszPin,
-1,
pszPin,
cPin,
NULL,
NULL);
Status = __ScHelper_CryptSetProvParam(
PkCreds->KerbHProv,
pszPin,
&dw
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "CryptSetProvParam failed %x\n", Status));
goto Cleanup;
}
}
Cleanup:
SafeAllocaFree(pwszPin);
SafeAllocaFree(pszPin);
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: KerbInitializePkCreds
//
// Synopsis: Initializes or re-initailizes the smart card data in
// the public key creds
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbInitializePkCreds(
IN PKERB_PUBLIC_KEY_CREDENTIALS PkCreds
)
{
NTSTATUS Status = STATUS_SUCCESS;
if ((PkCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
//
// Credman and explicit creds
//
if (((PkCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS) == 0) &&
((PkCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_ACH) == 0))
{
//
// This is the default logon case - we're using data just handed to us
// from LsaLogonUser().
//
Status = __ScHelperInitializeContext(
PkCreds->CspData,
PkCreds->CspDataLength
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"ScHelperInitializeContext failed- %x\n", Status));
goto Cleanup;
}
PkCreds->InitializationInfo |= CSP_DATA_INITIALIZED;
}
else
{
if (PkCreds->CertContext == NULL)
{
D_DebugLog((DEB_ERROR,"Using cred man creds but cert context is NULL.\n"));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
PkCreds->InitializationInfo |= CSP_DATA_INITIALIZED;
}
}
if (PkCreds->CertContext == NULL)
{
Status = __ScHelperGetCertFromLogonInfo(
PkCreds->CspData,
&PkCreds->Pin,
&PkCreds->CertContext
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to get cert from logon info: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
if (NT_SUCCESS(Status))
{
Status = STATUS_LOGON_FAILURE;
}
goto Cleanup;
}
}
Cleanup:
if (!NT_SUCCESS(Status))
{
if (((PkCreds->InitializationInfo & CSP_DATA_INITIALIZED) != 0) &&
((PkCreds->InitializationInfo & ( CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS | CONTEXT_INITIALIZED_WITH_ACH)) == 0))
{
__ScHelperRelease(
PkCreds->CspData
);
PkCreds->InitializationInfo &= ~CSP_DATA_INITIALIZED;
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbReleasePkCreds
//
// Synopsis: Releaes smart-card resources in the public key creds.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
KerbReleasePkCreds(
IN OPTIONAL PKERB_LOGON_SESSION LogonSession,
IN OPTIONAL PKERB_PUBLIC_KEY_CREDENTIALS PkCreds,
IN BOOLEAN OkForReuse
)
{
if (ARGUMENT_PRESENT(LogonSession))
{
KerbWriteLockLogonSessions(
LogonSession
);
PkCreds = LogonSession->PrimaryCredentials.PublicKeyCreds;
}
KerbFreePKCreds(PkCreds, OkForReuse);
if (ARGUMENT_PRESENT(LogonSession))
{
if (!OkForReuse)
{
LogonSession->PrimaryCredentials.PublicKeyCreds = NULL;
}
else
{
LogonSession->PrimaryCredentials.PublicKeyCreds->InitializationInfo |= CSP_DATA_REUSED;
}
KerbUnlockLogonSessions(
LogonSession
);
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbComputePkAuthenticatorSignature
//
// Synopsis: Computes the signature of the PK authenticator by
// marshalling the authenticator, checksumming it, then
// encrypting the checksum with the public key, more or less
//
// Effects:
//
// Arguments: AuthPackage - authenticator to sign
// Credentials - Client's credentials (containing keys)
// Signature - receives signature
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbComputePkAuthenticatorSignature(
IN PKERB_AUTH_PACKAGE AuthPackage,
IN PKERB_PRIMARY_CREDENTIAL Credentials,
OUT PKERB_SIGNATURE Signature
)
{
NTSTATUS Status = STATUS_SUCCESS;
KERBERR KerbErr = KDC_ERR_NONE;
PBYTE PackedAuthenticator = NULL;
ULONG PackedAuthenticatorSize;
BOOLEAN InitializedPkCreds = FALSE;
PUNICODE_STRING TmpPin = NULL;
#define KERB_PK_MAX_SIGNATURE_SIZE 128
BYTE PkSignature[KERB_PK_MAX_SIGNATURE_SIZE];
ULONG PkSignatureLength = KERB_PK_MAX_SIGNATURE_SIZE;
RtlZeroMemory(
Signature,
sizeof(KERB_SIGNATURE)
);
//
// First marshall the auth package
//
KerbErr = KerbPackData(
AuthPackage,
KERB_AUTH_PACKAGE_PDU,
&PackedAuthenticatorSize,
&PackedAuthenticator
);
if (!KERB_SUCCESS(KerbErr))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Make sure the csp data is available
//
if ((Credentials->PublicKeyCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
Status = KerbInitializePkCreds(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
InitializedPkCreds = TRUE;
}
else if (((Credentials->PublicKeyCreds->InitializationInfo
& (CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS | CONTEXT_INITIALIZED_WITH_ACH)) != 0))
{
// need to set the PIN and this function does that
Status = KerbInitializeHProvFromCert(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
// Initialize the PIN for ScHelperSignPkcs routines.
if (((Credentials->PublicKeyCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS) == 0) &&
(Credentials->PublicKeyCreds->Pin.Buffer != NULL))
{
TmpPin = &Credentials->PublicKeyCreds->Pin;
}
//
// Now generate the checksum
//
Status = __ScHelperSignMessage(
TmpPin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
KERB_PKINIT_SIGNATURE_ALG,
PackedAuthenticator,
PackedAuthenticatorSize,
PkSignature,
&PkSignatureLength
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to sign message with card: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Build the signature
//
Signature->signature_algorithm.algorithm = KerbSignatureAlg;
//
// Copy the temporary signature into the return structure
//
Signature->pkcs_signature.length = PkSignatureLength * 8; // because it is a bit string
Signature->pkcs_signature.value = (PBYTE) KerbAllocate( PkSignatureLength );
if (Signature->pkcs_signature.value == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlCopyMemory(
Signature->pkcs_signature.value,
PkSignature,
PkSignatureLength
);
Status = STATUS_SUCCESS;
Cleanup:
if (InitializedPkCreds)
{
KerbReleasePkCreds(
NULL,
Credentials->PublicKeyCreds,
FALSE
);
}
if (PackedAuthenticator != NULL)
{
MIDL_user_free(PackedAuthenticator);
}
return(Status);
}
NTSTATUS
KerbGetProvParamWrapper(
IN PUNICODE_STRING pPin,
IN PBYTE pbLogonInfo,
IN OPTIONAL ULONG_PTR KerbHProv,
DWORD dwParam,
BYTE*pbData,
DWORD *pdwDataLen,
DWORD dwFlags
)
{
NTSTATUS Status = STATUS_SUCCESS;
Status = __ScHelperGetProvParam(
pPin,
pbLogonInfo,
KerbHProv,
dwParam,
pbData,
pdwDataLen,
dwFlags
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failure in SC subsytem - %x\n",Status));
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: KerbGetSmartCardAlgorithms
//
// Synopsis: Gets the supported encryption types from the
// smart card provider
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbGetSmartCardAlgorithms(
IN PKERB_PRIMARY_CREDENTIAL Credentials,
OUT PKERB_CRYPT_LIST * CryptList
)
{
NTSTATUS Status = STATUS_SUCCESS;
PROV_ENUMALGS Data;
ULONG DataSize;
ULONG Flags = CRYPT_FIRST;
#define KERB_SUPPORTED_PK_CRYPT_COUNT 2
ULONG CryptTypes[KERB_SUPPORTED_PK_CRYPT_COUNT];
ULONG CryptCount = 0;
//
// Enumerate through to get the encrypt types
//
while (1)
{
DataSize = sizeof(Data);
Status = KerbGetProvParamWrapper(
&Credentials->PublicKeyCreds->Pin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
PP_ENUMALGS,
(BYTE *) &Data,
&DataSize,
Flags
);
if (Status == STATUS_NO_MORE_ENTRIES)
{
Status = STATUS_SUCCESS;
break;
}
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "GetProvPram failed: 0x%x\n", Status));
return(Status);
}
//
// Reset the flags to enumerate though
//
Flags = 0; // CRYPT_NEXT
//
// Check if it is an encryption algorithm. We only want
// to know about 3des and RC4
//
if (GET_ALG_CLASS(Data.aiAlgid) == ALG_CLASS_DATA_ENCRYPT)
{
//
// Check the type
//
if (GET_ALG_TYPE(Data.aiAlgid) == ALG_TYPE_BLOCK)
{
//
// Check for 3des
//
if (GET_ALG_SID(Data.aiAlgid) == ALG_SID_3DES)
{
//
// Add it to the list.
//
CryptTypes[CryptCount++] = KERB_ETYPE_DES_EDE3_CBC_ENV;
}
else if (GET_ALG_SID(Data.aiAlgid) == ALG_SID_RC2)
{
//
// Add it to the list.
//
CryptTypes[CryptCount++] = KERB_ETYPE_RC2_CBC_ENV;
}
}
}
if (CryptCount == KERB_SUPPORTED_PK_CRYPT_COUNT)
{
break;
}
}
//
// Now, if there are any crypt types, convert them.
//
if (CryptCount != 0)
{
KERBERR KerbErr;
KerbErr = KerbConvertArrayToCryptList(
CryptList,
CryptTypes,
CryptCount,
FALSE
);
return(KerbMapKerbError(KerbErr));
}
else
{
//
// We needed one of these, so bail now.
//
DebugLog((DEB_ERROR,"Smart card doesn't support rc2 or 3des for logon - failing out.\n"));
return(STATUS_CRYPTO_SYSTEM_INVALID);
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbBuildPkinitPreAuthData
//
// Synopsis: Builds the pre-auth data for a PK-INIT AS request
//
// Effects:
//
// Arguments: Credentials - Credentials to use for this request
// InputPaData - Any PA data returned from DC on previous
// call
// TimeSkew - Known time skew with KDC
// ServiceName - Name for which we are requesting a ticket
// RealmName - name of realm in which we are requesting a ticket
// PreAuthData - receives new PA data
// Done - if returned as TRUE, then routine need not be called
// again
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbBuildPkinitPreauthData(
IN PKERB_PRIMARY_CREDENTIAL Credentials,
IN OPTIONAL PKERB_PA_DATA_LIST InputPaData,
IN PTimeStamp TimeSkew,
IN PKERB_INTERNAL_NAME ServiceName,
IN PUNICODE_STRING RealmName,
IN ULONG Nonce,
OUT PKERB_PA_DATA_LIST * PreAuthData,
OUT PKERB_ENCRYPTION_KEY EncryptionKey,
OUT PKERB_CRYPT_LIST * CryptList,
OUT PBOOLEAN Done
)
{
NTSTATUS Status = STATUS_SUCCESS;
KERB_PA_PK_AS_REQ Request = {0};
KERB_AUTH_PACKAGE AuthPack = {0};
PKERB_PA_DATA_LIST ListElement = NULL;
ULONG PackedRequestSize = 0;
PBYTE PackedRequest = NULL;
PBYTE PackedAuthPack = NULL;
ULONG PackedAuthPackSize = 0;
PBYTE SignedAuthPack = NULL;
ULONG SignedAuthPackSize = 0;
TimeStamp TimeNow;
KERBERR KerbErr;
BOOLEAN FreePkCreds = FALSE;
CRYPT_ALGORITHM_IDENTIFIER CryptAlg = {0};
PUNICODE_STRING TmpPin = NULL;
BOOLEAN CleartextPin = FALSE;
//
// For the duration of this function, reveal the pin from
// its encrypted form.
//
if ( Credentials->PublicKeyCreds->Pin.Buffer )
{
KerbRevealPassword(&Credentials->PublicKeyCreds->Pin);
CleartextPin = TRUE;
}
//
// If there is any input, check to see if we succeeded the last time
// around
//
if (ARGUMENT_PRESENT(InputPaData))
{
Status = KerbVerifyPkAsReply(
InputPaData,
Credentials,
Nonce,
EncryptionKey,
Done
);
goto Cleanup;
}
//
// Make sure the csp data is available
//
if ((Credentials->PublicKeyCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
Status = KerbInitializePkCreds(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// In some cases, we free up the cspdata, then reacquire it later. This
// is a hack for TS cases, where the SC rpc server dies when the temp
// winlogon session goes away, thus invalidating the SChelper handle. But,
// once we've reacquired it, we should hold onto it.
//
if ((Credentials->PublicKeyCreds->InitializationInfo & CSP_DATA_REUSED) == 0)
{
FreePkCreds = TRUE;
}
}
else if (((Credentials->PublicKeyCreds->InitializationInfo
& (CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS | CONTEXT_INITIALIZED_WITH_ACH)) != 0))
{
// need to set the PIN and this function does that
Status = KerbInitializeHProvFromCert(
Credentials->PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
// Initialize the PIN for ScHelperSignPkcs routines.
if (((Credentials->PublicKeyCreds->InitializationInfo & CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS) == 0) &&
(Credentials->PublicKeyCreds->Pin.Buffer != NULL))
{
TmpPin = &Credentials->PublicKeyCreds->Pin;
}
Status = KerbGetSmartCardAlgorithms(
Credentials,
CryptList
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to get crypt list for smart card: 0x%x\n",
Status));
goto Cleanup;
}
//
// Do the new pa-pk-as-req
//
//
// Now comes the hard part - the PK authenticator
//
//
// First the KDC name
//
if (!KERB_SUCCESS(
KerbConvertKdcNameToPrincipalName(
&AuthPack.pk_authenticator.kdc_name,
ServiceName
)))
{
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Then the realm
//
if (!KERB_SUCCESS(
KerbConvertUnicodeStringToRealm(
&AuthPack.pk_authenticator.kdc_realm,
RealmName)))
{
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Now the time
//
GetSystemTimeAsFileTime((PFILETIME) &TimeNow);
#ifndef WIN32_CHICAGO
TimeNow.QuadPart += TimeSkew->QuadPart;
#else // !WIN32_CHICAGO
TimeNow += *TimeSkew;
#endif // WIN32_CHICAGO
KerbConvertLargeIntToGeneralizedTimeWrapper(
&AuthPack.pk_authenticator.client_time,
&AuthPack.pk_authenticator.cusec,
&TimeNow);
//
// And finally the nonce
//
AuthPack.pk_authenticator.nonce = Nonce;
//
// Pack up the auth pack so we can sign it
//
KerbErr = KerbPackData(
&AuthPack,
KERB_AUTH_PACKAGE_PDU,
&PackedAuthPackSize,
&PackedAuthPack
);
if (!KERB_SUCCESS(KerbErr))
{
DebugLog((DEB_ERROR,"Failed to pack auth package\n"));
Status = KerbMapKerbError(KerbErr);
goto Cleanup;
}
//
// Now sign it.
//
//
// Now generate the checksum
//
CryptAlg.pszObjId = KERB_PKINIT_SIGNATURE_OID;
Status = __ScHelperSignPkcsMessage(
TmpPin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
Credentials->PublicKeyCreds->CertContext,
&CryptAlg,
CRYPT_MESSAGE_SILENT_KEYSET_FLAG, // dwSignMessageFlags
PackedAuthPack,
PackedAuthPackSize,
SignedAuthPack,
&SignedAuthPackSize
);
if ((Status != STATUS_BUFFER_TOO_SMALL) && (Status != STATUS_SUCCESS))
{
DebugLog((DEB_ERROR,"Failed to sign message: %x\n",Status));
goto Cleanup;
}
SignedAuthPack = (PBYTE) MIDL_user_allocate(SignedAuthPackSize);
if (SignedAuthPack == NULL)
{
KerbErr = KRB_ERR_GENERIC;
goto Cleanup;
}
Status = __ScHelperSignPkcsMessage(
TmpPin,
Credentials->PublicKeyCreds->CspData,
Credentials->PublicKeyCreds->KerbHProv,
Credentials->PublicKeyCreds->CertContext,
&CryptAlg,
CRYPT_MESSAGE_SILENT_KEYSET_FLAG, // dwSignMessageFlags
PackedAuthPack,
PackedAuthPackSize,
SignedAuthPack,
&SignedAuthPackSize
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to sign pkcs message: 0x%x\n",Status));
goto Cleanup;
}
Request.signed_auth_pack.value = SignedAuthPack;
Request.signed_auth_pack.length = SignedAuthPackSize;
//
// Marshall the request
//
if (!KERB_SUCCESS(KerbPackData(
&Request,
KERB_PA_PK_AS_REQ_PDU,
&PackedRequestSize,
&PackedRequest)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
ListElement = (PKERB_PA_DATA_LIST) KerbAllocate(sizeof(KERB_PA_DATA_LIST));
if (ListElement == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
ListElement->value.preauth_data_type = KRB5_PADATA_PK_AS_REP;
ListElement->value.preauth_data.value = PackedRequest;
ListElement->value.preauth_data.length = PackedRequestSize;
PackedRequest = NULL;
ListElement->next = *PreAuthData;
*PreAuthData = ListElement;
ListElement = NULL;
Cleanup:
if (CleartextPin)
{
KerbHidePassword( &Credentials->PublicKeyCreds->Pin );
}
KerbFreeRealm(
&AuthPack.pk_authenticator.kdc_realm
);
KerbFreePrincipalName(
&AuthPack.pk_authenticator.kdc_name
);
if (ListElement != NULL)
{
KerbFree(ListElement);
}
if (PackedRequest != NULL)
{
MIDL_user_free(PackedRequest);
}
if (PackedAuthPack != NULL)
{
MIDL_user_free(PackedAuthPack);
}
if (SignedAuthPack != NULL)
{
MIDL_user_free(SignedAuthPack);
}
if ( FreePkCreds )
{
KerbReleasePkCreds(
NULL,
Credentials->PublicKeyCreds,
FALSE
);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbRetrieveDomainFromDn
//
// Synopsis: Looks for a subject DN in a smartcard, then calls DsCrackName
// (local, no wire traffic) to determine the domain name.
//
// Effects: Allocates a unicode string
//
// Arguments: IN PCERT_CONTEXT smartcard cert
// IN OUT PUNICODE_STRING CrackedName (free w/ KerbFreeString)
//
// Requires:
//
// Returns:
//
// Notes: Returns STATUS_NOT_FOUND if the name is missing
//
//
//--------------------------------------------------------------------------
BOOLEAN
KerbRetrieveDomainFromDn(
IN PCCERT_CONTEXT Cert,
IN OUT PUNICODE_STRING CrackedDomain
)
{
LPWSTR RDN = NULL;
NTSTATUS Status;
PCERT_NAME_INFO NameInfo = NULL;
DWORD Result, NameInfoLength = 0;
LONG i, LastPart = 0, FirstPart = 0xFFFFFFFF;
PWCHAR tmp;
UNICODE_STRING TmpString;
BOOLEAN fRet = FALSE;
if (!CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_UNICODE_NAME,
Cert->pCertInfo->Subject.pbData,
Cert->pCertInfo->Subject.cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&NameInfo,
&NameInfoLength
))
{
Result = GetLastError();
DebugLog((DEB_ERROR, "CryptDecodeObject failed %x\n", Result));
goto Cleanup;
}
//
// Now walk through the NameInfoStruct, looking for DC=
//
NameInfoLength = 0;
for ( i = 0; i < (LONG) NameInfo->cRDN ; i ++)
{
if (strcmp(NameInfo->rgRDN[i].rgRDNAttr->pszObjId,szOID_DOMAIN_COMPONENT))
{
continue;
}
if (FirstPart == 0xFFFFFFFF)
{
FirstPart = i;
}
NameInfoLength += NameInfo->rgRDN[i].rgRDNAttr->Value.cbData + sizeof(WCHAR);
LastPart = i;
}
if (FirstPart == 0xFFFFFFFF)
{
DebugLog((DEB_ERROR, "No DC component in RDN\n"));
KerbReportMissingRDN();
DsysAssert(FALSE);
goto Cleanup;
}
SafeAllocaAllocate(RDN, NameInfoLength);
if ( RDN == NULL )
{
goto Cleanup;
}
//
// The names are in reverse order, e.g. DC=COM, DC=MICROSOFT, DC=NTDEV
//
tmp = (PWCHAR) RDN;
for ( i = LastPart ; i >= 0 ; i-- )
{
RtlCopyMemory(
tmp,
NameInfo->rgRDN[i].rgRDNAttr->Value.pbData,
NameInfo->rgRDN[i].rgRDNAttr->Value.cbData
);
tmp += ( NameInfo->rgRDN[i].rgRDNAttr->Value.cbData / sizeof(WCHAR) );
if (i != FirstPart)
{
*tmp = L'.';
tmp++;
}
else
{
*tmp = L'\0';
}
}
RtlInitUnicodeString(
&TmpString,
RDN
);
Status = KerbDuplicateStringEx(
CrackedDomain,
&TmpString,
FALSE
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
fRet = TRUE;
Cleanup:
if ( NameInfo )
{
LocalFree( NameInfo );
}
SafeAllocaFree(RDN);
return (fRet);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCreateSmartCardLogonSessionFromCertContext
//
// Synopsis: Creats a logon session from the cert context and passed in
// data. Retrieves the email name from the certificate.
//
// This function is for use with LogonUser when a marshalled
// smart card cert is passed in the user name and the PIN is
// passed as the password.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCreateSmartCardLogonSessionFromCertContext(
IN PCERT_CONTEXT *ppCertContext,
IN PLUID pLogonId,
IN PUNICODE_STRING pAuthorityName,
IN PUNICODE_STRING pPin,
IN PUCHAR pCspData,
IN ULONG CspDataLength,
OUT PKERB_LOGON_SESSION *ppLogonSession,
OUT PUNICODE_STRING pAccountName
)
{
PKERB_LOGON_SESSION pLogonSession = NULL;
PKERB_PUBLIC_KEY_CREDENTIALS PkCredentials = NULL;
ULONG cbPkCreds = 0;
NTSTATUS Status = STATUS_SUCCESS;
//
// Get the client name from the cert.
// Place it in the return location
//
Status = KerbGetPrincipalNameFromCertificate(*ppCertContext, pAccountName);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Create a normal logon session. We willa add the public-key information
// later
//
Status = KerbCreateLogonSession(
pLogonId,
pAccountName,
pAuthorityName,
NULL, // no password
NULL, // no old password
0, // no flags
KERB_LOGON_SMARTCARD,
FALSE, // don't allow dup. This is a primary logon.
&pLogonSession
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Now create the public key credentials to be put in the logon
// session.
//
cbPkCreds = sizeof(KERB_PUBLIC_KEY_CREDENTIALS);
if ((NULL != pCspData) && (0 != CspDataLength))
{
cbPkCreds += CspDataLength;
}
PkCredentials = (PKERB_PUBLIC_KEY_CREDENTIALS) KerbAllocate(cbPkCreds);
if (PkCredentials == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
PkCredentials->CertContext = *ppCertContext;
*ppCertContext = NULL;
Status = KerbDuplicateString(
&PkCredentials->Pin,
pPin
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
KerbHidePassword(&PkCredentials->Pin);
//
// Copy in the CSP data for later use
//
if ((NULL != pCspData) && (0 != CspDataLength))
{
PkCredentials->CspDataLength = CspDataLength;
RtlCopyMemory(
PkCredentials->CspData,
pCspData,
CspDataLength
);
PkCredentials->InitializationInfo |= CSP_DATA_INITIALIZED;
}
else
{
PkCredentials->InitializationInfo |= CSP_DATA_INITIALIZED | CONTEXT_INITIALIZED_WITH_ACH;
}
KerbWriteLockLogonSessions(pLogonSession);
pLogonSession->PrimaryCredentials.PublicKeyCreds = PkCredentials;
PkCredentials = NULL;
KerbUnlockLogonSessions(pLogonSession);
*ppLogonSession = pLogonSession;
pLogonSession = NULL;
Cleanup:
if (*ppCertContext != NULL)
{
CertFreeCertificateContext(*ppCertContext);
}
KerbFreePKCreds(PkCredentials, FALSE);
if (pLogonSession != NULL)
{
KerbReferenceLogonSessionByPointer(pLogonSession, TRUE);
KerbDereferenceLogonSession(pLogonSession);
KerbDereferenceLogonSession(pLogonSession);
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: KerbMapCertChainError
//
// Synopsis: We don't have good winerrors for chaining //
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbMapCertChainError(ULONG ChainStatus, BOOLEAN Client)
{
NTSTATUS Status;
switch(ChainStatus)
{
case CRYPT_E_REVOKED:
Status = (Client ? STATUS_SMARTCARD_CERT_REVOKED : STATUS_KDC_CERT_REVOKED);
break;
case CERT_E_EXPIRED:
Status = (Client ? STATUS_SMARTCARD_CERT_EXPIRED : STATUS_KDC_CERT_EXPIRED);
break;
case CERT_E_UNTRUSTEDCA:
case CERT_E_UNTRUSTEDROOT:
Status = (Client ? STATUS_ISSUING_CA_UNTRUSTED : STATUS_ISSUING_CA_UNTRUSTED_KDC);
break;
case CRYPT_E_REVOCATION_OFFLINE:
Status = (Client ? STATUS_REVOCATION_OFFLINE_C : STATUS_REVOCATION_OFFLINE_KDC);
break;
// W2k or old whistler DC
case ERROR_NOT_SUPPORTED:
default:
Status = (Client ? STATUS_PKINIT_CLIENT_FAILURE : STATUS_PKINIT_FAILURE);
}
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: KerbCreateSmardCardLogonSession
//
// Synopsis: Creats a logon session from the smart card logon info. It
// creates a certificate context from the logon information,
// retrieves the email name from the certificate, and then
// uses that to create a context.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbCreateSmartCardLogonSession(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
IN SECURITY_LOGON_TYPE LogonType,
OUT PKERB_LOGON_SESSION *ReturnedLogonSession,
OUT PLUID ReturnedLogonId,
OUT PUNICODE_STRING AccountName,
OUT PUNICODE_STRING AuthorityName
)
{
PCERT_CONTEXT CertContext = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PKERB_SMART_CARD_LOGON LogonInfo = (PKERB_SMART_CARD_LOGON) ProtocolSubmitBuffer;
LUID LogonId = {0};
BOOLEAN InitializedContext = FALSE;
//
// We were passed a blob of data. First we need to update the pointers
// to be in this address space
//
RELOCATE_ONE(&LogonInfo->Pin);
LogonInfo->CspData = LogonInfo->CspData - (ULONG_PTR) ClientBufferBase + (ULONG_PTR) LogonInfo;
//
// Make sure it all fits in our address space
//
if ((LogonInfo->CspDataLength + LogonInfo->CspData) >
((PUCHAR) ProtocolSubmitBuffer + SubmitBufferSize))
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// First, initialize the crypt context
//
Status = __ScHelperInitializeContext(
LogonInfo->CspData,
LogonInfo->CspDataLength
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to initialize context from csp data: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
InitializedContext = TRUE;
//
// The first thing to do is to convert the CSP data into a certificate
// context
//
Status = __ScHelperGetCertFromLogonInfo(
LogonInfo->CspData,
&LogonInfo->Pin,
(PCCERT_CONTEXT*)&CertContext
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to get cert from logon info: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
if (NT_SUCCESS(Status))
{
Status = STATUS_LOGON_FAILURE;
}
goto Cleanup;
}
RtlInitUnicodeString(
AuthorityName,
NULL
);
//
// Now we have just about everything to create a logon session
//
Status = NtAllocateLocallyUniqueId( &LogonId );
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// For win95, if there is a logon session in our list, remove it.
// This is generated from the logon session dumped in the registry.
// But, we are about to do a new logon. Get rid of the old logon.
// If the new one does not succeed, too bad. But, that's by design.
//
#ifdef WIN32_CHICAGO
LsaApLogonTerminated(&LogonId);
#endif // WIN32_CHICAGO
Status = KerbCreateSmartCardLogonSessionFromCertContext(
&CertContext,
&LogonId,
AuthorityName,
&LogonInfo->Pin,
LogonInfo->CspData,
LogonInfo->CspDataLength,
ReturnedLogonSession,
AccountName
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
LogonInfo->CspDataLength = 0;
*ReturnedLogonId = LogonId;
Cleanup:
if (InitializedContext && LogonInfo->CspDataLength != 0)
{
__ScHelperRelease(
LogonInfo->CspData
);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbGetCertificateName
//
// Synopsis: Gets a name from a certificate name blob. The name is:
// subject@issuer
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbGetCertificateName(
OUT PUNICODE_STRING Name,
IN PCERT_INFO Certificate
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG IssuerLength;
ULONG SubjectLength;
RtlInitUnicodeString(
Name,
NULL
);
//
// First find the size of the name. The lengths include the
// null terminators.
//
SubjectLength = CertNameToStr(
X509_ASN_ENCODING,
&Certificate->Subject,
CERT_X500_NAME_STR,
NULL,
0
);
if (SubjectLength == 0)
{
DebugLog((DEB_ERROR,"Failed to convert name: %0x%x. %ws, line %d\n",GetLastError(), THIS_FILE, __LINE__));
Status = STATUS_PKINIT_FAILURE;
goto Cleanup;
}
IssuerLength = CertNameToStr(
X509_ASN_ENCODING,
&Certificate->Issuer,
CERT_X500_NAME_STR,
NULL,
0
);
if (IssuerLength == 0)
{
DebugLog((DEB_ERROR,"Failed to convert name: %0x%x. %ws, line %d\n",GetLastError(), THIS_FILE, __LINE__));
Status = STATUS_PKINIT_FAILURE;
goto Cleanup;
}
//
// Remove the null terminator from one name, but leave space for a
// ":" in the middle
//
Name->Buffer = (LPWSTR) KerbAllocate((SubjectLength + IssuerLength) * sizeof(WCHAR));
if (Name->Buffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Now get the name itself
//
SubjectLength = CertNameToStr(
X509_ASN_ENCODING,
&Certificate->Subject,
CERT_X500_NAME_STR,
Name->Buffer,
SubjectLength
);
if (SubjectLength == 0)
{
DebugLog((DEB_ERROR,"Failed to convert name: %0x%x. %ws, line %d\n",GetLastError(), THIS_FILE, __LINE__));
KerbFree(Name->Buffer);
Name->Buffer = NULL;
Status = STATUS_PKINIT_FAILURE;
goto Cleanup;
}
//
// Put an "@" in the middle so it is recognized by MSV as a UPN (just in case)
//
Name->Buffer[SubjectLength-1] = L'@';
IssuerLength = CertNameToStr(
X509_ASN_ENCODING,
&Certificate->Issuer,
CERT_X500_NAME_STR,
Name->Buffer + SubjectLength,
IssuerLength
);
if (IssuerLength == 0)
{
DebugLog((DEB_ERROR,"Failed to convert name: %0x%x. %ws, line %d\n",GetLastError(), THIS_FILE, __LINE__));
KerbFree(Name->Buffer);
Name->Buffer = NULL;
Status = STATUS_PKINIT_FAILURE;
goto Cleanup;
}
RtlInitUnicodeString(
Name,
Name->Buffer
);
Cleanup:
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbLookupSmartCardCachedLogon
//
// Synopsis: Looks up a cached smart card logon in the MSV cache
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: Free ValidationInfor with LocalFree
//
//
//--------------------------------------------------------------------------
BOOLEAN
KerbLookupSmartCardCachedLogon(
IN PCCERT_CONTEXT Certificate,
OUT PNETLOGON_VALIDATION_SAM_INFO4 * ValidationInfo,
OUT PKERB_MESSAGE_BUFFER SupplementalCreds
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING IssuerName = {0};
PMSV1_0_CACHE_LOOKUP_REQUEST CacheRequest = NULL;
PMSV1_0_CACHE_LOOKUP_RESPONSE CacheResponse = NULL;
UNICODE_STRING MsvPackageName = CONSTANT_UNICODE_STRING(TEXT(MSV1_0_PACKAGE_NAME));
NTSTATUS SubStatus = STATUS_SUCCESS;
ULONG OutputBufferSize = 0;
ULONG RequestSize = 0;
BOOLEAN Result = FALSE;
SupplementalCreds->BufferSize = 0;
SupplementalCreds->Buffer = NULL;
RequestSize = sizeof( MSV1_0_CACHE_LOOKUP_REQUEST ) +
SHA1DIGESTLEN -
sizeof( UCHAR );
SafeAllocaAllocate(CacheRequest, RequestSize);
if ( CacheRequest == NULL )
{
return( FALSE );
}
RtlZeroMemory(CacheRequest, RequestSize);
*ValidationInfo = NULL;
//
// Get the issuer & subject name from the cert. These will be used as
// user name & domain name for the lookup
//
Status = KerbGetCertificateName(
&IssuerName,
Certificate->pCertInfo
);
if (NT_SUCCESS(Status))
{
Status = KerbGetCertificateHash(
CacheRequest->CredentialSubmitBuffer,
SHA1DIGESTLEN,
Certificate
);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
CacheRequest->MessageType = MsV1_0CacheLookup;
CacheRequest->UserName = IssuerName;
CacheRequest->CredentialType = MSV1_0_CACHE_LOOKUP_CREDTYPE_RAW;
CacheRequest->CredentialInfoLength = SHA1DIGESTLEN;
//
// Leave the domain name portion blank.
//
//
// Call MSV1_0 to do the work
//
Status = LsaFunctions->CallPackage(
&MsvPackageName,
CacheRequest,
RequestSize,
(PVOID *) &CacheResponse,
&OutputBufferSize,
&SubStatus
);
if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
{
DebugLog((DEB_ERROR, "Failed to lookup cache credentials: 0x%x, 0x%x. %ws, line %d\n", Status, SubStatus, THIS_FILE, __LINE__));
goto Cleanup;
}
if (OutputBufferSize < sizeof(MSV1_0_CACHE_LOOKUP_RESPONSE))
{
DebugLog((DEB_ERROR, "Invalid response from cache lookup - return too small: %d bytes. %ws, line %d\n",
OutputBufferSize, THIS_FILE, __LINE__ ));
//
// Free it here so we don't do too much freeing in the cleanup portion.
// Don't free the internals as this is pretty bad.
//
LsaFunctions->FreeReturnBuffer(CacheResponse);
CacheResponse = NULL;
goto Cleanup;
}
if (CacheResponse->MessageType != MsV1_0CacheLookup)
{
DebugLog((DEB_ERROR, "Wrong message type from cache lookup: %d. %ws, line %d\n",
CacheResponse->MessageType, THIS_FILE, __LINE__ ));
//
// Free it here so we don't do too much freeing in the cleanup portion.
// Don't free the internals as this is pretty bad.
//
LsaFunctions->FreeReturnBuffer(CacheResponse);
CacheResponse = NULL;
goto Cleanup;
}
*ValidationInfo = (PNETLOGON_VALIDATION_SAM_INFO4) CacheResponse->ValidationInformation;
CacheResponse->ValidationInformation = NULL;
SupplementalCreds->Buffer = (PBYTE) CacheResponse->SupplementalCacheData;
SupplementalCreds->BufferSize = CacheResponse->SupplementalCacheDataLength;
CacheResponse->SupplementalCacheData = NULL;
Result = TRUE;
Cleanup:
SafeAllocaFree(CacheRequest);
if (CacheResponse != NULL)
{
//
// At this point we know it was a valid cache response, so we
// can free the validation info if it is present. NTLM uses
// MIDL_user_allocate to allocate these.
//
if (CacheResponse->ValidationInformation != NULL)
{
MIDL_user_free(CacheResponse->ValidationInformation);
}
if (CacheResponse->SupplementalCacheData != NULL)
{
MIDL_user_free(CacheResponse->SupplementalCacheData);
}
LsaFunctions->FreeReturnBuffer(CacheResponse);
}
KerbFreeString(&IssuerName);
return(Result);
}
//+-------------------------------------------------------------------------
//
// Function: KerbDoLocalSmartCardLogon
//
// Synopsis: Performs a local logon with the smart card by validating the
// card and PIN & then trying to map the name locally
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbDoLocalSmartCardLogon(
IN PKERB_LOGON_SESSION LogonSession,
OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
OUT PVOID *NewTokenInformation,
OUT PULONG ProfileBufferLength,
OUT PVOID * ProfileBuffer,
OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * CachedCredentials,
IN OUT PNETLOGON_VALIDATION_SAM_INFO4 * Validation4
)
{
NTSTATUS Status = STATUS_SUCCESS;
#ifndef WIN32_CHICAGO
PPACTYPE Pac = NULL;
PPAC_INFO_BUFFER LogonInfo = NULL;
PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL;
PNETLOGON_VALIDATION_SAM_INFO4 MsvValidationInfo = NULL;
PNETLOGON_VALIDATION_SAM_INFO3 PacValidationInfo = NULL;
PLSA_TOKEN_INFORMATION_V2 TokenInformation = NULL;
KERB_MESSAGE_BUFFER SupplementalCreds = {0};
#endif // !WIN32_CHICAGO
PKERB_INTERNAL_NAME ClientName = NULL;
PKERB_PUBLIC_KEY_CREDENTIALS PkCreds;
PBYTE DecryptedCreds = NULL;
ULONG DecryptedCredSize = 0;
NETLOGON_VALIDATION_SAM_INFO3 ValidationInfo3 = {0};
*Validation4 = NULL;
PrimaryCredentials->Flags = 0;
PkCreds = LogonSession->PrimaryCredentials.PublicKeyCreds;
//
// First, verify the card. This will verify the certificate as well
// as verify the PIN & that the ceritifcate matches the private key on
// the card.
//
if ((PkCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
Status = KerbInitializePkCreds(
PkCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
//
// Now build a PAC for the user
//
if (!KERB_SUCCESS(KerbConvertStringToKdcName(
&ClientName,
&LogonSession->PrimaryCredentials.UserName
)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
#ifndef WIN32_CHICAGO
//
// First check for a cached logon entry
//
if (KerbLookupSmartCardCachedLogon(
PkCreds->CertContext,
&MsvValidationInfo,
&SupplementalCreds))
{
MsvValidationInfo->UserFlags |= LOGON_CACHED_ACCOUNT;
PrimaryCredentials->Flags |= PRIMARY_CRED_CACHED_LOGON;
//
// Strip the domain postfix
//
if (MsvValidationInfo->LogonDomainName.Length >= KERB_SCLOGON_DOMAIN_SUFFIX_SIZE)
{
MsvValidationInfo->LogonDomainName.Length -= KERB_SCLOGON_DOMAIN_SUFFIX_SIZE;
}
if ((SupplementalCreds.Buffer != NULL) &&
(SupplementalCreds.BufferSize != 0))
{
DecryptedCredSize = SupplementalCreds.BufferSize;
DecryptedCreds = (PBYTE) MIDL_user_allocate(DecryptedCredSize);
if (DecryptedCreds == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
}
//
// NOTE: this makes use of the fact that _INFO3 and _INFO4 structures
// expand the _INFO2 structure in different ways, so we're keeping the
// common portion. The rest of _INFO3 is zeroed out in the declaration,
// so no ZeroMemory() call is necessary here
//
RtlCopyMemory(
&ValidationInfo3,
MsvValidationInfo,
sizeof( NETLOGON_VALIDATION_SAM_INFO2 )
);
ValidationInfo = &ValidationInfo3;
}
else
{
//
// Look for a name mapping
//
Status = KerbCreatePacForKerbClient(
&Pac,
ClientName,
&LogonSession->PrimaryCredentials.DomainName,
NULL
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Find the SAM validation info
//
LogonInfo = PAC_Find(
Pac,
PAC_LOGON_INFO,
NULL
);
if (LogonInfo == NULL)
{
DebugLog((DEB_ERROR, "Failed to find logon info! %ws, line %d\n", THIS_FILE, __LINE__));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Now unmarshall the validation info
//
Status = PAC_UnmarshallValidationInfo(
&PacValidationInfo,
LogonInfo->Data,
LogonInfo->cbBufferSize
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to unmarshall validation info: 0x%x. %ws, line %d\n",
Status, THIS_FILE, __LINE__));
goto Cleanup;
}
ValidationInfo = PacValidationInfo;
}
KerbRevealPassword(&PkCreds->Pin);
Status = __ScHelperVerifyCardAndCreds(
&PkCreds->Pin,
PkCreds->CertContext,
PkCreds->CspData,
SupplementalCreds.Buffer,
SupplementalCreds.BufferSize,
DecryptedCreds,
&DecryptedCredSize
);
KerbHidePassword(&PkCreds->Pin);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to verify card: 0x%x. %ws, line %d\n", Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// If we have any encrypted credentials, decode them here for return.
//
if (DecryptedCredSize != 0)
{
Status = PAC_UnmarshallCredentials(
CachedCredentials,
DecryptedCreds,
DecryptedCredSize
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
//
// Check to see if this is a non-user account. If so, don't allow the logon
//
if ((ValidationInfo->ExpansionRoom[SAMINFO_USER_ACCOUNT_CONTROL] & USER_MACHINE_ACCOUNT_MASK) != 0)
{
DebugLog((DEB_ERROR, "Logons to non-user accounts not allowed. UserAccountControl = 0x%x\n",
ValidationInfo->ExpansionRoom[SAMINFO_USER_ACCOUNT_CONTROL] ));
Status = STATUS_LOGON_TYPE_NOT_GRANTED;
goto Cleanup;
}
//
// Now we need to build a LSA_TOKEN_INFORMATION_V2 from the validation
// information
//
Status = KerbMakeTokenInformationV2(
ValidationInfo,
FALSE, // not local system
&TokenInformation
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Failed to make token informatin v2: 0x%x\n",
Status));
goto Cleanup;
}
//
// Allocate the client profile
//
Status = KerbAllocateInteractiveProfile(
(PKERB_INTERACTIVE_PROFILE *) ProfileBuffer,
ProfileBufferLength,
ValidationInfo,
LogonSession,
NULL,
NULL
);
if (!KERB_SUCCESS(Status))
{
goto Cleanup;
}
//
// Build the primary credential. We let someone else fill in the
// password.
//
PrimaryCredentials->LogonId = LogonSession->LogonId;
Status = KerbDuplicateString(
&PrimaryCredentials->DownlevelName,
&ValidationInfo->EffectiveName
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = KerbDuplicateString(
&PrimaryCredentials->DomainName,
&ValidationInfo->LogonDomainName
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
Status = KerbDuplicateSid(
&PrimaryCredentials->UserSid,
TokenInformation->User.User.Sid
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
*Validation4 = MsvValidationInfo;
MsvValidationInfo = NULL;
*NewTokenInformation = TokenInformation;
*TokenInformationType = LsaTokenInformationV2;
#endif // !WIN32_CHICAGO
Cleanup:
if (PacValidationInfo != NULL)
{
MIDL_user_free(PacValidationInfo);
}
KerbFreeKdcName(
&ClientName
);
if (MsvValidationInfo != NULL)
{
MIDL_user_free(MsvValidationInfo);
MsvValidationInfo = NULL;
}
if (SupplementalCreds.Buffer != NULL)
{
MIDL_user_free(SupplementalCreds.Buffer);
SupplementalCreds.Buffer = NULL;
}
#ifndef WIN32_CHICAGO
if (Pac != NULL)
{
MIDL_user_free(Pac);
}
if (!NT_SUCCESS(Status))
{
if (TokenInformation != NULL)
{
KerbFree( TokenInformation );
}
if (*ProfileBuffer != NULL)
{
LsaFunctions->FreeClientBuffer(NULL, *ProfileBuffer);
*ProfileBuffer = NULL;
}
KerbFreeString(
&PrimaryCredentials->DownlevelName
);
KerbFreeString(
&PrimaryCredentials->DomainName
);
if (PrimaryCredentials->UserSid != NULL)
{
KerbFree(PrimaryCredentials->UserSid);
PrimaryCredentials->UserSid = NULL;
}
}
#endif // WIN32_CHICAGO
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbCacheSmartCardLogon
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID
KerbCacheSmartCardLogon(
IN PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo,
IN OPTIONAL PUNICODE_STRING DnsDomainName,
IN OPTIONAL PUNICODE_STRING UPN,
IN PKERB_LOGON_SESSION LogonSession,
IN OPTIONAL PSECPKG_SUPPLEMENTAL_CRED_ARRAY CachedCredentials
)
{
NTSTATUS Status;
UNICODE_STRING IssuerName = {0};
UNICODE_STRING DomainName = {0};
UNICODE_STRING TempLogonDomainName = {0};
UNICODE_STRING LogonDomainName = {0};
BYTE CertificateHash[ SHA1DIGESTLEN ];
UNICODE_STRING CertificateHashString;
ULONG EncodedCredSize = 0;
PBYTE EncodedCreds = NULL;
ULONG EncryptedCredSize = 0;
PBYTE EncryptedCreds = NULL;
BOOLEAN LogonSessionLocked = FALSE;
BOOLEAN InitializedPkCreds = FALSE;
BOOLEAN CleartextPin = FALSE;
//
// Build the temporary logon domain name that indicates this is a
// smart card logon.
//
TempLogonDomainName.MaximumLength =
TempLogonDomainName.Length =
ValidationInfo->LogonDomainName.Length + KERB_SCLOGON_DOMAIN_SUFFIX_SIZE;
TempLogonDomainName.Buffer = (LPWSTR) MIDL_user_allocate(TempLogonDomainName.Length);
if (TempLogonDomainName.Buffer == NULL)
{
goto Cleanup;
}
//
// Create the new name
//
RtlCopyMemory(
TempLogonDomainName.Buffer,
ValidationInfo->LogonDomainName.Buffer,
ValidationInfo->LogonDomainName.Length
);
RtlCopyMemory(
((PUCHAR) TempLogonDomainName.Buffer) + ValidationInfo->LogonDomainName.Length,
KERB_SCLOGON_DOMAIN_SUFFIX,
KERB_SCLOGON_DOMAIN_SUFFIX_SIZE
);
LogonDomainName = ValidationInfo->LogonDomainName;
ValidationInfo->LogonDomainName = TempLogonDomainName;
//
// Get the name under which to store this.
//
DsysAssert( !LogonSessionLocked );
KerbReadLockLogonSessions(LogonSession);
LogonSessionLocked = TRUE;
Status = KerbGetCertificateName(
&IssuerName,
LogonSession->PrimaryCredentials.PublicKeyCreds->CertContext->pCertInfo
);
if ( Status == STATUS_SUCCESS )
{
Status = KerbGetCertificateHash(
CertificateHash,
SHA1DIGESTLEN,
LogonSession->PrimaryCredentials.PublicKeyCreds->CertContext
);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
CertificateHashString.Length = SHA1DIGESTLEN;
CertificateHashString.Buffer = (LPWSTR)CertificateHash;
CertificateHashString.MaximumLength = SHA1DIGESTLEN;
if (ARGUMENT_PRESENT(CachedCredentials))
{
ScHelper_RandomCredBits RandomBits;
Status = PAC_EncodeCredentialData(
CachedCredentials,
&EncodedCreds,
&EncodedCredSize
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if ((LogonSession->PrimaryCredentials.PublicKeyCreds->InitializationInfo & CSP_DATA_INITIALIZED) == 0)
{
Status = KerbInitializePkCreds(
LogonSession->PrimaryCredentials.PublicKeyCreds
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
InitializedPkCreds = TRUE;
}
Status = __ScHelperGenRandBits(
LogonSession->PrimaryCredentials.PublicKeyCreds->CspData,
&RandomBits
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to generate random bits: 0x%x\n",Status));
goto Cleanup;
}
KerbRevealPassword( &LogonSession->PrimaryCredentials.PublicKeyCreds->Pin );
CleartextPin = TRUE;
Status = __ScHelperEncryptCredentials(
&LogonSession->PrimaryCredentials.PublicKeyCreds->Pin,
LogonSession->PrimaryCredentials.PublicKeyCreds->CertContext,
&RandomBits,
LogonSession->PrimaryCredentials.PublicKeyCreds->CspData,
EncodedCreds,
EncodedCredSize,
NULL,
&EncryptedCredSize
);
if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL))
{
DebugLog((DEB_ERROR,"Failed to encrypt creds: 0x%x\n",Status));
goto Cleanup;
}
EncryptedCreds = (PBYTE) KerbAllocate(EncryptedCredSize);
if (EncryptedCreds == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Do the real encryption
//
Status = __ScHelperEncryptCredentials(
&LogonSession->PrimaryCredentials.PublicKeyCreds->Pin,
LogonSession->PrimaryCredentials.PublicKeyCreds->CertContext,
&RandomBits,
LogonSession->PrimaryCredentials.PublicKeyCreds->CspData,
EncodedCreds,
EncodedCredSize,
EncryptedCreds,
&EncryptedCredSize
);
if (Status != STATUS_SUCCESS)
{
DebugLog((DEB_ERROR,"Failed to encrypt creds: 0x%x\n",Status));
goto Cleanup;
}
}
DsysAssert( LogonSessionLocked );
KerbUnlockLogonSessions(LogonSession);
LogonSessionLocked = FALSE;
KerbCacheLogonInformation(
&IssuerName, // used as username
&DomainName, // blank - no domain
&CertificateHashString, // password is certificate hash,
DnsDomainName,
NULL, // UPN,
NULL, // not MIT realm logon, do not need to pass in LogonSession
MSV1_0_CACHE_LOGON_REQUEST_SMARTCARD_ONLY, // smartcard only
ValidationInfo,
EncryptedCreds,
EncryptedCredSize
);
Cleanup:
if (CleartextPin)
{
KerbHidePassword(&LogonSession->PrimaryCredentials.PublicKeyCreds->Pin);
}
if (InitializedPkCreds)
{
KerbFreePKCreds(
LogonSession->PrimaryCredentials.PublicKeyCreds,
FALSE
);
}
if (LogonSessionLocked)
{
KerbUnlockLogonSessions(LogonSession);
}
KerbFreeString(&IssuerName);
KerbFreeString(&TempLogonDomainName);
//
// Restore the original logon domain name
//
if (LogonDomainName.Buffer != NULL)
{
ValidationInfo->LogonDomainName = LogonDomainName;
}
if (EncodedCreds != NULL)
{
MIDL_user_free(EncodedCreds);
}
}
//+-------------------------------------------------------------------------
//
// Function: KerbInitializePkinit
//
// Synopsis: Inializes structures needed for PKINIT
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbInitializePkinit(
VOID
)
{
ULONG Index;
LPSTR StringCopy = NULL, TempString = NULL,EndPtr = NULL;
//
// Initialize the object IDs
//
Index = 0;
SafeAllocaAllocate(StringCopy, (ULONG) strlen(KERB_PKINIT_SIGNATURE_OID) + 1);
if (StringCopy == NULL)
{
return( STATUS_INSUFFICIENT_RESOURCES);
}
//
// Scan the string for every '.' separated number
//
strcpy(
StringCopy,
KERB_PKINIT_SIGNATURE_OID
);
TempString = StringCopy;
EndPtr = TempString;
while (TempString != NULL)
{
ULONG Temp;
while (*EndPtr != '\0' && *EndPtr != '.')
{
EndPtr++;
}
if (*EndPtr == '.')
{
*EndPtr = '\0';
EndPtr++;
}
else
{
EndPtr = NULL;
}
if (0 == sscanf(TempString,"%u",&Temp))
{
return STATUS_INSUFFICIENT_RESOURCES;
}
KerbSignatureAlg[Index].value = (USHORT) Temp;
KerbSignatureAlg[Index].next = &KerbSignatureAlg[Index+1];
Index++;
TempString = EndPtr;
}
DsysAssert(Index != 0);
KerbSignatureAlg[Index-1].next = NULL;
SafeAllocaFree(StringCopy);
TempString = NULL;
return(STATUS_SUCCESS);
}