mirror of https://github.com/tongzx/nt5src
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.
3714 lines
112 KiB
3714 lines
112 KiB
/*++
|
|
|
|
Copyright (c) 1997 - 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
userkey.cxx
|
|
|
|
Abstract:
|
|
|
|
EFS (Encrypting File System) Server
|
|
|
|
Author:
|
|
|
|
Robert Reichel (RobertRe) July 4, 1997
|
|
Robert Gu (RobertG) January 23, 1998
|
|
|
|
Environment:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <lsapch.hxx>
|
|
|
|
extern "C" {
|
|
#include <nt.h>
|
|
#include <ntdef.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <wincrypt.h>
|
|
#include <cryptui.h>
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <certca.h>
|
|
#include "lsasrvp.h"
|
|
#include "efssrv.hxx"
|
|
#include "userkey.h"
|
|
}
|
|
|
|
#define PROVIDER TEXT("Provider")
|
|
#define CONTAINER TEXT("Container")
|
|
#define PROVIDER_TYPE TEXT("Type")
|
|
#define CERT_HASH TEXT("CertificateHash")
|
|
#define CERT_FLAG TEXT("Flag")
|
|
|
|
#define KEYPATH TEXT("%ws\\Software\\Microsoft\\Windows NT\\CurrentVersion\\EFS\\CurrentKeys")
|
|
#define KEYPATHROOT HKEY_USERS
|
|
|
|
#define YEARCOUNT (LONGLONG) 10000000*3600*24*365 // One Year's tick count
|
|
|
|
|
|
#ifndef wszCERTTYPE_EFS
|
|
#define wszCERTTYPE_EFS L"EFS"
|
|
#endif
|
|
|
|
#ifndef wszCERTTYPE_EFS_RECOVERY
|
|
#define wszCERTTYPE_EFS_RECOVERY L"EFSRecovery"
|
|
#endif
|
|
|
|
LONG UserCertIsValidating = 0;
|
|
|
|
//
|
|
// Forward references
|
|
//
|
|
|
|
BOOL
|
|
EfspCreateSelfSignedCert(
|
|
OUT HCRYPTKEY * hKey,
|
|
OUT HCRYPTPROV * hProv,
|
|
OUT LPWSTR * lpContainerName,
|
|
OUT LPWSTR * lpProviderName,
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash,
|
|
OUT LPWSTR * lpDisplayInfo,
|
|
OUT PCCERT_CONTEXT *pCertContext
|
|
);
|
|
|
|
DWORD
|
|
GenerateUserKey (
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey OPTIONAL,
|
|
OUT HCRYPTPROV * hProv OPTIONAL,
|
|
OUT LPWSTR * ContainerName,
|
|
OUT LPWSTR * ProviderName,
|
|
OUT PDWORD ProviderType,
|
|
OUT LPWSTR * DisplayInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash
|
|
);
|
|
|
|
PWCHAR
|
|
ConstructKeyPath(
|
|
PWCHAR SidString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs the path to the current user key, in the form
|
|
|
|
<user-sid>\Software\Microsoft\Windows NT\CurrentVersion\EFS\CurrentKeys
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns the path the the current user keyset
|
|
|
|
--*/
|
|
|
|
{
|
|
PWCHAR KeyPath = NULL;
|
|
|
|
if (SidString) {
|
|
|
|
DWORD KeyPathLength = wcslen( KEYPATH );
|
|
|
|
//
|
|
// Subtract 3 for %ws and add 1 for NULL
|
|
//
|
|
|
|
DWORD StringLength = ( KeyPathLength - 3 + wcslen( SidString ) + 1) * sizeof( WCHAR );
|
|
|
|
KeyPath = (PWCHAR)LsapAllocateLsaHeap( StringLength );
|
|
|
|
if (KeyPath) {
|
|
|
|
swprintf( KeyPath, KEYPATH, SidString );
|
|
KeyPath[StringLength/sizeof( WCHAR ) - 1] = UNICODE_NULL;
|
|
}
|
|
|
|
}
|
|
|
|
return( KeyPath );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
EfspGetTokenUser(
|
|
IN OUT PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the TOKEN_USER structure for the
|
|
current user, and optionally, the AuthenticationId from his
|
|
token.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo - User Info.
|
|
|
|
Return Value:
|
|
|
|
NtStatus code
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE TokenHandle;
|
|
ULONG ReturnLength;
|
|
TOKEN_STATISTICS TokenStats;
|
|
PTOKEN_USER pTokenUser = NULL;
|
|
BOOLEAN b = FALSE;
|
|
BYTE PefBuffer[1024];
|
|
|
|
Status = NtOpenThreadToken(
|
|
NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
TRUE, // OpenAsSelf
|
|
&TokenHandle
|
|
);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
Status = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenUser,
|
|
PefBuffer,
|
|
sizeof (PefBuffer),
|
|
&ReturnLength
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status ) || (Status == STATUS_BUFFER_TOO_SMALL)) {
|
|
|
|
pEfsUserInfo->pTokenUser = (PTOKEN_USER)LsapAllocateLsaHeap( ReturnLength );
|
|
|
|
if (pEfsUserInfo->pTokenUser) {
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
RtlCopyMemory(pEfsUserInfo->pTokenUser, PefBuffer, ReturnLength);
|
|
|
|
//
|
|
// Fix the SID pointer
|
|
//
|
|
|
|
pEfsUserInfo->pTokenUser->User.Sid = (PSID)((PBYTE)(pEfsUserInfo->pTokenUser) + sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
ASSERT(RtlValidSid(pEfsUserInfo->pTokenUser->User.Sid));
|
|
|
|
} else {
|
|
|
|
//
|
|
// The stack performance buffer is not bigger enough
|
|
//
|
|
|
|
Status = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenUser,
|
|
pEfsUserInfo->pTokenUser,
|
|
ReturnLength,
|
|
&ReturnLength
|
|
);
|
|
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
Status = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenStatistics,
|
|
(PVOID)&TokenStats,
|
|
sizeof( TOKEN_STATISTICS ),
|
|
&ReturnLength
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
NTSTATUS Status1;
|
|
|
|
pEfsUserInfo->AuthId = TokenStats.AuthenticationId;
|
|
b = TRUE;
|
|
|
|
//
|
|
// If we failed to get the group info, we assume the user is not interactively logged on.
|
|
// The fail should not stop us, we could go without cache at the worst.
|
|
//
|
|
|
|
Status1 = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenGroups,
|
|
PefBuffer,
|
|
sizeof (PefBuffer),
|
|
&ReturnLength
|
|
);
|
|
|
|
if (NT_SUCCESS( Status1 ) || (Status1 == STATUS_BUFFER_TOO_SMALL)) {
|
|
|
|
PTOKEN_GROUPS pGroups = NULL;
|
|
PTOKEN_GROUPS pAllocGroups = NULL;
|
|
|
|
if ( NT_SUCCESS( Status1 ) ) {
|
|
|
|
pGroups = (PTOKEN_GROUPS) PefBuffer;
|
|
|
|
} else {
|
|
|
|
pAllocGroups = (PTOKEN_GROUPS)LsapAllocateLsaHeap( ReturnLength );
|
|
|
|
Status1 = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenGroups,
|
|
pAllocGroups,
|
|
ReturnLength,
|
|
&ReturnLength
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status1 )) {
|
|
|
|
pGroups = pAllocGroups;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (pGroups) {
|
|
|
|
//
|
|
// Search the interactive SID. Looks like this SID tends to appear at the
|
|
// end of the list. We search from back to the first.
|
|
//
|
|
|
|
int SidIndex;
|
|
|
|
for ( SidIndex = (int)(pGroups->GroupCount - 1); SidIndex >= 0; SidIndex--) {
|
|
if (RtlEqualSid(LsapInteractiveSid, pGroups->Groups[SidIndex].Sid)) {
|
|
pEfsUserInfo->InterActiveUser = USER_INTERACTIVE;
|
|
break;
|
|
}
|
|
}
|
|
if (pEfsUserInfo->InterActiveUser != USER_INTERACTIVE) {
|
|
pEfsUserInfo->InterActiveUser = USER_REMOTE;
|
|
}
|
|
|
|
}
|
|
|
|
if (pAllocGroups) {
|
|
LsapFreeLsaHeap( pAllocGroups );
|
|
}
|
|
|
|
}
|
|
|
|
// LsapInteractiveSid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!b) {
|
|
|
|
//
|
|
// Something failed, clean up what we were going to return
|
|
//
|
|
|
|
LsapFreeLsaHeap( pEfsUserInfo->pTokenUser );
|
|
pEfsUserInfo->pTokenUser = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
}
|
|
|
|
NtClose( TokenHandle );
|
|
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
PWCHAR
|
|
ConvertSidToWideCharString(
|
|
PSID Sid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
This function generates a printable unicode string representation
|
|
of a SID.
|
|
|
|
The resulting string will take one of two forms. If the
|
|
IdentifierAuthority value is not greater than 2^32, then
|
|
the SID will be in the form:
|
|
|
|
|
|
S-1-281736-12-72-9-110
|
|
^ ^^ ^^ ^ ^^^
|
|
| | | | |
|
|
+-----+--+-+--+---- Decimal
|
|
|
|
|
|
|
|
Otherwise it will take the form:
|
|
|
|
|
|
S-1-0x173495281736-12-72-9-110
|
|
^^^^^^^^^^^^^^ ^^ ^^ ^ ^^^
|
|
Hexidecimal | | | |
|
|
+--+-+--+---- Decimal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
UnicodeString - Returns a unicode string that is equivalent to
|
|
the SID. The maximum length field is only set if
|
|
AllocateDestinationString is TRUE.
|
|
|
|
Sid - Supplies the SID that is to be converted to unicode.
|
|
|
|
AllocateDestinationString - Supplies a flag that controls whether or
|
|
not this API allocates the buffer space for the destination
|
|
string. If it does, then the buffer must be deallocated using
|
|
RtlFreeUnicodeString (note that only storage for
|
|
DestinationString->Buffer is allocated by this API).
|
|
|
|
Return Value:
|
|
|
|
SUCCESS - The conversion was successful
|
|
|
|
STATUS_INVALID_SID - The sid provided does not have a valid structure,
|
|
or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).
|
|
|
|
STATUS_NO_MEMORY - There was not sufficient memory to allocate the
|
|
target string. This is returned only if AllocateDestinationString
|
|
is specified as TRUE.
|
|
|
|
STATUS_BUFFER_OVERFLOW - This is returned only if
|
|
AllocateDestinationString is specified as FALSE.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING Result;
|
|
|
|
if ( STATUS_SUCCESS != RtlConvertSidToUnicodeString( &Result, Sid, TRUE )) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return Result.Buffer;
|
|
}
|
|
|
|
BOOLEAN
|
|
EfspIsSystem(
|
|
PEFS_USER_INFO pEfsUserInfo,
|
|
OUT PBOOLEAN System
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the current user is running in system context or not.
|
|
|
|
Arguments:
|
|
|
|
System - Receives whether or not the current user is system.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*System = RtlEqualSid(LsapLocalSystemSid, pEfsUserInfo->pTokenUser->User.Sid);
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOL
|
|
EfspIsDomainUser(
|
|
IN LPWSTR lpDomainName,
|
|
OUT PBOOLEAN IsDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the current user is logged on to a domain account
|
|
or a local machine account.
|
|
|
|
Arguments:
|
|
|
|
lpDomainName - Supplies the domain name.
|
|
|
|
IsDomain - Returns TRUE if the current user is logged on to a domain
|
|
account, FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*IsDomain = (wcscmp( EfsComputerName, lpDomainName ) != 0);
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnrollKeyPair(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey,
|
|
OUT HCRYPTPROV * hProv,
|
|
OUT LPWSTR * lpContainerName,
|
|
OUT LPWSTR * lpProviderName,
|
|
IN DWORD dwProviderType,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash,
|
|
OUT LPWSTR * lpDisplayInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a keypair and attempts to enroll it.
|
|
|
|
Arguments:
|
|
|
|
hKey - Optionally returns a handle to the user's key.
|
|
|
|
hProv - Optionally returns a handle to the user's key's provider.
|
|
|
|
lpContainerName - Returns the name of the user's key container.
|
|
|
|
lpProviderName - Returns the name of the user's key provider.
|
|
|
|
ProviderType - Returns the type of the provider.
|
|
|
|
pbHash - Returns the hash of the user's certificate.
|
|
|
|
cbHash - Returns the length in bytes of the certificate hash.
|
|
|
|
DisplayInfo - Returns the display information associated with this
|
|
certificate.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. Call GetLastError() for details.
|
|
|
|
--*/
|
|
{
|
|
BOOL b = FALSE;
|
|
BOOL fResult = FALSE;
|
|
NTSTATUS Status;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
DWORD ImpersonationError;
|
|
|
|
HCRYPTKEY hLocalKey = NULL;
|
|
HCRYPTPROV hLocalProv = NULL;
|
|
|
|
PCCERT_CONTEXT pCertContext = NULL ;
|
|
//
|
|
// Initialize OUT parameters
|
|
//
|
|
|
|
*pbHash = NULL;
|
|
*lpDisplayInfo = NULL;
|
|
*lpContainerName = NULL;
|
|
*lpProviderName = NULL;
|
|
*cbHash = 0;
|
|
|
|
*hKey = NULL;
|
|
*hProv = NULL;
|
|
|
|
//
|
|
// Initialize output parameters
|
|
//
|
|
|
|
if (pEfsUserInfo->bDomainAccount) {
|
|
|
|
HRESULT AutoEnrollSuccess = S_OK;
|
|
|
|
//
|
|
// Attempt to create the auto-enroll object so that this cert re-enrolls.
|
|
//
|
|
// DLL is demand load.
|
|
//
|
|
|
|
__try {
|
|
|
|
AutoEnrollSuccess = CACreateLocalAutoEnrollmentObject(
|
|
wszCERTTYPE_EFS,
|
|
NULL,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER
|
|
);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
rc = GetExceptionCode();
|
|
AutoEnrollSuccess = (HRESULT)rc;
|
|
}
|
|
|
|
if (S_OK == AutoEnrollSuccess) {
|
|
|
|
HCERTTYPE hCertType = 0;
|
|
|
|
CRYPTUI_WIZ_CERT_REQUEST_INFO CertRequest;
|
|
CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW NewKeyInfo;
|
|
CRYPT_KEY_PROV_INFO KeyProvInfo;
|
|
CRYPTUI_WIZ_CERT_TYPE CertType;
|
|
|
|
memset( &CertRequest, 0, sizeof( CRYPTUI_WIZ_CERT_REQUEST_INFO ));
|
|
|
|
KeyProvInfo.pwszContainerName = NULL;
|
|
KeyProvInfo.pwszProvName = NULL;
|
|
KeyProvInfo.dwProvType = dwProviderType;
|
|
KeyProvInfo.dwFlags = 0;
|
|
KeyProvInfo.cProvParam = 0;
|
|
KeyProvInfo.rgProvParam = NULL;
|
|
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
|
|
|
|
memset( &NewKeyInfo, 0, sizeof( CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW ));
|
|
|
|
NewKeyInfo.dwSize = sizeof( CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW );
|
|
NewKeyInfo.pKeyProvInfo = &KeyProvInfo;
|
|
NewKeyInfo.dwGenKeyFlags = RSA1024BIT_KEY | CRYPT_EXPORTABLE;
|
|
|
|
LPWSTR lpwstr = wszCERTTYPE_EFS;
|
|
CertType.dwSize = sizeof( CRYPTUI_WIZ_CERT_TYPE );
|
|
CertType.cCertType = 1;
|
|
CertType.rgwszCertType = &lpwstr;
|
|
|
|
//
|
|
// Fill in the fields of the CertRequest structure
|
|
//
|
|
|
|
CertRequest.dwSize = sizeof( CRYPTUI_WIZ_CERT_REQUEST_INFO ); // required
|
|
CertRequest.dwPurpose = CRYPTUI_WIZ_CERT_ENROLL; // enroll the certificate
|
|
CertRequest.pwszMachineName = NULL;
|
|
CertRequest.pwszAccountName = NULL;
|
|
CertRequest.pAuthentication = NULL; // must be NULL
|
|
CertRequest.pCertRequestString = NULL; // Reserved, must be NULL
|
|
CertRequest.pwszDesStore = NULL; // defaults to MY store
|
|
CertRequest.pszHashAlg = NULL;
|
|
CertRequest.pRenewCertContext = NULL; // We're enrolling, not renewing
|
|
CertRequest.dwPvkChoice = CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW; // Create keyset
|
|
CertRequest.pPvkNew = &NewKeyInfo;
|
|
CertRequest.pwszCALocation = NULL;
|
|
CertRequest.pwszCAName = NULL;
|
|
CertRequest.dwPostOption = 0;
|
|
CertRequest.pCertRequestExtensions = NULL;
|
|
CertRequest.dwCertChoice = CRYPTUI_WIZ_CERT_REQUEST_CERT_TYPE;
|
|
CertRequest.pCertType = &CertType;
|
|
CertRequest.pwszCertDNName = L"CN=EFS";
|
|
CertRequest.pwszFriendlyName = NULL; // Optional if CRYPTUI_WIZ_CERT_ENROLL is set in dwPurpose
|
|
CertRequest.pwszDescription = NULL; // Optional if CRYPTUI_WIZ_CERT_ENROLL is set in dwPurpose
|
|
|
|
DWORD CAdwStatus = 0 ;
|
|
BOOL CryptUiResult ;
|
|
|
|
__try {
|
|
|
|
CryptUiResult = CryptUIWizCertRequest(
|
|
CRYPTUI_WIZ_NO_UI,
|
|
NULL,
|
|
NULL,
|
|
&CertRequest,
|
|
&pCertContext,
|
|
&CAdwStatus
|
|
);
|
|
}
|
|
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
CryptUiResult = FALSE ;
|
|
SetLastError( GetExceptionCode() );
|
|
}
|
|
|
|
if (CryptUiResult) {
|
|
|
|
//
|
|
// Got back success, see if we got a cert back
|
|
//
|
|
|
|
if (CAdwStatus == CRYPTUI_WIZ_CERT_REQUEST_STATUS_SUCCEEDED) {
|
|
|
|
//
|
|
// We got back a valid Cert Context.
|
|
// Get the hash out of it.
|
|
//
|
|
|
|
*pbHash = GetCertHashFromCertContext(
|
|
pCertContext,
|
|
cbHash
|
|
);
|
|
|
|
*lpDisplayInfo = EfspGetCertDisplayInformation( pCertContext );
|
|
|
|
if (*pbHash && *lpDisplayInfo) {
|
|
|
|
PCRYPT_KEY_PROV_INFO pCryptKeyProvInfo = GetKeyProvInfo( pCertContext );
|
|
|
|
if (pCryptKeyProvInfo) {
|
|
|
|
//
|
|
// According to Xiaohong, we always get back container name and provider name here.
|
|
// There is no need for us to check NULL here.
|
|
//
|
|
|
|
|
|
*lpContainerName = (LPWSTR)LsapAllocateLsaHeap( wcslen(pCryptKeyProvInfo->pwszContainerName) * sizeof( WCHAR ) + sizeof( UNICODE_NULL ));
|
|
*lpProviderName = (LPWSTR)LsapAllocateLsaHeap( wcslen(pCryptKeyProvInfo->pwszProvName) * sizeof( WCHAR ) + sizeof( UNICODE_NULL ));
|
|
|
|
if (*lpContainerName && *lpProviderName) {
|
|
|
|
wcscpy( *lpContainerName, pCryptKeyProvInfo->pwszContainerName );
|
|
wcscpy( *lpProviderName, pCryptKeyProvInfo->pwszProvName );
|
|
|
|
if (CryptAcquireContext( hProv, *lpContainerName, *lpProviderName, PROV_RSA_FULL, CRYPT_SILENT )) {
|
|
|
|
if (CryptGetUserKey(*hProv, AT_KEYEXCHANGE, hKey)) {
|
|
|
|
fResult = TRUE;
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
LsapFreeLsaHeap( pCryptKeyProvInfo );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
// CertFreeCertificateContext( pCertContext );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We failed. Get the error. This error will be overwritten in the following code.
|
|
// It only helps debug for now.
|
|
//
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
if (!fResult) {
|
|
|
|
//
|
|
// We failed to get one from the CA. Issue a self-signed cert.
|
|
//
|
|
|
|
//
|
|
// Free the memory allocated above first
|
|
//
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (*lpDisplayInfo) {
|
|
LsapFreeLsaHeap( *lpDisplayInfo );
|
|
*lpDisplayInfo = NULL;
|
|
}
|
|
|
|
if (*lpContainerName) {
|
|
LsapFreeLsaHeap( *lpContainerName );
|
|
*lpContainerName = NULL;
|
|
}
|
|
|
|
if (*lpProviderName) {
|
|
LsapFreeLsaHeap( *lpProviderName );
|
|
*lpProviderName = NULL;
|
|
}
|
|
|
|
if (pCertContext) {
|
|
CertFreeCertificateContext( pCertContext );
|
|
}
|
|
|
|
if ( *hKey) {
|
|
CryptDestroyKey( *hKey );
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if ( *hProv) {
|
|
CryptReleaseContext( *hProv, 0 );
|
|
*hProv = NULL;
|
|
}
|
|
|
|
if (EfspCreateSelfSignedCert( hKey,
|
|
hProv,
|
|
lpContainerName,
|
|
lpProviderName,
|
|
pEfsUserInfo,
|
|
pbHash,
|
|
cbHash,
|
|
lpDisplayInfo,
|
|
&pCertContext
|
|
)) {
|
|
fResult = TRUE;
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
DebugLog((DEB_WARN, "Unable to create auto-enrollment object, error = %x\n" , AutoEnrollSuccess));
|
|
rc = AutoEnrollSuccess;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// It's not a domain account, which means we can't get to the CA.
|
|
// Issue a self-signed cert.
|
|
//
|
|
|
|
if (EfspCreateSelfSignedCert( hKey,
|
|
hProv,
|
|
lpContainerName,
|
|
lpProviderName,
|
|
pEfsUserInfo,
|
|
pbHash,
|
|
cbHash,
|
|
lpDisplayInfo,
|
|
&pCertContext
|
|
)) {
|
|
fResult = TRUE;
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Let's add this cert to the local store
|
|
//
|
|
|
|
if (fResult) {
|
|
rc = EfsAddCertToTrustStoreStore(
|
|
pCertContext,
|
|
&ImpersonationError
|
|
);
|
|
|
|
if ( ERROR_SUCCESS != rc ) {
|
|
|
|
DebugLog((DEB_ERROR, "Failed to add the cert to LM CA store, error = %x\n" , rc));
|
|
if (ImpersonationError) {
|
|
|
|
//
|
|
// We got serious error. We reverted but could not impersonate back.
|
|
// Quit the procedure. This should not happen.
|
|
//
|
|
|
|
DebugLog((DEB_ERROR, "Failed to impersonate after revert.\n"));
|
|
fResult = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Fail to add the cert to the store should not prevent us continue.
|
|
//
|
|
|
|
rc = ERROR_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Let's update the registry. This is the best effort. No need to see if succeed or not.
|
|
//
|
|
|
|
EfsMarkCertAddedToStore(pEfsUserInfo);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Now check if we need create the cache
|
|
//
|
|
|
|
//
|
|
// Create the cache node.
|
|
// We could have a cache node which is not validated or no cache at all.
|
|
// We could also have a valid cache but was not allowed to use since it
|
|
// is being freed.
|
|
//
|
|
|
|
if (fResult && !pEfsUserInfo->UserCacheStop) {
|
|
|
|
//
|
|
// Cache is allowed now
|
|
//
|
|
|
|
if (!pEfsUserInfo->pUserCache || (pEfsUserInfo->pUserCache->CertValidated != CERT_VALIDATED)) {
|
|
|
|
//
|
|
// Let's create the cache
|
|
//
|
|
|
|
PUSER_CACHE pCacheNode;
|
|
|
|
pCacheNode = (PUSER_CACHE) LsapAllocateLsaHeap(sizeof(USER_CACHE));
|
|
|
|
if (pCacheNode) {
|
|
|
|
memset( pCacheNode, 0, sizeof( USER_CACHE ));
|
|
|
|
if (NT_SUCCESS( NtQuerySystemTime(&(pCacheNode->TimeStamp)))){
|
|
|
|
if (EfspInitUserCacheNode(
|
|
pCacheNode,
|
|
*pbHash,
|
|
*cbHash,
|
|
*lpContainerName,
|
|
*lpProviderName,
|
|
*lpDisplayInfo,
|
|
pCertContext,
|
|
*hKey,
|
|
*hProv,
|
|
&(pEfsUserInfo->AuthId),
|
|
CERT_VALIDATED
|
|
)){
|
|
|
|
//
|
|
// Cache node created and ready for use. Do not delete or close the info
|
|
// we just got.
|
|
//
|
|
|
|
*lpContainerName = NULL;
|
|
*lpProviderName = NULL;
|
|
*lpDisplayInfo = NULL;
|
|
*pbHash = NULL;
|
|
*cbHash = NULL;
|
|
*hProv = NULL;
|
|
*hKey = NULL;
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
|
|
//
|
|
// We had a not validated cache
|
|
//
|
|
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
|
|
}
|
|
|
|
pEfsUserInfo->pUserCache = pCacheNode;
|
|
|
|
pCertContext = NULL;
|
|
|
|
} else {
|
|
|
|
LsapFreeLsaHeap(pCacheNode);
|
|
pCacheNode = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LsapFreeLsaHeap(pCacheNode);
|
|
pCacheNode = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Even if cache failed to create, but we can proceed without cache
|
|
//
|
|
|
|
}
|
|
|
|
if (pCertContext) {
|
|
CertFreeCertificateContext( pCertContext );
|
|
pCertContext = NULL;
|
|
}
|
|
|
|
if (!fResult) {
|
|
|
|
//
|
|
// Something failed, free all the OUT parameters
|
|
// that were allocated.
|
|
//
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (*lpDisplayInfo) {
|
|
LsapFreeLsaHeap( *lpDisplayInfo );
|
|
*lpDisplayInfo = NULL;
|
|
}
|
|
|
|
if (*lpContainerName) {
|
|
LsapFreeLsaHeap( *lpContainerName );
|
|
*lpContainerName = NULL;
|
|
}
|
|
|
|
if (*lpProviderName) {
|
|
LsapFreeLsaHeap( *lpProviderName );
|
|
*lpProviderName = NULL;
|
|
}
|
|
|
|
if ( *hKey) {
|
|
CryptDestroyKey( *hKey );
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if ( *hProv) {
|
|
CryptReleaseContext( *hProv, 0 );
|
|
*hProv = NULL;
|
|
}
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
return ( fResult );
|
|
}
|
|
|
|
|
|
BOOL
|
|
EfspCreateSelfSignedCert(
|
|
OUT HCRYPTKEY * hKey,
|
|
OUT HCRYPTPROV * hProv,
|
|
OUT LPWSTR * lpContainerName,
|
|
OUT LPWSTR * lpProviderName,
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash,
|
|
OUT LPWSTR * lpDisplayInfo,
|
|
OUT PCCERT_CONTEXT *pCertContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up and creates a self-signed certificate.
|
|
|
|
Arguments:
|
|
|
|
lpContainerName - Returns the container name of the new certificate.
|
|
|
|
lpProviderName - Returns the provider name of the new certificate.
|
|
|
|
pbHash - Returns the hash of the new certificate.
|
|
|
|
cbHash - Returns the length in bytes of the new certificate hash.
|
|
|
|
lpDisplayInfo - Returns the display string for the new certificate.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure. Call GetLastError() for more details.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = FALSE;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
//
|
|
// Initialize OUT parameters
|
|
//
|
|
|
|
*lpContainerName = NULL;
|
|
*lpProviderName = NULL;
|
|
*pbHash = NULL;
|
|
*lpDisplayInfo = NULL;
|
|
*pCertContext = NULL;
|
|
|
|
*hKey = NULL;
|
|
*hProv = NULL;
|
|
|
|
//
|
|
// Croft up a key pair
|
|
//
|
|
|
|
//
|
|
// Container name
|
|
//
|
|
|
|
GUID guidContainerName;
|
|
|
|
UuidCreate(&guidContainerName);
|
|
|
|
LPWSTR TmpContainerName;
|
|
|
|
if (ERROR_SUCCESS == UuidToStringW(&guidContainerName, &TmpContainerName )) {
|
|
|
|
//
|
|
// Copy the container name into LSA heap memory
|
|
//
|
|
|
|
*lpContainerName = (LPWSTR)LsapAllocateLsaHeap( (wcslen( TmpContainerName ) + 1) * sizeof( WCHAR ) );
|
|
|
|
if (*lpContainerName) {
|
|
|
|
wcscpy( *lpContainerName, TmpContainerName );
|
|
|
|
*lpProviderName = (PWCHAR)LsapAllocateLsaHeap( (wcslen( MS_DEF_PROV ) + 1) * sizeof( WCHAR ) );
|
|
|
|
if (*lpProviderName == NULL) {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
wcscpy( *lpProviderName, MS_DEF_PROV );
|
|
|
|
//
|
|
// Create the key container
|
|
//
|
|
|
|
if (CryptAcquireContext(hProv, *lpContainerName, *lpProviderName, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT )) {
|
|
|
|
if (CryptGenKey(*hProv, AT_KEYEXCHANGE, RSA1024BIT_KEY | CRYPT_EXPORTABLE, hKey)) {
|
|
|
|
//
|
|
// Construct the subject name information
|
|
//
|
|
|
|
LPWSTR UPNName = NULL;
|
|
LPWSTR SubName = NULL;
|
|
|
|
//*lpDisplayInfo = MakeDNName( FALSE, pEfsUserInfo);
|
|
rc = EfsMakeCertNames(
|
|
pEfsUserInfo,
|
|
lpDisplayInfo,
|
|
&SubName,
|
|
&UPNName
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
//
|
|
// Use this piece of code to create the PCERT_NAME_BLOB going into CertCreateSelfSignCertificate()
|
|
//
|
|
|
|
CERT_NAME_BLOB SubjectName;
|
|
|
|
SubjectName.cbData = 0;
|
|
|
|
if(CertStrToNameW(
|
|
CRYPT_ASN_ENCODING,
|
|
SubName,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&SubjectName.cbData,
|
|
NULL)) {
|
|
|
|
SubjectName.pbData = (BYTE *) LsapAllocateLsaHeap(SubjectName.cbData);
|
|
|
|
if (SubjectName.pbData) {
|
|
|
|
if (CertStrToNameW(
|
|
CRYPT_ASN_ENCODING,
|
|
SubName,
|
|
0,
|
|
NULL,
|
|
SubjectName.pbData,
|
|
&SubjectName.cbData,
|
|
NULL) ) {
|
|
|
|
//
|
|
// Make the UPN Name
|
|
//
|
|
|
|
PCERT_EXTENSION altNameExt = NULL;
|
|
|
|
|
|
if (EfsGetAltNameExt(&altNameExt, UPNName)) {
|
|
|
|
//
|
|
// Make the basic restrain extension
|
|
//
|
|
|
|
PCERT_EXTENSION basicRestraint = NULL;
|
|
|
|
if (EfsGetBasicConstraintExt(&basicRestraint)) {
|
|
|
|
//
|
|
// Make the enhanced key usage
|
|
//
|
|
|
|
CERT_ENHKEY_USAGE certEnhKeyUsage;
|
|
LPSTR lpstr;
|
|
CERT_EXTENSION certExt[3];
|
|
|
|
lpstr = szOID_EFS_CRYPTO;
|
|
certEnhKeyUsage.cUsageIdentifier = 1;
|
|
certEnhKeyUsage.rgpszUsageIdentifier = &lpstr;
|
|
|
|
// now call CryptEncodeObject to encode the enhanced key usage into the extension struct
|
|
|
|
certExt[0].Value.cbData = 0;
|
|
certExt[0].Value.pbData = NULL;
|
|
certExt[0].fCritical = FALSE;
|
|
certExt[0].pszObjId = szOID_ENHANCED_KEY_USAGE;
|
|
|
|
//
|
|
// Encode it
|
|
//
|
|
|
|
if (EncodeAndAlloc(
|
|
CRYPT_ASN_ENCODING,
|
|
X509_ENHANCED_KEY_USAGE,
|
|
&certEnhKeyUsage,
|
|
&certExt[0].Value.pbData,
|
|
&certExt[0].Value.cbData
|
|
)) {
|
|
|
|
//
|
|
// finally, set up the array of extensions in the certInfo struct
|
|
// any further extensions need to be added to this array.
|
|
//
|
|
|
|
CERT_EXTENSIONS certExts;
|
|
|
|
certExts.cExtension = sizeof(certExt) / sizeof(CERT_EXTENSION);
|
|
certExts.rgExtension = &certExt[0];
|
|
certExt[1] = *altNameExt;
|
|
certExt[2] = *basicRestraint;
|
|
|
|
CRYPT_KEY_PROV_INFO KeyProvInfo;
|
|
|
|
memset( &KeyProvInfo, 0, sizeof( CRYPT_KEY_PROV_INFO ));
|
|
|
|
KeyProvInfo.pwszContainerName = *lpContainerName;
|
|
KeyProvInfo.pwszProvName = *lpProviderName;
|
|
KeyProvInfo.dwProvType = PROV_RSA_FULL;
|
|
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
|
|
|
|
//
|
|
// Make the expiration time very very long (100 years)
|
|
//
|
|
|
|
SYSTEMTIME StartTime;
|
|
FILETIME FileTime;
|
|
LARGE_INTEGER TimeData;
|
|
SYSTEMTIME EndTime;
|
|
|
|
GetSystemTime(&StartTime);
|
|
SystemTimeToFileTime(&StartTime, &FileTime);
|
|
TimeData.LowPart = FileTime.dwLowDateTime;
|
|
TimeData.HighPart = (LONG) FileTime.dwHighDateTime;
|
|
|
|
TimeData.QuadPart += YEARCOUNT * 100;
|
|
FileTime.dwLowDateTime = TimeData.LowPart;
|
|
FileTime.dwHighDateTime = (DWORD) TimeData.HighPart;
|
|
|
|
FileTimeToSystemTime(&FileTime, &EndTime);
|
|
|
|
*pCertContext = CertCreateSelfSignCertificate(
|
|
*hProv,
|
|
&SubjectName,
|
|
0,
|
|
&KeyProvInfo,
|
|
NULL,
|
|
&StartTime,
|
|
&EndTime,
|
|
&certExts
|
|
);
|
|
|
|
if (*pCertContext) {
|
|
|
|
*pbHash = GetCertHashFromCertContext(
|
|
*pCertContext,
|
|
cbHash
|
|
);
|
|
|
|
if (*pbHash) {
|
|
|
|
HCERTSTORE hStore;
|
|
|
|
// hStore = CertOpenSystemStoreW( NULL, L"MY" );
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
0, // dwEncodingType
|
|
0, // hCryptProv,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"My"
|
|
);
|
|
|
|
if (hStore) {
|
|
|
|
//
|
|
// save the temp cert
|
|
//
|
|
|
|
if(CertAddCertificateContextToStore(
|
|
hStore,
|
|
*pCertContext,
|
|
CERT_STORE_ADD_NEW,
|
|
NULL) ) {
|
|
|
|
b = TRUE;
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
CertCloseStore( hStore, 0 );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
LsapFreeLsaHeap( certExt[0].Value.pbData );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
LsapFreeLsaHeap(basicRestraint->Value.pbData);
|
|
LsapFreeLsaHeap(basicRestraint);
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap(altNameExt->Value.pbData);
|
|
LsapFreeLsaHeap(altNameExt);
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
LsapFreeLsaHeap( SubjectName.pbData );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap(UPNName);
|
|
LsapFreeLsaHeap(SubName);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We create the container but failed to get the keys. We need to
|
|
// clean up the useless container here.
|
|
//
|
|
|
|
rc = GetLastError();
|
|
CryptReleaseContext( *hProv, 0 );
|
|
CryptAcquireContext(hProv, *lpContainerName, *lpProviderName, PROV_RSA_FULL, CRYPT_DELETEKEYSET | CRYPT_SILENT );
|
|
|
|
//
|
|
// No need to call CryptReleaseContext any more.
|
|
//
|
|
|
|
*hProv = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
RpcStringFree( &TmpContainerName );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (!b) {
|
|
|
|
//
|
|
// Something failed, clean up whatever we allocated
|
|
//
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (*lpDisplayInfo) {
|
|
LsapFreeLsaHeap( *lpDisplayInfo );
|
|
*lpDisplayInfo = NULL;
|
|
}
|
|
|
|
if (*lpContainerName) {
|
|
LsapFreeLsaHeap( *lpContainerName );
|
|
*lpContainerName = NULL;
|
|
}
|
|
|
|
if (*lpProviderName) {
|
|
LsapFreeLsaHeap( *lpProviderName );
|
|
*lpProviderName = NULL;
|
|
}
|
|
|
|
if ( *pCertContext ) {
|
|
|
|
CertFreeCertificateContext( *pCertContext );
|
|
*pCertContext = NULL;
|
|
|
|
}
|
|
|
|
if (*hKey) {
|
|
CryptDestroyKey( *hKey );
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if (*hProv) {
|
|
CryptReleaseContext( *hProv, 0 );
|
|
*hProv = NULL;
|
|
}
|
|
}
|
|
|
|
SetLastError( rc );
|
|
|
|
return( b );
|
|
}
|
|
|
|
DWORD
|
|
GenerateUserKey (
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey OPTIONAL,
|
|
OUT HCRYPTPROV * hProv OPTIONAL,
|
|
OUT LPWSTR * lpContainerName,
|
|
OUT LPWSTR * lpProviderName,
|
|
OUT PDWORD ProviderType,
|
|
OUT LPWSTR * DisplayInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will construct a default user key for the current user
|
|
and install it in the registry. The constructed key takes the form
|
|
|
|
<UserName>_<MachineName>_EFS_<i>
|
|
|
|
Where i is increased as necessary to construct a valid key name.
|
|
|
|
Arguments:
|
|
|
|
hKey - Optionally returns a handle to the new key. This key must
|
|
be destroyed by the caller via CryptDestroyKey().
|
|
|
|
hProv - Optionally returns a handle to the provider of the new key.
|
|
This handle must be closed by the user via CryptReleaseContext().
|
|
|
|
lpContainerName - Returns a pointer to the name of the new key. This
|
|
buffer must be freed via LsapFreeHeap().
|
|
|
|
lpProviderName - Returns a pointer to the provider of the new key. This
|
|
buffer must be freed via LsapFreeHeap().
|
|
|
|
ProviderType - Returns the type of the provider of the new key.
|
|
|
|
pbHash - Returns a pointer to the certificate hash for this key.
|
|
|
|
cbHash - Returns the size fo the pbHash buffer.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS for succeed.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
DWORD Disposition = 0;
|
|
DWORD dwDataLength = 0;
|
|
|
|
HCRYPTKEY LocalhKey = 0;
|
|
HCRYPTPROV LocalhProv = 0;
|
|
|
|
HKEY KeyHandle = NULL;
|
|
|
|
LONG rc = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Initialize our output parameters
|
|
//
|
|
|
|
*ProviderType = PROV_RSA_FULL;
|
|
*lpProviderName = NULL;
|
|
*lpContainerName = NULL;
|
|
*DisplayInfo = NULL;
|
|
*pbHash = NULL;
|
|
|
|
if (ARGUMENT_PRESENT(hKey)) {
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(hProv)) {
|
|
*hProv = NULL;
|
|
}
|
|
|
|
//
|
|
// Use the user name as a mutex name to synchronize access to the following
|
|
// code. This way we don't hang up every thread to come through here, only
|
|
// ones from this user.
|
|
//
|
|
|
|
HANDLE hMutex = CreateMutex( NULL, TRUE, pEfsUserInfo->lpUserName );
|
|
|
|
if (hMutex != NULL) {
|
|
|
|
if (GetLastError() == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// If we're here, we have the mutex, so we can proceed.
|
|
//
|
|
// Note that it is possible for some other thread to have
|
|
// come through here and created keys for the user while
|
|
// we were busy doing all of this. We will attempt to create
|
|
// the registry key where we will store the key information,
|
|
// and if it's already there, we will assume that someone
|
|
// came in and did all this while we were on our way.
|
|
//
|
|
|
|
//
|
|
// Create the registry path, if it does not already exist.
|
|
//
|
|
|
|
rc = RegCreateKeyEx( KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
TEXT("REG_SZ"),
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&KeyHandle,
|
|
&Disposition
|
|
);
|
|
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// The key didn't exist. Create a cert
|
|
//
|
|
|
|
if (EnrollKeyPair( pEfsUserInfo,
|
|
&LocalhKey,
|
|
&LocalhProv,
|
|
lpContainerName,
|
|
lpProviderName,
|
|
PROV_RSA_FULL,
|
|
pbHash,
|
|
cbHash,
|
|
DisplayInfo
|
|
)) {
|
|
|
|
PBYTE pbLocalHash;
|
|
DWORD cbLocalHash;
|
|
|
|
//
|
|
// Write the hash value to the registry
|
|
//
|
|
|
|
if (*pbHash) {
|
|
pbLocalHash = *pbHash;
|
|
cbLocalHash = *cbHash;
|
|
} else {
|
|
|
|
ASSERT(pEfsUserInfo->pUserCache);
|
|
|
|
pbLocalHash = pEfsUserInfo->pUserCache->pbHash;
|
|
cbLocalHash = pEfsUserInfo->pUserCache->cbHash;
|
|
|
|
}
|
|
|
|
rc = RegSetValueEx(
|
|
KeyHandle, // handle of key to set value for
|
|
CERT_HASH,
|
|
0,
|
|
REG_BINARY,
|
|
pbLocalHash,
|
|
cbLocalHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Mark entire operation as successful
|
|
//
|
|
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
RegCloseKey( KeyHandle );
|
|
|
|
} else {
|
|
|
|
KeyHandle = NULL; // paranoia
|
|
|
|
//
|
|
// We couldn't create the registry key for some reason,
|
|
// fail the entire operation.
|
|
//
|
|
}
|
|
|
|
} else {
|
|
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
|
|
|
DebugLog((DEB_TRACE_EFS, "KeyGen mutex %ws exists\n", pEfsUserInfo->lpUserName ));
|
|
|
|
//
|
|
// Some other thread is in here trying to create the keys.
|
|
// Wait on this mutex, and assume that once we come back,
|
|
// the other thread is done and we can just leave and
|
|
// try to get the keys again.
|
|
//
|
|
|
|
WaitForSingleObject( hMutex, INFINITE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we're here, then CreateMutex did not fail, but GetLastError is
|
|
// set to something other than success or ERROR_ALREADY_EXISTS. Is
|
|
// this an error?
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
rc = ERROR_RETRY;
|
|
}
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Closing mutex handle\n" ));
|
|
|
|
ReleaseMutex( hMutex );
|
|
CloseHandle( hMutex );
|
|
|
|
} else {
|
|
|
|
DebugLog((DEB_ERROR, "CreateMutex failed, error = %x\n", GetLastError() ));
|
|
rc = GetLastError();
|
|
}
|
|
|
|
|
|
if (fSuccess) {
|
|
|
|
//
|
|
// Return these to the caller
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( hKey ) && ARGUMENT_PRESENT( hProv )) {
|
|
|
|
//
|
|
// If the caller passed this in, he wants
|
|
// the key and provider passed back
|
|
//
|
|
|
|
*hKey = LocalhKey;
|
|
*hProv = LocalhProv;
|
|
|
|
} else {
|
|
|
|
if ( LocalhKey ) {
|
|
CryptDestroyKey( LocalhKey );
|
|
}
|
|
|
|
if ( LocalhProv ) {
|
|
CryptReleaseContext( LocalhProv, 0 );
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We failed somewhere, free what we were going
|
|
// to return.
|
|
//
|
|
|
|
if (*lpProviderName != NULL) {
|
|
LsapFreeLsaHeap( *lpProviderName );
|
|
*lpProviderName = NULL;
|
|
}
|
|
|
|
if (*lpContainerName != NULL) {
|
|
LsapFreeLsaHeap( *lpContainerName );
|
|
*lpContainerName = NULL;
|
|
}
|
|
|
|
if (*DisplayInfo != NULL) {
|
|
LsapFreeLsaHeap( *DisplayInfo );
|
|
*DisplayInfo = NULL;
|
|
}
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (LocalhKey != 0) {
|
|
CryptDestroyKey( LocalhKey );
|
|
}
|
|
|
|
if (LocalhProv != 0) {
|
|
CryptReleaseContext(LocalhProv, 0);
|
|
}
|
|
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
BOOL
|
|
EqualCertPublicKeys(
|
|
PCERT_PUBLIC_KEY_INFO pKey1,
|
|
PCERT_PUBLIC_KEY_INFO pKey2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Helper routine to compare the public key portions of two certificates.
|
|
|
|
Arguments:
|
|
|
|
pKey1 - One of the public key info structures.
|
|
|
|
pKey2 - The other one.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE on match, FALSE on failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
CRYPT_BIT_BLOB * PublicKey1 = &pKey1->PublicKey;
|
|
CRYPT_BIT_BLOB * PublicKey2 = &pKey2->PublicKey;
|
|
|
|
if (PublicKey1->cbData == PublicKey2->cbData) {
|
|
|
|
if (memcmp(PublicKey1->pbData, PublicKey2->pbData, PublicKey2->cbData) == 0) {
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
LONG
|
|
SearchMyStoreForEFSCert(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey OPTIONAL,
|
|
OUT HCRYPTPROV * hProv OPTIONAL,
|
|
OUT LPWSTR * ContainerName,
|
|
OUT LPWSTR * ProviderName,
|
|
OUT LPWSTR * DisplayInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
description-of-function.
|
|
|
|
Arguments:
|
|
|
|
argument-name - Supplies | Returns description of argument.
|
|
.
|
|
.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc = ERROR_NO_USER_KEYS;
|
|
HKEY hRegKey;
|
|
DWORD Disposition = 0;
|
|
HCRYPTPROV hLocalProv = NULL;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Initialize required return values.
|
|
//
|
|
|
|
*ProviderName = NULL;
|
|
*ContainerName = NULL;
|
|
*DisplayInfo = NULL;
|
|
*pbHash = NULL;
|
|
*cbHash = 0;
|
|
|
|
//
|
|
// Initialize optional return values
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(hKey)) {
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(hProv)) {
|
|
*hProv = NULL;
|
|
}
|
|
|
|
//
|
|
// Assume that there's no current EFS information
|
|
// for this guy. Create the registry key.
|
|
//
|
|
|
|
rc = RegCreateKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
TEXT("REG_SZ"),
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hRegKey,
|
|
&Disposition // address of disposition value buffer
|
|
);
|
|
|
|
//
|
|
// Open up the user's MY store and see if there's an EFS
|
|
// certificate floating around in there somewhere.
|
|
//
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
CERT_ENHKEY_USAGE certEnhKeyUsage;
|
|
|
|
LPSTR lpstr = szOID_EFS_CRYPTO;
|
|
certEnhKeyUsage.cUsageIdentifier = 1;
|
|
certEnhKeyUsage.rgpszUsageIdentifier = &lpstr;
|
|
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
|
|
//
|
|
// If this fails, there's no cert that matches.
|
|
//
|
|
|
|
HCERTSTORE hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
0, // dwEncodingType
|
|
0, // hCryptProv,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"My"
|
|
);
|
|
|
|
// hStore = CertOpenSystemStoreW( NULL, L"MY");
|
|
|
|
if (hStore) {
|
|
|
|
do {
|
|
|
|
//
|
|
// This will go to success if everything works...
|
|
//
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
|
|
pCertContext = CertFindCertificateInStore(
|
|
hStore,
|
|
X509_ASN_ENCODING,
|
|
0, //CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
CERT_FIND_ENHKEY_USAGE,
|
|
&certEnhKeyUsage,
|
|
pCertContext
|
|
);
|
|
|
|
if (pCertContext) {
|
|
|
|
DebugLog((DEB_TRACE_EFS, "Found matching cert in MY store\n" ));
|
|
|
|
//
|
|
// Do cert validity checking.
|
|
//
|
|
|
|
|
|
if ( CertVerifyTimeValidity(
|
|
NULL,
|
|
pCertContext->pCertInfo
|
|
)){
|
|
|
|
rc = CERT_E_EXPIRED;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Test the cert usage here.
|
|
// CERT_E_WRONG_USAGE
|
|
//
|
|
|
|
BOOL OidFound;
|
|
|
|
|
|
rc = EfsFindCertOid(
|
|
szOID_KP_EFS,
|
|
pCertContext,
|
|
&OidFound
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
|
|
if (OidFound) {
|
|
|
|
*pbHash = GetCertHashFromCertContext(
|
|
pCertContext,
|
|
cbHash
|
|
);
|
|
|
|
if (*pbHash) {
|
|
|
|
//
|
|
// See if we can get container and provider info.
|
|
//
|
|
// Once we've got them, make sure we can call CryptAcquireContext
|
|
// and have it work. That guarantees there's a private key available.
|
|
//
|
|
|
|
PCRYPT_KEY_PROV_INFO pCryptKeyProvInfo = GetKeyProvInfo( pCertContext );
|
|
|
|
if (pCryptKeyProvInfo) {
|
|
|
|
if (!wcscmp(pCryptKeyProvInfo->pwszProvName, MS_DEF_PROV_W) ||
|
|
!wcscmp(pCryptKeyProvInfo->pwszProvName, MS_ENHANCED_PROV_W) ||
|
|
!wcscmp(pCryptKeyProvInfo->pwszProvName, MS_STRONG_PROV_W)) {
|
|
|
|
if (CryptAcquireContext(
|
|
&hLocalProv,
|
|
pCryptKeyProvInfo->pwszContainerName,
|
|
pCryptKeyProvInfo->pwszProvName,
|
|
pCryptKeyProvInfo->dwProvType,
|
|
pCryptKeyProvInfo->dwFlags | CRYPT_SILENT
|
|
)) {
|
|
|
|
//
|
|
// Make sure the public key in the cert matches the one
|
|
// that's in this context.
|
|
//
|
|
|
|
DWORD cbPubKeyInfo = 0;
|
|
|
|
PCERT_PUBLIC_KEY_INFO pPubKeyInfo = ExportPublicKeyInfo(
|
|
hLocalProv,
|
|
pCryptKeyProvInfo->dwKeySpec,
|
|
X509_ASN_ENCODING,
|
|
&cbPubKeyInfo
|
|
);
|
|
|
|
if (pPubKeyInfo) {
|
|
|
|
//
|
|
// Get the public key from the cert context
|
|
//
|
|
|
|
PCERT_INFO pCertInfo = pCertContext->pCertInfo;
|
|
PCERT_PUBLIC_KEY_INFO pSubjectPublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
|
|
|
|
if (EqualCertPublicKeys( pPubKeyInfo, pSubjectPublicKeyInfo )) {
|
|
|
|
//
|
|
// They match. We want to make sure not to return
|
|
// an error indicating that we didn't find a key.
|
|
// The next call will reset the value of rc
|
|
//
|
|
|
|
rc = RegSetValueEx(
|
|
hRegKey, // handle of key to set value for
|
|
CERT_HASH,
|
|
0,
|
|
REG_BINARY,
|
|
*pbHash,
|
|
*cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (pCryptKeyProvInfo->pwszProvName) {
|
|
*ProviderName = (LPWSTR)LsapAllocateLsaHeap( wcslen(pCryptKeyProvInfo->pwszProvName) * sizeof( WCHAR ) + sizeof(L'\0') );
|
|
if (*ProviderName) {
|
|
wcscpy( *ProviderName, pCryptKeyProvInfo->pwszProvName );
|
|
} else {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
if (pCryptKeyProvInfo->pwszContainerName) {
|
|
*ContainerName = (LPWSTR)LsapAllocateLsaHeap( wcslen(pCryptKeyProvInfo->pwszContainerName) * sizeof( WCHAR ) + sizeof(L'\0') );
|
|
if (*ContainerName) {
|
|
wcscpy( *ContainerName, pCryptKeyProvInfo->pwszContainerName );
|
|
} else {
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (!(*DisplayInfo = EfspGetCertDisplayInformation( pCertContext ))) {
|
|
|
|
//
|
|
// At least for now, we do not accept Cert without display name
|
|
//
|
|
rc = GetLastError();
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the cache node.
|
|
// We could have a cache node which is not validated or no cache at all.
|
|
// We could also have a valid cache but was not allowed to use since it
|
|
// is being freed.
|
|
//
|
|
|
|
if ((rc == ERROR_SUCCESS) && !pEfsUserInfo->UserCacheStop) {
|
|
|
|
//
|
|
// Cache is allowed now
|
|
//
|
|
|
|
if (!pEfsUserInfo->pUserCache || (pEfsUserInfo->pUserCache->CertValidated != CERT_VALIDATED)) {
|
|
|
|
//
|
|
// Let's create the cache
|
|
//
|
|
|
|
PUSER_CACHE pCacheNode;
|
|
|
|
pCacheNode = (PUSER_CACHE) LsapAllocateLsaHeap(sizeof(USER_CACHE));
|
|
|
|
if (pCacheNode) {
|
|
|
|
memset( pCacheNode, 0, sizeof( USER_CACHE ));
|
|
|
|
if (NT_SUCCESS( status = NtQuerySystemTime(&(pCacheNode->TimeStamp)))){
|
|
|
|
HCRYPTKEY hLocalKey;
|
|
|
|
if (CryptGetUserKey( hLocalProv, AT_KEYEXCHANGE, &hLocalKey )){
|
|
|
|
if (EfspInitUserCacheNode(
|
|
pCacheNode,
|
|
*pbHash,
|
|
*cbHash,
|
|
*ContainerName,
|
|
*ProviderName,
|
|
*DisplayInfo,
|
|
pCertContext,
|
|
hLocalKey,
|
|
hLocalProv,
|
|
&(pEfsUserInfo->AuthId),
|
|
CERT_VALIDATED
|
|
)){
|
|
|
|
//
|
|
// Cache node created and ready for use. Do not delete or close the info
|
|
// we just got.
|
|
//
|
|
|
|
*ContainerName = NULL;
|
|
*ProviderName = NULL;
|
|
*DisplayInfo = NULL;
|
|
*pbHash = NULL;
|
|
*cbHash = NULL;
|
|
hLocalProv = NULL;
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
|
|
//
|
|
// We had a not validated cache
|
|
//
|
|
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
|
|
}
|
|
|
|
pEfsUserInfo->pUserCache = pCacheNode;
|
|
|
|
pCertContext = NULL;
|
|
|
|
} else {
|
|
|
|
if (hLocalKey) {
|
|
CryptDestroyKey( hLocalKey );
|
|
}
|
|
|
|
LsapFreeLsaHeap(pCacheNode);
|
|
pCacheNode = NULL;
|
|
|
|
}
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
LsapFreeLsaHeap(pCacheNode);
|
|
pCacheNode = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = RtlNtStatusToDosError( status );
|
|
LsapFreeLsaHeap(pCacheNode);
|
|
pCacheNode = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
}
|
|
|
|
LsapFreeLsaHeap( pPubKeyInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
LsapFreeLsaHeap( pCryptKeyProvInfo );
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we failed for any reason other than running
|
|
// out of memory, assume that there is no key of
|
|
// use to us and return an appropriate error.
|
|
//
|
|
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We want to keep trying until we're out of certificates or we get
|
|
// an unexpected error (like out of memory).
|
|
//
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Something failed, clean up anything we allocated that
|
|
// we were going to return.
|
|
//
|
|
|
|
if (*ProviderName) {
|
|
LsapFreeLsaHeap( *ProviderName );
|
|
*ProviderName = NULL;
|
|
}
|
|
|
|
if (*ContainerName) {
|
|
LsapFreeLsaHeap( *ContainerName );
|
|
*ContainerName = NULL;
|
|
}
|
|
|
|
if (*DisplayInfo) {
|
|
LsapFreeLsaHeap( *DisplayInfo );
|
|
*DisplayInfo = NULL;
|
|
}
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (hLocalProv) {
|
|
CryptReleaseContext( hLocalProv, 0 );
|
|
hLocalProv = NULL;
|
|
}
|
|
}
|
|
|
|
} while ( pCertContext && rc != ERROR_SUCCESS );
|
|
|
|
if (pCertContext) {
|
|
CertFreeCertificateContext( pCertContext );
|
|
}
|
|
|
|
CertCloseStore( hStore, 0 );
|
|
|
|
if (ARGUMENT_PRESENT(hKey) && ARGUMENT_PRESENT(hProv) && (rc == ERROR_SUCCESS) && (*pbHash)) {
|
|
|
|
*hProv = hLocalProv;
|
|
CryptGetUserKey( *hProv, AT_KEYEXCHANGE, hKey );
|
|
|
|
} else {
|
|
|
|
if (hLocalProv) {
|
|
CryptReleaseContext( hLocalProv, 0 );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we couldn't open the store, return
|
|
// no user keys and continue.
|
|
//
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
|
|
}
|
|
|
|
RegCloseKey( hRegKey );
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
|
|
LONG
|
|
GetInfoFromCertHash(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey OPTIONAL,
|
|
OUT HCRYPTPROV * hProv OPTIONAL,
|
|
OUT LPWSTR * ContainerName,
|
|
OUT LPWSTR * ProviderName,
|
|
OUT LPWSTR * DisplayInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will query the cert hash from the registry for this user and
|
|
return the useful information from the corresponding cert.
|
|
|
|
Arguments:
|
|
|
|
KeyPath - Supplies the fully formed path to the user's EFS key.
|
|
|
|
hKey - Optionally returns a handle to a key context.
|
|
|
|
hProv - Optionally returns a handle to a provider context.
|
|
|
|
ContainerName - Returns the name of the key container for this key.
|
|
|
|
ProviderName - Returns the name of the provider for this key.
|
|
|
|
DisplayInfo - Returns the display information from this certificate.
|
|
|
|
pbHash - Returns the hash found in the registry.
|
|
|
|
cbHash - Returns the size of the hash in bytes.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
HKEY hRegKey = NULL;
|
|
BOOLEAN bIsValid = TRUE;
|
|
|
|
//
|
|
// Initialize non-optional parameters
|
|
//
|
|
|
|
*ProviderName = NULL;
|
|
*ContainerName = NULL;
|
|
*DisplayInfo = NULL;
|
|
*pbHash = NULL;
|
|
*cbHash = 0;
|
|
|
|
//
|
|
// Initialize optional parameters
|
|
//
|
|
|
|
if (hKey) {
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if (hProv) {
|
|
*hProv = NULL;
|
|
}
|
|
|
|
rc = RegOpenKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
GENERIC_READ | KEY_SET_VALUE,
|
|
&hRegKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
/* It is time to close this.
|
|
(VOID) RegSetValueEx(
|
|
hRegKey, // handle of key to set value for
|
|
TEXT("EfsInUse"),
|
|
0,
|
|
REG_BINARY,
|
|
NULL,
|
|
0
|
|
);
|
|
*/
|
|
|
|
|
|
//
|
|
// If there's a certificate thumbprint there, get it and use it.
|
|
//
|
|
|
|
DWORD Type;
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Query out the thumbprint, find the cert, and return the key information.
|
|
//
|
|
|
|
if (*pbHash = (PBYTE)LsapAllocateLsaHeap( *cbHash )) {
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
*pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
rc = GetKeyInfoFromCertHash(
|
|
pEfsUserInfo,
|
|
*pbHash,
|
|
*cbHash,
|
|
hKey,
|
|
hProv,
|
|
ContainerName,
|
|
ProviderName,
|
|
DisplayInfo,
|
|
&bIsValid
|
|
);
|
|
|
|
if (((*hKey == NULL) && (pEfsUserInfo->pUserCache) && (pEfsUserInfo->pUserCache->CertValidated != CERT_VALIDATED))
|
|
|| !bIsValid) {
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
}
|
|
if ((rc == ERROR_SUCCESS) && (*hKey == NULL)) {
|
|
|
|
//
|
|
// The key in the cache is current. Free the pbHash
|
|
//
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
}
|
|
|
|
RegCloseKey( hRegKey );
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Something failed, clean up anything we allocated that
|
|
// we were going to return.
|
|
//
|
|
|
|
if (*ProviderName) {
|
|
LsapFreeLsaHeap( *ProviderName );
|
|
*ProviderName = NULL;
|
|
}
|
|
|
|
if (*ContainerName) {
|
|
LsapFreeLsaHeap( *ContainerName );
|
|
*ContainerName = NULL;
|
|
}
|
|
|
|
if (*DisplayInfo) {
|
|
LsapFreeLsaHeap( *DisplayInfo );
|
|
*DisplayInfo = NULL;
|
|
}
|
|
|
|
if (*pbHash) {
|
|
LsapFreeLsaHeap( *pbHash );
|
|
*pbHash = NULL;
|
|
}
|
|
|
|
if (hKey && *hKey) {
|
|
CryptDestroyKey( *hKey );
|
|
*hKey = NULL;
|
|
}
|
|
|
|
if (hProv && *hProv) {
|
|
CryptReleaseContext( *hProv, 0 );
|
|
*hProv = NULL;
|
|
}
|
|
|
|
//
|
|
// If anything other out out of memory failed, assume that there are no keys
|
|
// for this user.
|
|
//
|
|
|
|
if (rc != ERROR_NOT_ENOUGH_MEMORY) {
|
|
rc = ERROR_NO_USER_KEYS;
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
LONG
|
|
GetCurrentKey(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT HCRYPTKEY * hKey OPTIONAL,
|
|
OUT HCRYPTPROV * hProv OPTIONAL,
|
|
OUT LPWSTR * ContainerName,
|
|
OUT LPWSTR * ProviderName,
|
|
OUT PDWORD ProviderType,
|
|
OUT LPWSTR * DisplayInfo,
|
|
OUT PBYTE * pbHash,
|
|
OUT PDWORD cbHash
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the top level routine to get the user's current EFS key.
|
|
It will do the following, in order:
|
|
|
|
1) Open the registry and attempt to find a certificate hash for
|
|
the user. If it finds one, it will attempt to find this hash
|
|
in the user's MY store and obtain all the useful information
|
|
with it.
|
|
|
|
2) If that fails, it will check to see if there's old key data
|
|
(beta 1 and before) in the registry, and if it finds it, it
|
|
will convert that key data into a certificate and return
|
|
all the needed information.
|
|
|
|
3) If that doesn't work, it will search the user's MY store for
|
|
an EFS certificate. If it finds one, it will install this as
|
|
the user's current EFS key.
|
|
|
|
4) If that doesn't work, it will generate a new user key from scratch.
|
|
|
|
If that doesn't work, the operation fails.
|
|
|
|
Arguments:
|
|
|
|
hKey - Optionally returns a handle to the user's key.
|
|
|
|
hProv - Optionally returns a handle to the user's key's provider.
|
|
|
|
ContainerName - Returns the name of the user's key container.
|
|
|
|
ProviderName - Returns the name of the user's key provider.
|
|
|
|
ProviderType - Returns the type of the provider.
|
|
|
|
DisplayInfo - Returns the display information associated with this
|
|
certificate.
|
|
|
|
pbHash - Returns the hash of the user's certificate.
|
|
|
|
cbHash - Returns the length in bytes of the certificate hash.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or Win32 error.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
|
|
//
|
|
// There are four places we can get key information:
|
|
//
|
|
// 1) There's a hash in the registry. This is the typical
|
|
// case and the one we're going to try first.
|
|
//
|
|
// 2) Previous stuff left over from the last release of the
|
|
// system.
|
|
//
|
|
// 3) There's a cert in the user's MY store.
|
|
//
|
|
// 4) There's nothing.
|
|
//
|
|
|
|
//
|
|
// First, look for a hash.
|
|
//
|
|
|
|
rc = GetInfoFromCertHash(
|
|
pEfsUserInfo,
|
|
hKey,
|
|
hProv,
|
|
ContainerName,
|
|
ProviderName,
|
|
DisplayInfo,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if ((ERROR_SUCCESS == rc) && ( NULL == *pbHash)) {
|
|
|
|
LARGE_INTEGER TimeStamp;
|
|
|
|
//
|
|
// We are using the cache
|
|
//
|
|
|
|
ASSERT( pEfsUserInfo->pUserCache );
|
|
ASSERT( pEfsUserInfo->pUserCache->pCertContext);
|
|
|
|
//
|
|
// Check if we need to validate the cert again
|
|
//
|
|
|
|
if (NT_SUCCESS( NtQuerySystemTime(&TimeStamp)) &&
|
|
(TimeStamp.QuadPart - pEfsUserInfo->pUserCache->TimeStamp.QuadPart > CACHE_CERT_VALID_TIME )){
|
|
|
|
//
|
|
// It is due to check the certificate.
|
|
// Do cert validity checking.
|
|
//
|
|
|
|
LONG IsCertBeingValidated;
|
|
|
|
IsCertBeingValidated = InterlockedExchange(&UserCertIsValidating, 1);
|
|
|
|
if (IsCertBeingValidated != 1) {
|
|
|
|
|
|
if ( CertVerifyTimeValidity(
|
|
NULL,
|
|
pEfsUserInfo->pUserCache->pCertContext->pCertInfo
|
|
)){
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
pEfsUserInfo->pUserCache->CertValidated = CERT_NOT_VALIDATED;
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
pEfsUserInfo->pUserCache = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Test the cert usage here.
|
|
// CERT_E_WRONG_USAGE
|
|
//
|
|
|
|
BOOL OidFound;
|
|
|
|
rc = EfsFindCertOid(
|
|
szOID_KP_EFS,
|
|
pEfsUserInfo->pUserCache->pCertContext,
|
|
&OidFound
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
if (OidFound) {
|
|
|
|
//
|
|
// Reset the time stamp.
|
|
// Do I need the sync object to protect this?
|
|
// Mixing the high word and low word is not a big problem here.
|
|
//
|
|
|
|
pEfsUserInfo->pUserCache->TimeStamp = TimeStamp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NO_USER_KEYS;
|
|
pEfsUserInfo->pUserCache->CertValidated = CERT_NOT_VALIDATED;
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
pEfsUserInfo->pUserCache = NULL;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (IsCertBeingValidated != 1) {
|
|
|
|
InterlockedExchange(&UserCertIsValidating, IsCertBeingValidated);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (rc == ERROR_NO_USER_KEYS) {
|
|
|
|
//
|
|
// That didn't work. Look for a cert in the
|
|
// user's MY store and use it.
|
|
//
|
|
|
|
rc = SearchMyStoreForEFSCert(
|
|
pEfsUserInfo,
|
|
hKey,
|
|
hProv,
|
|
ContainerName,
|
|
ProviderName,
|
|
DisplayInfo,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_NO_USER_KEYS) {
|
|
|
|
//
|
|
// That didn't work. Last resort:
|
|
// generate a new keyset for the user.
|
|
//
|
|
|
|
rc = GenerateUserKey(
|
|
pEfsUserInfo,
|
|
hKey,
|
|
hProv,
|
|
ContainerName,
|
|
ProviderName,
|
|
ProviderType,
|
|
DisplayInfo,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_RETRY) {
|
|
|
|
//
|
|
// There was another thread creating keys.
|
|
// Try one more time to get them.
|
|
// If this fails, fail the entire attempt.
|
|
//
|
|
|
|
rc = GetInfoFromCertHash(
|
|
pEfsUserInfo,
|
|
hKey,
|
|
hProv,
|
|
ContainerName,
|
|
ProviderName,
|
|
DisplayInfo,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
NTSTATUS
|
|
EfspGetUserName(
|
|
IN OUT PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the LSA Server worker routine for the LsaGetUserName
|
|
API.
|
|
|
|
|
|
WARNING: This routine allocates memory for its output. The caller is
|
|
responsible for freeing this memory after use. See description of the
|
|
Names parameter.
|
|
|
|
Arguments:
|
|
|
|
UserName - Receives name of the current user.
|
|
|
|
DomainName - Optionally receives domain name of the current user.
|
|
|
|
Return Values:
|
|
|
|
NTSTATUS - Standard Nt Result Code
|
|
|
|
STATUS_SUCCESS - The call completed successfully and all Sids have
|
|
been translated to names.
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
|
|
such as memory to complete the call.
|
|
--*/
|
|
|
|
{
|
|
PUNICODE_STRING AccountName = NULL;
|
|
PUNICODE_STRING AuthorityName = NULL;
|
|
PUNICODE_STRING ProfilePath = NULL;
|
|
PLSAP_LOGON_SESSION LogonSession;
|
|
NTSTATUS Status;
|
|
|
|
|
|
//
|
|
// Let's see if we're trying to look up the currently logged on
|
|
// user.
|
|
//
|
|
//
|
|
// TokenUserInformation from this call must be freed by calling
|
|
// LsapFreeLsaHeap().
|
|
//
|
|
|
|
Status = EfspGetTokenUser( pEfsUserInfo );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
pEfsUserInfo->lpUserSid = ConvertSidToWideCharString( pEfsUserInfo->pTokenUser->User.Sid );
|
|
|
|
if (pEfsUserInfo->lpUserSid) {
|
|
|
|
//
|
|
// If the user ID is Anonymous then there is no name and domain in the
|
|
// logon session
|
|
//
|
|
|
|
if (RtlEqualSid(
|
|
pEfsUserInfo->pTokenUser->User.Sid,
|
|
LsapAnonymousSid
|
|
)) {
|
|
|
|
DebugLog((DEB_WARN, "Current user is Anonymous\n" ));
|
|
AccountName = &WellKnownSids[LsapAnonymousSidIndex].Name;
|
|
AuthorityName = &WellKnownSids[LsapAnonymousSidIndex].DomainName;
|
|
ProfilePath = NULL;
|
|
|
|
} else {
|
|
|
|
LogonSession = LsapLocateLogonSession ( &(pEfsUserInfo->AuthId) );
|
|
|
|
//
|
|
// During setup, we may get NULL returned for the logon session.
|
|
//
|
|
|
|
if (LogonSession != NULL) {
|
|
|
|
//
|
|
// Got a match. Get the username and domain information
|
|
// from the LogonId
|
|
//
|
|
|
|
AccountName = &LogonSession->AccountName;
|
|
AuthorityName = &LogonSession->AuthorityName;
|
|
ProfilePath = &LogonSession->ProfilePath;
|
|
|
|
LsapReleaseLogonSession( LogonSession );
|
|
|
|
} else {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
}
|
|
}
|
|
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
|
|
pEfsUserInfo->lpUserName = (PWSTR)LsapAllocateLsaHeap(AccountName->Length + sizeof(UNICODE_NULL));
|
|
|
|
if (pEfsUserInfo->lpUserName != NULL) {
|
|
|
|
memcpy( pEfsUserInfo->lpUserName, AccountName->Buffer, AccountName->Length );
|
|
(pEfsUserInfo->lpUserName)[AccountName->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
|
|
pEfsUserInfo->lpDomainName = (PWSTR)LsapAllocateLsaHeap(AuthorityName->Length + sizeof(UNICODE_NULL));
|
|
|
|
if (pEfsUserInfo->lpDomainName != NULL) {
|
|
|
|
memcpy( pEfsUserInfo->lpDomainName, AuthorityName->Buffer, AuthorityName->Length );
|
|
(pEfsUserInfo->lpDomainName)[AuthorityName->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if ((ProfilePath != NULL) && (ProfilePath->Length != 0)) {
|
|
|
|
pEfsUserInfo->lpProfilePath = (PWSTR)LsapAllocateLsaHeap(ProfilePath->Length + sizeof(UNICODE_NULL));
|
|
|
|
if (pEfsUserInfo->lpProfilePath != NULL) {
|
|
|
|
memcpy( pEfsUserInfo->lpProfilePath, ProfilePath->Buffer, ProfilePath->Length );
|
|
(pEfsUserInfo->lpProfilePath)[ProfilePath->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
pEfsUserInfo->lpKeyPath = ConstructKeyPath(pEfsUserInfo->lpUserSid);
|
|
if ( pEfsUserInfo->lpKeyPath == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Something failed, clean up what we were going to return
|
|
//
|
|
|
|
EfspFreeUserInfo( pEfsUserInfo );
|
|
memset( pEfsUserInfo, 0, sizeof( EFS_USER_INFO ));
|
|
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
DWORD
|
|
EfspReplaceUserKeyInformation(
|
|
PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Forces the regeneration of the user's EFS key.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Win32 error or ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
LPWSTR ProviderName;
|
|
LPWSTR ContainerName;
|
|
LPWSTR DisplayInfo;
|
|
DWORD ProviderType;
|
|
DWORD cbHash;
|
|
PBYTE pbHash;
|
|
DWORD rc;
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
|
|
//
|
|
// We are going to create a new key. The current cache is going away.
|
|
// We will insert a new cache node in the header.
|
|
//
|
|
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
pEfsUserInfo->pUserCache = NULL;
|
|
|
|
}
|
|
|
|
rc = RegDeleteKey(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
rc = GenerateUserKey(
|
|
pEfsUserInfo,
|
|
NULL,
|
|
NULL,
|
|
&ContainerName,
|
|
&ProviderName,
|
|
&ProviderType,
|
|
&DisplayInfo,
|
|
&pbHash,
|
|
&cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (ContainerName) {
|
|
LsapFreeLsaHeap( ContainerName );
|
|
}
|
|
if (ProviderName) {
|
|
LsapFreeLsaHeap( ProviderName );
|
|
}
|
|
if (DisplayInfo) {
|
|
LsapFreeLsaHeap( DisplayInfo );
|
|
}
|
|
if (pbHash) {
|
|
LsapFreeLsaHeap( pbHash );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
DWORD
|
|
EfspInstallCertAsUserKey(
|
|
PEFS_USER_INFO pEfsUserInfo,
|
|
PENCRYPTION_CERTIFICATE pEncryptionCertificate
|
|
)
|
|
{
|
|
//
|
|
// Find the passed certificate in the user's MY store.
|
|
// If it's not there, we don't use the cert.
|
|
//
|
|
|
|
DWORD rc = ERROR_SUCCESS;
|
|
PCCERT_CONTEXT pCertContext;
|
|
DWORD cbHash = 0;
|
|
PBYTE pbHash;
|
|
BOOLEAN bIsValid;
|
|
|
|
//
|
|
// If this fails, there's no cert that matches.
|
|
//
|
|
|
|
|
|
pCertContext = CertCreateCertificateContext(
|
|
pEncryptionCertificate->pCertBlob->dwCertEncodingType,
|
|
pEncryptionCertificate->pCertBlob->pbData,
|
|
pEncryptionCertificate->pCertBlob->cbData
|
|
);
|
|
|
|
if (pCertContext) {
|
|
|
|
if (CertGetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
NULL,
|
|
&cbHash
|
|
)) {
|
|
|
|
pbHash = (PBYTE)LsapAllocateLsaHeap( cbHash );
|
|
|
|
if (pbHash) {
|
|
|
|
if (CertGetCertificateContextProperty(
|
|
pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
pbHash,
|
|
&cbHash
|
|
)) {
|
|
|
|
HCRYPTKEY hKey;
|
|
HCRYPTPROV hProv;
|
|
LPWSTR ContainerName;
|
|
LPWSTR ProviderName;
|
|
LPWSTR DisplayInfo;
|
|
|
|
if (pEfsUserInfo->pUserCache) {
|
|
|
|
//
|
|
// We are going to create a new key. The current cache is going away.
|
|
// We will insert a new cache node in the header.
|
|
//
|
|
|
|
EfspReleaseUserCache(pEfsUserInfo->pUserCache);
|
|
pEfsUserInfo->pUserCache = NULL;
|
|
|
|
}
|
|
|
|
rc = GetKeyInfoFromCertHash(
|
|
pEfsUserInfo,
|
|
pbHash,
|
|
cbHash,
|
|
&hKey,
|
|
&hProv,
|
|
&ContainerName,
|
|
&ProviderName,
|
|
&DisplayInfo,
|
|
&bIsValid
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
//
|
|
// We don't care about any of the stuff that came back,
|
|
// all we care about is that it was all found, meaning
|
|
// that the key exists and can be used. Now that we know
|
|
// that, we can jam the hash into the registry.
|
|
//
|
|
|
|
HKEY KeyHandle;
|
|
DWORD Disposition;
|
|
|
|
rc = RegCreateKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
TEXT("REG_SZ"),
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&KeyHandle,
|
|
&Disposition // address of disposition value buffer
|
|
);
|
|
|
|
if (ERROR_SUCCESS == rc) {
|
|
|
|
rc = RegSetValueEx(
|
|
KeyHandle, // handle of key to set value for
|
|
CERT_HASH,
|
|
0,
|
|
REG_BINARY,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
|
|
RegCloseKey( KeyHandle );
|
|
}
|
|
|
|
if (hKey) {
|
|
|
|
//
|
|
// hKey not NULL means we have not put the data in the cache.
|
|
//
|
|
|
|
CryptDestroyKey( hKey );
|
|
CryptReleaseContext( hProv, 0 );
|
|
LsapFreeLsaHeap( ContainerName );
|
|
LsapFreeLsaHeap( ProviderName );
|
|
LsapFreeLsaHeap( DisplayInfo );
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
LsapFreeLsaHeap( pbHash );
|
|
|
|
} else {
|
|
|
|
rc = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
CertFreeCertificateContext( pCertContext );
|
|
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
}
|
|
|
|
|
|
return( rc );
|
|
}
|
|
|
|
BOOLEAN
|
|
CurrentHashOK(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
IN PBYTE pbHash,
|
|
IN DWORD cbHash,
|
|
OUT DWORD *dFlag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See if the value pbHash is already in the user's key. If not, try to see if we can add it back.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo -- User Info
|
|
|
|
pbHash -- Hash value
|
|
|
|
cbHash -- Hash length
|
|
|
|
dFlag -- Cert Flag to indicate if the cert has been added to the LM intermediate store
|
|
|
|
Return Value:
|
|
|
|
TRUE if the pbHash found or added successfully
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD rc;
|
|
BOOLEAN b = FALSE;
|
|
HKEY hRegKey = NULL;
|
|
PBYTE pbLocalHash;
|
|
DWORD cbLocalHash;
|
|
|
|
rc = RegOpenKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
GENERIC_READ,
|
|
&hRegKey
|
|
);
|
|
|
|
*dFlag = 0;
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// If there's a certificate thumbprint there, get it and use it.
|
|
//
|
|
|
|
DWORD Type;
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
&cbLocalHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (cbLocalHash == cbHash) {
|
|
//
|
|
// Query out the thumbprint, find the cert, and return the key information.
|
|
//
|
|
|
|
if (pbLocalHash = (PBYTE)LsapAllocateLsaHeap( cbLocalHash )) {
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
pbLocalHash,
|
|
&cbLocalHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Check if the hash value matches
|
|
//
|
|
|
|
if (RtlEqualMemory( pbLocalHash, pbHash, cbHash)){
|
|
|
|
b = TRUE;
|
|
cbLocalHash = sizeof (DWORD);
|
|
if (RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_FLAG,
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE) dFlag,
|
|
&cbLocalHash
|
|
)){
|
|
//
|
|
// Make sure dFlag set to 0 if error occurs. This may not be needed.
|
|
//
|
|
|
|
*dFlag = 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LsapFreeLsaHeap(pbLocalHash);
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hRegKey );
|
|
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Let's see if we can create one
|
|
//
|
|
|
|
DWORD Disposition = 0;
|
|
|
|
//
|
|
// Assume that there's no current EFS information
|
|
// for this guy. Create the registry key.
|
|
//
|
|
|
|
rc = RegCreateKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
TEXT("REG_SZ"),
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hRegKey,
|
|
&Disposition // address of disposition value buffer
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
rc = RegSetValueEx(
|
|
hRegKey, // handle of key to set value for
|
|
CERT_HASH,
|
|
0,
|
|
REG_BINARY,
|
|
pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
b = TRUE;
|
|
|
|
}
|
|
|
|
RegCloseKey( hRegKey );
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
DWORD
|
|
GetCurrentHash(
|
|
IN PEFS_USER_INFO pEfsUserInfo,
|
|
OUT PBYTE *pbHash,
|
|
OUT DWORD *cbHash
|
|
)
|
|
{
|
|
HKEY hRegKey = NULL;
|
|
DWORD rc;
|
|
|
|
ASSERT(pbHash);
|
|
ASSERT(cbHash);
|
|
|
|
*pbHash = NULL;
|
|
|
|
rc = RegOpenKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
GENERIC_READ,
|
|
&hRegKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
DWORD Type;
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
cbHash
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
if (*pbHash = (PBYTE)LsapAllocateLsaHeap( *cbHash )) {
|
|
|
|
rc = RegQueryValueEx(
|
|
hRegKey,
|
|
CERT_HASH,
|
|
NULL,
|
|
&Type,
|
|
*pbHash,
|
|
cbHash
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
|
|
LsapFreeLsaHeap(*pbHash);
|
|
*pbHash = NULL;
|
|
|
|
}
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hRegKey );
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EfspInitUserCacheNode(
|
|
IN OUT PUSER_CACHE pCacheNode,
|
|
IN PBYTE pbHash,
|
|
IN DWORD cbHash,
|
|
IN LPWSTR ContainerName,
|
|
IN LPWSTR ProviderName,
|
|
IN LPWSTR DisplayInformation,
|
|
IN PCCERT_CONTEXT pCertContext,
|
|
IN HCRYPTKEY hKey,
|
|
IN HCRYPTPROV hProv,
|
|
IN LUID *AuthId,
|
|
IN LONG CertValidated
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the cache node and insert it into the list.
|
|
|
|
Arguments:
|
|
|
|
pCacheNode -- Node to be inserted
|
|
|
|
pbHash -- User Cert Hash
|
|
|
|
cbHash -- Length of the hash data
|
|
|
|
ContainerName -- Container name
|
|
|
|
ProviderName -- Provider name
|
|
|
|
DisplayInformation -- Display information
|
|
|
|
pCertContext -- Cert Context
|
|
|
|
hKey -- User Key
|
|
|
|
hProv -- Provider handle
|
|
|
|
AuthId -- Authentication ID
|
|
|
|
CertValidated -- Cert validation info
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful
|
|
|
|
--*/
|
|
{
|
|
if (!pCacheNode) {
|
|
return FALSE;
|
|
}
|
|
pCacheNode->pbHash = pbHash;
|
|
pCacheNode->cbHash = cbHash;
|
|
pCacheNode->ContainerName = ContainerName;
|
|
pCacheNode->ProviderName = ProviderName;
|
|
pCacheNode->DisplayInformation = DisplayInformation;
|
|
pCacheNode->pCertContext = pCertContext;
|
|
pCacheNode->hUserKey = hKey;
|
|
pCacheNode->hProv = hProv;
|
|
pCacheNode->CertValidated = CertValidated;
|
|
pCacheNode->UseRefCount = 1; // The caller's hold on this node
|
|
pCacheNode->StopUseCount = 0;
|
|
pCacheNode->AuthId = *AuthId;
|
|
|
|
return (EfspAddUserCache(pCacheNode));
|
|
}
|
|
|
|
BOOL
|
|
EfsGetBasicConstraintExt(
|
|
IN OUT PCERT_EXTENSION *basicRestraint
|
|
)
|
|
{
|
|
|
|
BOOL bRet = TRUE;
|
|
CERT_BASIC_CONSTRAINTS2_INFO CertConstraints2;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
RtlZeroMemory( &CertConstraints2, sizeof(CERT_BASIC_CONSTRAINTS2_INFO) );
|
|
|
|
*basicRestraint = (PCERT_EXTENSION) LsapAllocateLsaHeap(sizeof(CERT_EXTENSION));
|
|
if (*basicRestraint) {
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_BASIC_CONSTRAINTS2,
|
|
&CertConstraints2,
|
|
NULL,
|
|
&((*basicRestraint)->Value.cbData)
|
|
);
|
|
|
|
if (bRet) {
|
|
|
|
(*basicRestraint)->Value.pbData = (PBYTE) LsapAllocateLsaHeap( (*basicRestraint)->Value.cbData );
|
|
if ((*basicRestraint)->Value.pbData) {
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_BASIC_CONSTRAINTS2,
|
|
&CertConstraints2,
|
|
(*basicRestraint)->Value.pbData,
|
|
&((*basicRestraint)->Value.cbData)
|
|
);
|
|
|
|
if (bRet) {
|
|
(*basicRestraint)->pszObjId = szOID_BASIC_CONSTRAINTS2;
|
|
(*basicRestraint)->fCritical = FALSE;
|
|
} else {
|
|
|
|
rc = GetLastError();
|
|
LsapFreeLsaHeap((*basicRestraint)->Value.pbData);
|
|
SetLastError(rc);
|
|
|
|
}
|
|
} else {
|
|
bRet = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (!bRet) {
|
|
|
|
rc = GetLastError();
|
|
LsapFreeLsaHeap(*basicRestraint);
|
|
SetLastError(rc);
|
|
*basicRestraint = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
bRet = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
BOOL
|
|
EfsGetAltNameExt(
|
|
IN OUT PCERT_EXTENSION *altNameExt,
|
|
IN LPWSTR UPNName
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
DWORD cbData = 0;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
CERT_NAME_VALUE prnName;
|
|
CERT_OTHER_NAME certOtherName;
|
|
CERT_ALT_NAME_ENTRY altName;
|
|
CERT_ALT_NAME_INFO nameInfo;
|
|
|
|
*altNameExt = (PCERT_EXTENSION) LsapAllocateLsaHeap(sizeof(CERT_EXTENSION));
|
|
if (*altNameExt) {
|
|
prnName.dwValueType = CERT_RDN_UTF8_STRING;
|
|
prnName.Value.cbData = (wcslen(UPNName) + 1) * sizeof(WCHAR);
|
|
prnName.Value.pbData = (BYTE *)UPNName;
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
&prnName,
|
|
NULL,
|
|
&certOtherName.Value.cbData
|
|
);
|
|
if (bRet) {
|
|
|
|
certOtherName.Value.pbData = (PBYTE)LsapAllocateLsaHeap( certOtherName.Value.cbData );
|
|
if (certOtherName.Value.pbData) {
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
&prnName,
|
|
certOtherName.Value.pbData,
|
|
&certOtherName.Value.cbData
|
|
);
|
|
|
|
if (bRet) {
|
|
|
|
altName.dwAltNameChoice = CERT_ALT_NAME_OTHER_NAME;
|
|
certOtherName.pszObjId = szOID_NT_PRINCIPAL_NAME;
|
|
altName.pOtherName = &certOtherName;
|
|
nameInfo.cAltEntry = 1;
|
|
nameInfo.rgAltEntry = &altName;
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
szOID_SUBJECT_ALT_NAME,
|
|
&nameInfo,
|
|
NULL,
|
|
&((*altNameExt)->Value.cbData)
|
|
);
|
|
if (bRet) {
|
|
|
|
(*altNameExt)->Value.pbData = (PBYTE) LsapAllocateLsaHeap( (*altNameExt)->Value.cbData );
|
|
if ((*altNameExt)->Value.pbData) {
|
|
|
|
bRet = CryptEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
szOID_SUBJECT_ALT_NAME,
|
|
&nameInfo,
|
|
(*altNameExt)->Value.pbData,
|
|
&((*altNameExt)->Value.cbData)
|
|
);
|
|
if (bRet) {
|
|
(*altNameExt)->pszObjId = szOID_SUBJECT_ALT_NAME2;
|
|
(*altNameExt)->fCritical = FALSE;
|
|
} else {
|
|
|
|
DWORD rc = GetLastError();
|
|
LsapFreeLsaHeap((*altNameExt)->Value.pbData);
|
|
SetLastError(rc);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bRet = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
rc = GetLastError();
|
|
LsapFreeLsaHeap(certOtherName.Value.pbData);
|
|
SetLastError(rc);
|
|
|
|
} else {
|
|
bRet = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (!bRet) {
|
|
|
|
GetLastError();
|
|
LsapFreeLsaHeap(*altNameExt);
|
|
SetLastError(rc);
|
|
*altNameExt = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
bRet = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
VOID
|
|
EfsMarkCertAddedToStore(
|
|
IN PEFS_USER_INFO pEfsUserInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Mark in the registry that we have add the cert to the LM store.
|
|
|
|
Arguments:
|
|
|
|
pEfsUserInfo -- User Info
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HKEY hRegKey = NULL;
|
|
DWORD rc;
|
|
|
|
rc = RegOpenKeyEx(
|
|
KEYPATHROOT,
|
|
pEfsUserInfo->lpKeyPath,
|
|
0,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&hRegKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
rc = CERTINLMTRUSTEDSTORE;
|
|
|
|
RegSetValueEx(
|
|
hRegKey, // handle of key to set value for
|
|
CERT_FLAG,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&rc,
|
|
sizeof (DWORD)
|
|
);
|
|
|
|
RegCloseKey( hRegKey );
|
|
}
|
|
|
|
return;
|
|
}
|