MEDIUM: ssl: Add options to forge SSL certificates

With this patch, it is possible to configure HAProxy to forge the SSL
certificate sent to a client using the SNI servername. We do it in the SNI
callback.

To enable this feature, you must pass following BIND options:

 * ca-sign-file <FILE> : This is the PEM file containing the CA certitifacte and
   the CA private key to create and sign server's certificates.

 * (optionally) ca-sign-pass <PASS>: This is the CA private key passphrase, if
   any.

 * generate-certificates: Enable the dynamic generation of certificates for a
   listener.

Because generating certificates is expensive, there is a LRU cache to store
them. Its size can be customized by setting the global parameter
'tune.ssl.ssl-ctx-cache-size'.
This commit is contained in:
Christopher Faulet 2015-06-09 17:29:50 +02:00 committed by Willy Tarreau
parent 92939d20fa
commit 31af49d62b
8 changed files with 361 additions and 2 deletions

View File

@ -581,6 +581,7 @@ The following keywords are supported in the "global" section :
- tune.ssl.force-private-cache - tune.ssl.force-private-cache
- tune.ssl.maxrecord - tune.ssl.maxrecord
- tune.ssl.default-dh-param - tune.ssl.default-dh-param
- tune.ssl.ssl-ctx-cache-size
- tune.zlib.memlevel - tune.zlib.memlevel
- tune.zlib.windowsize - tune.zlib.windowsize
@ -1278,6 +1279,12 @@ tune.ssl.default-dh-param <number>
used if static Diffie-Hellman parameters are supplied either directly used if static Diffie-Hellman parameters are supplied either directly
in the certificate file or by using the ssl-dh-param-file parameter. in the certificate file or by using the ssl-dh-param-file parameter.
tune.ssl.ssl-ctx-cache-size <number>
Sets the size of the cache used to store generated certificates to <number>
entries. This is a LRU cache. Because generating a SSL certificate
dynamically is expensive, they are cached. The default cache size is set to
1000 entries.
tune.zlib.memlevel <number> tune.zlib.memlevel <number>
Sets the memLevel parameter in zlib initialization for each session. It Sets the memLevel parameter in zlib initialization for each session. It
defines how much memory should be allocated for the internal compression defines how much memory should be allocated for the internal compression
@ -9039,6 +9046,19 @@ ca-ignore-err [all|<errorID>,...]
If set to 'all', all errors are ignored. SSL handshake is not aborted if an If set to 'all', all errors are ignored. SSL handshake is not aborted if an
error is ignored. error is ignored.
ca-sign-file <cafile>
This setting is only available when support for OpenSSL was built in. It
designates a PEM file containing both the CA certificate and the CA private
key used to create and sign server's certificates. This is a mandatory
setting when the dynamic generation of certificates is enabled. See
'generate-certificates' for details.
ca-sign-passphrase <passphrase>
This setting is only available when support for OpenSSL was built in. It is
the CA private key passphrase. This setting is optional and used only when
the dynamic generation of certificates is enabled. See
'generate-certificates' for details.
ciphers <ciphers> ciphers <ciphers>
This setting is only available when support for OpenSSL was built in. It sets This setting is only available when support for OpenSSL was built in. It sets
the string describing the list of cipher algorithms ("cipher suite") that are the string describing the list of cipher algorithms ("cipher suite") that are
@ -9164,6 +9184,24 @@ force-tlsv12
this listener. This option is also available on global statement this listener. This option is also available on global statement
"ssl-default-bind-options". See also "no-tlsv*", and "no-sslv3". "ssl-default-bind-options". See also "no-tlsv*", and "no-sslv3".
generate-certificates
This setting is only available when support for OpenSSL was built in. It
enables the dynamic SSL certificates generation. A CA certificate and its
private key are necessary (see 'ca-sign-file'). When HAProxy is configured as
a transparent forward proxy, SSL requests generate errors because of a common
name mismatch on the certificate presented to the client. With this option
enabled, HAProxy will try to forge a certificate using the SNI hostname
indicated by the client. This is done only if no certificate matches the SNI
hostname (see 'crt-list'). If an error occurs, the default certificate is
used, else the 'strict-sni' option is set.
It can also be used when HAProxy is configured as a reverse proxy to ease the
deployment of an architecture with many backends.
Creating a SSL certificate is an expensive operation, so a LRU cache is used
to store forged certificates (see 'tune.ssl.ssl-ctx-cache-size'). It
increases the HAProxy's memroy footprint to reduce latency when the same
certificate is used many times.
gid <gid> gid <gid>
Sets the group of the UNIX sockets to the designated system gid. It can also Sets the group of the UNIX sockets to the designated system gid. It can also
be set by default in the global section's "unix-bind" statement. Note that be set by default in the global section's "unix-bind" statement. Note that

View File

@ -257,6 +257,10 @@
#define SSL_HANDSHAKE_MAX_COST (76*1024) // measured #define SSL_HANDSHAKE_MAX_COST (76*1024) // measured
#endif #endif
#ifndef DEFAULT_SSL_CTX_CACHE
#define DEFAULT_SSL_CTX_CACHE 1000
#endif
/* approximate stream size (for maxconn estimate) */ /* approximate stream size (for maxconn estimate) */
#ifndef STREAM_MAX_COST #ifndef STREAM_MAX_COST
#define STREAM_MAX_COST (sizeof(struct stream) + \ #define STREAM_MAX_COST (sizeof(struct stream) + \

View File

@ -44,10 +44,11 @@ int ssl_sock_is_ssl(struct connection *conn)
int ssl_sock_handshake(struct connection *conn, unsigned int flag); int ssl_sock_handshake(struct connection *conn, unsigned int flag);
int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *proxy); int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *proxy);
void ssl_sock_free_certs(struct bind_conf *bind_conf);
int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf, struct proxy *px); int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf, struct proxy *px);
int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *px); int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *px);
void ssl_sock_free_all_ctx(struct bind_conf *bind_conf); void ssl_sock_free_all_ctx(struct bind_conf *bind_conf);
int ssl_sock_load_ca(struct bind_conf *bind_conf, struct proxy *px);
void ssl_sock_free_ca(struct bind_conf *bind_conf);
const char *ssl_sock_get_cipher_name(struct connection *conn); const char *ssl_sock_get_cipher_name(struct connection *conn);
const char *ssl_sock_get_proto_version(struct connection *conn); const char *ssl_sock_get_proto_version(struct connection *conn);
char *ssl_sock_get_version(struct connection *conn); char *ssl_sock_get_version(struct connection *conn);

View File

@ -154,6 +154,7 @@ struct global {
unsigned int ssllifetime; /* SSL session lifetime in seconds */ unsigned int ssllifetime; /* SSL session lifetime in seconds */
unsigned int ssl_max_record; /* SSL max record size */ unsigned int ssl_max_record; /* SSL max record size */
unsigned int ssl_default_dh_param; /* SSL maximum DH parameter size */ unsigned int ssl_default_dh_param; /* SSL maximum DH parameter size */
int ssl_ctx_cache; /* max number of entries in the ssl_ctx cache. */
#endif #endif
#ifdef USE_ZLIB #ifdef USE_ZLIB
int zlibmemlevel; /* zlib memlevel */ int zlibmemlevel; /* zlib memlevel */

View File

@ -133,8 +133,15 @@ struct bind_conf {
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */ struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */ struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */ struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
char *ca_sign_file; /* CAFile used to generate and sign server certificates */
char *ca_sign_pass; /* CAKey passphrase */
X509 *ca_sign_cert; /* CA certificate referenced by ca_file */
EVP_PKEY *ca_sign_pkey; /* CA private key referenced by ca_key */
#endif #endif
int is_ssl; /* SSL is required for these listeners */ int is_ssl; /* SSL is required for these listeners */
int generate_certs; /* 1 if generate-certificates option is set, else 0 */
unsigned long bind_proc; /* bitmask of processes allowed to use these listeners */ unsigned long bind_proc; /* bitmask of processes allowed to use these listeners */
struct { /* UNIX socket permissions */ struct { /* UNIX socket permissions */
uid_t uid; /* -1 to leave unchanged */ uid_t uid; /* -1 to leave unchanged */

View File

@ -770,6 +770,22 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
} }
} }
#endif #endif
else if (!strcmp(args[0], "tune.ssl.ssl-ctx-cache-size")) {
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
global.tune.ssl_ctx_cache = atoi(args[1]);
if (global.tune.ssl_ctx_cache < 0) {
Alert("parsing [%s:%d] : '%s' expects a positive numeric value\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
#endif #endif
else if (!strcmp(args[0], "tune.buffers.limit")) { else if (!strcmp(args[0], "tune.buffers.limit")) {
if (alertif_too_many_args(1, file, linenum, args, &err_code)) if (alertif_too_many_args(1, file, linenum, args, &err_code))
@ -8219,6 +8235,9 @@ int check_config_validity()
/* initialize all certificate contexts */ /* initialize all certificate contexts */
cfgerr += ssl_sock_prepare_all_ctx(bind_conf, curproxy); cfgerr += ssl_sock_prepare_all_ctx(bind_conf, curproxy);
/* initialize CA variables if the certificates generation is enabled */
cfgerr += ssl_sock_load_ca(bind_conf, curproxy);
} }
#endif /* USE_OPENSSL */ #endif /* USE_OPENSSL */
@ -8287,8 +8306,11 @@ int check_config_validity()
if (bind_conf->is_ssl) if (bind_conf->is_ssl)
continue; continue;
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
ssl_sock_free_ca(bind_conf);
ssl_sock_free_all_ctx(bind_conf); ssl_sock_free_all_ctx(bind_conf);
free(bind_conf->ca_file); free(bind_conf->ca_file);
free(bind_conf->ca_sign_file);
free(bind_conf->ca_sign_pass);
free(bind_conf->ciphers); free(bind_conf->ciphers);
free(bind_conf->ecdhe); free(bind_conf->ecdhe);
free(bind_conf->crl_file); free(bind_conf->crl_file);

View File

@ -161,6 +161,7 @@ struct global global = {
#ifdef DEFAULT_SSL_MAX_RECORD #ifdef DEFAULT_SSL_MAX_RECORD
.ssl_max_record = DEFAULT_SSL_MAX_RECORD, .ssl_max_record = DEFAULT_SSL_MAX_RECORD,
#endif #endif
.ssl_ctx_cache = DEFAULT_SSL_CTX_CACHE,
#endif #endif
#ifdef USE_ZLIB #ifdef USE_ZLIB
.zlibmemlevel = 8, .zlibmemlevel = 8,
@ -1456,8 +1457,11 @@ void deinit(void)
/* Release unused SSL configs. */ /* Release unused SSL configs. */
list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) { list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) {
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
ssl_sock_free_ca(bind_conf);
ssl_sock_free_all_ctx(bind_conf); ssl_sock_free_all_ctx(bind_conf);
free(bind_conf->ca_file); free(bind_conf->ca_file);
free(bind_conf->ca_sign_file);
free(bind_conf->ca_sign_pass);
free(bind_conf->ciphers); free(bind_conf->ciphers);
free(bind_conf->ecdhe); free(bind_conf->ecdhe);
free(bind_conf->crl_file); free(bind_conf->crl_file);

View File

@ -35,7 +35,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
@ -51,6 +51,9 @@
#include <openssl/dh.h> #include <openssl/dh.h>
#endif #endif
#include <import/lru.h>
#include <import/xxhash.h>
#include <common/buffer.h> #include <common/buffer.h>
#include <common/compat.h> #include <common/compat.h>
#include <common/config.h> #include <common/config.h>
@ -75,6 +78,7 @@
#include <proto/frontend.h> #include <proto/frontend.h>
#include <proto/listener.h> #include <proto/listener.h>
#include <proto/pattern.h> #include <proto/pattern.h>
#include <proto/proto_tcp.h>
#include <proto/server.h> #include <proto/server.h>
#include <proto/log.h> #include <proto/log.h>
#include <proto/proxy.h> #include <proto/proxy.h>
@ -138,6 +142,27 @@ struct certificate_ocsp {
long expire; long expire;
}; };
/* X509V3 Extensions that will be added on generated certificates */
#define X509V3_EXT_SIZE 5
static char *x509v3_ext_names[X509V3_EXT_SIZE] = {
"basicConstraints",
"nsComment",
"subjectKeyIdentifier",
"authorityKeyIdentifier",
"keyUsage",
};
static char *x509v3_ext_values[X509V3_EXT_SIZE] = {
"CA:FALSE",
"\"OpenSSL Generated Certificate\"",
"hash",
"keyid,issuer:always",
"nonRepudiation,digitalSignature,keyEncipherment"
};
/* LRU cache to store generated certificate */
static struct lru64_head *ssl_ctx_lru_tree = NULL;
static unsigned int ssl_ctx_lru_seed = 0;
/* /*
* This function returns the number of seconds elapsed * This function returns the number of seconds elapsed
* since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the * since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the
@ -978,6 +1003,134 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out,
} }
#endif #endif
static SSL_CTX *
ssl_sock_create_cert(const char *servername, unsigned int serial, X509 *cacert, EVP_PKEY *capkey)
{
SSL_CTX *ssl_ctx = NULL;
X509 *newcrt = NULL;
EVP_PKEY *pkey = NULL;
RSA *rsa;
X509_NAME *name;
const EVP_MD *digest;
X509V3_CTX ctx;
unsigned int i;
/* Generate the public key */
if (!(rsa = RSA_generate_key(2048, 3, NULL, NULL)))
goto mkcert_error;
if (!(pkey = EVP_PKEY_new()))
goto mkcert_error;
if (EVP_PKEY_assign_RSA(pkey, rsa) != 1)
goto mkcert_error;
/* Create the certificate */
if (!(newcrt = X509_new()))
goto mkcert_error;
/* Set version number for the certificate (X509v3) and the serial
* number */
if (X509_set_version(newcrt, 2L) != 1)
goto mkcert_error;
ASN1_INTEGER_set(X509_get_serialNumber(newcrt), serial);
/* Set duration for the certificate */
if (!X509_gmtime_adj(X509_get_notBefore(newcrt), (long)-60*60*24) ||
!X509_gmtime_adj(X509_get_notAfter(newcrt),(long)60*60*24*365))
goto mkcert_error;
/* set public key in the certificate */
if (X509_set_pubkey(newcrt, pkey) != 1)
goto mkcert_error;
/* Set issuer name from the CA */
if (!(name = X509_get_subject_name(cacert)))
goto mkcert_error;
if (X509_set_issuer_name(newcrt, name) != 1)
goto mkcert_error;
/* Set the subject name using the same, but the CN */
name = X509_NAME_dup(name);
if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(const unsigned char *)servername,
-1, -1, 0) != 1) {
X509_NAME_free(name);
goto mkcert_error;
}
if (X509_set_subject_name(newcrt, name) != 1) {
X509_NAME_free(name);
goto mkcert_error;
}
X509_NAME_free(name);
/* Add x509v3 extensions as specified */
X509V3_set_ctx(&ctx, cacert, newcrt, NULL, NULL, 0);
for (i = 0; i < X509V3_EXT_SIZE; i++) {
X509_EXTENSION *ext;
if (!(ext = X509V3_EXT_conf(NULL, &ctx, x509v3_ext_names[i], x509v3_ext_values[i])))
goto mkcert_error;
if (!X509_add_ext(newcrt, ext, -1)) {
X509_EXTENSION_free(ext);
goto mkcert_error;
}
X509_EXTENSION_free(ext);
}
/* Sign the certificate with the CA private key */
if (EVP_PKEY_type(capkey->type) == EVP_PKEY_DSA)
digest = EVP_dss1();
else if (EVP_PKEY_type (capkey->type) == EVP_PKEY_RSA)
digest = EVP_sha256();
else
goto mkcert_error;
if (!(X509_sign(newcrt, capkey, digest)))
goto mkcert_error;
/* Create and set the new SSL_CTX */
if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
goto mkcert_error;
if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey))
goto mkcert_error;
if (!SSL_CTX_use_certificate(ssl_ctx, newcrt))
goto mkcert_error;
if (!SSL_CTX_check_private_key(ssl_ctx))
goto mkcert_error;
if (newcrt) X509_free(newcrt);
if (pkey) EVP_PKEY_free(pkey);
return ssl_ctx;
mkcert_error:
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
if (newcrt) X509_free(newcrt);
if (pkey) EVP_PKEY_free(pkey);
return NULL;
}
static SSL_CTX *
ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind_conf)
{
X509 *cacert = bind_conf->ca_sign_cert;
EVP_PKEY *capkey = bind_conf->ca_sign_pkey;
SSL_CTX *ssl_ctx = NULL;
struct lru64 *lru = NULL;
unsigned int serial;
serial = XXH32(servername, strlen(servername), ssl_ctx_lru_seed);
if (ssl_ctx_lru_tree) {
lru = lru64_get(serial, ssl_ctx_lru_tree, cacert, 0);
if (lru && lru->domain)
ssl_ctx = (SSL_CTX *)lru->data;
}
if (!ssl_ctx) {
ssl_ctx = ssl_sock_create_cert(servername, serial, cacert, capkey);
if (lru)
lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
}
return ssl_ctx;
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
/* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a /* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
* warning when no match is found, which implies the default (first) cert * warning when no match is found, which implies the default (first) cert
@ -1022,6 +1175,14 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
node = ebst_lookup(&s->sni_w_ctx, wildp); node = ebst_lookup(&s->sni_w_ctx, wildp);
} }
if (!node || container_of(node, struct sni_ctx, name)->neg) { if (!node || container_of(node, struct sni_ctx, name)->neg) {
SSL_CTX *ctx;
if (s->generate_certs &&
(ctx = ssl_sock_generate_certificate(servername, s))) {
/* switch ctx */
SSL_set_SSL_CTX(ssl, ctx);
return SSL_TLSEXT_ERR_OK;
}
return (s->strict_sni ? return (s->strict_sni ?
SSL_TLSEXT_ERR_ALERT_FATAL : SSL_TLSEXT_ERR_ALERT_FATAL :
SSL_TLSEXT_ERR_ALERT_WARNING); SSL_TLSEXT_ERR_ALERT_WARNING);
@ -2245,6 +2406,75 @@ void ssl_sock_free_all_ctx(struct bind_conf *bind_conf)
bind_conf->default_ctx = NULL; bind_conf->default_ctx = NULL;
} }
/* Load CA cert file and private key used to generate certificates */
int
ssl_sock_load_ca(struct bind_conf *bind_conf, struct proxy *px)
{
FILE *fp;
X509 *cacert = NULL;
EVP_PKEY *capkey = NULL;
int err = 0;
if (!bind_conf || !bind_conf->generate_certs)
return err;
if (!bind_conf->ca_sign_file) {
Alert("Proxy '%s': cannot enable certificate generation, "
"no CA certificate File configured at [%s:%d].\n",
px->id, bind_conf->file, bind_conf->line);
err++;
}
if (err)
goto load_error;
/* read in the CA certificate */
if (!(fp = fopen(bind_conf->ca_sign_file, "r"))) {
Alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d].\n",
px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
err++;
goto load_error;
}
if (!(cacert = PEM_read_X509(fp, NULL, NULL, NULL))) {
Alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d].\n",
px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
fclose (fp);
err++;
goto load_error;
}
if (!(capkey = PEM_read_PrivateKey(fp, NULL, NULL, bind_conf->ca_sign_pass))) {
Alert("Proxy '%s': Failed to read CA private key file '%s' at [%s:%d].\n",
px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
fclose (fp);
err++;
goto load_error;
}
fclose (fp);
bind_conf->ca_sign_cert = cacert;
bind_conf->ca_sign_pkey = capkey;
return err;
load_error:
bind_conf->generate_certs = 0;
if (capkey) EVP_PKEY_free(capkey);
if (cacert) X509_free(cacert);
return err;
}
/* Release CA cert and private key used to generate certificated */
void
ssl_sock_free_ca(struct bind_conf *bind_conf)
{
if (!bind_conf)
return;
if (bind_conf->ca_sign_pkey)
EVP_PKEY_free(bind_conf->ca_sign_pkey);
if (bind_conf->ca_sign_cert)
X509_free(bind_conf->ca_sign_cert);
}
/* /*
* This function is called if SSL * context is not yet allocated. The function * This function is called if SSL * context is not yet allocated. The function
* is designed to be called before any other data-layer operation and sets the * is designed to be called before any other data-layer operation and sets the
@ -3994,6 +4224,36 @@ static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct
return 0; return 0;
} }
/* parse the "ca-sign-file" bind keyword */
static int bind_parse_ca_sign_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
if (err)
memprintf(err, "'%s' : missing CAfile path", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
if ((*args[cur_arg + 1] != '/') && global.ca_base)
memprintf(&conf->ca_sign_file, "%s/%s", global.ca_base, args[cur_arg + 1]);
else
memprintf(&conf->ca_sign_file, "%s", args[cur_arg + 1]);
return 0;
}
/* parse the ca-sign-pass bind keyword */
static int bind_parse_ca_sign_pass(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
if (err)
memprintf(err, "'%s' : missing CAkey password", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
memprintf(&conf->ca_sign_pass, "%s", args[cur_arg + 1]);
return 0;
}
/* parse the "ciphers" bind keyword */ /* parse the "ciphers" bind keyword */
static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{ {
@ -4326,6 +4586,18 @@ static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bin
return 0; return 0;
} }
/* parse the "generate-certificates" bind keyword */
static int bind_parse_generate_certs(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
conf->generate_certs = 1;
#else
memprintf(err, "%sthis version of openssl cannot generate SSL certificates.\n",
err && *err ? *err : "");
#endif
return 0;
}
/* parse the "strict-sni" bind keyword */ /* parse the "strict-sni" bind keyword */
static int bind_parse_strict_sni(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) static int bind_parse_strict_sni(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{ {
@ -4833,6 +5105,8 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
{ "alpn", bind_parse_alpn, 1 }, /* set ALPN supported protocols */ { "alpn", bind_parse_alpn, 1 }, /* set ALPN supported protocols */
{ "ca-file", bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */ { "ca-file", bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */
{ "ca-ignore-err", bind_parse_ignore_err, 1 }, /* set error IDs to ignore on verify depth > 0 */ { "ca-ignore-err", bind_parse_ignore_err, 1 }, /* set error IDs to ignore on verify depth > 0 */
{ "ca-sign-file", bind_parse_ca_sign_file, 1 }, /* set CAFile used to generate and sign server certs */
{ "ca-sign-pass", bind_parse_ca_sign_pass, 1 }, /* set CAKey passphrase */
{ "ciphers", bind_parse_ciphers, 1 }, /* set SSL cipher suite */ { "ciphers", bind_parse_ciphers, 1 }, /* set SSL cipher suite */
{ "crl-file", bind_parse_crl_file, 1 }, /* set certificat revocation list file use on client cert verify */ { "crl-file", bind_parse_crl_file, 1 }, /* set certificat revocation list file use on client cert verify */
{ "crt", bind_parse_crt, 1 }, /* load SSL certificates from this location */ { "crt", bind_parse_crt, 1 }, /* load SSL certificates from this location */
@ -4843,6 +5117,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
{ "force-tlsv10", bind_parse_force_tlsv10, 0 }, /* force TLSv10 */ { "force-tlsv10", bind_parse_force_tlsv10, 0 }, /* force TLSv10 */
{ "force-tlsv11", bind_parse_force_tlsv11, 0 }, /* force TLSv11 */ { "force-tlsv11", bind_parse_force_tlsv11, 0 }, /* force TLSv11 */
{ "force-tlsv12", bind_parse_force_tlsv12, 0 }, /* force TLSv12 */ { "force-tlsv12", bind_parse_force_tlsv12, 0 }, /* force TLSv12 */
{ "generate-certificates", bind_parse_generate_certs, 0 }, /* enable the server certificates generation */
{ "no-sslv3", bind_parse_no_sslv3, 0 }, /* disable SSLv3 */ { "no-sslv3", bind_parse_no_sslv3, 0 }, /* disable SSLv3 */
{ "no-tlsv10", bind_parse_no_tlsv10, 0 }, /* disable TLSv10 */ { "no-tlsv10", bind_parse_no_tlsv10, 0 }, /* disable TLSv10 */
{ "no-tlsv11", bind_parse_no_tlsv11, 0 }, /* disable TLSv11 */ { "no-tlsv11", bind_parse_no_tlsv11, 0 }, /* disable TLSv11 */
@ -4953,11 +5228,18 @@ static void __ssl_sock_init(void)
#ifndef OPENSSL_NO_DH #ifndef OPENSSL_NO_DH
ssl_dh_ptr_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); ssl_dh_ptr_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
#endif #endif
/* Add a global parameter for the LRU cache size */
if (global.tune.ssl_ctx_cache)
ssl_ctx_lru_tree = lru64_new(global.tune.ssl_ctx_cache);
ssl_ctx_lru_seed = (unsigned int)time(NULL);
} }
__attribute__((destructor)) __attribute__((destructor))
static void __ssl_sock_deinit(void) static void __ssl_sock_deinit(void)
{ {
lru64_destroy(ssl_ctx_lru_tree);
#ifndef OPENSSL_NO_DH #ifndef OPENSSL_NO_DH
if (local_dh_1024) { if (local_dh_1024) {
DH_free(local_dh_1024); DH_free(local_dh_1024);