diff -bru httpd-2.0.x\ssl_engine_config.c httpd-2.0.x.new\ssl_engine_config.c --- httpd-2.0.x\ssl_engine_config.c Mon Jan 16 14:06:01 2012 +++ httpd-2.0.x.new\ssl_engine_config.c Mon Jan 16 16:01:34 2012 @@ -74,6 +74,7 @@ mc->tPublicCert = apr_hash_make(pool); #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) mc->szCryptoDevice = NULL; + mc->engine = NULL; #endif memset(mc->pTmpKeys, 0, sizeof(mc->pTmpKeys)); @@ -508,9 +509,9 @@ if (strcEQ(arg, "builtin")) { mc->szCryptoDevice = NULL; } - else if ((e = ENGINE_by_id(arg))) { + else if ((e = ssl_ossle_get_engine(cmd->server, arg, NULL, 0))) { mc->szCryptoDevice = arg; - ENGINE_free(e); + ssl_ossle_release_engine(cmd->server); } else { err = "SSLCryptoDevice: Invalid argument; must be one of: " @@ -763,10 +764,6 @@ SSLSrvConfigRec *sc = mySrvConfig(parms->server); const char *err, *desc=NULL, **files=NULL; int i; - - if ((err = ssl_cmd_check_file(parms, &arg))) { - return err; - } switch (idx) { case SSL_AIDX_CERTS: diff -bru httpd-2.0.x\ssl_engine_init.c httpd-2.0.x.new\ssl_engine_init.c --- httpd-2.0.x\ssl_engine_init.c Mon Jan 16 14:06:25 2012 +++ httpd-2.0.x.new\ssl_engine_init.c Mon Jan 16 15:53:50 2012 @@ -51,6 +51,134 @@ modver, AP_SERVER_BASEVERSION, incver); } +/* _________________________________________________________________ +** +** Centralized engine loading & configuration +** _________________________________________________________________ +*/ + +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) +void ssl_ossle_release_engine(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(s); + if (!mc->engine) { + return; + } + + /* unload any dynamic engines pulled in by CONF_modules_load_file, + * but don't unload the built in engines */ + CONF_modules_unload(0); + + /* Releases all the references retained via ssl_ossl_get_engine */ + ENGINE_finish(mc->engine); + + mc->engine = 0; +} + +ENGINE * ssl_ossle_get_engine(server_rec *s, const char *enginename, + const char *opensslcnf, int disablecnf) +{ + SSLModConfigRec *mc = myModConfig(s); + ENGINE *e = 0; + int conf_modules = 0; + int funct_ref = 0; + + if (mc->engine) { + return mc->engine; + } + + /* CONF_modules_load_file allows for the declaration of engine names, and + * associated parameters, in an openssl formated configuration file. + * opensslcnf may be NULL, in which case openssl's default cofniguration + * file is used automaticaly. (Eg, "/etc/ssl/openssl.cnf"). + * + * To completely disable openssl config processing pass NULL for + * `opensslcnf' and set `disablecnf' to any non zero value. */ + + if (!disablecnf && CONF_modules_load_file(opensslcnf, 0, 0) <= 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Failed to load openssl config file `%s'", + opensslcnf); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + goto x_fail; + } + if (!disablecnf) { + conf_modules = 1; + } + + /* side effect: aquires an additional `structure ref' */ + e = ENGINE_by_id(enginename); + if (!e) { + goto x_fail; + } + + ENGINE_free(e); /* drop the struct_ref taken out by ENGINE_by_id */ + + /* Take out a functional reference, all user configuration is handled in + * openssl.cnf and is now complete, so ENGINE_init can succede if the + * chosen engine has been configured appropriately. */ + if (!(funct_ref = ENGINE_init(e))) { + goto x_fail; + } + + /* Remove our engine from openssl's internal list and drop the associated + * reference. This means ENGINE_by_id will no longer succede and hence + * can't cause further reference counting hell. */ + if (!ENGINE_remove(e)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Failed to apply engine configuration `%s' `%s'", + enginename, opensslcnf + ); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + goto x_fail; + } + + mc->engine = e; + return mc->engine; + +x_fail: + if (e) { + if (funct_ref) { + ENGINE_finish(e); + } + } + if (conf_modules) { + /* unload any dynamic engines pulled in by CONF_modules_load_file, + * but don't unload the built in engines. */ + CONF_modules_unload(0); + } + + return NULL; + +} + +/* ssl_ossle_load_cert_engine_pkcs11 + * + * This is specific to the engine_pkcs11 wrapper provided by the opensc + * project. more general support *is* possible but lets get some feed back + * before writing a tone of code. + */ +X509* ssl_ossle_load_cert_engine_pkcs11(server_rec *s, const char *identifier) +{ + int cmdno; + struct { + const char *identifier; + void *cert; + } param = {identifier, 0}; + + SSLModConfigRec *mc = myModConfig(s); + if (!mc->engine) { + return NULL; + } + + cmdno = ENGINE_ctrl(mc->engine, + ENGINE_CTRL_GET_CMD_FROM_NAME, -1, "LOAD_CERT_CTRL", 0); + /* XXX: check the value of cmdno */ + ENGINE_ctrl(mc->engine, cmdno, -1, ¶m, 0); + + return (X509 *)param.cert; +} +#endif /* * Handle the Temporary RSA Keys and DH Params @@ -352,7 +480,7 @@ ENGINE *e; if (mc->szCryptoDevice) { - if (!(e = ENGINE_by_id(mc->szCryptoDevice))) { + if (!(e = ssl_ossle_get_engine(s, mc->szCryptoDevice, NULL, 0))) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Init: Failed to load Crypto Device API `%s'", mc->szCryptoDevice); @@ -374,8 +502,6 @@ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "Init: loaded Crypto Device API `%s'", mc->szCryptoDevice); - - ENGINE_free(e); } } #endif @@ -835,13 +961,26 @@ "Configuring %s server private key", type); ptr = asn1->cpData; - if (!(pkey = d2i_PrivateKey(pkey_type, NULL, &ptr, asn1->nData))) - { + + if (!mc->engine) { + if (!(pkey = d2i_PrivateKey(pkey_type, NULL, &ptr, asn1->nData))) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Unable to import %s server private key", type); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } + } + else { + /* cpData is a null terminated character string */ + if (!(pkey = ENGINE_load_private_key(mc->engine, (char*)asn1->cpData, + 0 /*ui_method*/, 0 /*cbdata*/))) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Unable to import (ENGINE) %s server private key", + type); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + } if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) <= 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, @@ -867,6 +1006,9 @@ } mctx->pks->keys[idx] = pkey; + + /* Release the engine if there is one. */ + ssl_ossle_release_engine(s); return TRUE; } diff -bru httpd-2.0.x\ssl_engine_pphrase.c httpd-2.0.x.new\ssl_engine_pphrase.c --- httpd-2.0.x\ssl_engine_pphrase.c Mon Jan 16 14:06:33 2012 +++ httpd-2.0.x.new\ssl_engine_pphrase.c Mon Jan 16 16:10:53 2012 @@ -197,18 +197,45 @@ for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->server->pks->cert_files[i] != NULL; i++) { apr_cpystrn(szPath, sc->server->pks->cert_files[i], sizeof(szPath)); + if (!mc->engine) { if ((rv = exists_and_readable(szPath, p, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Init: Can't open server certificate file %s", szPath); ssl_die(); } + if ((pX509Cert = SSL_read_X509(szPath, NULL, NULL)) == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "Init: Unable to read server certificate from file %s", szPath); + "Init: Unable to read server certificate " + "from file %s", szPath); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + } + else { + /* For backwards compat, and convenience for engine + * implementors, we should probably have a configurable + * means to say: "for these named engines, use apaches + * existing pem file support to load server keys and + * certificates", this patch treats all engines the same + * TODO: + * 1. explicit support for passing UI callbacks and + * data necessary for hardware that needs password entry, + * or other interactions, in order to load keys. It's a can + * of worms so I'm leaving it out for now. + * 2. Add at least "SSLCryptoDeviceLoadCertCmd" in order to + * enable a more general purpose implementation of engine + * based certificate loading. */ + pX509Cert = ssl_ossle_load_cert_engine_pkcs11(s, szPath); + if (!pX509Cert) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Unable to server certificate `%s' using " + "engine `%s'", szPath, mc->szCryptoDevice); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } + } /* * check algorithm type of certificate and make @@ -291,7 +318,7 @@ * the callback function which serves the pass * phrases to OpenSSL */ - if ((rv = exists_and_readable(szPath, p, + if (!mc->engine && (rv = exists_and_readable(szPath, p, &pkey_mtime)) != APR_SUCCESS ) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Init: Can't open server private key file " @@ -347,13 +374,19 @@ * is not empty. */ ERR_clear_error(); - bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL, - ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE); + if (!mc->engine) { + bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, + NULL, ssl_pphrase_Handle_CB, s)) + != NULL ? TRUE : FALSE); + } + else { + pPrivateKey = ENGINE_load_private_key(mc->engine, szPath, + UI_OpenSSL() + /*ui_method*/, + 0 /*cbdata*/); + bReadable = pPrivateKey != NULL ? TRUE : FALSE; + } - /* - * when the private key file now was readable, - * it's fine and we go out of the loop - */ if (bReadable) break; @@ -440,13 +473,24 @@ } if (pPrivateKey == NULL) { + if (!mc->engine) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Init: Unable to read server private key from " - "file %s [Hint: Perhaps it is in a separate file? " - " See SSLCertificateKeyFile]", szPath); + "file %s [Hint: Perhaps it is in a separate " + "file? See SSLCertificateKeyFile]", szPath); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } + else { + /* log the error with clear indication that engine format + * keys are in play. */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Init: Unable to load server private key " + "using the identifier `%s' and engine `%s'", + szPath, mc->szCryptoDevice); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + } + } /* * check algorithm type of private key and make @@ -502,14 +546,33 @@ * RSA structure which do not survive DSO reloads!) */ cp = asn1_table_vhost_key(mc, p, cpVHostID, an); + + if (!mc->engine){ length = i2d_PrivateKey(pPrivateKey, NULL); ucp = ssl_asn1_table_set(mc->tPrivateKey, cp, length); - (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */ + /* 2nd arg increments */ + (void)i2d_PrivateKey(pPrivateKey, &ucp); if (nPassPhraseDialogCur != 0) { /* remember mtime of encrypted keys */ asn1 = ssl_asn1_table_get(mc->tPrivateKey, cp); asn1->source_mtime = pkey_mtime; + } + } + else { + /* the asn1 trick does not play well with hardware hosted + keys. */ + length = strlen(szPath) + 1; + /* cpData is the key_id */ + ucp = ssl_asn1_table_set(mc->tPrivateKey, cp, length); + memcpy(ucp, szPath, length); + /* un-necessary, given we will reload, but retained for + * consistency. */ + if (nPassPhraseDialogCur != 0) { + /* remember mtime of encrypted keys */ + asn1 = ssl_asn1_table_get(mc->tPrivateKey, cp); + asn1->source_mtime = pkey_mtime; + } } /* diff -bru httpd-2.0.x\ssl_private.h httpd-2.0.x.new\ssl_private.h --- httpd-2.0.x\ssl_private.h Mon Jan 16 14:06:44 2012 +++ httpd-2.0.x.new\ssl_private.h Tue Nov 15 14:27:34 2011 @@ -398,6 +398,7 @@ apr_hash_t *tPrivateKey; #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) const char *szCryptoDevice; + ENGINE *engine; #endif struct { void *pV1, *pV2, *pV3, *pV4, *pV5, *pV6, *pV7, *pV8, *pV9, *pV10; @@ -571,6 +572,7 @@ const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag); + /** module initialization */ int ssl_init_Module(apr_pool_t *, apr_pool_t *, apr_pool_t *, server_rec *); void ssl_init_Engine(server_rec *, apr_pool_t *); @@ -580,6 +582,12 @@ *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *); void ssl_init_Child(apr_pool_t *, server_rec *); apr_status_t ssl_init_ModuleKill(void *data); + +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) +void ssl_ossle_release_engine(server_rec *s); +ENGINE *ssl_ossle_get_engine(server_rec *s, const char *enginename, const char *opensslcnf, int disablecnf); +X509 *ssl_ossle_load_cert_engine_pkcs11(server_rec *s, const char *identifier); +#endif /** Apache API hooks */ int ssl_hook_Auth(request_rec *);