Index: engines/e_capi.c =================================================================== --- engines/e_capi.c (revision 29716) +++ engines/e_capi.c (working copy) @@ -12,7 +12,7 @@ #ifdef _WIN32 # ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0400 +# define _WIN32_WINNT 0x0600 # endif # include # include @@ -21,10 +21,11 @@ # include # include # include +# include # ifndef alloca # define alloca _alloca # endif - +# pragma comment(lib, "shlwapi.lib") # include # ifndef OPENSSL_NO_CAPIENG @@ -34,6 +35,15 @@ # include # include +struct X509_name_st { + STACK_OF(X509_NAME_ENTRY) *entries; /* DN components */ + int modified; /* true if 'bytes' needs to be built */ + BUF_MEM *bytes; /* cached encoding: cannot be NULL */ + /* canonical encoding used for rapid Name comparison */ + unsigned char *canon_enc; + int canon_enclen; +} /* X509_NAME */ ; + /* * This module uses several "new" interfaces, among which is * CertGetCertificateContextProperty. CERT_KEY_PROV_INFO_PROP_ID is @@ -102,6 +112,96 @@ # include "e_capi_err.h" # include "e_capi_err.c" +char lastUsedAuthCacheHash[100] = {0}; + + void TSVN_GetSHA1HashFromX509(STACK_OF(X509_NAME) *ca_dn, char * outbuf) + { + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + DWORD cbHash = 0; + BYTE rgbHash[20]; + char sha1hashstring[50]; + CHAR rgbDigits[] = "0123456789abcdef"; + int i; + X509_NAME * nm; + + outbuf[0] = 0; + if (CryptAcquireContext(&hProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + { + if (CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) + { + for (i = 0; i < sk_X509_NAME_num(ca_dn); ++i) + { + nm = sk_X509_NAME_value(ca_dn, i); + CryptHashData(hHash, nm->canon_enc, nm->canon_enclen, 0); + } + + cbHash = 20; + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) + { + for (i = 0; i < (int)cbHash; ++i) + { + sha1hashstring[i*2] = rgbDigits[rgbHash[i] >> 4]; + sha1hashstring[i*2+1] = rgbDigits[rgbHash[i] & 0xf]; + } + sha1hashstring[cbHash] = 0; + strcpy(outbuf, sha1hashstring); + } + CryptDestroyHash(hHash); + } + CryptReleaseContext(hProv, 0); + } + } + + int TSVN_GetSavedIndexForHash(const char* hash) + { + int ret = -1; + DWORD dwType = 0; + DWORD dwData = 0; + DWORD dwDataSize = 4; + int bLoad = 1; + if (SHGetValueA(HKEY_CURRENT_USER, "Software\\TortoiseSVN\\CAPIAuthz", hash, &dwType, &dwData, &dwDataSize) == ERROR_SUCCESS) + { + if (dwType == REG_DWORD) + { + ret = (int)dwData; + } + } + return ret; + } + + void TSVN_SaveIndexForHash(const char* hash, int index) + { + DWORD value = index; + SHSetValueA(HKEY_CURRENT_USER, "Software\\TortoiseSVN\\CAPIAuthz", hash, REG_DWORD, &value, sizeof(value)); + } + + void TSVN_ClearLastUsedAuthCache() + { + SHDeleteValueA(HKEY_CURRENT_USER, "Software\\TortoiseSVN\\CAPIAuthz", lastUsedAuthCacheHash); + } + + BOOL CALLBACK FindWindoProc(HWND hwnd, LPARAM lParam) + { + HWND * pWnd; + DWORD pid = 0; + if ((GetWindowLongPtr(hwnd, GWL_STYLE) & WS_VISIBLE)) + { + GetWindowThreadProcessId(hwnd, &pid); + if (pid == GetCurrentProcessId()) + { + pWnd = (HWND*)lParam; + (*pWnd) = hwnd; + return FALSE; + } + } + return TRUE; + } + static const char *engine_capi_id = "capi"; static const char *engine_capi_name = "CryptoAPI ENGINE"; @@ -597,6 +697,22 @@ void engine_load_capi_int(void) { + DWORD dwType = 0; + DWORD dwData = 0; + DWORD dwDataSize = 4; + int bLoad = 1; + if (SHGetValueA(HKEY_CURRENT_USER, "Software\\TortoiseSVN", "OpenSSLCapi", &dwType, &dwData, &dwDataSize) == ERROR_SUCCESS) + { + if (dwType == REG_DWORD) + { + if (dwData == 0) + { + bLoad = 0; + } + } + } + if (bLoad) + { /* Copied from eng_[openssl|dyn].c */ ENGINE *toadd = engine_capi(); if (!toadd) @@ -614,6 +730,7 @@ * ENGINE_load_builtin_engines() perhaps). */ ERR_pop_to_mark(); + } } # endif @@ -1538,9 +1655,11 @@ dwFlags = CRYPT_MACHINE_KEYSET; if (!CryptAcquireContextW(&key->hprov, contname, provname, ptype, dwFlags)) { - CAPIerr(CAPI_F_CAPI_GET_KEY, CAPI_R_CRYPTACQUIRECONTEXT_ERROR); - capi_addlasterror(); - goto err; + if (!CryptAcquireContext(&key->hprov, contname, provname, ptype, dwFlags)) { + CAPIerr(CAPI_F_CAPI_GET_KEY, CAPI_R_CRYPTACQUIRECONTEXT_ERROR); + capi_addlasterror(); + goto err; + } } if (!CryptGetUserKey(key->hprov, keyspec, &key->key)) { CAPIerr(CAPI_F_CAPI_GET_KEY, CAPI_R_GETUSERKEY_ERROR); @@ -1730,6 +1849,7 @@ PCCERT_CONTEXT cert = NULL, excert = NULL; CAPI_CTX *ctx; CAPI_KEY *key; + char hash[100]; ctx = ENGINE_get_ex_data(e, capi_idx); *pcert = NULL; @@ -1786,9 +1906,22 @@ return 0; /* Select the appropriate certificate */ + TSVN_GetSHA1HashFromX509(ca_dn, hash); + strcpy(lastUsedAuthCacheHash, hash); + client_cert_idx = TSVN_GetSavedIndexForHash(hash); + if ((client_cert_idx < 0) || (client_cert_idx >= sk_X509_num(certs))) + { + client_cert_idx = ctx->client_cert_select(e, ssl, certs); + if (client_cert_idx >= 0) + { + TSVN_SaveIndexForHash(hash, client_cert_idx); + } + } + else if (client_cert_idx >= sk_X509_num(certs)) + { + TSVN_ClearLastUsedAuthCache(); + } - client_cert_idx = ctx->client_cert_select(e, ssl, certs); - /* Set the selected certificate and free the rest */ for (i = 0; i < sk_X509_num(certs); i++) { @@ -1821,7 +1954,9 @@ static int cert_select_simple(ENGINE *e, SSL *ssl, STACK_OF(X509) *certs) { - return 0; + if (sk_X509_num(certs) == 1) + return 0; + return -1; /* let TSVN decide which certificate to use */ } # ifdef OPENSSL_CAPIENG_DIALOG @@ -1841,7 +1976,7 @@ # define CRYPTUI_SELECT_INTENDEDUSE_COLUMN 0x000000004 # endif -# define dlg_title L"OpenSSL Application SSL Client Certificate Selection" +# define dlg_title L"TortoiseSVN SSL Client Certificate Selection" # define dlg_prompt L"Select a certificate to use for authentication" # define dlg_columns CRYPTUI_SELECT_LOCATION_COLUMN \ |CRYPTUI_SELECT_INTENDEDUSE_COLUMN @@ -1879,8 +2014,10 @@ } } - hwnd = GetForegroundWindow(); + EnumWindows(FindWindoProc, (LPARAM)&hwnd); if (!hwnd) + hwnd = GetForegroundWindow(); + if (!hwnd) hwnd = GetActiveWindow(); if (!hwnd && ctx->getconswindow) hwnd = ctx->getconswindow();