| /* |
| * Copyright 2008-2021 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| /* We need to use some deprecated APIs */ |
| #define OPENSSL_SUPPRESS_DEPRECATED |
| |
| #ifdef _WIN32 |
| # ifndef _WIN32_WINNT |
| # define _WIN32_WINNT 0x0400 |
| # endif |
| # include <windows.h> |
| # include <wincrypt.h> |
| |
| # include <stdio.h> |
| # include <string.h> |
| # include <stdlib.h> |
| # include <malloc.h> |
| # ifndef alloca |
| # define alloca _alloca |
| # endif |
| |
| # include <openssl/crypto.h> |
| |
| # ifndef OPENSSL_NO_CAPIENG |
| |
| # include <openssl/buffer.h> |
| # include <openssl/bn.h> |
| # include <openssl/rsa.h> |
| # include <openssl/dsa.h> |
| |
| /* |
| * This module uses several "new" interfaces, among which is |
| * CertGetCertificateContextProperty. CERT_KEY_PROV_INFO_PROP_ID is |
| * one of possible values you can pass to function in question. By |
| * checking if it's defined we can see if wincrypt.h and accompanying |
| * crypt32.lib are in shape. The native MingW32 headers up to and |
| * including __W32API_VERSION 3.14 lack of struct DSSPUBKEY and the |
| * defines CERT_STORE_PROV_SYSTEM_A and CERT_STORE_READONLY_FLAG, |
| * so we check for these too and avoid compiling. |
| * Yes, it's rather "weak" test and if compilation fails, |
| * then re-configure with -DOPENSSL_NO_CAPIENG. |
| */ |
| # if defined(CERT_KEY_PROV_INFO_PROP_ID) && \ |
| defined(CERT_STORE_PROV_SYSTEM_A) && \ |
| defined(CERT_STORE_READONLY_FLAG) |
| # define __COMPILE_CAPIENG |
| # endif /* CERT_KEY_PROV_INFO_PROP_ID */ |
| # endif /* OPENSSL_NO_CAPIENG */ |
| #endif /* _WIN32 */ |
| |
| #ifdef __COMPILE_CAPIENG |
| |
| # undef X509_EXTENSIONS |
| |
| /* Definitions which may be missing from earlier version of headers */ |
| # ifndef CERT_STORE_OPEN_EXISTING_FLAG |
| # define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 |
| # endif |
| |
| # ifndef CERT_STORE_CREATE_NEW_FLAG |
| # define CERT_STORE_CREATE_NEW_FLAG 0x00002000 |
| # endif |
| |
| # ifndef CERT_SYSTEM_STORE_CURRENT_USER |
| # define CERT_SYSTEM_STORE_CURRENT_USER 0x00010000 |
| # endif |
| |
| # ifndef ALG_SID_SHA_256 |
| # define ALG_SID_SHA_256 12 |
| # endif |
| # ifndef ALG_SID_SHA_384 |
| # define ALG_SID_SHA_384 13 |
| # endif |
| # ifndef ALG_SID_SHA_512 |
| # define ALG_SID_SHA_512 14 |
| # endif |
| |
| # ifndef CALG_SHA_256 |
| # define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) |
| # endif |
| # ifndef CALG_SHA_384 |
| # define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) |
| # endif |
| # ifndef CALG_SHA_512 |
| # define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) |
| # endif |
| |
| # ifndef PROV_RSA_AES |
| # define PROV_RSA_AES 24 |
| # endif |
| |
| # include <openssl/engine.h> |
| # include <openssl/pem.h> |
| # include <openssl/x509v3.h> |
| |
| # include "e_capi_err.h" |
| # include "e_capi_err.c" |
| |
| static const char *engine_capi_id = "capi"; |
| static const char *engine_capi_name = "CryptoAPI ENGINE"; |
| |
| typedef struct CAPI_CTX_st CAPI_CTX; |
| typedef struct CAPI_KEY_st CAPI_KEY; |
| |
| static void capi_addlasterror(void); |
| static void capi_adderror(DWORD err); |
| |
| static void CAPI_trace(CAPI_CTX *ctx, char *format, ...); |
| |
| static int capi_list_providers(CAPI_CTX *ctx, BIO *out); |
| static int capi_list_containers(CAPI_CTX *ctx, BIO *out); |
| int capi_list_certs(CAPI_CTX *ctx, BIO *out, char *storename); |
| void capi_free_key(CAPI_KEY *key); |
| |
| static PCCERT_CONTEXT capi_find_cert(CAPI_CTX *ctx, const char *id, |
| HCERTSTORE hstore); |
| |
| CAPI_KEY *capi_find_key(CAPI_CTX *ctx, const char *id); |
| |
| static EVP_PKEY *capi_load_privkey(ENGINE *eng, const char *key_id, |
| UI_METHOD *ui_method, void *callback_data); |
| static int capi_rsa_sign(int dtype, const unsigned char *m, |
| unsigned int m_len, unsigned char *sigret, |
| unsigned int *siglen, const RSA *rsa); |
| static int capi_rsa_priv_enc(int flen, const unsigned char *from, |
| unsigned char *to, RSA *rsa, int padding); |
| static int capi_rsa_priv_dec(int flen, const unsigned char *from, |
| unsigned char *to, RSA *rsa, int padding); |
| static int capi_rsa_free(RSA *rsa); |
| |
| # ifndef OPENSSL_NO_DSA |
| static DSA_SIG *capi_dsa_do_sign(const unsigned char *digest, int dlen, |
| DSA *dsa); |
| static int capi_dsa_free(DSA *dsa); |
| # endif |
| |
| static int capi_load_ssl_client_cert(ENGINE *e, SSL *ssl, |
| STACK_OF(X509_NAME) *ca_dn, X509 **pcert, |
| EVP_PKEY **pkey, STACK_OF(X509) **pother, |
| UI_METHOD *ui_method, |
| void *callback_data); |
| |
| static int cert_select_simple(ENGINE *e, SSL *ssl, STACK_OF(X509) *certs); |
| # ifdef OPENSSL_CAPIENG_DIALOG |
| static int cert_select_dialog(ENGINE *e, SSL *ssl, STACK_OF(X509) *certs); |
| # endif |
| |
| void engine_load_capi_int(void); |
| |
| typedef PCCERT_CONTEXT(WINAPI *CERTDLG)(HCERTSTORE, HWND, LPCWSTR, |
| LPCWSTR, DWORD, DWORD, void *); |
| typedef HWND(WINAPI *GETCONSWIN)(void); |
| |
| /* |
| * This structure contains CAPI ENGINE specific data: it contains various |
| * global options and affects how other functions behave. |
| */ |
| |
| # define CAPI_DBG_TRACE 2 |
| # define CAPI_DBG_ERROR 1 |
| |
| struct CAPI_CTX_st { |
| int debug_level; |
| char *debug_file; |
| /* Parameters to use for container lookup */ |
| DWORD keytype; |
| LPSTR cspname; |
| DWORD csptype; |
| /* Certificate store name to use */ |
| LPSTR storename; |
| LPSTR ssl_client_store; |
| /* System store flags */ |
| DWORD store_flags; |
| /* Lookup string meanings in load_private_key */ |
| # define CAPI_LU_SUBSTR 1 /* Substring of subject: uses "storename" */ |
| # define CAPI_LU_FNAME 2 /* Friendly name: uses storename */ |
| # define CAPI_LU_CONTNAME 3 /* Container name: uses cspname, keytype */ |
| int lookup_method; |
| /* Info to dump with dumpcerts option */ |
| # define CAPI_DMP_SUMMARY 0x1 /* Issuer and serial name strings */ |
| # define CAPI_DMP_FNAME 0x2 /* Friendly name */ |
| # define CAPI_DMP_FULL 0x4 /* Full X509_print dump */ |
| # define CAPI_DMP_PEM 0x8 /* Dump PEM format certificate */ |
| # define CAPI_DMP_PSKEY 0x10 /* Dump pseudo key (if possible) */ |
| # define CAPI_DMP_PKEYINFO 0x20 /* Dump key info (if possible) */ |
| DWORD dump_flags; |
| int (*client_cert_select) (ENGINE *e, SSL *ssl, STACK_OF(X509) *certs); |
| CERTDLG certselectdlg; |
| GETCONSWIN getconswindow; |
| }; |
| |
| static CAPI_CTX *capi_ctx_new(void); |
| static void capi_ctx_free(CAPI_CTX *ctx); |
| static int capi_ctx_set_provname(CAPI_CTX *ctx, LPSTR pname, DWORD type, |
| int check); |
| static int capi_ctx_set_provname_idx(CAPI_CTX *ctx, int idx); |
| |
| # define CAPI_CMD_LIST_CERTS ENGINE_CMD_BASE |
| # define CAPI_CMD_LOOKUP_CERT (ENGINE_CMD_BASE + 1) |
| # define CAPI_CMD_DEBUG_LEVEL (ENGINE_CMD_BASE + 2) |
| # define CAPI_CMD_DEBUG_FILE (ENGINE_CMD_BASE + 3) |
| # define CAPI_CMD_KEYTYPE (ENGINE_CMD_BASE + 4) |
| # define CAPI_CMD_LIST_CSPS (ENGINE_CMD_BASE + 5) |
| # define CAPI_CMD_SET_CSP_IDX (ENGINE_CMD_BASE + 6) |
| # define CAPI_CMD_SET_CSP_NAME (ENGINE_CMD_BASE + 7) |
| # define CAPI_CMD_SET_CSP_TYPE (ENGINE_CMD_BASE + 8) |
| # define CAPI_CMD_LIST_CONTAINERS (ENGINE_CMD_BASE + 9) |
| # define CAPI_CMD_LIST_OPTIONS (ENGINE_CMD_BASE + 10) |
| # define CAPI_CMD_LOOKUP_METHOD (ENGINE_CMD_BASE + 11) |
| # define CAPI_CMD_STORE_NAME (ENGINE_CMD_BASE + 12) |
| # define CAPI_CMD_STORE_FLAGS (ENGINE_CMD_BASE + 13) |
| |
| static const ENGINE_CMD_DEFN capi_cmd_defns[] = { |
| {CAPI_CMD_LIST_CERTS, |
| "list_certs", |
| "List all certificates in store", |
| ENGINE_CMD_FLAG_NO_INPUT}, |
| {CAPI_CMD_LOOKUP_CERT, |
| "lookup_cert", |
| "Lookup and output certificates", |
| ENGINE_CMD_FLAG_STRING}, |
| {CAPI_CMD_DEBUG_LEVEL, |
| "debug_level", |
| "debug level (1=errors, 2=trace)", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_DEBUG_FILE, |
| "debug_file", |
| "debugging filename)", |
| ENGINE_CMD_FLAG_STRING}, |
| {CAPI_CMD_KEYTYPE, |
| "key_type", |
| "Key type: 1=AT_KEYEXCHANGE (default), 2=AT_SIGNATURE", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_LIST_CSPS, |
| "list_csps", |
| "List all CSPs", |
| ENGINE_CMD_FLAG_NO_INPUT}, |
| {CAPI_CMD_SET_CSP_IDX, |
| "csp_idx", |
| "Set CSP by index", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_SET_CSP_NAME, |
| "csp_name", |
| "Set CSP name, (default CSP used if not specified)", |
| ENGINE_CMD_FLAG_STRING}, |
| {CAPI_CMD_SET_CSP_TYPE, |
| "csp_type", |
| "Set CSP type, (default RSA_PROV_FULL)", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_LIST_CONTAINERS, |
| "list_containers", |
| "list container names", |
| ENGINE_CMD_FLAG_NO_INPUT}, |
| {CAPI_CMD_LIST_OPTIONS, |
| "list_options", |
| "Set list options (1=summary,2=friendly name, 4=full printout, 8=PEM output, 16=XXX, " |
| "32=private key info)", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_LOOKUP_METHOD, |
| "lookup_method", |
| "Set key lookup method (1=substring, 2=friendlyname, 3=container name)", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| {CAPI_CMD_STORE_NAME, |
| "store_name", |
| "certificate store name, default \"MY\"", |
| ENGINE_CMD_FLAG_STRING}, |
| {CAPI_CMD_STORE_FLAGS, |
| "store_flags", |
| "Certificate store flags: 1 = system store", |
| ENGINE_CMD_FLAG_NUMERIC}, |
| |
| {0, NULL, NULL, 0} |
| }; |
| |
| static int capi_idx = -1; |
| static int rsa_capi_idx = -1; |
| static int dsa_capi_idx = -1; |
| static int cert_capi_idx = -1; |
| |
| static int capi_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) |
| { |
| int ret = 1; |
| CAPI_CTX *ctx; |
| BIO *out; |
| LPSTR tmpstr; |
| if (capi_idx == -1) { |
| CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_ENGINE_NOT_INITIALIZED); |
| return 0; |
| } |
| ctx = ENGINE_get_ex_data(e, capi_idx); |
| out = BIO_new_fp(stdout, BIO_NOCLOSE); |
| if (out == NULL) { |
| CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_FILE_OPEN_ERROR); |
| return 0; |
| } |
| switch (cmd) { |
| case CAPI_CMD_LIST_CSPS: |
| ret = capi_list_providers(ctx, out); |
| break; |
| |
| case CAPI_CMD_LIST_CERTS: |
| ret = capi_list_certs(ctx, out, NULL); |
| break; |
| |
| case CAPI_CMD_LOOKUP_CERT: |
| ret = capi_list_certs(ctx, out, p); |
| break; |
| |
| case CAPI_CMD_LIST_CONTAINERS: |
| ret = capi_list_containers(ctx, out); |
| break; |
| |
| case CAPI_CMD_STORE_NAME: |
| tmpstr = OPENSSL_strdup(p); |
| if (tmpstr != NULL) { |
| OPENSSL_free(ctx->storename); |
| ctx->storename = tmpstr; |
| CAPI_trace(ctx, "Setting store name to %s\n", p); |
| } else { |
| CAPIerr(CAPI_F_CAPI_CTRL, ERR_R_MALLOC_FAILURE); |
| ret = 0; |
| } |
| break; |
| |
| case CAPI_CMD_STORE_FLAGS: |
| if (i & 1) { |
| ctx->store_flags |= CERT_SYSTEM_STORE_LOCAL_MACHINE; |
| ctx->store_flags &= ~CERT_SYSTEM_STORE_CURRENT_USER; |
| } else { |
| ctx->store_flags |= CERT_SYSTEM_STORE_CURRENT_USER; |
| ctx->store_flags &= ~CERT_SYSTEM_STORE_LOCAL_MACHINE; |
| } |
| CAPI_trace(ctx, "Setting flags to %d\n", i); |
| break; |
| |
| case CAPI_CMD_DEBUG_LEVEL: |
| ctx->debug_level = (int)i; |
| CAPI_trace(ctx, "Setting debug level to %d\n", ctx->debug_level); |
| break; |
| |
| case CAPI_CMD_DEBUG_FILE: |
| tmpstr = OPENSSL_strdup(p); |
| if (tmpstr != NULL) { |
| ctx->debug_file = tmpstr; |
| CAPI_trace(ctx, "Setting debug file to %s\n", ctx->debug_file); |
| } else { |
| CAPIerr(CAPI_F_CAPI_CTRL, ERR_R_MALLOC_FAILURE); |
| ret = 0; |
| } |
| break; |
| |
| case CAPI_CMD_KEYTYPE: |
| ctx->keytype = i; |
| CAPI_trace(ctx, "Setting key type to %d\n", ctx->keytype); |
| break; |
| |
| case CAPI_CMD_SET_CSP_IDX: |
| ret = capi_ctx_set_provname_idx(ctx, i); |
| break; |
| |
| case CAPI_CMD_LIST_OPTIONS: |
| ctx->dump_flags = i; |
| break; |
| |
| case CAPI_CMD_LOOKUP_METHOD: |
| if (i < 1 || i > 3) { |
| CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_INVALID_LOOKUP_METHOD); |
| BIO_free(out); |
| return 0; |
| } |
| ctx->lookup_method = i; |
| break; |
| |
| case CAPI_CMD_SET_CSP_NAME: |
| ret = capi_ctx_set_provname(ctx, p, ctx->csptype, 1); |
| break; |
| |
| case CAPI_CMD_SET_CSP_TYPE: |
| ctx->csptype = i; |
| break; |
| |
| default: |
| CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_UNKNOWN_COMMAND); |
| ret = 0; |
| } |
| |
| BIO_free(out); |
| return ret; |
| |
| } |
| |
| static RSA_METHOD *capi_rsa_method = NULL; |
| # ifndef OPENSSL_NO_DSA |
| static DSA_METHOD *capi_dsa_method = NULL; |
| # endif |
| |
| static int use_aes_csp = 0; |
| static const WCHAR rsa_aes_cspname[] = |
| L"Microsoft Enhanced RSA and AES Cryptographic Provider"; |
| static const WCHAR rsa_enh_cspname[] = |
| L"Microsoft Enhanced Cryptographic Provider v1.0"; |
| |
| static int capi_init(ENGINE *e) |
| { |
| CAPI_CTX *ctx; |
| const RSA_METHOD *ossl_rsa_meth; |
| # ifndef OPENSSL_NO_DSA |
| const DSA_METHOD *ossl_dsa_meth; |
| # endif |
| HCRYPTPROV hprov; |
| |
| if (capi_idx < 0) { |
| capi_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 0); |
| if (capi_idx < 0) |
| goto memerr; |
| |
| cert_capi_idx = X509_get_ex_new_index(0, NULL, NULL, NULL, 0); |
| |
| /* Setup RSA_METHOD */ |
| rsa_capi_idx = RSA_get_ex_new_index(0, NULL, NULL, NULL, 0); |
| ossl_rsa_meth = RSA_PKCS1_OpenSSL(); |
| if (!RSA_meth_set_pub_enc(capi_rsa_method, |
| RSA_meth_get_pub_enc(ossl_rsa_meth)) |
| || !RSA_meth_set_pub_dec(capi_rsa_method, |
| RSA_meth_get_pub_dec(ossl_rsa_meth)) |
| || !RSA_meth_set_priv_enc(capi_rsa_method, capi_rsa_priv_enc) |
| || !RSA_meth_set_priv_dec(capi_rsa_method, capi_rsa_priv_dec) |
| || !RSA_meth_set_mod_exp(capi_rsa_method, |
| RSA_meth_get_mod_exp(ossl_rsa_meth)) |
| || !RSA_meth_set_bn_mod_exp(capi_rsa_method, |
| RSA_meth_get_bn_mod_exp(ossl_rsa_meth)) |
| || !RSA_meth_set_finish(capi_rsa_method, capi_rsa_free) |
| || !RSA_meth_set_sign(capi_rsa_method, capi_rsa_sign)) { |
| goto memerr; |
| } |
| |
| # ifndef OPENSSL_NO_DSA |
| /* Setup DSA Method */ |
| dsa_capi_idx = DSA_get_ex_new_index(0, NULL, NULL, NULL, 0); |
| ossl_dsa_meth = DSA_OpenSSL(); |
| if (!DSA_meth_set_sign(capi_dsa_method, capi_dsa_do_sign) |
| || !DSA_meth_set_verify(capi_dsa_method, |
| DSA_meth_get_verify(ossl_dsa_meth)) |
| || !DSA_meth_set_finish(capi_dsa_method, capi_dsa_free) |
| || !DSA_meth_set_mod_exp(capi_dsa_method, |
| DSA_meth_get_mod_exp(ossl_dsa_meth)) |
| || !DSA_meth_set_bn_mod_exp(capi_dsa_method, |
| DSA_meth_get_bn_mod_exp(ossl_dsa_meth))) { |
| goto memerr; |
| } |
| # endif |
| } |
| |
| ctx = capi_ctx_new(); |
| if (ctx == NULL) |
| goto memerr; |
| |
| ENGINE_set_ex_data(e, capi_idx, ctx); |
| |
| # ifdef OPENSSL_CAPIENG_DIALOG |
| { |
| HMODULE cryptui = LoadLibrary(TEXT("CRYPTUI.DLL")); |
| HMODULE kernel = GetModuleHandle(TEXT("KERNEL32.DLL")); |
| if (cryptui) |
| ctx->certselectdlg = |
| (CERTDLG) GetProcAddress(cryptui, |
| "CryptUIDlgSelectCertificateFromStore"); |
| if (kernel) |
| ctx->getconswindow = |
| (GETCONSWIN) GetProcAddress(kernel, "GetConsoleWindow"); |
| if (cryptui && !OPENSSL_isservice()) |
| ctx->client_cert_select = cert_select_dialog; |
| } |
| # endif |
| |
| /* See if there is RSA+AES CSP */ |
| if (CryptAcquireContextW(&hprov, NULL, rsa_aes_cspname, PROV_RSA_AES, |
| CRYPT_VERIFYCONTEXT)) { |
| use_aes_csp = 1; |
| CryptReleaseContext(hprov, 0); |
| } |
| |
| return 1; |
| |
| memerr: |
| CAPIerr(CAPI_F_CAPI_INIT, ERR_R_MALLOC_FAILURE); |
| return 0; |
| |
| return 1; |
| } |
| |
| static int capi_destroy(ENGINE *e) |
| { |
| RSA_meth_free(capi_rsa_method); |
| capi_rsa_method = NULL; |
| # ifndef OPENSSL_NO_DSA |
| DSA_meth_free(capi_dsa_method); |
| capi_dsa_method = NULL; |
| # endif |
| ERR_unload_CAPI_strings(); |
| return 1; |
| } |
| |
| static int capi_finish(ENGINE *e) |
| { |
| CAPI_CTX *ctx; |
| ctx = ENGINE_get_ex_data(e, capi_idx); |
| capi_ctx_free(ctx); |
| ENGINE_set_ex_data(e, capi_idx, NULL); |
| return 1; |
| } |
| |
| /* |
| * CryptoAPI key application data. This contains a handle to the private key |
| * container (for sign operations) and a handle to the key (for decrypt |
| * operations). |
| */ |
| |
| struct CAPI_KEY_st { |
| /* Associated certificate context (if any) */ |
| PCCERT_CONTEXT pcert; |
| HCRYPTPROV hprov; |
| HCRYPTKEY key; |
| DWORD keyspec; |
| }; |
| |
| static int bind_capi(ENGINE *e) |
| { |
| capi_rsa_method = RSA_meth_new("CryptoAPI RSA method", 0); |
| if (capi_rsa_method == NULL) |
| return 0; |
| # ifndef OPENSSL_NO_DSA |
| capi_dsa_method = DSA_meth_new("CryptoAPI DSA method", 0); |
| if (capi_dsa_method == NULL) |
| goto memerr; |
| # endif |
| if (!ENGINE_set_id(e, engine_capi_id) |
| || !ENGINE_set_name(e, engine_capi_name) |
| || !ENGINE_set_flags(e, ENGINE_FLAGS_NO_REGISTER_ALL) |
| || !ENGINE_set_init_function(e, capi_init) |
| || !ENGINE_set_finish_function(e, capi_finish) |
| || !ENGINE_set_destroy_function(e, capi_destroy) |
| || !ENGINE_set_RSA(e, capi_rsa_method) |
| # ifndef OPENSSL_NO_DSA |
| || !ENGINE_set_DSA(e, capi_dsa_method) |
| # endif |
| || !ENGINE_set_load_privkey_function(e, capi_load_privkey) |
| || !ENGINE_set_load_ssl_client_cert_function(e, |
| capi_load_ssl_client_cert) |
| || !ENGINE_set_cmd_defns(e, capi_cmd_defns) |
| || !ENGINE_set_ctrl_function(e, capi_ctrl)) |
| goto memerr; |
| ERR_load_CAPI_strings(); |
| |
| return 1; |
| memerr: |
| RSA_meth_free(capi_rsa_method); |
| capi_rsa_method = NULL; |
| # ifndef OPENSSL_NO_DSA |
| DSA_meth_free(capi_dsa_method); |
| capi_dsa_method = NULL; |
| # endif |
| return 0; |
| } |
| |
| # ifndef OPENSSL_NO_DYNAMIC_ENGINE |
| static int bind_helper(ENGINE *e, const char *id) |
| { |
| if (id && (strcmp(id, engine_capi_id) != 0)) |
| return 0; |
| if (!bind_capi(e)) |
| return 0; |
| return 1; |
| } |
| |
| IMPLEMENT_DYNAMIC_CHECK_FN() |
| IMPLEMENT_DYNAMIC_BIND_FN(bind_helper) |
| # else |
| static ENGINE *engine_capi(void) |
| { |
| ENGINE *ret = ENGINE_new(); |
| if (ret == NULL) |
| return NULL; |
| if (!bind_capi(ret)) { |
| ENGINE_free(ret); |
| return NULL; |
| } |
| return ret; |
| } |
| |
| void engine_load_capi_int(void) |
| { |
| /* Copied from eng_[openssl|dyn].c */ |
| ENGINE *toadd = engine_capi(); |
| if (!toadd) |
| return; |
| ERR_set_mark(); |
| ENGINE_add(toadd); |
| /* |
| * If the "add" worked, it gets a structural reference. So either way, we |
| * release our just-created reference. |
| */ |
| ENGINE_free(toadd); |
| /* |
| * If the "add" didn't work, it was probably a conflict because it was |
| * already added (eg. someone calling ENGINE_load_blah then calling |
| * ENGINE_load_builtin_engines() perhaps). |
| */ |
| ERR_pop_to_mark(); |
| } |
| # endif |
| |
| static int lend_tobn(BIGNUM *bn, unsigned char *bin, int binlen) |
| { |
| int i; |
| /* |
| * Reverse buffer in place: since this is a keyblob structure that will |
| * be freed up after conversion anyway it doesn't matter if we change |
| * it. |
| */ |
| for (i = 0; i < binlen / 2; i++) { |
| unsigned char c; |
| c = bin[i]; |
| bin[i] = bin[binlen - i - 1]; |
| bin[binlen - i - 1] = c; |
| } |
| |
| if (!BN_bin2bn(bin, binlen, bn)) |
| return 0; |
| return 1; |
| } |
| |
| /* Given a CAPI_KEY get an EVP_PKEY structure */ |
| |
| static EVP_PKEY *capi_get_pkey(ENGINE *eng, CAPI_KEY *key) |
| { |
| unsigned char *pubkey = NULL; |
| DWORD len; |
| BLOBHEADER *bh; |
| RSA *rkey = NULL; |
| DSA *dkey = NULL; |
| EVP_PKEY *ret = NULL; |
| if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, NULL, &len)) { |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, CAPI_R_PUBKEY_EXPORT_LENGTH_ERROR); |
| capi_addlasterror(); |
| return NULL; |
| } |
| |
| pubkey = OPENSSL_malloc(len); |
| |
| if (pubkey == NULL) |
| goto memerr; |
| |
| if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, pubkey, &len)) { |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, CAPI_R_PUBKEY_EXPORT_ERROR); |
| capi_addlasterror(); |
| goto err; |
| } |
| |
| bh = (BLOBHEADER *) pubkey; |
| if (bh->bType != PUBLICKEYBLOB) { |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, CAPI_R_INVALID_PUBLIC_KEY_BLOB); |
| goto err; |
| } |
| if (bh->aiKeyAlg == CALG_RSA_SIGN || bh->aiKeyAlg == CALG_RSA_KEYX) { |
| RSAPUBKEY *rp; |
| DWORD rsa_modlen; |
| BIGNUM *e = NULL, *n = NULL; |
| unsigned char *rsa_modulus; |
| rp = (RSAPUBKEY *) (bh + 1); |
| if (rp->magic != 0x31415352) { |
| char magstr[10]; |
| BIO_snprintf(magstr, 10, "%lx", rp->magic); |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, |
| CAPI_R_INVALID_RSA_PUBLIC_KEY_BLOB_MAGIC_NUMBER); |
| ERR_add_error_data(2, "magic=0x", magstr); |
| goto err; |
| } |
| rsa_modulus = (unsigned char *)(rp + 1); |
| rkey = RSA_new_method(eng); |
| if (!rkey) |
| goto memerr; |
| |
| e = BN_new(); |
| n = BN_new(); |
| |
| if (e == NULL || n == NULL) { |
| BN_free(e); |
| BN_free(n); |
| goto memerr; |
| } |
| |
| RSA_set0_key(rkey, n, e, NULL); |
| |
| if (!BN_set_word(e, rp->pubexp)) |
| goto memerr; |
| |
| rsa_modlen = rp->bitlen / 8; |
| if (!lend_tobn(n, rsa_modulus, rsa_modlen)) |
| goto memerr; |
| |
| RSA_set_ex_data(rkey, rsa_capi_idx, key); |
| |
| if ((ret = EVP_PKEY_new()) == NULL) |
| goto memerr; |
| |
| EVP_PKEY_assign_RSA(ret, rkey); |
| rkey = NULL; |
| |
| # ifndef OPENSSL_NO_DSA |
| } else if (bh->aiKeyAlg == CALG_DSS_SIGN) { |
| DSSPUBKEY *dp; |
| DWORD dsa_plen; |
| unsigned char *btmp; |
| BIGNUM *p, *q, *g, *pub_key; |
| dp = (DSSPUBKEY *) (bh + 1); |
| if (dp->magic != 0x31535344) { |
| char magstr[10]; |
| BIO_snprintf(magstr, 10, "%lx", dp->magic); |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, |
| CAPI_R_INVALID_DSA_PUBLIC_KEY_BLOB_MAGIC_NUMBER); |
| ERR_add_error_data(2, "magic=0x", magstr); |
| goto err; |
| } |
| dsa_plen = dp->bitlen / 8; |
| btmp = (unsigned char *)(dp + 1); |
| dkey = DSA_new_method(eng); |
| if (!dkey) |
| goto memerr; |
| p = BN_new(); |
| q = BN_new(); |
| g = BN_new(); |
| pub_key = BN_new(); |
| if (p == NULL || q == NULL || g == NULL || pub_key == NULL) { |
| BN_free(p); |
| BN_free(q); |
| BN_free(g); |
| BN_free(pub_key); |
| goto memerr; |
| } |
| DSA_set0_pqg(dkey, p, q, g); |
| DSA_set0_key(dkey, pub_key, NULL); |
| if (!lend_tobn(p, btmp, dsa_plen)) |
| goto memerr; |
| btmp += dsa_plen; |
| if (!lend_tobn(q, btmp, 20)) |
| goto memerr; |
| btmp += 20; |
| if (!lend_tobn(g, btmp, dsa_plen)) |
| goto memerr; |
| btmp += dsa_plen; |
| if (!lend_tobn(pub_key, btmp, dsa_plen)) |
| goto memerr; |
| btmp += dsa_plen; |
| |
| DSA_set_ex_data(dkey, dsa_capi_idx, key); |
| |
| if ((ret = EVP_PKEY_new()) == NULL) |
| goto memerr; |
| |
| EVP_PKEY_assign_DSA(ret, dkey); |
| dkey = NULL; |
| # endif |
| } else { |
| char algstr[10]; |
| BIO_snprintf(algstr, 10, "%ux", bh->aiKeyAlg); |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, |
| CAPI_R_UNSUPPORTED_PUBLIC_KEY_ALGORITHM); |
| ERR_add_error_data(2, "aiKeyAlg=0x", algstr); |
| goto err; |
| } |
| |
| err: |
| OPENSSL_free(pubkey); |
| if (!ret) { |
| RSA_free(rkey); |
| # ifndef OPENSSL_NO_DSA |
| DSA_free(dkey); |
| # endif |
| } |
| |
| return ret; |
| |
| memerr: |
| CAPIerr(CAPI_F_CAPI_GET_PKEY, ERR_R_MALLOC_FAILURE); |
| goto err; |
| |
| } |
| |
| static EVP_PKEY *capi_load_privkey(ENGINE *eng, const char *key_id, |
| UI_METHOD *ui_method, void *callback_data) |
| { |
| CAPI_CTX *ctx; |
| CAPI_KEY *key; |
| EVP_PKEY *ret; |
| ctx = ENGINE_get_ex_data(eng, capi_idx); |
| |
| if (!ctx) { |
| CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_CANT_FIND_CAPI_CONTEXT); |
| return NULL; |
| } |
| |
| key = capi_find_key(ctx, key_id); |
| |
| if (!key) |
| return NULL; |
| |
| ret = capi_get_pkey(eng, key); |
| |
| if (!ret) |
| capi_free_key(key); |
| return ret; |
| |
| } |
| |
| /* CryptoAPI RSA operations */ |
| |
| int capi_rsa_priv_enc(int flen, const unsigned char *from, |
| unsigned char *to, RSA *rsa, int padding) |
| { |
| CAPIerr(CAPI_F_CAPI_RSA_PRIV_ENC, CAPI_R_FUNCTION_NOT_SUPPORTED); |
| return -1; |
| } |
| |
| int capi_rsa_sign(int dtype, const unsigned char *m, unsigned int m_len, |
| unsigned char *sigret, unsigned int *siglen, const RSA *rsa) |
| { |
| ALG_ID alg; |
| HCRYPTHASH hash; |
| DWORD slen; |
| unsigned int i; |
| int ret = -1; |
| CAPI_KEY *capi_key; |
| CAPI_CTX *ctx; |
| |
| ctx = ENGINE_get_ex_data(RSA_get0_engine(rsa), capi_idx); |
| |
| CAPI_trace(ctx, "Called CAPI_rsa_sign()\n"); |
| |
| capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); |
| if (!capi_key) { |
| CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_GET_KEY); |
| return -1; |
| } |
| /* Convert the signature type to a CryptoAPI algorithm ID */ |
| switch (dtype) { |
| case NID_sha256: |
| alg = CALG_SHA_256; |
| break; |
| |
| case NID_sha384: |
| alg = CALG_SHA_384; |
| break; |
| |
| case NID_sha512: |
| alg = CALG_SHA_512; |
| break; |
| |
| case NID_sha1: |
| alg = CALG_SHA1; |
| break; |
| |
| case NID_md5: |
| alg = CALG_MD5; |
| break; |
| |
| case NID_md5_sha1: |
| alg = CALG_SSL3_SHAMD5; |
| break; |
| default: |
| { |
| char algstr[10]; |
| BIO_snprintf(algstr, 10, "%x", dtype); |
| CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_UNSUPPORTED_ALGORITHM_NID); |
| ERR_add_error_data(2, "NID=0x", algstr); |
| return -1; |
| } |
| } |
| |
| /* Create the hash object */ |
| if (!CryptCreateHash(capi_key->hprov, alg, 0, 0, &hash)) { |
| CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_CREATE_HASH_OBJECT); |
| capi_addlasterror(); |
| return -1; |
| } |
| /* Set the hash value to the value passed */ |
| |
| if (!CryptSetHashParam(hash, HP_HASHVAL, (unsigned char *)m, 0)) { |
| CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_SET_HASH_VALUE); |
| capi_addlasterror(); |
| goto err; |
| } |
| |
| /* Finally sign it */ |
| slen = RSA_size(rsa); |
| if (!CryptSignHash(hash, capi_key->keyspec, NULL, 0, sigret, &slen)) { |
| CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_ERROR_SIGNING_HASH); |
| capi_addlasterror(); |
| goto err; |
| } else { |
| ret = 1; |
| /* Inplace byte reversal of signature */ |
| for (i = 0; i < slen / 2; i++) { |
| unsigned char c; |
| c = sigret[i]; |
| sigret[i] = sigret[slen - i - 1]; |
| sigret[slen - i - 1] = c; |
| } |
| *siglen = slen; |
| } |
| |
| /* Now cleanup */ |
| |
| err: |
| CryptDestroyHash(hash); |
| |
| return ret; |
| } |
| |
| int capi_rsa_priv_dec(int flen, const unsigned char *from, |
| unsigned char *to, RSA *rsa, int padding) |
| { |
| int i; |
| unsigned char *tmpbuf; |
| CAPI_KEY *capi_key; |
| CAPI_CTX *ctx; |
| DWORD flags = 0; |
| DWORD dlen; |
| |
| if (flen <= 0) |
| return flen; |
| |
| ctx = ENGINE_get_ex_data(RSA_get0_engine(rsa), capi_idx); |
| |
| CAPI_trace(ctx, "Called capi_rsa_priv_dec()\n"); |
| |
| capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); |
| if (!capi_key) { |
| CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_CANT_GET_KEY); |
| return -1; |
| } |
| |
| switch (padding) { |
| case RSA_PKCS1_PADDING: |
| /* Nothing to do */ |
| break; |
| #ifdef CRYPT_DECRYPT_RSA_NO_PADDING_CHECK |
| case RSA_NO_PADDING: |
| flags = CRYPT_DECRYPT_RSA_NO_PADDING_CHECK; |
| break; |
| #endif |
| default: |
| { |
| char errstr[10]; |
| BIO_snprintf(errstr, 10, "%d", padding); |
| CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_UNSUPPORTED_PADDING); |
| ERR_add_error_data(2, "padding=", errstr); |
| return -1; |
| } |
| } |
| |
| /* Create temp reverse order version of input */ |
| if ((tmpbuf = OPENSSL_malloc(flen)) == NULL) { |
| CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, ERR_R_MALLOC_FAILURE); |
| return -1; |
| } |
| for (i = 0; i < flen; i++) |
| tmpbuf[flen - i - 1] = from[i]; |
| |
| /* Finally decrypt it */ |
| dlen = flen; |
| if (!CryptDecrypt(capi_key->key, 0, TRUE, flags, tmpbuf, &dlen)) { |
| CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_DECRYPT_ERROR); |
| capi_addlasterror(); |
| OPENSSL_cleanse(tmpbuf, dlen); |
| OPENSSL_free(tmpbuf); |
| return -1; |
| } else { |
| memcpy(to, tmpbuf, (flen = (int)dlen)); |
| } |
| OPENSSL_cleanse(tmpbuf, flen); |
| OPENSSL_free(tmpbuf); |
| |
| return flen; |
| } |
| |
| static int capi_rsa_free(RSA *rsa) |
| { |
| CAPI_KEY *capi_key; |
| capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); |
| capi_free_key(capi_key); |
| RSA_set_ex_data(rsa, rsa_capi_idx, 0); |
| return 1; |
| } |
| |
| # ifndef OPENSSL_NO_DSA |
| /* CryptoAPI DSA operations */ |
| |
| static DSA_SIG *capi_dsa_do_sign(const unsigned char *digest, int dlen, |
| DSA *dsa) |
| { |
| HCRYPTHASH hash; |
| DWORD slen; |
| DSA_SIG *ret = NULL; |
| CAPI_KEY *capi_key; |
| CAPI_CTX *ctx; |
| unsigned char csigbuf[40]; |
| |
| ctx = ENGINE_get_ex_data(DSA_get0_engine(dsa), capi_idx); |
| |
| CAPI_trace(ctx, "Called CAPI_dsa_do_sign()\n"); |
| |
| capi_key = DSA_get_ex_data(dsa, dsa_capi_idx); |
| |
| if (!capi_key) { |
| CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_GET_KEY); |
| return NULL; |
| } |
| |
| if (dlen != 20) { |
| CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_INVALID_DIGEST_LENGTH); |
| return NULL; |
| } |
| |
| /* Create the hash object */ |
| if (!CryptCreateHash(capi_key->hprov, CALG_SHA1, 0, 0, &hash)) { |
| CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_CREATE_HASH_OBJECT); |
| capi_addlasterror(); |
| return NULL; |
| } |
| |
| /* Set the hash value to the value passed */ |
| if (!CryptSetHashParam(hash, HP_HASHVAL, (unsigned char *)digest, 0)) { |
| CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_SET_HASH_VALUE); |
| capi_addlasterror(); |
| goto err; |
| } |
| |
| /* Finally sign it */ |
| slen = sizeof(csigbuf); |
| if (!CryptSignHash(hash, capi_key->keyspec, NULL, 0, csigbuf, &slen)) { |
| CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_ERROR_SIGNING_HASH); |
| capi_addlasterror(); |
| goto err; |
| } else { |
| BIGNUM *r = BN_new(), *s = BN_new(); |
| |
| if (r == NULL || s == NULL |
| || !lend_tobn(r, csigbuf, 20) |
| || !lend_tobn(s, csigbuf + 20, 20) |
| || (ret = DSA_SIG_new()) == NULL) { |
| BN_free(r); /* BN_free checks for BIGNUM * being NULL */ |
| BN_free(s); |
| goto err; |
| } |
| DSA_SIG_set0(ret, r, s); |
| } |
| |
| /* Now cleanup */ |
| |
| err: |
| OPENSSL_cleanse(csigbuf, 40); |
| CryptDestroyHash(hash); |
| return ret; |
| } |
| |
| static int capi_dsa_free(DSA *dsa) |
| { |
| CAPI_KEY *capi_key; |
| capi_key = DSA_get_ex_data(dsa, dsa_capi_idx); |
| capi_free_key(capi_key); |
| DSA_set_ex_data(dsa, dsa_capi_idx, 0); |
| return 1; |
| } |
| # endif |
| |
| static void capi_vtrace(CAPI_CTX *ctx, int level, char *format, |
| va_list argptr) |
| { |
| BIO *out; |
| |
| if (!ctx || (ctx->debug_level < level) || (!ctx->debug_file)) |
| return; |
| out = BIO_new_file(ctx->debug_file, "a+"); |
| if (out == NULL) { |
| CAPIerr(CAPI_F_CAPI_VTRACE, CAPI_R_FILE_OPEN_ERROR); |
| return; |
| } |
| BIO_vprintf(out, format, argptr); |
| BIO_free(out); |
| } |
| |
| static void CAPI_trace(CAPI_CTX *ctx, char *format, ...) |
| { |
| va_list args; |
| va_start(args, format); |
| capi_vtrace(ctx, CAPI_DBG_TRACE, format, args); |
| va_end(args); |
| } |
| |
| static void capi_addlasterror(void) |
| { |
| capi_adderror(GetLastError()); |
| } |
| |
| static void capi_adderror(DWORD err) |
| { |
| char errstr[10]; |
| BIO_snprintf(errstr, 10, "%lX", err); |
| ERR_add_error_data(2, "Error code= 0x", errstr); |
| } |
| |
| static char *wide_to_asc(LPCWSTR wstr) |
| { |
| char *str; |
| int len_0, sz; |
| size_t len_1; |
| |
| if (!wstr) |
| return NULL; |
| |
| len_1 = wcslen(wstr) + 1; |
| |
| if (len_1 > INT_MAX) { |
| CAPIerr(CAPI_F_WIDE_TO_ASC, CAPI_R_FUNCTION_NOT_SUPPORTED); |
| return NULL; |
| } |
| |
| len_0 = (int)len_1; /* WideCharToMultiByte expects int */ |
| sz = WideCharToMultiByte(CP_ACP, 0, wstr, len_0, NULL, 0, NULL, NULL); |
| if (!sz) { |
| CAPIerr(CAPI_F_WIDE_TO_ASC, CAPI_R_WIN32_ERROR); |
| return NULL; |
| } |
| str = OPENSSL_malloc(sz); |
| if (str == NULL) { |
| CAPIerr(CAPI_F_WIDE_TO_ASC, ERR_R_MALLOC_FAILURE); |
| return NULL; |
| } |
| if (!WideCharToMultiByte(CP_ACP, 0, wstr, len_0, str, sz, NULL, NULL)) { |
| OPENSSL_free(str); |
| CAPIerr(CAPI_F_WIDE_TO_ASC, CAPI_R_WIN32_ERROR); |
| return NULL; |
| } |
| return str; |
| } |
| |
| static int capi_get_provname(CAPI_CTX *ctx, LPSTR *pname, DWORD *ptype, |
| DWORD idx) |
| { |
| DWORD len, err; |
| LPTSTR name; |
| CAPI_trace(ctx, "capi_get_provname, index=%d\n", idx); |
| if (!CryptEnumProviders(idx, NULL, 0, ptype, NULL, &len)) { |
| err = GetLastError(); |
| if (err == ERROR_NO_MORE_ITEMS) |
| return 2; |
| CAPIerr(CAPI_F_CAPI_GET_PROVNAME, CAPI_R_CRYPTENUMPROVIDERS_ERROR); |
| capi_adderror(err); |
| return 0; |
| } |
| name = OPENSSL_malloc(len); |
| if (name == NULL) { |
| CAPIerr(CAPI_F_CAPI_GET_PROVNAME, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| if (!CryptEnumProviders(idx, NULL, 0, ptype, name, &len)) { |
| err = GetLastError(); |
| OPENSSL_free(name); |
| if (err == ERROR_NO_MORE_ITEMS) |
| return 2; |
| CAPIerr(CAPI_F_CAPI_GET_PROVNAME, CAPI_R_CRYPTENUMPROVIDERS_ERROR); |
| capi_adderror(err); |
| return 0; |
| } |
| if (sizeof(TCHAR) != sizeof(char)) { |
| *pname = wide_to_asc((WCHAR *)name); |
| OPENSSL_free(name); |
| if (*pname == NULL) |
| return 0; |
| } else { |
| *pname = (char *)name; |
| } |
| CAPI_trace(ctx, "capi_get_provname, returned name=%s, type=%d\n", *pname, |
| *ptype); |
| |
| return 1; |
| } |
| |
| static int capi_list_providers(CAPI_CTX *ctx, BIO *out) |
| { |
| DWORD idx, ptype; |
| int ret; |
| LPSTR provname = NULL; |
| CAPI_trace(ctx, "capi_list_providers\n"); |
| BIO_printf(out, "Available CSPs:\n"); |
| for (idx = 0;; idx++) { |
| ret = capi_get_provname(ctx, &provname, &ptype, idx); |
| if (ret == 2) |
| break; |
| if (ret == 0) |
| break; |
| BIO_printf(out, "%lu. %s, type %lu\n", idx, provname, ptype); |
| OPENSSL_free(provname); |
| } |
| return 1; |
| } |
| |
| static int capi_list_containers(CAPI_CTX *ctx, BIO *out) |
| { |
| int ret = 1; |
| HCRYPTPROV hprov; |
| DWORD err, idx, flags, buflen = 0, clen; |
| LPSTR cname; |
| LPWSTR cspname = NULL; |
| |
| CAPI_trace(ctx, "Listing containers CSP=%s, type = %d\n", ctx->cspname, |
| ctx->csptype); |
| if (ctx->cspname != NULL) { |
| if ((clen = MultiByteToWideChar(CP_ACP, 0, ctx->cspname, -1, |
| NULL, 0))) { |
| cspname = alloca(clen * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, ctx->cspname, -1, (WCHAR *)cspname, |
| clen); |
| } |
| if (cspname == NULL) { |
| CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, ERR_R_MALLOC_FAILURE); |
| capi_addlasterror(); |
| return 0; |
| } |
| } |
| if (!CryptAcquireContextW(&hprov, NULL, cspname, ctx->csptype, |
| CRYPT_VERIFYCONTEXT)) { |
| CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, |
| CAPI_R_CRYPTACQUIRECONTEXT_ERROR); |
| capi_addlasterror(); |
| return 0; |
| } |
| if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS, NULL, &buflen, |
| CRYPT_FIRST)) { |
| CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, CAPI_R_ENUMCONTAINERS_ERROR); |
| capi_addlasterror(); |
| CryptReleaseContext(hprov, 0); |
| return 0; |
| } |
| CAPI_trace(ctx, "Got max container len %d\n", buflen); |
| if (buflen == 0) |
| buflen = 1024; |
| cname = OPENSSL_malloc(buflen); |
| if (cname == NULL) { |
| CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| |
| for (idx = 0;; idx++) { |
| clen = buflen; |
| cname[0] = 0; |
| |
| if (idx == 0) |
| flags = CRYPT_FIRST; |
| else |
| flags = 0; |
| if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS, (BYTE *)cname, |
| &clen, flags)) { |
| err = GetLastError(); |
| if (err == ERROR_NO_MORE_ITEMS) |
| goto done; |
| CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, CAPI_R_ENUMCONTAINERS_ERROR); |
| capi_adderror(err); |
| goto err; |
| } |
| CAPI_trace(ctx, "Container name %s, len=%d, index=%d, flags=%d\n", |
| cname, clen, idx, flags); |
| if (!cname[0] && (clen == buflen)) { |
| CAPI_trace(ctx, "Enumerate bug: using workaround\n"); |
| goto done; |
| } |
| BIO_printf(out, "%lu. %s\n", idx, cname); |
| } |
| err: |
| |
| ret = 0; |
| |
| done: |
| OPENSSL_free(cname); |
| CryptReleaseContext(hprov, 0); |
| |
| return ret; |
| } |
| |
| static CRYPT_KEY_PROV_INFO *capi_get_prov_info(CAPI_CTX *ctx, |
| PCCERT_CONTEXT cert) |
| { |
| DWORD len; |
| CRYPT_KEY_PROV_INFO *pinfo; |
| |
| if (!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, |
| NULL, &len)) |
| return NULL; |
| pinfo = OPENSSL_malloc(len); |
| if (pinfo == NULL) { |
| CAPIerr(CAPI_F_CAPI_GET_PROV_INFO, ERR_R_MALLOC_FAILURE); |
| return NULL; |
| } |
| if (!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, |
| pinfo, &len)) { |
| CAPIerr(CAPI_F_CAPI_GET_PROV_INFO, |
| CAPI_R_ERROR_GETTING_KEY_PROVIDER_INFO); |
| capi_addlasterror(); |
| OPENSSL_free(pinfo); |
| return NULL; |
| } |
| return pinfo; |
| } |
| |
| static void capi_dump_prov_info(CAPI_CTX *ctx, BIO *out, |
| CRYPT_KEY_PROV_INFO *pinfo) |
| { |
| char *provname = NULL, *contname = NULL; |
| |
| if (pinfo == NULL) { |
| BIO_printf(out, " No Private Key\n"); |
| return; |
| } |
| provname = wide_to_asc(pinfo->pwszProvName); |
| contname = wide_to_asc(pinfo->pwszContainerName); |
| if (provname == NULL || contname == NULL) |
| goto err; |
| |
| BIO_printf(out, " Private Key Info:\n"); |
| BIO_printf(out, " Provider Name: %s, Provider Type %lu\n", provname, |
| pinfo->dwProvType); |
| BIO_printf(out, " Container Name: %s, Key Type %lu\n", contname, |
| pinfo->dwKeySpec); |
| err: |
| OPENSSL_free(provname); |
| OPENSSL_free(contname); |
| } |
| |
| static char *capi_cert_get_fname(CAPI_CTX *ctx, PCCERT_CONTEXT cert) |
| { |
| LPWSTR wfname; |
| DWORD dlen; |
| |
| CAPI_trace(ctx, "capi_cert_get_fname\n"); |
| if (!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, |
| NULL, &dlen)) |
| return NULL; |
| wfname = OPENSSL_malloc(dlen); |
| if (wfname == NULL) |
| return NULL; |
| if (CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, |
| wfname, &dlen)) { |
| char *fname = wide_to_asc(wfname); |
| OPENSSL_free(wfname); |
| return fname; |
| } |
| CAPIerr(CAPI_F_CAPI_CERT_GET_FNAME, CAPI_R_ERROR_GETTING_FRIENDLY_NAME); |
| capi_addlasterror(); |
| |
| OPENSSL_free(wfname); |
| return NULL; |
| } |
| |
| static void capi_dump_cert(CAPI_CTX *ctx, BIO *out, PCCERT_CONTEXT cert) |
| { |
| X509 *x; |
| const unsigned char *p; |
| unsigned long flags = ctx->dump_flags; |
| if (flags & CAPI_DMP_FNAME) { |
| char *fname; |
| fname = capi_cert_get_fname(ctx, cert); |
| if (fname) { |
| BIO_printf(out, " Friendly Name \"%s\"\n", fname); |
| OPENSSL_free(fname); |
| } else { |
| BIO_printf(out, " <No Friendly Name>\n"); |
| } |
| } |
| |
| p = cert->pbCertEncoded; |
| x = d2i_X509(NULL, &p, cert->cbCertEncoded); |
| if (!x) |
| BIO_printf(out, " <Can't parse certificate>\n"); |
| if (flags & CAPI_DMP_SUMMARY) { |
| BIO_printf(out, " Subject: "); |
| X509_NAME_print_ex(out, X509_get_subject_name(x), 0, XN_FLAG_ONELINE); |
| BIO_printf(out, "\n Issuer: "); |
| X509_NAME_print_ex(out, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE); |
| BIO_printf(out, "\n"); |
| } |
| if (flags & CAPI_DMP_FULL) |
| X509_print_ex(out, x, XN_FLAG_ONELINE, 0); |
| |
| if (flags & CAPI_DMP_PKEYINFO) { |
| CRYPT_KEY_PROV_INFO *pinfo; |
| pinfo = capi_get_prov_info(ctx, cert); |
| capi_dump_prov_info(ctx, out, pinfo); |
| OPENSSL_free(pinfo); |
| } |
| |
| if (flags & CAPI_DMP_PEM) |
| PEM_write_bio_X509(out, x); |
| X509_free(x); |
| } |
| |
| static HCERTSTORE capi_open_store(CAPI_CTX *ctx, char *storename) |
| { |
| HCERTSTORE hstore; |
| |
| if (!storename) |
| storename = ctx->storename; |
| if (!storename) |
| storename = "MY"; |
| CAPI_trace(ctx, "Opening certificate store %s\n", storename); |
| |
| hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, |
| ctx->store_flags, storename); |
| if (!hstore) { |
| CAPIerr(CAPI_F_CAPI_OPEN_STORE, CAPI_R_ERROR_OPENING_STORE); |
| capi_addlasterror(); |
| } |
| return hstore; |
| } |
| |
| int capi_list_certs(CAPI_CTX *ctx, BIO *out, char *id) |
| { |
| char *storename; |
| int idx; |
| int ret = 1; |
| HCERTSTORE hstore; |
| PCCERT_CONTEXT cert = NULL; |
| |
| storename = ctx->storename; |
| if (!storename) |
| storename = "MY"; |
| CAPI_trace(ctx, "Listing certs for store %s\n", storename); |
| |
| hstore = capi_open_store(ctx, storename); |
| if (!hstore) |
| return 0; |
| if (id) { |
| cert = capi_find_cert(ctx, id, hstore); |
| if (!cert) { |
| ret = 0; |
| goto err; |
| } |
| capi_dump_cert(ctx, out, cert); |
| CertFreeCertificateContext(cert); |
| } else { |
| for (idx = 0;; idx++) { |
| cert = CertEnumCertificatesInStore(hstore, cert); |
| if (!cert) |
| break; |
| BIO_printf(out, "Certificate %d\n", idx); |
| capi_dump_cert(ctx, out, cert); |
| } |
| } |
| err: |
| CertCloseStore(hstore, 0); |
| return ret; |
| } |
| |
| static PCCERT_CONTEXT capi_find_cert(CAPI_CTX *ctx, const char *id, |
| HCERTSTORE hstore) |
| { |
| PCCERT_CONTEXT cert = NULL; |
| char *fname = NULL; |
| int match; |
| switch (ctx->lookup_method) { |
| case CAPI_LU_SUBSTR: |
| return CertFindCertificateInStore(hstore, X509_ASN_ENCODING, 0, |
| CERT_FIND_SUBJECT_STR_A, id, NULL); |
| case CAPI_LU_FNAME: |
| for (;;) { |
| cert = CertEnumCertificatesInStore(hstore, cert); |
| if (!cert) |
| return NULL; |
| fname = capi_cert_get_fname(ctx, cert); |
| if (fname) { |
| if (strcmp(fname, id)) |
| match = 0; |
| else |
| match = 1; |
| OPENSSL_free(fname); |
| if (match) |
| return cert; |
| } |
| } |
| default: |
| return NULL; |
| } |
| } |
| |
| static CAPI_KEY *capi_get_key(CAPI_CTX *ctx, const WCHAR *contname, |
| const WCHAR *provname, DWORD ptype, |
| DWORD keyspec) |
| { |
| DWORD dwFlags = 0; |
| CAPI_KEY *key = OPENSSL_malloc(sizeof(*key)); |
| |
| if (key == NULL) |
| return NULL; |
| /* If PROV_RSA_AES supported use it instead */ |
| if (ptype == PROV_RSA_FULL && use_aes_csp && |
| wcscmp(provname, rsa_enh_cspname) == 0) { |
| provname = rsa_aes_cspname; |
| ptype = PROV_RSA_AES; |
| } |
| if (ctx && ctx->debug_level >= CAPI_DBG_TRACE && ctx->debug_file) { |
| /* |
| * above 'if' is [complementary] copy from CAPI_trace and serves |
| * as optimization to minimize [below] malloc-ations |
| */ |
| char *_contname = wide_to_asc(contname); |
| char *_provname = wide_to_asc(provname); |
| |
| CAPI_trace(ctx, "capi_get_key, contname=%s, provname=%s, type=%d\n", |
| _contname, _provname, ptype); |
| OPENSSL_free(_provname); |
| OPENSSL_free(_contname); |
| } |
| if (ctx->store_flags & CERT_SYSTEM_STORE_LOCAL_MACHINE) |
| 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 (!CryptGetUserKey(key->hprov, keyspec, &key->key)) { |
| CAPIerr(CAPI_F_CAPI_GET_KEY, CAPI_R_GETUSERKEY_ERROR); |
| capi_addlasterror(); |
| CryptReleaseContext(key->hprov, 0); |
| goto err; |
| } |
| key->keyspec = keyspec; |
| key->pcert = NULL; |
| return key; |
| |
| err: |
| OPENSSL_free(key); |
| return NULL; |
| } |
| |
| static CAPI_KEY *capi_get_cert_key(CAPI_CTX *ctx, PCCERT_CONTEXT cert) |
| { |
| CAPI_KEY *key = NULL; |
| CRYPT_KEY_PROV_INFO *pinfo = NULL; |
| |
| pinfo = capi_get_prov_info(ctx, cert); |
| |
| if (pinfo != NULL) |
| key = capi_get_key(ctx, pinfo->pwszContainerName, pinfo->pwszProvName, |
| pinfo->dwProvType, pinfo->dwKeySpec); |
| |
| OPENSSL_free(pinfo); |
| return key; |
| } |
| |
| CAPI_KEY *capi_find_key(CAPI_CTX *ctx, const char *id) |
| { |
| PCCERT_CONTEXT cert; |
| HCERTSTORE hstore; |
| CAPI_KEY *key = NULL; |
| |
| switch (ctx->lookup_method) { |
| case CAPI_LU_SUBSTR: |
| case CAPI_LU_FNAME: |
| hstore = capi_open_store(ctx, NULL); |
| if (!hstore) |
| return NULL; |
| cert = capi_find_cert(ctx, id, hstore); |
| if (cert) { |
| key = capi_get_cert_key(ctx, cert); |
| CertFreeCertificateContext(cert); |
| } |
| CertCloseStore(hstore, 0); |
| break; |
| |
| case CAPI_LU_CONTNAME: |
| { |
| WCHAR *contname, *provname; |
| DWORD len; |
| |
| if ((len = MultiByteToWideChar(CP_ACP, 0, id, -1, NULL, 0)) && |
| (contname = alloca(len * sizeof(WCHAR)), |
| MultiByteToWideChar(CP_ACP, 0, id, -1, contname, len)) && |
| (len = MultiByteToWideChar(CP_ACP, 0, ctx->cspname, -1, |
| NULL, 0)) && |
| (provname = alloca(len * sizeof(WCHAR)), |
| MultiByteToWideChar(CP_ACP, 0, ctx->cspname, -1, |
| provname, len))) |
| key = capi_get_key(ctx, contname, provname, |
| ctx->csptype, ctx->keytype); |
| } |
| break; |
| } |
| |
| return key; |
| } |
| |
| void capi_free_key(CAPI_KEY *key) |
| { |
| if (!key) |
| return; |
| CryptDestroyKey(key->key); |
| CryptReleaseContext(key->hprov, 0); |
| if (key->pcert) |
| CertFreeCertificateContext(key->pcert); |
| OPENSSL_free(key); |
| } |
| |
| /* Initialize a CAPI_CTX structure */ |
| |
| static CAPI_CTX *capi_ctx_new(void) |
| { |
| CAPI_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); |
| |
| if (ctx == NULL) { |
| CAPIerr(CAPI_F_CAPI_CTX_NEW, ERR_R_MALLOC_FAILURE); |
| return NULL; |
| } |
| ctx->csptype = PROV_RSA_FULL; |
| ctx->dump_flags = CAPI_DMP_SUMMARY | CAPI_DMP_FNAME; |
| ctx->keytype = AT_KEYEXCHANGE; |
| ctx->store_flags = CERT_STORE_OPEN_EXISTING_FLAG | |
| CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER; |
| ctx->lookup_method = CAPI_LU_SUBSTR; |
| ctx->client_cert_select = cert_select_simple; |
| return ctx; |
| } |
| |
| static void capi_ctx_free(CAPI_CTX *ctx) |
| { |
| CAPI_trace(ctx, "Calling capi_ctx_free with %lx\n", ctx); |
| if (!ctx) |
| return; |
| OPENSSL_free(ctx->cspname); |
| OPENSSL_free(ctx->debug_file); |
| OPENSSL_free(ctx->storename); |
| OPENSSL_free(ctx->ssl_client_store); |
| OPENSSL_free(ctx); |
| } |
| |
| static int capi_ctx_set_provname(CAPI_CTX *ctx, LPSTR pname, DWORD type, |
| int check) |
| { |
| LPSTR tmpcspname; |
| |
| CAPI_trace(ctx, "capi_ctx_set_provname, name=%s, type=%d\n", pname, type); |
| if (check) { |
| HCRYPTPROV hprov; |
| LPWSTR name = NULL; |
| DWORD len; |
| |
| if ((len = MultiByteToWideChar(CP_ACP, 0, pname, -1, NULL, 0))) { |
| name = alloca(len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, pname, -1, (WCHAR *)name, len); |
| } |
| if (name == NULL || !CryptAcquireContextW(&hprov, NULL, name, type, |
| CRYPT_VERIFYCONTEXT)) { |
| CAPIerr(CAPI_F_CAPI_CTX_SET_PROVNAME, |
| CAPI_R_CRYPTACQUIRECONTEXT_ERROR); |
| capi_addlasterror(); |
| return 0; |
| } |
| CryptReleaseContext(hprov, 0); |
| } |
| tmpcspname = OPENSSL_strdup(pname); |
| if (tmpcspname == NULL) { |
| CAPIerr(CAPI_F_CAPI_CTX_SET_PROVNAME, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| OPENSSL_free(ctx->cspname); |
| ctx->cspname = tmpcspname; |
| ctx->csptype = type; |
| return 1; |
| } |
| |
| static int capi_ctx_set_provname_idx(CAPI_CTX *ctx, int idx) |
| { |
| LPSTR pname; |
| DWORD type; |
| int res; |
| if (capi_get_provname(ctx, &pname, &type, idx) != 1) |
| return 0; |
| res = capi_ctx_set_provname(ctx, pname, type, 0); |
| OPENSSL_free(pname); |
| return res; |
| } |
| |
| static int cert_issuer_match(STACK_OF(X509_NAME) *ca_dn, X509 *x) |
| { |
| int i; |
| X509_NAME *nm; |
| /* Special case: empty list: match anything */ |
| if (sk_X509_NAME_num(ca_dn) <= 0) |
| return 1; |
| for (i = 0; i < sk_X509_NAME_num(ca_dn); i++) { |
| nm = sk_X509_NAME_value(ca_dn, i); |
| if (!X509_NAME_cmp(nm, X509_get_issuer_name(x))) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int capi_load_ssl_client_cert(ENGINE *e, SSL *ssl, |
| STACK_OF(X509_NAME) *ca_dn, X509 **pcert, |
| EVP_PKEY **pkey, STACK_OF(X509) **pother, |
| UI_METHOD *ui_method, |
| void *callback_data) |
| { |
| STACK_OF(X509) *certs = NULL; |
| X509 *x; |
| char *storename; |
| const unsigned char *p; |
| int i, client_cert_idx; |
| HCERTSTORE hstore; |
| PCCERT_CONTEXT cert = NULL, excert = NULL; |
| CAPI_CTX *ctx; |
| CAPI_KEY *key; |
| ctx = ENGINE_get_ex_data(e, capi_idx); |
| |
| *pcert = NULL; |
| *pkey = NULL; |
| |
| storename = ctx->ssl_client_store; |
| if (!storename) |
| storename = "MY"; |
| |
| hstore = capi_open_store(ctx, storename); |
| if (!hstore) |
| return 0; |
| /* Enumerate all certificates collect any matches */ |
| for (i = 0;; i++) { |
| cert = CertEnumCertificatesInStore(hstore, cert); |
| if (!cert) |
| break; |
| p = cert->pbCertEncoded; |
| x = d2i_X509(NULL, &p, cert->cbCertEncoded); |
| if (!x) { |
| CAPI_trace(ctx, "Can't Parse Certificate %d\n", i); |
| continue; |
| } |
| if (cert_issuer_match(ca_dn, x) |
| && X509_check_purpose(x, X509_PURPOSE_SSL_CLIENT, 0)) { |
| key = capi_get_cert_key(ctx, cert); |
| if (!key) { |
| X509_free(x); |
| continue; |
| } |
| /* |
| * Match found: attach extra data to it so we can retrieve the |
| * key later. |
| */ |
| excert = CertDuplicateCertificateContext(cert); |
| key->pcert = excert; |
| X509_set_ex_data(x, cert_capi_idx, key); |
| |
| if (!certs) |
| certs = sk_X509_new_null(); |
| |
| sk_X509_push(certs, x); |
| } else { |
| X509_free(x); |
| } |
| } |
| |
| if (cert) |
| CertFreeCertificateContext(cert); |
| if (hstore) |
| CertCloseStore(hstore, 0); |
| |
| if (!certs) |
| return 0; |
| |
| /* Select the appropriate certificate */ |
| |
| 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++) { |
| x = sk_X509_value(certs, i); |
| if (i == client_cert_idx) |
| *pcert = x; |
| else { |
| key = X509_get_ex_data(x, cert_capi_idx); |
| capi_free_key(key); |
| X509_free(x); |
| } |
| } |
| |
| sk_X509_free(certs); |
| |
| if (*pcert == NULL) |
| return 0; |
| |
| /* Setup key for selected certificate */ |
| |
| key = X509_get_ex_data(*pcert, cert_capi_idx); |
| *pkey = capi_get_pkey(e, key); |
| X509_set_ex_data(*pcert, cert_capi_idx, NULL); |
| |
| return 1; |
| |
| } |
| |
| /* Simple client cert selection function: always select first */ |
| |
| static int cert_select_simple(ENGINE *e, SSL *ssl, STACK_OF(X509) *certs) |
| { |
| return 0; |
| } |
| |
| # ifdef OPENSSL_CAPIENG_DIALOG |
| |
| /* |
| * More complex cert selection function, using standard function |
| * CryptUIDlgSelectCertificateFromStore() to produce a dialog box. |
| */ |
| |
| /* |
| * Definitions which are in cryptuiapi.h but this is not present in older |
| * versions of headers. |
| */ |
| |
| # ifndef CRYPTUI_SELECT_LOCATION_COLUMN |
| # define CRYPTUI_SELECT_LOCATION_COLUMN 0x000000010 |
| # define CRYPTUI_SELECT_INTENDEDUSE_COLUMN 0x000000004 |
| # endif |
| |
| # define dlg_title L"OpenSSL Application 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 |
| |
| static int cert_select_dialog(ENGINE *e, SSL *ssl, STACK_OF(X509) *certs) |
| { |
| X509 *x; |
| HCERTSTORE dstore; |
| PCCERT_CONTEXT cert; |
| CAPI_CTX *ctx; |
| CAPI_KEY *key; |
| HWND hwnd; |
| int i, idx = -1; |
| if (sk_X509_num(certs) == 1) |
| return 0; |
| ctx = ENGINE_get_ex_data(e, capi_idx); |
| /* Create an in memory store of certificates */ |
| dstore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, |
| CERT_STORE_CREATE_NEW_FLAG, NULL); |
| if (!dstore) { |
| CAPIerr(CAPI_F_CERT_SELECT_DIALOG, CAPI_R_ERROR_CREATING_STORE); |
| capi_addlasterror(); |
| goto err; |
| } |
| /* Add all certificates to store */ |
| for (i = 0; i < sk_X509_num(certs); i++) { |
| x = sk_X509_value(certs, i); |
| key = X509_get_ex_data(x, cert_capi_idx); |
| |
| if (!CertAddCertificateContextToStore(dstore, key->pcert, |
| CERT_STORE_ADD_NEW, NULL)) { |
| CAPIerr(CAPI_F_CERT_SELECT_DIALOG, CAPI_R_ERROR_ADDING_CERT); |
| capi_addlasterror(); |
| goto err; |
| } |
| |
| } |
| hwnd = GetForegroundWindow(); |
| if (!hwnd) |
| hwnd = GetActiveWindow(); |
| if (!hwnd && ctx->getconswindow) |
| hwnd = ctx->getconswindow(); |
| /* Call dialog to select one */ |
| cert = ctx->certselectdlg(dstore, hwnd, dlg_title, dlg_prompt, |
| dlg_columns, 0, NULL); |
| |
| /* Find matching cert from list */ |
| if (cert) { |
| for (i = 0; i < sk_X509_num(certs); i++) { |
| x = sk_X509_value(certs, i); |
| key = X509_get_ex_data(x, cert_capi_idx); |
| if (CertCompareCertificate |
| (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert->pCertInfo, |
| key->pcert->pCertInfo)) { |
| idx = i; |
| break; |
| } |
| } |
| } |
| |
| err: |
| if (dstore) |
| CertCloseStore(dstore, 0); |
| return idx; |
| |
| } |
| # endif |
| |
| #else /* !__COMPILE_CAPIENG */ |
| # include <openssl/engine.h> |
| # ifndef OPENSSL_NO_DYNAMIC_ENGINE |
| OPENSSL_EXPORT |
| int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns); |
| OPENSSL_EXPORT |
| int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) |
| { |
| return 0; |
| } |
| |
| IMPLEMENT_DYNAMIC_CHECK_FN() |
| # else |
| void engine_load_capi_int(void); |
| void engine_load_capi_int(void) |
| { |
| } |
| # endif |
| #endif |