diff options
author | Joe Orton <jorton@apache.org> | 2018-07-06 14:01:29 +0200 |
---|---|---|
committer | Joe Orton <jorton@apache.org> | 2018-07-06 14:01:29 +0200 |
commit | 45e3cf951120b9db11280c7bbba50a839e237007 (patch) | |
tree | ed5796297b0b4935e81734f8b785a65d05f8a91f /modules/ssl | |
parent | XML update. (diff) | |
download | apache2-45e3cf951120b9db11280c7bbba50a839e237007.tar.xz apache2-45e3cf951120b9db11280c7bbba50a839e237007.zip |
Hook up PKCS#11 PIN entry through configured passphrase entry method.
* modules/ssl/ssl_engine_pphrase.c: Add wrappers for OpenSSL UI * API
around passphrase entry.
(modssl_load_engine_keypair): Take vhost ID and use above rather than
default OpenSSL UI.
* modules/ssl/ssl_engine_init.c (ssl_init_server_certs): Pass vhost ID.
Submitted by: Anderson Sasaki<ansaski redhat.com>, jorton
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1835240 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules/ssl')
-rw-r--r-- | modules/ssl/ssl_engine_init.c | 5 | ||||
-rw-r--r-- | modules/ssl/ssl_engine_pphrase.c | 235 | ||||
-rw-r--r-- | modules/ssl/ssl_private.h | 1 |
3 files changed, 232 insertions, 9 deletions
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index c8a3365e0c..be4184ff86 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1293,8 +1293,9 @@ static apr_status_t ssl_init_server_certs(server_rec *s, cert = NULL; - if ((rv = modssl_load_engine_keypair(s, ptemp, engine_certfile, - keyfile, &cert, &pkey))) { + if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id, + engine_certfile, keyfile, + &cert, &pkey))) { return rv; } diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c index 85982c80c1..7688b2425c 100644 --- a/modules/ssl/ssl_engine_pphrase.c +++ b/modules/ssl/ssl_engine_pphrase.c @@ -588,13 +588,239 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv) } #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) + +/* OpenSSL UI implementation for passphrase entry; largely duplicated + * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be + * worth trying to shift pphrase handling over to the UI API + * completely. */ +static int passphrase_ui_open(UI *ui) +{ + pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); + SSLSrvConfigRec *sc = mySrvConfig(ppcb->s); + + ppcb->nPassPhraseDialog++; + ppcb->nPassPhraseDialogCur++; + + /* + * Builtin or Pipe dialog + */ + if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN + || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { + if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { + if (!readtty) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, + APLOGNO() + "Init: Creating pass phrase dialog pipe child " + "'%s'", sc->server->pphrase_dialog_path); + if (ssl_pipe_child_create(ppcb->p, + sc->server->pphrase_dialog_path) + != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, + APLOGNO() + "Init: Failed to create pass phrase pipe '%s'", + sc->server->pphrase_dialog_path); + return 0; + } + } + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO() + "Init: Requesting pass phrase via piped dialog"); + } + else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */ +#ifdef WIN32 + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO() + "Init: Failed to create pass phrase pipe '%s'", + sc->server->pphrase_dialog_path); + return 0; +#else + /* + * stderr has already been redirected to the error_log. + * rather than attempting to temporarily rehook it to the terminal, + * we print the prompt to stdout before EVP_read_pw_string turns + * off tty echo + */ + apr_file_open_stdout(&writetty, ppcb->p); + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO() + "Init: Requesting pass phrase via builtin terminal " + "dialog"); +#endif + } + + /* + * The first time display a header to inform the user about what + * program he actually speaks to, which module is responsible for + * this terminal dialog and why to the hell he has to enter + * something... + */ + if (ppcb->nPassPhraseDialog == 1) { + apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n", + AP_SERVER_BASEVERSION); + apr_file_printf(writetty, + "A pass phrase is required to access the private key.\n"); + } + if (ppcb->bPassPhraseDialogOnce) { + ppcb->bPassPhraseDialogOnce = FALSE; + apr_file_printf(writetty, "\n"); + apr_file_printf(writetty, "Private key %s (%s)\n", + ppcb->key_id, ppcb->pkey_file); + } + } + + return 1; +} + +static int passphrase_ui_read(UI *ui, UI_STRING *uis) +{ + pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); + SSLSrvConfigRec *sc = mySrvConfig(ppcb->s); + const char *prompt; + int i; + int bufsize; + int len; + char *buf; + + prompt = UI_get0_output_string(uis); + if (prompt == NULL) { + prompt = "Enter pass phrase:"; + } + + /* + * Get the maximum expected size and allocate the buffer + */ + bufsize = UI_get_result_maxsize(uis); + buf = apr_pcalloc(ppcb->p, bufsize); + + if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN + || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { + /* + * Get the pass phrase through a callback. + * Empty input is not accepted. + */ + for (;;) { + if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { + i = pipe_get_passwd_cb(buf, bufsize, "", FALSE); + } + else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */ + i = EVP_read_pw_string(buf, bufsize, "", FALSE); + } + if (i != 0) { + OPENSSL_cleanse(buf, bufsize); + return 0; + } + len = strlen(buf); + if (len < 1){ + apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase" + "empty (needs to be at least 1 character).\n"); + apr_file_puts(prompt, writetty); + } + else { + break; + } + } + } + /* + * Filter program + */ + else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) { + const char *cmd = sc->server->pphrase_dialog_path; + const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3); + char *result; + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO() + "Init: Requesting pass phrase from dialog filter " + "program (%s)", cmd); + + argv[0] = cmd; + argv[1] = ppcb->key_id; + argv[2] = NULL; + + result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv); + apr_cpystrn(buf, result, bufsize); + len = strlen(buf); + } + + /* + * Ok, we now have the pass phrase, so give it back + */ + ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf); + UI_set_result(ui, uis, buf); + + /* Clear sensitive data. */ + OPENSSL_cleanse(buf, bufsize); + return 1; +} + +static int passphrase_ui_write(UI *ui, UI_STRING *uis) +{ + pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); + SSLSrvConfigRec *sc; + const char *prompt; + + sc = mySrvConfig(ppcb->s); + + if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN + || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { + prompt = UI_get0_output_string(uis); + apr_file_puts(prompt, writetty); + } + + return 1; +} + +static int passphrase_ui_close(UI *ui) +{ + /* + * Close the pipes if they were opened + */ + if (readtty) { + apr_file_close(readtty); + apr_file_close(writetty); + readtty = writetty = NULL; + } + return 1; +} + +static apr_status_t pp_ui_method_cleanup(void *uip) +{ + UI_METHOD *uim = uip; + + UI_destroy_method(uim); + + return APR_SUCCESS; +} + +static UI_METHOD *get_passphrase_ui(apr_pool_t *p) +{ + UI_METHOD *ui_method = UI_create_method("Passphrase UI"); + + UI_method_set_opener(ui_method, passphrase_ui_open); + UI_method_set_reader(ui_method, passphrase_ui_read); + UI_method_set_writer(ui_method, passphrase_ui_write); + UI_method_set_closer(ui_method, passphrase_ui_close); + + apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup, + pp_ui_method_cleanup); + + return ui_method; +} + + apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, + const char *vhostid, const char *certid, const char *keyid, X509 **pubkey, EVP_PKEY **privkey) { SSLModConfigRec *mc = myModConfig(s); ENGINE *e; - UI_METHOD *ui_method; + UI_METHOD *ui_method = get_passphrase_ui(p); + pphrase_cb_arg_t ppcb; + + memset(&ppcb, 0, sizeof ppcb); + ppcb.s = s; + ppcb.p = p; + ppcb.bPassPhraseDialogOnce = TRUE; + ppcb.key_id = vhostid; + ppcb.pkey_file = keyid; if (!mc->szCryptoDevice) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131) @@ -603,11 +829,6 @@ apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, return ssl_die(s); } - /* - * Using the builtin OpenSSL UI only, for now... - */ - ui_method = UI_OpenSSL(); - if (!(e = ENGINE_by_id(mc->szCryptoDevice))) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132) "Init: Failed to load Crypto Device API `%s'", @@ -636,7 +857,7 @@ apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, *pubkey = params.cert; } - *privkey = ENGINE_load_private_key(e, keyid, ui_method, NULL); + *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb); if (*privkey == NULL) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133) "Init: Unable to get the private key"); diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 1f629a46e3..8524c515ba 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -994,6 +994,7 @@ apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int, * key returned as *pkey. certid can be NULL, in which case *pubkey * is not altered. Errors logged on failure. */ apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, + const char *vhostid, const char *certid, const char *keyid, X509 **pubkey, EVP_PKEY **privkey); |