available for an SSL module like mod_ssl.
- ap_ssl_answer_challenge() to enable other modules like mod_md to
provide a certificate as used in the RFC 8555 'tls-alpn-01' challenge
- for the ACME protocol for an SSL module like mod_ssl.
+ for the ACME protocol for an SSL module like mod_ssl. The function
+ and its hook provide PEM encoded data instead of file names.
- Hooks for 'ssl_add_cert_files', 'ssl_add_fallback_cert_files' and
'ssl_answer_challenge' where modules like mod_md can provide providers
to the above mentioned functions.
apr_array_header_t *key_files);
-/**
- * On TLS connections that do not relate to a configured virtual host,
- * allow modules to provide a certificate and key to
- * be used on the connection.
+/**
+ * On TLS connections that do not relate to a configured virtual host
+ * allow modules to provide a certificate and key to be used on the connection.
+ *
+ * A Certificate PEM added must be accompanied by a private key PEM. The private
+ * key PEM may be given by a NULL pointer, in which case it is expected to be found in
+ * the certificate PEM string.
*/
-AP_DECLARE_HOOK(int, ssl_answer_challenge, (conn_rec *c, const char *server_name,
- const char **pcert_file, const char **pkey_file))
+AP_DECLARE_HOOK(int, ssl_answer_challenge, (conn_rec *c, const char *server_name,
+ const char **pcert_pem, const char **pkey_pem))
/**
* Returns != 0 iff the connection is a challenge to the server, for example
* as defined in RFC 8555 for the 'tls-alpn-01' domain verification, and needs
* a specific certificate as answer in the handshake.
+ *
* ALPN protocol negotiation via the hooks 'protocol_propose' and 'protocol_switch'
* need to have run before this call is made.
+ *
+ * Certificate PEMs added must be accompanied by a private key PEM. The private
+ * key PEM may be given by a NULL pointer, in which case it is expected to be found in
+ * the certificate PEM string.
+ *
+ * A certificate provided this way needs to replace any other certificates selected
+ * by configuration or 'ssl_add_cert_pems` on this connection.
*/
-AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name,
- const char **pcert_file, const char **pkey_file);
+AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name,
+ const char **pcert_pem, const char **pkey_pem);
#ifdef __cplusplus
int ssl_is_challenge(conn_rec *c, const char *servername,
X509 **pcert, EVP_PKEY **pkey,
- const char **pcert_file, const char **pkey_file)
+ const char **pcert_pem, const char **pkey_pem)
{
*pcert = NULL;
*pkey = NULL;
- *pcert_file = *pkey_file = NULL;
- if (ap_ssl_answer_challenge(c, servername, pcert_file, pkey_file)) {
+ *pcert_pem = *pkey_pem = NULL;
+ if (ap_ssl_answer_challenge(c, servername, pcert_pem, pkey_pem)) {
return 1;
}
else if (OK == ssl_run_answer_challenge(c, servername, pcert, pkey)) {
static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
SSL *ssl, X509 *cert, EVP_PKEY *key,
- const char *cert_file, const char *key_file)
+ const char *cert_pem, const char *key_pem)
{
SSLConnRec *sslcon = myConnConfig(c);
+ apr_status_t rv = APR_SUCCESS;
+ int our_data = 0;
sslcon->service_unavailable = 1;
- if (cert_file) {
- if (SSL_use_certificate_file(ssl, cert_file, SSL_FILETYPE_PEM) < 1) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10264)
- "Failed to configure challenge certificate %s",
- servername);
- return APR_EGENERAL;
- }
- if (key_file == NULL) key_file = cert_file;
- if (SSL_use_PrivateKey_file(ssl, key_file, SSL_FILETYPE_PEM) < 1) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10265)
- "Failed to configure challenge private key %s",
+ if (cert_pem) {
+ cert = NULL;
+ key = NULL;
+ our_data = 1;
+
+ rv = modssl_read_cert(c->pool, cert_pem, key_pem, NULL, NULL, &cert, &key);
+ if (rv != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO()
+ "Failed to parse PEM of challenge certificate %s",
servername);
- return APR_EGENERAL;
+ goto cleanup;
}
- goto check;
}
if ((SSL_use_certificate(ssl, cert) < 1)) {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086)
"Failed to configure challenge certificate %s",
servername);
- return APR_EGENERAL;
+ rv = APR_EGENERAL; goto cleanup;
}
if (!SSL_use_PrivateKey(ssl, key)) {
"error '%s' using Challenge key: %s",
ERR_error_string(ERR_peek_last_error(), NULL),
servername);
- return APR_EGENERAL;
+ rv = APR_EGENERAL; goto cleanup;
}
-check:
if (SSL_check_private_key(ssl) < 1) {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
"Challenge certificate and private key %s "
"do not match", servername);
- return APR_EGENERAL;
+ rv = APR_EGENERAL; goto cleanup;
}
+
+cleanup:
+ if (our_data && cert) X509_free(cert);
+ if (our_data && key) EVP_PKEY_free(key);
return APR_SUCCESS;
}
*/
static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
{
- X509 *cert;
- EVP_PKEY *key;
- const char *cert_file, *key_file;
-
if (c) {
SSLConnRec *sslcon = myConnConfig(c);
sslcon->vhost_found = +1;
return APR_SUCCESS;
}
- else if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) {
- /* With ACMEv1 we can have challenge connections to a unknown domains
- * that need to be answered with a special certificate and will
- * otherwise not answer any requests. */
- if (set_challenge_creds(c, servername, ssl, cert, key,
- cert_file, key_file) != APR_SUCCESS) {
- return APR_EGENERAL;
- }
- SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
- }
else {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044)
"No matching SSL virtual host for servername "
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
X509 *cert;
EVP_PKEY *key;
- const char *cert_file, *key_file;
+ const char *cert_pem, *key_pem;
- if (ssl_is_challenge(c, servername, &cert, &key, &cert_file, &key_file)) {
+ if (ssl_is_challenge(c, servername, &cert, &key, &cert_pem, &key_pem)) {
if (set_challenge_creds(c, servername, ssl, cert, key,
- cert_file, key_file) != APR_SUCCESS) {
+ cert_pem, key_pem) != APR_SUCCESS) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
SSL_set_verify(ssl, SSL_VERIFY_NONE, ssl_callback_SSLVerify);
sslconn->reneg_state = state;
#endif
}
+
+/* _________________________________________________________________
+**
+** Certficate/Key Stuff
+** _________________________________________________________________
+*/
+
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey)
+{
+ BIO *in;
+ X509 *x = NULL;
+ EVP_PKEY *key = NULL;
+ apr_status_t rv = APR_SUCCESS;
+
+ in = BIO_new_mem_buf(cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM; goto cleanup;
+ }
+
+ x = PEM_read_bio_X509(in, NULL, cb, ud);
+ if (x == NULL) {
+ rv = APR_ENOENT; goto cleanup;
+ }
+
+ BIO_free(in);
+ in = BIO_new_mem_buf(key_pem? key_pem : cert_pem, -1);
+ if (in == NULL) {
+ rv = APR_ENOMEM; goto cleanup;
+ }
+ key = PEM_read_bio_PrivateKey(in, NULL, cb, ud);
+ if (key == NULL) {
+ rv = APR_ENOENT; goto cleanup;
+ }
+
+cleanup:
+ if (rv == APR_SUCCESS) {
+ *pcert = x;
+ *pkey = key;
+ }
+ else {
+ *pcert = NULL;
+ *pkey = NULL;
+ if (x) X509_free(x);
+ if (key) EVP_PKEY_free(key);
+ }
+ if (in != NULL) BIO_free(in);
+ return rv;
+}
* pool-allocated string. If empty, returns NULL. BIO_free(bio) is
* called for both cases. */
char *modssl_bio_free_read(apr_pool_t *p, BIO *bio);
-
+
+/* Read a single certificate and its private key from the give string in PEM format.
+ * If `key_pem` is NULL, it will expect the key in `cert_pem`.
+ */
+apr_status_t modssl_read_cert(apr_pool_t *p,
+ const char *cert_pem, const char *key_pem,
+ pem_password_cb *cb, void *ud,
+ X509 **pcert, EVP_PKEY **pkey);
+
#endif /* __SSL_UTIL_SSL_H__ */
/** @} */
}
AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name,
- const char **pcert_file, const char **pkey_file)
+ const char **pcert_pem, const char **pkey_pem)
{
- return (ap_run_ssl_answer_challenge(c, server_name, pcert_file, pkey_file) == OK);
+ return (ap_run_ssl_answer_challenge(c, server_name, pcert_pem, pkey_pem) == OK);
}
AP_IMPLEMENT_HOOK_VOID(pre_read_request,
apr_array_header_t *cert_files, apr_array_header_t *key_files),
(s, p, cert_files, key_files), OK, DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_answer_challenge,
- (conn_rec *c, const char *server_name, const char **pcert_file, const char **pkey_file),
- (c, server_name, pcert_file, pkey_file), DECLINED)
+ (conn_rec *c, const char *server_name, const char **pcert_pem, const char **pkey_pem),
+ (c, server_name, pcert_pem, pkey_pem), DECLINED)