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