1092 lines
29 KiB
C
1092 lines
29 KiB
C
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Widows
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1997.
|
||
|
//
|
||
|
// File: defcreds.c
|
||
|
//
|
||
|
// Contents: Routines for acquiring default credentials.
|
||
|
//
|
||
|
// Classes:
|
||
|
//
|
||
|
// Functions:
|
||
|
//
|
||
|
// History: 12-05-97 jbanes Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <spbase.h>
|
||
|
#include <softpub.h>
|
||
|
|
||
|
void
|
||
|
GetImplementationType(
|
||
|
PCCERT_CONTEXT pCertContext,
|
||
|
PDWORD pdwImpType);
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
AssignNewClientCredential(
|
||
|
PSPContext pContext,
|
||
|
PCCERT_CONTEXT pCertContext,
|
||
|
BOOL fPromptNow)
|
||
|
{
|
||
|
PSPCredential pCred = NULL;
|
||
|
NTSTATUS Status;
|
||
|
BOOL fEventLogged;
|
||
|
LSA_SCHANNEL_SUB_CRED SubCred;
|
||
|
|
||
|
//
|
||
|
// Does this certificate have an acceptable public key type?
|
||
|
//
|
||
|
|
||
|
{
|
||
|
BOOL fFound;
|
||
|
DWORD dwKeyType;
|
||
|
DWORD i;
|
||
|
|
||
|
fFound = FALSE;
|
||
|
dwKeyType = MapOidToCertType(pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId);
|
||
|
|
||
|
for(i = 0; i < pContext->cSsl3ClientCertTypes; i++)
|
||
|
{
|
||
|
if(pContext->Ssl3ClientCertTypes[i] == dwKeyType)
|
||
|
{
|
||
|
fFound = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(!fFound)
|
||
|
{
|
||
|
// Don't use this certificate.
|
||
|
Status = SP_LOG_RESULT(PCT_INT_UNKNOWN_CREDENTIAL);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Build a credential structure for the certificate.
|
||
|
//
|
||
|
|
||
|
pCred = SPExternalAlloc(sizeof(SPCredential));
|
||
|
if(pCred == NULL)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
memset(&SubCred, 0, sizeof(SubCred));
|
||
|
|
||
|
SubCred.pCert = pCertContext;
|
||
|
|
||
|
Status = SPCreateCred(pContext->dwProtocol,
|
||
|
&SubCred,
|
||
|
pCred,
|
||
|
&fEventLogged);
|
||
|
if(Status != PCT_ERR_OK)
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Release the existing credential, if one exists.
|
||
|
//
|
||
|
|
||
|
if(pContext->RipeZombie->pClientCred)
|
||
|
{
|
||
|
SPDeleteCred(pContext->RipeZombie->pClientCred);
|
||
|
pContext->RipeZombie->pClientCred = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Assign the credential to the cache element.
|
||
|
//
|
||
|
|
||
|
pContext->RipeZombie->pClientCred = pCred;
|
||
|
pContext->pActiveClientCred = pCred;
|
||
|
|
||
|
if(fPromptNow == FALSE)
|
||
|
{
|
||
|
pContext->RipeZombie->dwFlags |= SP_CACHE_FLAG_USE_VALIDATED;
|
||
|
}
|
||
|
|
||
|
Status = PCT_ERR_OK;
|
||
|
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(pCred && Status != PCT_ERR_OK)
|
||
|
{
|
||
|
SPDeleteCred(pCred);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
QueryCredentialManagerForCert(
|
||
|
PSPContext pContext,
|
||
|
LPWSTR pszTarget)
|
||
|
{
|
||
|
PCCERT_CONTEXT pCertContext = NULL;
|
||
|
LUID LogonId;
|
||
|
PENCRYPTED_CREDENTIALW pCredential = NULL;
|
||
|
BOOL fImpersonating = FALSE;
|
||
|
CRYPT_HASH_BLOB HashBlob;
|
||
|
NTSTATUS Status;
|
||
|
HCERTSTORE hStore = 0;
|
||
|
PCERT_CREDENTIAL_INFO pCertInfo = NULL;
|
||
|
CRED_MARSHAL_TYPE CredType;
|
||
|
|
||
|
//
|
||
|
// Obtain client logon id.
|
||
|
//
|
||
|
|
||
|
Status = SslGetClientLogonId(&LogonId);
|
||
|
|
||
|
if(!NT_SUCCESS(Status))
|
||
|
{
|
||
|
SP_LOG_RESULT(Status);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
|
||
|
|
||
|
//
|
||
|
// Query the credential manager for a certificate.
|
||
|
//
|
||
|
|
||
|
Status = LsaTable->CrediRead(&LogonId,
|
||
|
CREDP_FLAGS_IN_PROCESS,
|
||
|
pszTarget,
|
||
|
CRED_TYPE_DOMAIN_CERTIFICATE,
|
||
|
0,
|
||
|
&pCredential);
|
||
|
if(!NT_SUCCESS(Status))
|
||
|
{
|
||
|
if(Status == STATUS_NOT_FOUND)
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "No certificate found in credential manager.\n"));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SP_LOG_RESULT(Status);
|
||
|
}
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Extract the certificate thumbprint and (optional) PIN.
|
||
|
//
|
||
|
|
||
|
if(!CredIsMarshaledCredentialW(pCredential->Cred.UserName))
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!CredUnmarshalCredentialW(pCredential->Cred.UserName,
|
||
|
&CredType,
|
||
|
&pCertInfo))
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if(CredType != CertCredential)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Look up the certificate in the MY certificate store.
|
||
|
//
|
||
|
|
||
|
hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
|
||
|
X509_ASN_ENCODING, 0,
|
||
|
CERT_SYSTEM_STORE_CURRENT_USER |
|
||
|
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
|
||
|
L"MY");
|
||
|
if(!hStore)
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
HashBlob.cbData = sizeof(pCertInfo->rgbHashOfCert);
|
||
|
HashBlob.pbData = pCertInfo->rgbHashOfCert;
|
||
|
|
||
|
pCertContext = CertFindCertificateInStore(hStore,
|
||
|
X509_ASN_ENCODING,
|
||
|
0,
|
||
|
CERT_FIND_HASH,
|
||
|
&HashBlob,
|
||
|
NULL);
|
||
|
if(pCertContext == NULL)
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Certificate designated by credential manager was not found in certificate store (0x%x).\n", GetLastError()));
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Attempt to add this certificate context to the current credential.
|
||
|
//
|
||
|
|
||
|
Status = AssignNewClientCredential(pContext,
|
||
|
pCertContext,
|
||
|
pCredential->Cred.Flags & CRED_FLAGS_PROMPT_NOW);
|
||
|
if(!NT_SUCCESS(Status))
|
||
|
{
|
||
|
SP_LOG_RESULT(Status);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(pCertContext)
|
||
|
{
|
||
|
CertFreeCertificateContext(pCertContext);
|
||
|
}
|
||
|
|
||
|
if(hStore)
|
||
|
{
|
||
|
CertCloseStore(hStore, 0);
|
||
|
}
|
||
|
|
||
|
if(pCredential)
|
||
|
{
|
||
|
LsaTable->FreeLsaHeap(pCredential);
|
||
|
}
|
||
|
|
||
|
if(pCertInfo)
|
||
|
{
|
||
|
CredFree(pCertInfo);
|
||
|
}
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
IsThreadLocalSystem(
|
||
|
BOOL *pfIsLocalSystem)
|
||
|
{
|
||
|
DWORD Status;
|
||
|
HANDLE hToken = 0;
|
||
|
UCHAR InfoBuffer[1024];
|
||
|
DWORD dwInfoBufferSize = sizeof(InfoBuffer);
|
||
|
PTOKEN_USER SlowBuffer = NULL;
|
||
|
PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
|
||
|
PSID psidLocalSystem = NULL;
|
||
|
PSID psidNetworkService = NULL;
|
||
|
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
||
|
|
||
|
*pfIsLocalSystem = FALSE;
|
||
|
|
||
|
//
|
||
|
// Get SID of calling thread.
|
||
|
//
|
||
|
|
||
|
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
|
||
|
{
|
||
|
Status = GetLastError();
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!GetTokenInformation(hToken, TokenUser, pTokenUser,
|
||
|
dwInfoBufferSize, &dwInfoBufferSize))
|
||
|
{
|
||
|
//
|
||
|
// if fast buffer wasn't big enough, allocate enough storage
|
||
|
// and try again.
|
||
|
//
|
||
|
|
||
|
Status = GetLastError();
|
||
|
if(Status != ERROR_INSUFFICIENT_BUFFER)
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
SlowBuffer = (PTOKEN_USER)LocalAlloc(LPTR, dwInfoBufferSize);
|
||
|
if(NULL == SlowBuffer)
|
||
|
{
|
||
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
pTokenUser = SlowBuffer;
|
||
|
if(!GetTokenInformation(hToken, TokenUser, pTokenUser,
|
||
|
dwInfoBufferSize, &dwInfoBufferSize))
|
||
|
{
|
||
|
Status = GetLastError();
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Check for local system.
|
||
|
//
|
||
|
|
||
|
if(!AllocateAndInitializeSid(&siaNtAuthority,
|
||
|
1,
|
||
|
SECURITY_LOCAL_SYSTEM_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&psidLocalSystem))
|
||
|
{
|
||
|
Status = GetLastError();
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (EqualSid(psidLocalSystem, pTokenUser->User.Sid))
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "Client is using the LOCAL SYSTEM account.\n"));
|
||
|
*pfIsLocalSystem = TRUE;
|
||
|
Status = ERROR_SUCCESS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Check for network service.
|
||
|
//
|
||
|
|
||
|
if(!AllocateAndInitializeSid(&siaNtAuthority,
|
||
|
1,
|
||
|
SECURITY_NETWORK_SERVICE_RID,
|
||
|
0, 0, 0, 0, 0, 0, 0,
|
||
|
&psidNetworkService))
|
||
|
{
|
||
|
Status = GetLastError();
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (EqualSid(psidNetworkService, pTokenUser->User.Sid))
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "Client is using the NETWORK SERVICE account.\n"));
|
||
|
*pfIsLocalSystem = TRUE;
|
||
|
Status = ERROR_SUCCESS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
Status = ERROR_SUCCESS;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(NULL != SlowBuffer)
|
||
|
{
|
||
|
LocalFree(SlowBuffer);
|
||
|
}
|
||
|
|
||
|
if(NULL != psidLocalSystem)
|
||
|
{
|
||
|
FreeSid(psidLocalSystem);
|
||
|
}
|
||
|
if(NULL != psidNetworkService)
|
||
|
{
|
||
|
FreeSid(psidNetworkService);
|
||
|
}
|
||
|
|
||
|
if(NULL != hToken)
|
||
|
{
|
||
|
CloseHandle(hToken);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
FindClientCertificate(
|
||
|
PSPContext pContext,
|
||
|
HCERTSTORE hMyStore,
|
||
|
CERT_CHAIN_FIND_BY_ISSUER_PARA *pFindByIssuerPara,
|
||
|
BOOL fSkipExpiredCerts,
|
||
|
BOOL fSoftwareCspOnly)
|
||
|
{
|
||
|
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
|
||
|
HTTPSPolicyCallbackData polHttps;
|
||
|
CERT_CHAIN_POLICY_PARA PolicyPara;
|
||
|
CERT_CHAIN_POLICY_STATUS PolicyStatus;
|
||
|
PCCERT_CONTEXT pCertContext;
|
||
|
NTSTATUS Status;
|
||
|
ULONG j;
|
||
|
|
||
|
pChainContext = NULL;
|
||
|
|
||
|
while(TRUE)
|
||
|
{
|
||
|
// Find a certificate chain.
|
||
|
pChainContext = CertFindChainInStore(hMyStore,
|
||
|
X509_ASN_ENCODING,
|
||
|
0,
|
||
|
CERT_CHAIN_FIND_BY_ISSUER,
|
||
|
pFindByIssuerPara,
|
||
|
pChainContext);
|
||
|
if(pChainContext == NULL)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Make sure that every certificate in the chain either has the
|
||
|
// client auth EKU or it has no EKUs at all.
|
||
|
{
|
||
|
PCERT_SIMPLE_CHAIN pSimpleChain;
|
||
|
PCCERT_CONTEXT pCurrentCert;
|
||
|
BOOL fIsAllowed = FALSE;
|
||
|
|
||
|
pSimpleChain = pChainContext->rgpChain[0];
|
||
|
|
||
|
for(j = 0; j < pSimpleChain->cElement; j++)
|
||
|
{
|
||
|
pCurrentCert = pSimpleChain->rgpElement[j]->pCertContext;
|
||
|
|
||
|
Status = SPCheckKeyUsage(pCurrentCert,
|
||
|
szOID_PKIX_KP_CLIENT_AUTH,
|
||
|
TRUE,
|
||
|
&fIsAllowed);
|
||
|
if(Status != SEC_E_OK || !fIsAllowed)
|
||
|
{
|
||
|
fIsAllowed = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(!fIsAllowed)
|
||
|
{
|
||
|
// skip this certificate chain.
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Set up validate chain structures.
|
||
|
ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
|
||
|
polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
|
||
|
polHttps.dwAuthType = AUTHTYPE_CLIENT;
|
||
|
polHttps.fdwChecks = 0;
|
||
|
polHttps.pwszServerName = NULL;
|
||
|
|
||
|
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
|
||
|
PolicyStatus.cbSize = sizeof(PolicyStatus);
|
||
|
|
||
|
ZeroMemory(&PolicyPara, sizeof(PolicyPara));
|
||
|
PolicyPara.cbSize = sizeof(PolicyPara);
|
||
|
PolicyPara.pvExtraPolicyPara= &polHttps;
|
||
|
|
||
|
PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG |
|
||
|
CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
|
||
|
if(!fSkipExpiredCerts)
|
||
|
{
|
||
|
PolicyPara.dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
|
||
|
}
|
||
|
|
||
|
// Validate chain
|
||
|
if(!CertVerifyCertificateChainPolicy(
|
||
|
CERT_CHAIN_POLICY_SSL,
|
||
|
pChainContext,
|
||
|
&PolicyPara,
|
||
|
&PolicyStatus))
|
||
|
{
|
||
|
DebugLog((DEB_WARN,"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", GetLastError()));
|
||
|
continue;
|
||
|
}
|
||
|
Status = MapWinTrustError(PolicyStatus.dwError, 0, 0);
|
||
|
if(Status)
|
||
|
{
|
||
|
// Certificate did not validate, move on to the next one.
|
||
|
DebugLog((DEB_WARN, "Client certificate failed validation with 0x%x\n", Status));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Get pointer to leaf certificate context.
|
||
|
if(pChainContext->cChain == 0 || pChainContext->rgpChain[0] == NULL)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if(pChainContext->rgpChain[0]->cElement == 0 ||
|
||
|
pChainContext->rgpChain[0]->rgpElement == NULL)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Is the private key stored in a software CSP?
|
||
|
//
|
||
|
|
||
|
if(fSoftwareCspOnly)
|
||
|
{
|
||
|
DWORD dwImpType;
|
||
|
|
||
|
GetImplementationType(pCertContext, &dwImpType);
|
||
|
|
||
|
if(dwImpType != CRYPT_IMPL_SOFTWARE)
|
||
|
{
|
||
|
// Skip this certificate
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Assign the certificate to the current context.
|
||
|
//
|
||
|
|
||
|
Status = AssignNewClientCredential(pContext,
|
||
|
pCertContext,
|
||
|
FALSE);
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
// Success! Our work here is done.
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No suitable client credential was found.
|
||
|
Status = SP_LOG_RESULT(SEC_E_INCOMPLETE_CREDENTIALS);
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(pChainContext)
|
||
|
{
|
||
|
CertFreeCertificateChain(pChainContext);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
AcquireDefaultClientCredential(
|
||
|
PSPContext pContext,
|
||
|
BOOL fCredManagerOnly)
|
||
|
{
|
||
|
CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
|
||
|
CERT_NAME_BLOB * prgIssuers = NULL;
|
||
|
DWORD cIssuers = 0;
|
||
|
HCERTSTORE hStore = 0;
|
||
|
NTSTATUS Status;
|
||
|
BOOL fImpersonating = FALSE;
|
||
|
BOOL fLocalSystem = FALSE;
|
||
|
ULONG i;
|
||
|
|
||
|
DebugLog((DEB_TRACE,"AcquireDefaultClientCredential\n"));
|
||
|
|
||
|
//
|
||
|
// Is the application running under local system?
|
||
|
//
|
||
|
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
Status = IsThreadLocalSystem(&fLocalSystem);
|
||
|
if(Status)
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "IsThreadLocalSystem returned error 0x%x.\n", Status));
|
||
|
}
|
||
|
|
||
|
RevertToSelf();
|
||
|
fImpersonating = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Ask the credential manager to select a certificate for us.
|
||
|
//
|
||
|
|
||
|
Status = QueryCredentialManagerForCert(
|
||
|
pContext,
|
||
|
pContext->pszTarget);
|
||
|
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "Credential manager found a certificate for us.\n"));
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(fCredManagerOnly)
|
||
|
{
|
||
|
// No suitable client credential was found.
|
||
|
Status = SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get list of trusted issuers as a list of CERT_NAME_BLOBs.
|
||
|
//
|
||
|
|
||
|
if(pContext->pbIssuerList && pContext->cbIssuerList > 2)
|
||
|
{
|
||
|
PBYTE pbIssuerList = pContext->pbIssuerList + 2;
|
||
|
DWORD cbIssuerList = pContext->cbIssuerList - 2;
|
||
|
PBYTE pbIssuer;
|
||
|
|
||
|
// Count issuers.
|
||
|
cIssuers = 0;
|
||
|
pbIssuer = pbIssuerList;
|
||
|
while(pbIssuer + 1 < pbIssuerList + cbIssuerList)
|
||
|
{
|
||
|
pbIssuer += 2 + COMBINEBYTES(pbIssuer[0], pbIssuer[1]);
|
||
|
cIssuers++;
|
||
|
}
|
||
|
|
||
|
// Allocate memory for list of blobs.
|
||
|
prgIssuers = SPExternalAlloc(cIssuers * sizeof(CERT_NAME_BLOB));
|
||
|
if(prgIssuers == NULL)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// Build the issuer blob list.
|
||
|
pbIssuer = pbIssuerList;
|
||
|
for(i = 0; i < cIssuers; i++)
|
||
|
{
|
||
|
prgIssuers[i].pbData = pbIssuer + 2;
|
||
|
prgIssuers[i].cbData = COMBINEBYTES(pbIssuer[0], pbIssuer[1]);
|
||
|
|
||
|
pbIssuer += 2 + prgIssuers[i].cbData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Enumerate the certificates in the MY store, looking for a suitable
|
||
|
// client certificate.
|
||
|
//
|
||
|
|
||
|
fImpersonating = SslImpersonateClient();
|
||
|
|
||
|
if(fLocalSystem)
|
||
|
{
|
||
|
hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
|
||
|
X509_ASN_ENCODING, 0,
|
||
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
||
|
CERT_STORE_READONLY_FLAG |
|
||
|
CERT_STORE_OPEN_EXISTING_FLAG,
|
||
|
L"MY");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hStore = CertOpenSystemStore(0, "MY");
|
||
|
}
|
||
|
|
||
|
if(!hStore)
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
Status = SEC_E_INTERNAL_ERROR;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
|
||
|
FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
|
||
|
FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
|
||
|
FindByIssuerPara.dwKeySpec = 0;
|
||
|
FindByIssuerPara.cIssuer = cIssuers;
|
||
|
FindByIssuerPara.rgIssuer = prgIssuers;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Attempt to find a suitable certificate.
|
||
|
//
|
||
|
|
||
|
Status = FindClientCertificate(pContext,
|
||
|
hStore,
|
||
|
&FindByIssuerPara,
|
||
|
TRUE, // skip expired certs
|
||
|
TRUE); // software CSPs only
|
||
|
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
// Success! Our work here is done.
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
Status = FindClientCertificate(pContext,
|
||
|
hStore,
|
||
|
&FindByIssuerPara,
|
||
|
TRUE, // skip expired certs
|
||
|
FALSE); // software CSPs only
|
||
|
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
// Success! Our work here is done.
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
Status = FindClientCertificate(pContext,
|
||
|
hStore,
|
||
|
&FindByIssuerPara,
|
||
|
FALSE, // skip expired certs
|
||
|
TRUE); // software CSPs only
|
||
|
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
// Success! Our work here is done.
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
Status = FindClientCertificate(pContext,
|
||
|
hStore,
|
||
|
&FindByIssuerPara,
|
||
|
FALSE, // skip expired certs
|
||
|
FALSE); // software CSPs only
|
||
|
|
||
|
if(NT_SUCCESS(Status))
|
||
|
{
|
||
|
// Success! Our work here is done.
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
// No suitable client credential was found.
|
||
|
Status = SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS);
|
||
|
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
|
||
|
if(hStore)
|
||
|
{
|
||
|
CertCloseStore(hStore, 0);
|
||
|
}
|
||
|
|
||
|
if(fImpersonating)
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
}
|
||
|
|
||
|
if(prgIssuers)
|
||
|
{
|
||
|
SPExternalFree(prgIssuers);
|
||
|
}
|
||
|
|
||
|
DebugLog((DEB_TRACE,"AcquireDefaultClientCredential returned 0x%x\n", Status));
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
FindDefaultMachineCred(
|
||
|
PSPCredentialGroup *ppCred,
|
||
|
DWORD dwProtocol)
|
||
|
{
|
||
|
HTTPSPolicyCallbackData polHttps;
|
||
|
CERT_CHAIN_POLICY_PARA PolicyPara;
|
||
|
CERT_CHAIN_POLICY_STATUS PolicyStatus;
|
||
|
CERT_CHAIN_PARA ChainPara;
|
||
|
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
|
||
|
PCCERT_CONTEXT pCertContext = NULL;
|
||
|
LSA_SCHANNEL_CRED SchannelCred;
|
||
|
LSA_SCHANNEL_SUB_CRED SubCred;
|
||
|
HCERTSTORE hStore = 0;
|
||
|
|
||
|
#define SERVER_USAGE_COUNT 3
|
||
|
LPSTR rgszUsages[SERVER_USAGE_COUNT] = {
|
||
|
szOID_PKIX_KP_SERVER_AUTH,
|
||
|
szOID_SERVER_GATED_CRYPTO,
|
||
|
szOID_SGC_NETSCAPE };
|
||
|
|
||
|
LPWSTR pwszMachineName = NULL;
|
||
|
DWORD cchMachineName;
|
||
|
DWORD Status;
|
||
|
DWORD i;
|
||
|
|
||
|
// Get the machine name
|
||
|
cchMachineName = 0;
|
||
|
if(!GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchMachineName))
|
||
|
{
|
||
|
if(GetLastError() != ERROR_MORE_DATA)
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to get computer name size: 0x%x\n",GetLastError()));
|
||
|
Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
pwszMachineName = SPExternalAlloc(cchMachineName * sizeof(WCHAR));
|
||
|
if(pwszMachineName == NULL)
|
||
|
{
|
||
|
Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
if(!GetComputerNameExW(ComputerNameDnsFullyQualified, pwszMachineName, &cchMachineName))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to get computer name: 0x%x\n",GetLastError()));
|
||
|
Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// Remove the trailing "." if any. This can happen in the stand-alone
|
||
|
// server case.
|
||
|
cchMachineName = lstrlenW(pwszMachineName);
|
||
|
if(cchMachineName > 0 && pwszMachineName[cchMachineName - 1] == L'.')
|
||
|
{
|
||
|
pwszMachineName[cchMachineName - 1] = L'\0';
|
||
|
}
|
||
|
|
||
|
|
||
|
DebugLog((DEB_TRACE,"Computer name: %ls\n",pwszMachineName));
|
||
|
|
||
|
|
||
|
// Open the system MY store.
|
||
|
hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
|
||
|
X509_ASN_ENCODING, 0,
|
||
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
||
|
CERT_STORE_READONLY_FLAG |
|
||
|
CERT_STORE_OPEN_EXISTING_FLAG,
|
||
|
L"MY");
|
||
|
if(hStore == NULL)
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Enumerate the certificates in the MY store, looking for a suitable
|
||
|
// server certificate. Do this twice, the first time looking for the
|
||
|
// perfect certificate, and if this fails then look again, this time
|
||
|
// being a little less picky.
|
||
|
//
|
||
|
|
||
|
for(i = 0; i < 2; i++)
|
||
|
{
|
||
|
pCertContext = NULL;
|
||
|
|
||
|
while(TRUE)
|
||
|
{
|
||
|
// Get leaf certificate in the MY store.
|
||
|
pCertContext = CertEnumCertificatesInStore(hStore, pCertContext);
|
||
|
if(pCertContext == NULL)
|
||
|
{
|
||
|
// No more certificates.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build a certificate chain from the leaf certificate.
|
||
|
//
|
||
|
|
||
|
ZeroMemory(&ChainPara, sizeof(ChainPara));
|
||
|
ChainPara.cbSize = sizeof(ChainPara);
|
||
|
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
|
||
|
ChainPara.RequestedUsage.Usage.cUsageIdentifier = SERVER_USAGE_COUNT;
|
||
|
ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
|
||
|
|
||
|
if(!CertGetCertificateChain(
|
||
|
NULL,
|
||
|
pCertContext,
|
||
|
NULL,
|
||
|
0,
|
||
|
&ChainPara,
|
||
|
CERT_CHAIN_REVOCATION_CHECK_END_CERT,
|
||
|
NULL,
|
||
|
&pChainContext))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "Error 0x%x returned by CertGetCertificateChain!\n", GetLastError()));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Set up validate chain structures.
|
||
|
ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
|
||
|
polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
|
||
|
polHttps.dwAuthType = AUTHTYPE_SERVER;
|
||
|
polHttps.fdwChecks = 0;
|
||
|
polHttps.pwszServerName = pwszMachineName;
|
||
|
|
||
|
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
|
||
|
PolicyStatus.cbSize = sizeof(PolicyStatus);
|
||
|
|
||
|
ZeroMemory(&PolicyPara, sizeof(PolicyPara));
|
||
|
PolicyPara.cbSize = sizeof(PolicyPara);
|
||
|
PolicyPara.pvExtraPolicyPara= &polHttps;
|
||
|
if(i == 0)
|
||
|
{
|
||
|
// Look for the perfect certificate.
|
||
|
PolicyPara.dwFlags = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Ignore expiration.
|
||
|
PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
|
||
|
}
|
||
|
|
||
|
// Validate chain
|
||
|
if(!CertVerifyCertificateChainPolicy(
|
||
|
CERT_CHAIN_POLICY_SSL,
|
||
|
pChainContext,
|
||
|
&PolicyPara,
|
||
|
&PolicyStatus))
|
||
|
{
|
||
|
SP_LOG_RESULT(GetLastError());
|
||
|
CertFreeCertificateChain(pChainContext);
|
||
|
continue;
|
||
|
}
|
||
|
Status = MapWinTrustError(PolicyStatus.dwError,
|
||
|
0,
|
||
|
CRED_FLAG_IGNORE_NO_REVOCATION_CHECK | CRED_FLAG_IGNORE_REVOCATION_OFFLINE);
|
||
|
if(Status)
|
||
|
{
|
||
|
// Certificate did not validate, move on to the next one.
|
||
|
DebugLog((DEB_WARN, "Machine certificate failed validation with 0x%x\n", Status));
|
||
|
CertFreeCertificateChain(pChainContext);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
CertFreeCertificateChain(pChainContext);
|
||
|
|
||
|
|
||
|
// Build an schannel credential.
|
||
|
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
|
||
|
|
||
|
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
|
||
|
SchannelCred.cSubCreds = 1;
|
||
|
SchannelCred.paSubCred = &SubCred;
|
||
|
|
||
|
ZeroMemory(&SubCred, sizeof(SubCred));
|
||
|
|
||
|
SubCred.pCert = pCertContext;
|
||
|
|
||
|
Status = SPCreateCredential(ppCred,
|
||
|
dwProtocol,
|
||
|
&SchannelCred);
|
||
|
if(Status != PCT_ERR_OK)
|
||
|
{
|
||
|
// Don't use this certificate.
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// We have a winner!
|
||
|
DebugLog((DEB_TRACE, "Machine certificate automatically acquired\n"));
|
||
|
Status = PCT_ERR_OK;
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No suitable machine credential was found.
|
||
|
Status = SP_LOG_RESULT(SEC_E_NO_CREDENTIALS);
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(Status != PCT_ERR_OK)
|
||
|
{
|
||
|
LogNoDefaultServerCredEvent();
|
||
|
}
|
||
|
|
||
|
if(pwszMachineName)
|
||
|
{
|
||
|
SPExternalFree(pwszMachineName);
|
||
|
}
|
||
|
|
||
|
if(pCertContext)
|
||
|
{
|
||
|
CertFreeCertificateContext(pCertContext);
|
||
|
}
|
||
|
|
||
|
if(hStore)
|
||
|
{
|
||
|
CertCloseStore(hStore, 0);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
GetImplementationType(
|
||
|
PCCERT_CONTEXT pCertContext,
|
||
|
PDWORD pdwImpType)
|
||
|
{
|
||
|
PCRYPT_KEY_PROV_INFO pProvInfo = NULL;
|
||
|
HCRYPTPROV hProv = 0;
|
||
|
DWORD cbSize;
|
||
|
DWORD dwImpType;
|
||
|
|
||
|
*pdwImpType = CRYPT_IMPL_UNKNOWN;
|
||
|
|
||
|
if(!CertGetCertificateContextProperty(pCertContext,
|
||
|
CERT_KEY_PROV_INFO_PROP_ID,
|
||
|
NULL,
|
||
|
&cbSize))
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
pProvInfo = SPExternalAlloc(cbSize);
|
||
|
if(pProvInfo == NULL)
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if(!CertGetCertificateContextProperty(pCertContext,
|
||
|
CERT_KEY_PROV_INFO_PROP_ID,
|
||
|
pProvInfo,
|
||
|
&cbSize))
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
// HACKHACK - clear the smart-card specific flag.
|
||
|
pProvInfo->dwFlags &= ~CERT_SET_KEY_CONTEXT_PROP_ID;
|
||
|
|
||
|
if(!CryptAcquireContextW(&hProv,
|
||
|
pProvInfo->pwszContainerName,
|
||
|
pProvInfo->pwszProvName,
|
||
|
pProvInfo->dwProvType,
|
||
|
pProvInfo->dwFlags | CRYPT_SILENT))
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
cbSize = sizeof(dwImpType);
|
||
|
if(!CryptGetProvParam(hProv,
|
||
|
PP_IMPTYPE,
|
||
|
(PBYTE)&dwImpType,
|
||
|
&cbSize,
|
||
|
0))
|
||
|
{
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
*pdwImpType = dwImpType;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if(pProvInfo)
|
||
|
{
|
||
|
SPExternalFree(pProvInfo);
|
||
|
}
|
||
|
|
||
|
if(hProv)
|
||
|
{
|
||
|
CryptReleaseContext(hProv, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|