[mod_openssl] set cert from callback in 1.0.2+ (fixes #2842)
set server certificate from callback in openssl 1.0.2 and later (SSL_CTX_set_cert_cb()) For existing versions of lighttpd, certificate selection influenced by ssl.cipher-list which can be used to set server cipher order preference (along with ssl.honor-cipher-order = "enable", which is the default) x-ref: "Lighttpd Returns Wrong Cert In Multi-cert Set-up" https://redmine.lighttpd.net/issues/2842 "lighttpd uses wrong pem-file" https://redmine.lighttpd.net/issues/3009
This commit is contained in:
parent
2a5b7c648a
commit
01b1f16b3f
|
@ -358,8 +358,7 @@ mod_openssl_load_verify_locn (SSL_CTX *ssl_ctx, const buffer *b, server *srv)
|
|||
static int
|
||||
mod_openssl_load_ca_files (SSL_CTX *ssl_ctx, plugin_data *p, server *srv)
|
||||
{
|
||||
/* load all ssl.ca-files specified in the config
|
||||
* into each SSL_CTX to be prepared for SNI */
|
||||
/* load all ssl.ca-files specified in the config into each SSL_CTX */
|
||||
|
||||
for (uint32_t i = 0, used = p->cafiles->used; i < used; ++i) {
|
||||
const buffer *b = &((data_string *)p->cafiles->data[i])->value;
|
||||
|
@ -592,9 +591,69 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
|||
return !hctx->conf.ssl_verifyclient_enforce;
|
||||
}
|
||||
|
||||
static int
|
||||
mod_openssl_cert_cb (SSL *ssl, void *arg)
|
||||
{
|
||||
handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
|
||||
UNUSED(arg);
|
||||
|
||||
if (NULL == hctx->conf.ssl_pemfile_x509
|
||||
|| NULL == hctx->conf.ssl_pemfile_pkey) {
|
||||
/* x509/pkey available <=> pemfile was set <=> pemfile got patched:
|
||||
* so this should never happen, unless you nest $SERVER["socket"] */
|
||||
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: no certificate/private key for TLS server name %s",
|
||||
hctx->r->uri.authority.ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* first set certificate!
|
||||
* setting private key checks whether certificate matches it */
|
||||
if (1 != SSL_use_certificate(ssl, hctx->conf.ssl_pemfile_x509)) {
|
||||
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: failed to set certificate for TLS server name %s: %s",
|
||||
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (1 != SSL_use_PrivateKey(ssl, hctx->conf.ssl_pemfile_pkey)) {
|
||||
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: failed to set private key for TLS server name %s: %s",
|
||||
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hctx->conf.ssl_verifyclient) {
|
||||
int mode;
|
||||
STACK_OF(X509_NAME) * const cert_names = hctx->conf.ssl_ca_dn_file
|
||||
? hctx->conf.ssl_ca_dn_file
|
||||
: hctx->conf.ssl_ca_file;
|
||||
if (NULL == cert_names) {
|
||||
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: can't verify client without ssl.ca-file "
|
||||
"or ssl.ca-dn-file for TLS server name %s: %s",
|
||||
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(),
|
||||
NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SSL_set_client_CA_list(ssl, SSL_dup_CA_list(cert_names));
|
||||
mode = SSL_VERIFY_PEER;
|
||||
if (hctx->conf.ssl_verifyclient_enforce)
|
||||
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
SSL_set_verify(ssl, mode, verify_callback);
|
||||
SSL_set_verify_depth(ssl, hctx->conf.ssl_verifyclient_depth + 1);
|
||||
}
|
||||
else {
|
||||
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_TLSEXT
|
||||
static int
|
||||
mod_openssl_SNI (SSL *ssl, handler_ctx *hctx, const char *servername, size_t len)
|
||||
mod_openssl_SNI (handler_ctx *hctx, const char *servername, size_t len)
|
||||
{
|
||||
request_st * const r = hctx->r;
|
||||
if (len >= 1024) { /*(expecting < 256; TLSEXT_MAXLEN_host_name is 255)*/
|
||||
|
@ -614,8 +673,6 @@ mod_openssl_SNI (SSL *ssl, handler_ctx *hctx, const char *servername, size_t len
|
|||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
#endif
|
||||
|
||||
const buffer * const ssl_pemfile = hctx->conf.ssl_pemfile;
|
||||
|
||||
r->conditional_is_valid |= (1 << COMP_HTTP_SCHEME)
|
||||
| (1 << COMP_HTTP_HOST);
|
||||
mod_openssl_patch_config(r, &hctx->conf);
|
||||
|
@ -624,63 +681,13 @@ mod_openssl_SNI (SSL *ssl, handler_ctx *hctx, const char *servername, size_t len
|
|||
/*config_cond_cache_reset_item(r, COMP_HTTP_HOST);*/
|
||||
/*buffer_clear(&r->uri.authority);*/
|
||||
|
||||
if (!buffer_is_equal(hctx->conf.ssl_pemfile, ssl_pemfile)) {
|
||||
/* reconfigure to use SNI-specific cert if SNI-specific cert provided */
|
||||
|
||||
if (NULL == hctx->conf.ssl_pemfile_x509
|
||||
|| NULL == hctx->conf.ssl_pemfile_pkey) {
|
||||
/* x509/pkey available <=> pemfile was set <=> pemfile got patched:
|
||||
* so this should never happen, unless you nest $SERVER["socket"] */
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: no certificate/private key for TLS server name %s",
|
||||
r->uri.authority.ptr);
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
/* first set certificate!
|
||||
* setting private key checks whether certificate matches it */
|
||||
if (1 != SSL_use_certificate(ssl, hctx->conf.ssl_pemfile_x509)) {
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: failed to set certificate for TLS server name %s: %s",
|
||||
r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
if (1 != SSL_use_PrivateKey(ssl, hctx->conf.ssl_pemfile_pkey)) {
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: failed to set private key for TLS server name %s: %s",
|
||||
r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (hctx->conf.ssl_verifyclient) {
|
||||
int mode;
|
||||
STACK_OF(X509_NAME) * const cert_names = hctx->conf.ssl_ca_dn_file
|
||||
? hctx->conf.ssl_ca_dn_file
|
||||
: hctx->conf.ssl_ca_file;
|
||||
if (NULL == cert_names) {
|
||||
log_error(r->conf.errh, __FILE__, __LINE__,
|
||||
"SSL: can't verify client without ssl.ca-file "
|
||||
"or ssl.ca-dn-file for TLS server name %s: %s",
|
||||
r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
|
||||
SSL_set_client_CA_list(ssl, SSL_dup_CA_list(cert_names));
|
||||
/* forcing verification here is really not that useful
|
||||
* -- a client could just connect without SNI */
|
||||
mode = SSL_VERIFY_PEER;
|
||||
if (hctx->conf.ssl_verifyclient_enforce) {
|
||||
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
}
|
||||
SSL_set_verify(ssl, mode, verify_callback);
|
||||
SSL_set_verify_depth(ssl, hctx->conf.ssl_verifyclient_depth + 1);
|
||||
} else {
|
||||
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
#else
|
||||
return (mod_openssl_cert_cb(hctx->ssl, NULL) == 1)
|
||||
? SSL_TLSEXT_ERR_OK
|
||||
: SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SSL_CLIENT_HELLO_SUCCESS
|
||||
|
@ -702,7 +709,7 @@ mod_openssl_client_hello_cb (SSL *ssl, int *al, void *srv)
|
|||
&& (size_t)((name[0] << 8) + name[1]) == len-2
|
||||
&& name[2] == TLSEXT_TYPE_server_name
|
||||
&& (slen = (name[3] << 8) + name[4]) <= len-5) { /*(first)*/
|
||||
int rc = mod_openssl_SNI(ssl, hctx, (const char *)name+5, slen);
|
||||
int rc = mod_openssl_SNI(hctx, (const char *)name+5, slen);
|
||||
if (rc == SSL_TLSEXT_ERR_OK)
|
||||
return SSL_CLIENT_HELLO_SUCCESS;
|
||||
}
|
||||
|
@ -712,7 +719,7 @@ mod_openssl_client_hello_cb (SSL *ssl, int *al, void *srv)
|
|||
}
|
||||
#else
|
||||
static int
|
||||
network_ssl_servername_callback (SSL *ssl, int *al, server *srv)
|
||||
network_ssl_servername_callback (SSL *ssl, int *al, void *srv)
|
||||
{
|
||||
handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
|
||||
buffer_copy_string(&hctx->r->uri.scheme, "https");
|
||||
|
@ -721,7 +728,7 @@ network_ssl_servername_callback (SSL *ssl, int *al, server *srv)
|
|||
|
||||
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||
return (NULL != servername)
|
||||
? mod_openssl_SNI(ssl, hctx, servername, strlen(servername))
|
||||
? mod_openssl_SNI(hctx, servername, strlen(servername))
|
||||
: SSL_TLSEXT_ERR_NOACK; /* client did not provide SNI */
|
||||
}
|
||||
#endif
|
||||
|
@ -1284,8 +1291,19 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000
|
||||
|
||||
SSL_CTX_set_cert_cb(s->ssl_ctx, mod_openssl_cert_cb, NULL);
|
||||
UNUSED(p);
|
||||
|
||||
#endif
|
||||
#if 1
|
||||
/*#else*/ /* OPENSSL_VERSION_NUMBER < 0x10002000 */
|
||||
|
||||
/* load all ssl.ca-files specified in the config into each SSL_CTX
|
||||
* to be prepared for SNI */
|
||||
* XXX: This might be a bit excessive, but are all trusted CAs
|
||||
* TODO: prefer to load on-demand in mod_openssl_cert_cb()
|
||||
* for openssl >= 1.0.2 */
|
||||
if (!mod_openssl_load_ca_files(s->ssl_ctx, p, srv))
|
||||
return -1;
|
||||
|
||||
|
@ -1340,6 +1358,9 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
|
|||
s->ssl_pemfile->ptr, s->ssl_privkey->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* OPENSSL_VERSION_NUMBER < 0x10002000 */
|
||||
|
||||
SSL_CTX_set_default_read_ahead(s->ssl_ctx, s->ssl_read_ahead);
|
||||
SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx)
|
||||
| SSL_MODE_ENABLE_PARTIAL_WRITE
|
||||
|
@ -1727,7 +1748,7 @@ SETDEFAULTS_FUNC(mod_openssl_set_defaults)
|
|||
}
|
||||
}
|
||||
|
||||
/* load all ssl.ca-files into a single chain to be prepared for SNI */
|
||||
/* load all ssl.ca-files into a single chain */
|
||||
/*(certificate load order might matter)*/
|
||||
if (ssl_ca_dn_file)
|
||||
array_insert_value(p->cafiles, CONST_BUF_LEN(ssl_ca_dn_file));
|
||||
|
|
Loading…
Reference in New Issue