From 3054880e09bbff8a885d44f2a304db9a63da4d53 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Thu, 11 Jun 2015 13:39:32 +0200 Subject: [PATCH] MINOR: ssl: Export functions to manipulate generated certificates Following functions are now available in the SSL public API: * ssl_sock_create_cert * ssl_sock_get_generated_cert * ssl_sock_set_generated_cert * ssl_sock_generated_cert_serial These functions could be used to create a certificate by hand, set it in the cache used to store generated certificates and retrieve it. Here is an example (pseudo code): X509 *cacert = ...; EVP_PKEY *capkey = ...; char *servername = ...; unsigned int serial; serial = ssl_sock_generated_cert_serial(servername, strlen(servername)); if (!ssl_sock_get_generated_cert(serial, cacert)) { SSL_CTX *ctx = ssl_sock_create_cert(servername, serial, cacert, capkey); ssl_sock_set_generated_cert(ctx, serial, cacert); } --- include/proto/ssl_sock.h | 5 ++++ src/ssl_sock.c | 57 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h index 7a9e98823..ec616dca9 100644 --- a/include/proto/ssl_sock.h +++ b/include/proto/ssl_sock.h @@ -69,6 +69,11 @@ void tlskeys_finalize_config(void); int ssl_sock_load_global_dh_param_from_file(const char *filename); #endif +SSL_CTX *ssl_sock_create_cert(const char *servername, unsigned int serial, X509 *cacert, EVP_PKEY *capkey); +SSL_CTX *ssl_sock_get_generated_cert(unsigned int serial, X509 *cacert); +void ssl_sock_set_generated_cert(SSL_CTX *ctx, unsigned int serial, X509 *cacert); +unsigned int ssl_sock_generated_cert_serial(void *data, size_t len); + #endif /* _PROTO_SSL_SOCK_H */ /* diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 75876a26d..dfc992549 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -1003,7 +1003,9 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out, } #endif -static SSL_CTX * +/* Create a X509 certificate with the specified servername and serial. This + * function returns a SSL_CTX object or NULL if an error occurs. */ +SSL_CTX * ssl_sock_create_cert(const char *servername, unsigned int serial, X509 *cacert, EVP_PKEY *capkey) { SSL_CTX *ssl_ctx = NULL; @@ -1107,6 +1109,44 @@ ssl_sock_create_cert(const char *servername, unsigned int serial, X509 *cacert, return NULL; } +/* Do a lookup for a certificate in the LRU cache used to store generated + * certificates. */ +SSL_CTX * +ssl_sock_get_generated_cert(unsigned int serial, X509 *cacert) +{ + struct lru64 *lru = NULL; + + if (ssl_ctx_lru_tree) { + lru = lru64_lookup(serial, ssl_ctx_lru_tree, cacert, 0); + if (lru && lru->domain) + return (SSL_CTX *)lru->data; + } + return NULL; +} + +/* Set a certificate int the LRU cache used to store generated certificate. */ +void +ssl_sock_set_generated_cert(SSL_CTX *ssl_ctx, unsigned int serial, X509 *cacert) +{ + struct lru64 *lru = NULL; + + if (ssl_ctx_lru_tree) { + lru = lru64_get(serial, ssl_ctx_lru_tree, cacert, 0); + if (!lru) + return; + if (lru->domain && lru->data) + lru->free((SSL_CTX *)lru->data); + lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free); + } +} + +/* Compute the serial that will be used to create/set/get a certificate. */ +unsigned int +ssl_sock_generated_cert_serial(void *data, size_t len) +{ + return XXH32(data, len, ssl_ctx_lru_seed); +} + static SSL_CTX * ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind_conf) { @@ -1146,6 +1186,21 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s) servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!servername) { + struct sockaddr to; + int fd; + + if (s->generate_certs && + (fd = SSL_get_fd(ssl)) != -1 && + tcp_get_dst(fd, &to, sizeof(to), 0) != -1) { + unsigned int serial = ssl_sock_generated_cert_serial(&to, sizeof(to)); + SSL_CTX *ctx = ssl_sock_get_generated_cert(serial, s->ca_sign_cert); + if (ctx) { + /* switch ctx */ + SSL_set_SSL_CTX(ssl, ctx); + return SSL_TLSEXT_ERR_OK; + } + } + return (s->strict_sni ? SSL_TLSEXT_ERR_ALERT_FATAL : SSL_TLSEXT_ERR_NOACK);