MAJOR: ssl: bind configuration per certificat
crt-list is extend to support ssl configuration. You can now have
such line in crt-list <file>:
mycert.pem [npn h2,http/1.1]
Support include "npn", "alpn", "verify", "ca_file", "crl_file",
"ecdhe", "ciphers" configuration and ssl options.
"crt-base" is also supported to fetch certificates.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 739fad9..abb36df 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10248,10 +10248,14 @@
crt-list <file>
This setting is only available when support for OpenSSL was built in. It
- designates a list of PEM file with an optional list of SNI filter per
- certificate, with the following format for each line :
+ designates a list of PEM file with an optional ssl configuration and a SNI
+ filter per certificate, with the following format for each line :
- <crtfile> [[!]<snifilter> ...]
+ <crtfile> [\[<sslbindconf> ...\]] [[!]<snifilter> ...]
+
+ sslbindconf support "npn", "alpn", "verify", "ca_file", "crl_file", "ecdhe",
+ "ciphers" configuration and ssl options (see "ssl-default-bind-options").
+ It override the configuration set in bind line for the certificate.
Wildcards are supported in the SNI filter. Negative filter are also supported,
only useful in combination with a wildcard filter to exclude a particular SNI.
@@ -10266,6 +10270,12 @@
the base name is given in the crt-list. SNI filter will do the same work on
all bundled certificates.
+ crt-list file example:
+ cert1.pem
+ cert2.pem [npn h2,http/1.1]
+ certW.pem *.domain.tld !secure.domain.tld
+ certS.pem [ecdhe secp521r1 ciphers ECDHE-ECDSA-AES256-GCM-SHA384] secure.domain.tld
+
defer-accept
Is an optional keyword which is supported only on certain Linux kernels. It
states that a connection will only be accepted once some data arrive on it,
diff --git a/include/common/defaults.h b/include/common/defaults.h
index 3e04f02..1618ab4 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -74,8 +74,11 @@
// max # args on a configuration line
#define MAX_LINE_ARGS 64
-// crt-list parsing factor for LINESIZE and MAX_LINE_ARGS
-#define CRTLIST_FACTOR 32
+// maximum line size when parsing crt-bind-list config
+#define CRT_LINESIZE 65536
+
+// max # args on crt-bind-list configuration line
+#define MAX_CRT_ARGS 2048
// max # args on a stats socket
// This should cover at least 5 + twice the # of data_types
diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
index 9f43adc..6f779fa 100644
--- a/include/proto/ssl_sock.h
+++ b/include/proto/ssl_sock.h
@@ -42,7 +42,7 @@
}
int ssl_sock_handshake(struct connection *conn, unsigned int flag);
-int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx);
+int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *, SSL_CTX *ctx);
int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf);
int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf);
int ssl_sock_prepare_srv_ctx(struct server *srv);
diff --git a/include/types/listener.h b/include/types/listener.h
index 03f4a72..b534c47 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -115,22 +115,34 @@
#define BC_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
#endif
-/* "bind" line settings */
-struct bind_conf {
+/* ssl "bind" settings */
+struct ssl_bind_conf {
#ifdef USE_OPENSSL
+#ifdef OPENSSL_NPN_NEGOTIATED
+ char *npn_str; /* NPN protocol string */
+ int npn_len; /* NPN protocol string length */
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ char *alpn_str; /* ALPN protocol string */
+ int alpn_len; /* ALPN protocol string length */
+#endif
+ int verify; /* verify method (set of SSL_VERIFY_* flags) */
char *ca_file; /* CAfile to use on verify */
- unsigned long long ca_ignerr; /* ignored verify errors in handshake if depth > 0 */
- unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
- char *ciphers; /* cipher suite to use if non-null */
char *crl_file; /* CRLfile to use on verify */
+ char *ciphers; /* cipher suite to use if non-null */
char *ecdhe; /* named curve to use for ECDHE */
int ssl_options; /* ssl options */
- int verify; /* verify method (set of SSL_VERIFY_* flags) */
+#endif
+};
+
+/* "bind" line settings */
+struct bind_conf {
+#ifdef USE_OPENSSL
+ struct ssl_bind_conf ssl_conf; /* ssl conf for ctx setting */
+ unsigned long long ca_ignerr; /* ignored verify errors in handshake if depth > 0 */
+ unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
SSL_CTX *default_ctx; /* SSL context of first/default certificate */
- char *npn_str; /* NPN protocol string */
- int npn_len; /* NPN protocol string length */
- char *alpn_str; /* ALPN protocol string */
- int alpn_len; /* ALPN protocol string length */
+ struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
int strict_sni; /* refuse negotiation if sni doesn't match a certificate */
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 */
@@ -213,6 +225,11 @@
int (*parse)(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err);
int skip; /* nb of args to skip */
};
+struct ssl_bind_kw {
+ const char *kw;
+ int (*parse)(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err);
+ int skip; /* nb of args to skip */
+};
/*
* A keyword list. It is a NULL-terminated array of keywords. It embeds a
diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index e71ba79..a384f05 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -22,6 +22,7 @@
#ifndef _TYPES_SSL_SOCK_H
#define _TYPES_SSL_SOCK_H
+#include <types/listener.h>
#include <openssl/ssl.h>
#include <ebmbtree.h>
@@ -29,6 +30,7 @@
SSL_CTX *ctx; /* context associated to the certificate */
int order; /* load order for the certificate */
int neg; /* reject if match */
+ struct ssl_bind_conf *conf; /* ssl "bind" conf for the certificate */
struct ebmb_node name; /* node holding the servername value */
};
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 3705917..b94166c 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -194,6 +194,8 @@
"nonRepudiation,digitalSignature,keyEncipherment"
};
+static struct ssl_bind_kw ssl_bind_kws[];
+
/* LRU cache to store generated certificate */
static struct lru64_head *ssl_ctx_lru_tree = NULL;
static unsigned int ssl_ctx_lru_seed = 0;
@@ -1182,7 +1184,7 @@
static int ssl_sock_advertise_npn_protos(SSL *s, const unsigned char **data,
unsigned int *len, void *arg)
{
- struct bind_conf *conf = arg;
+ struct ssl_bind_conf *conf = arg;
*data = (const unsigned char *)conf->npn_str;
*len = conf->npn_len;
@@ -1199,7 +1201,7 @@
const unsigned char *server,
unsigned int server_len, void *arg)
{
- struct bind_conf *conf = arg;
+ struct ssl_bind_conf *conf = arg;
if (SSL_select_next_proto((unsigned char**) out, outlen, (const unsigned char *)conf->alpn_str,
conf->alpn_len, server, server_len) != OPENSSL_NPN_NEGOTIATED) {
@@ -1330,7 +1332,7 @@
SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_get_tmp_dh);
#if defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH)
{
- const char *ecdhe = (bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE);
+ const char *ecdhe = (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE);
EC_KEY *ecc;
int nid;
@@ -1776,7 +1778,8 @@
}
#endif
-static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int order)
+static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s,
+ struct ssl_bind_conf *conf, char *name, int order)
{
struct sni_ctx *sc;
int wild = 0, neg = 0;
@@ -1809,7 +1812,7 @@
node = ebst_lookup(&s->sni_ctx, trash.str);
for (; node; node = ebmb_next_dup(node)) {
sc = ebmb_entry(node, struct sni_ctx, name);
- if (sc->ctx == ctx && sc->neg == neg)
+ if (sc->ctx == ctx && sc->conf == conf && sc->neg == neg)
return order;
}
@@ -1818,6 +1821,7 @@
return order;
memcpy(sc->name.key, trash.str, len + 1);
sc->ctx = ctx;
+ sc->conf = conf;
sc->order = order++;
sc->neg = neg;
if (wild)
@@ -2072,7 +2076,8 @@
* 0 on success
* 1 on failure
*/
-static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+ char **sni_filter, int fcount, char **err)
{
char fp[MAXPATHLEN+1] = {0};
int n = 0;
@@ -2246,7 +2251,7 @@
}
/* Update SNI Tree */
- key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, bind_conf, str, key_combos[i-1].order);
+ key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, bind_conf, ssl_conf, str, key_combos[i-1].order);
node = ebmb_next(node);
}
@@ -2256,6 +2261,7 @@
for (i = SSL_SOCK_POSSIBLE_KT_COMBOS - 1; i >= 0; i--) {
if (key_combos[i].ctx) {
bind_conf->default_ctx = key_combos[i].ctx;
+ bind_conf->default_ssl_conf = ssl_conf;
break;
}
}
@@ -2280,7 +2286,8 @@
}
#else
/* This is a dummy, that just logs an error and returns error */
-static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+ char **sni_filter, int fcount, char **err)
{
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
err && *err ? *err : "", path, strerror(errno));
@@ -2292,7 +2299,8 @@
/* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
* an early error happens and the caller must call SSL_CTX_free() by itelf.
*/
-static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s, char **sni_filter, int fcount)
+static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s,
+ struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount)
{
BIO *in;
X509 *x = NULL, *ca;
@@ -2325,7 +2333,7 @@
if (fcount) {
while (fcount--)
- order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], order);
+ order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, sni_filter[fcount], order);
}
else {
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -2335,7 +2343,7 @@
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
if (name->type == GEN_DNS) {
if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
- order = ssl_sock_add_cert_sni(ctx, s, str, order);
+ order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, str, order);
OPENSSL_free(str);
}
}
@@ -2351,7 +2359,7 @@
value = X509_NAME_ENTRY_get_data(entry);
if (ASN1_STRING_to_UTF8((unsigned char **)&str, value) >= 0) {
- order = ssl_sock_add_cert_sni(ctx, s, str, order);
+ order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, str, order);
OPENSSL_free(str);
}
}
@@ -2394,7 +2402,8 @@
return ret;
}
-static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+ char **sni_filter, int fcount, char **err)
{
int ret;
SSL_CTX *ctx;
@@ -2413,7 +2422,7 @@
return 1;
}
- ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, sni_filter, fcount);
+ ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, ssl_conf, sni_filter, fcount);
if (ret <= 0) {
memprintf(err, "%sunable to load SSL certificate from PEM file '%s'.\n",
err && *err ? *err : "", path);
@@ -2476,8 +2485,10 @@
return 1;
}
#endif
- if (!bind_conf->default_ctx)
+ if (!bind_conf->default_ctx) {
bind_conf->default_ctx = ctx;
+ bind_conf->default_ssl_conf = ssl_conf;
+ }
return 0;
}
@@ -2499,7 +2510,7 @@
if (stat(path, &buf) == 0) {
dir = opendir(path);
if (!dir)
- return ssl_sock_load_cert_file(path, bind_conf, NULL, 0, err);
+ return ssl_sock_load_cert_file(path, bind_conf, NULL, NULL, 0, err);
/* strip trailing slashes, including first one */
for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
@@ -2559,7 +2570,7 @@
}
snprintf(fp, sizeof(fp), "%s/%s", path, dp);
- ssl_sock_load_multi_cert(fp, bind_conf, NULL, 0, err);
+ ssl_sock_load_multi_cert(fp, bind_conf, NULL, NULL, 0, err);
/* Successfully processed the bundle */
goto ignore_entry;
@@ -2567,7 +2578,7 @@
}
#endif
- cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, 0, err);
+ cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, NULL, 0, err);
ignore_entry:
free(de);
}
@@ -2577,7 +2588,7 @@
return cfgerr;
}
- cfgerr = ssl_sock_load_multi_cert(path, bind_conf, NULL, 0, err);
+ cfgerr = ssl_sock_load_multi_cert(path, bind_conf, NULL, NULL, 0, err);
return cfgerr;
}
@@ -2598,9 +2609,33 @@
return random_initialized;
}
-int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char **err)
+/* release ssl bind conf */
+void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
{
- char thisline[LINESIZE*CRTLIST_FACTOR];
+ if (conf) {
+#ifdef OPENSSL_NPN_NEGOTIATED
+ free(conf->npn_str);
+ conf->npn_str = NULL;
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ free(conf->alpn_str);
+ conf->alpn_str = NULL;
+#endif
+ free(conf->ca_file);
+ conf->ca_file = NULL;
+ free(conf->crl_file);
+ conf->crl_file = NULL;
+ free(conf->ciphers);
+ conf->ciphers = NULL;
+ free(conf->ecdhe);
+ conf->ecdhe = NULL;
+ }
+}
+
+int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
+{
+ char thisline[CRT_LINESIZE];
+ char path[MAXPATHLEN+1];
FILE *f;
struct stat buf;
int linenum = 0;
@@ -2612,11 +2647,12 @@
}
while (fgets(thisline, sizeof(thisline), f) != NULL) {
- int arg;
- int newarg;
+ int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
char *end;
- char *args[MAX_LINE_ARGS*CRTLIST_FACTOR + 1];
+ char *args[MAX_CRT_ARGS + 1];
char *line = thisline;
+ char *crt_path;
+ struct ssl_bind_conf *ssl_conf = NULL;
linenum++;
end = line + strlen(line);
@@ -2637,18 +2673,43 @@
/* end of string, end of loop */
*line = 0;
break;
- }
- else if (isspace(*line)) {
+ } else if (isspace(*line)) {
newarg = 1;
*line = 0;
- }
- else if (newarg) {
- if (arg == MAX_LINE_ARGS*CRTLIST_FACTOR) {
- memprintf(err, "too many args on line %d in file '%s'.",
- linenum, file);
+ } else if (*line == '[') {
+ if (ssl_b) {
+ memprintf(err, "too many '[' on line %d in file '%s'.", linenum, file);
+ cfgerr = 1;
+ break;
+ }
+ if (!arg) {
+ memprintf(err, "file must start with a cert on line %d in file '%s'", linenum, file);
+ cfgerr = 1;
+ break;
+ }
+ ssl_b = arg;
+ newarg = 1;
+ *line = 0;
+ } else if (*line == ']') {
+ if (ssl_e) {
+ memprintf(err, "too many ']' on line %d in file '%s'.", linenum, file);
cfgerr = 1;
break;
}
+ if (!ssl_b) {
+ memprintf(err, "missing '[' in line %d in file '%s'.", linenum, file);
+ cfgerr = 1;
+ break;
+ }
+ ssl_e = arg;
+ newarg = 1;
+ *line = 0;
+ } else if (newarg) {
+ if (arg == MAX_CRT_ARGS) {
+ memprintf(err, "too many args on line %d in file '%s'.", linenum, file);
+ cfgerr = 1;
+ break;
+ }
newarg = 0;
args[arg++] = line;
}
@@ -2656,15 +2717,61 @@
}
if (cfgerr)
break;
+ args[arg++] = line;
/* empty line */
- if (!arg)
+ if (!*args[0])
continue;
- if (stat(args[0], &buf) == 0) {
- cfgerr = ssl_sock_load_cert_file(args[0], bind_conf, &args[1], arg-1, err);
+ crt_path = args[0];
+ if (*crt_path != '/' && global_ssl.crt_base) {
+ if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > MAXPATHLEN) {
+ memprintf(err, "'%s' : path too long on line %d in file '%s'",
+ crt_path, linenum, file);
+ cfgerr = 1;
+ break;
+ }
+ snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path);
+ crt_path = path;
+ }
+
+ ssl_conf = calloc(1, sizeof *ssl_conf);
+ cur_arg = ssl_b ? ssl_b : 1;
+ while (cur_arg < ssl_e) {
+ newarg = 0;
+ for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
+ if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
+ newarg = 1;
+ cfgerr = ssl_bind_kws[i].parse(args, cur_arg, curproxy, ssl_conf, err);
+ if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
+ memprintf(err, "ssl args out of '[]' for %s on line %d in file '%s'",
+ args[cur_arg], linenum, file);
+ cfgerr = 1;
+ }
+ cur_arg += 1 + ssl_bind_kws[i].skip;
+ break;
+ }
+ }
+ if (!cfgerr && !newarg) {
+ memprintf(err, "unknown ssl keyword %s on line %d in file '%s'.",
+ args[cur_arg], linenum, file);
+ cfgerr = 1;
+ break;
+ }
+ }
+ if (cfgerr) {
+ ssl_sock_free_ssl_conf(ssl_conf);
+ free(ssl_conf);
+ ssl_conf = NULL;
+ break;
+ }
+
+ if (stat(crt_path, &buf) == 0) {
+ cfgerr = ssl_sock_load_cert_file(crt_path, bind_conf, ssl_conf,
+ &args[cur_arg], arg - cur_arg - 1, err);
} else {
- cfgerr = ssl_sock_load_multi_cert(args[0], bind_conf, &args[1], arg-1, err);
+ cfgerr = ssl_sock_load_multi_cert(crt_path, bind_conf, ssl_conf,
+ &args[cur_arg], arg - cur_arg - 1, err);
}
if (cfgerr) {
@@ -2712,7 +2819,7 @@
#define SSL_MODE_SMALL_BUFFERS 0
#endif
-int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
+int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx)
{
struct proxy *curproxy = bind_conf->frontend;
int cfgerr = 0;
@@ -2741,6 +2848,9 @@
int idx = 0;
int dhe_found = 0;
SSL *ssl = NULL;
+ struct ssl_bind_conf *ssl_conf_cur;
+ int conf_ssl_options = bind_conf->ssl_conf.ssl_options | (ssl_conf ? ssl_conf->ssl_options : 0);
+ const char *conf_ciphers;
/* Make sure openssl opens /dev/urandom before the chroot */
if (!ssl_initialize_random()) {
@@ -2748,17 +2858,17 @@
cfgerr++;
}
- if (bind_conf->ssl_options & BC_SSL_O_NO_SSLV3)
+ if (conf_ssl_options & BC_SSL_O_NO_SSLV3)
ssloptions |= SSL_OP_NO_SSLv3;
- if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV10)
+ if (conf_ssl_options & BC_SSL_O_NO_TLSV10)
ssloptions |= SSL_OP_NO_TLSv1;
- if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV11)
+ if (conf_ssl_options & BC_SSL_O_NO_TLSV11)
ssloptions |= SSL_OP_NO_TLSv1_1;
- if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV12)
+ if (conf_ssl_options & BC_SSL_O_NO_TLSV12)
ssloptions |= SSL_OP_NO_TLSv1_2;
- if (bind_conf->ssl_options & BC_SSL_O_NO_TLS_TICKETS)
+ if (conf_ssl_options & BC_SSL_O_NO_TLS_TICKETS)
ssloptions |= SSL_OP_NO_TICKET;
- if (bind_conf->ssl_options & BC_SSL_O_USE_SSLV3) {
+ if (conf_ssl_options & BC_SSL_O_USE_SSLV3) {
#ifndef OPENSSL_NO_SSL3
SSL_CTX_set_ssl_version(ctx, SSLv3_server_method());
#else
@@ -2766,20 +2876,20 @@
cfgerr++;
#endif
}
- if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV10)
+ if (conf_ssl_options & BC_SSL_O_USE_TLSV10)
SSL_CTX_set_ssl_version(ctx, TLSv1_server_method());
#if SSL_OP_NO_TLSv1_1
- if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV11)
+ if (conf_ssl_options & BC_SSL_O_USE_TLSV11)
SSL_CTX_set_ssl_version(ctx, TLSv1_1_server_method());
#endif
#if SSL_OP_NO_TLSv1_2
- if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV12)
+ if (conf_ssl_options & BC_SSL_O_USE_TLSV12)
SSL_CTX_set_ssl_version(ctx, TLSv1_2_server_method());
#endif
SSL_CTX_set_options(ctx, ssloptions);
SSL_CTX_set_mode(ctx, sslmode);
- switch (bind_conf->verify) {
+ switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
case SSL_SOCK_VERIFY_NONE:
verify = SSL_VERIFY_NONE;
break;
@@ -2792,15 +2902,17 @@
}
SSL_CTX_set_verify(ctx, verify, ssl_sock_bind_verifycbk);
if (verify & SSL_VERIFY_PEER) {
- if (bind_conf->ca_file) {
+ char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
+ char *crl_file = (ssl_conf && ssl_conf->crl_file) ? ssl_conf->crl_file : bind_conf->ssl_conf.crl_file;
+ if (ca_file) {
/* load CAfile to verify */
- if (!SSL_CTX_load_verify_locations(ctx, bind_conf->ca_file, NULL)) {
+ if (!SSL_CTX_load_verify_locations(ctx, ca_file, NULL)) {
Alert("Proxy '%s': unable to load CA file '%s' for bind '%s' at [%s:%d].\n",
- curproxy->id, bind_conf->ca_file, bind_conf->arg, bind_conf->file, bind_conf->line);
+ curproxy->id, ca_file, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
}
/* set CA names fo client cert request, function returns void */
- SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(bind_conf->ca_file));
+ SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(ca_file));
}
else {
Alert("Proxy '%s': verify is enabled but no CA file specified for bind '%s' at [%s:%d].\n",
@@ -2808,12 +2920,12 @@
cfgerr++;
}
#ifdef X509_V_FLAG_CRL_CHECK
- if (bind_conf->crl_file) {
+ if (crl_file) {
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
- if (!store || !X509_STORE_load_locations(store, bind_conf->crl_file, NULL)) {
+ if (!store || !X509_STORE_load_locations(store, crl_file, NULL)) {
Alert("Proxy '%s': unable to configure CRL file '%s' for bind '%s' at [%s:%d].\n",
- curproxy->id, bind_conf->crl_file, bind_conf->arg, bind_conf->file, bind_conf->line);
+ curproxy->id, crl_file, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
}
else {
@@ -2838,10 +2950,11 @@
SSL_CTX_set_timeout(ctx, global_ssl.life_time);
shared_context_set_cache(ctx);
- if (bind_conf->ciphers &&
- !SSL_CTX_set_cipher_list(ctx, bind_conf->ciphers)) {
+ conf_ciphers = (ssl_conf && ssl_conf->ciphers) ? ssl_conf->ciphers : bind_conf->ssl_conf.ciphers;
+ if (conf_ciphers &&
+ !SSL_CTX_set_cipher_list(ctx, conf_ciphers)) {
Alert("Proxy '%s': unable to set SSL cipher list to '%s' for bind '%s' at [%s:%d].\n",
- curproxy->id, bind_conf->ciphers, bind_conf->arg, bind_conf->file, bind_conf->line);
+ curproxy->id, conf_ciphers, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
}
@@ -2905,12 +3018,22 @@
#endif
#ifdef OPENSSL_NPN_NEGOTIATED
- if (bind_conf->npn_str)
- SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, bind_conf);
+ ssl_conf_cur = NULL;
+ if (ssl_conf && ssl_conf->npn_str)
+ ssl_conf_cur = ssl_conf;
+ else if (bind_conf->ssl_conf.npn_str)
+ ssl_conf_cur = &bind_conf->ssl_conf;
+ if (ssl_conf_cur)
+ SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, ssl_conf_cur);
#endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
- if (bind_conf->alpn_str)
- SSL_CTX_set_alpn_select_cb(ctx, ssl_sock_advertise_alpn_protos, bind_conf);
+ ssl_conf_cur = NULL;
+ if (ssl_conf && ssl_conf->alpn_str)
+ ssl_conf_cur = ssl_conf;
+ else if (bind_conf->ssl_conf.alpn_str)
+ ssl_conf_cur = &bind_conf->ssl_conf;
+ if (ssl_conf_cur)
+ SSL_CTX_set_alpn_select_cb(ctx, ssl_sock_advertise_alpn_protos, ssl_conf_cur);
#endif
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -2921,12 +3044,13 @@
{
int i;
EC_KEY *ecdh;
+ const char *ecdhe = (ssl_conf && ssl_conf->ecdhe) ? ssl_conf->ecdhe :
+ (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE);
- i = OBJ_sn2nid(bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE);
+ i = OBJ_sn2nid(ecdhe);
if (!i || ((ecdh = EC_KEY_new_by_curve_name(i)) == NULL)) {
Alert("Proxy '%s': unable to set elliptic named curve to '%s' for bind '%s' at [%s:%d].\n",
- curproxy->id, bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE,
- bind_conf->arg, bind_conf->file, bind_conf->line);
+ curproxy->id, ecdhe, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
}
else {
@@ -3241,7 +3365,7 @@
global.ssl_used_frontend = 1;
if (bind_conf->default_ctx)
- err += ssl_sock_prepare_ctx(bind_conf, bind_conf->default_ctx);
+ err += ssl_sock_prepare_ctx(bind_conf, bind_conf->default_ssl_conf, bind_conf->default_ctx);
node = ebmb_first(&bind_conf->sni_ctx);
while (node) {
@@ -3249,7 +3373,7 @@
if (!sni->order && sni->ctx != bind_conf->default_ctx)
/* only initialize the CTX on its first occurrence and
if it is not the default_ctx */
- err += ssl_sock_prepare_ctx(bind_conf, sni->ctx);
+ err += ssl_sock_prepare_ctx(bind_conf, sni->conf, sni->ctx);
node = ebmb_next(node);
}
@@ -3259,7 +3383,7 @@
if (!sni->order && sni->ctx != bind_conf->default_ctx)
/* only initialize the CTX on its first occurrence and
if it is not the default_ctx */
- err += ssl_sock_prepare_ctx(bind_conf, sni->ctx);
+ err += ssl_sock_prepare_ctx(bind_conf, sni->conf, sni->ctx);
node = ebmb_next(node);
}
return err;
@@ -3330,8 +3454,12 @@
sni = ebmb_entry(node, struct sni_ctx, name);
back = ebmb_next(node);
ebmb_delete(node);
- if (!sni->order) /* only free the CTX on its first occurrence */
+ if (!sni->order) { /* only free the CTX on its first occurrence */
SSL_CTX_free(sni->ctx);
+ ssl_sock_free_ssl_conf(sni->conf);
+ free(sni->conf);
+ sni->conf = NULL;
+ }
free(sni);
node = back;
}
@@ -3341,13 +3469,18 @@
sni = ebmb_entry(node, struct sni_ctx, name);
back = ebmb_next(node);
ebmb_delete(node);
- if (!sni->order) /* only free the CTX on its first occurrence */
+ if (!sni->order) { /* only free the CTX on its first occurrence */
SSL_CTX_free(sni->ctx);
+ ssl_sock_free_ssl_conf(sni->conf);
+ free(sni->conf);
+ sni->conf = NULL;
+ }
free(sni);
node = back;
}
bind_conf->default_ctx = NULL;
+ bind_conf->default_ssl_conf = NULL;
}
/* Destroys all the contexts for a bind_conf. This is used during deinit(). */
@@ -3355,12 +3488,9 @@
{
ssl_sock_free_ca(bind_conf);
ssl_sock_free_all_ctx(bind_conf);
- free(bind_conf->ca_file);
+ ssl_sock_free_ssl_conf(&bind_conf->ssl_conf);
free(bind_conf->ca_sign_file);
free(bind_conf->ca_sign_pass);
- free(bind_conf->ciphers);
- free(bind_conf->ecdhe);
- free(bind_conf->crl_file);
if (bind_conf->keys_ref) {
free(bind_conf->keys_ref->filename);
free(bind_conf->keys_ref->tlskeys);
@@ -3368,12 +3498,8 @@
free(bind_conf->keys_ref);
}
bind_conf->keys_ref = NULL;
- bind_conf->crl_file = NULL;
- bind_conf->ecdhe = NULL;
- bind_conf->ciphers = NULL;
bind_conf->ca_sign_pass = NULL;
bind_conf->ca_sign_file = NULL;
- bind_conf->ca_file = NULL;
}
/* Load CA cert file and private key used to generate certificates */
@@ -5249,7 +5375,7 @@
}
/* parse the "ca-file" bind keyword */
-static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
if (err)
@@ -5264,6 +5390,10 @@
return 0;
}
+static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_ca_file(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* 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)
@@ -5295,7 +5425,7 @@
}
/* 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 ssl_bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
memprintf(err, "'%s' : missing cipher suite", args[cur_arg]);
@@ -5306,7 +5436,10 @@
conf->ciphers = strdup(args[cur_arg + 1]);
return 0;
}
-
+static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_ciphers(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "crt" bind keyword */
static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
@@ -5343,7 +5476,7 @@
return ERR_ALERT | ERR_FATAL;
}
- if (ssl_sock_load_cert_list_file(args[cur_arg + 1], conf, err) > 0) {
+ if (ssl_sock_load_cert_list_file(args[cur_arg + 1], conf, px, err) > 0) {
memprintf(err, "'%s' : %s", args[cur_arg], *err);
return ERR_ALERT | ERR_FATAL;
}
@@ -5352,7 +5485,7 @@
}
/* parse the "crl-file" bind keyword */
-static int bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#ifndef X509_V_FLAG_CRL_CHECK
if (err)
@@ -5373,9 +5506,13 @@
return 0;
#endif
}
+static int bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_crl_file(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "ecdhe" bind keyword keyword */
-static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#if OPENSSL_VERSION_NUMBER < 0x0090800fL
if (err)
@@ -5397,6 +5534,10 @@
return 0;
#endif
}
+static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_ecdhe(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "crt-ignore-err" and "ca-ignore-err" bind keywords */
static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@@ -5437,21 +5578,29 @@
}
/* parse the "force-sslv3" bind keyword */
-static int bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_USE_SSLV3;
return 0;
}
+static int bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_force_sslv3(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "force-tlsv10" bind keyword */
-static int bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_USE_TLSV10;
return 0;
}
+static int bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_force_tlsv10(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "force-tlsv11" bind keyword */
-static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#if SSL_OP_NO_TLSv1_1
conf->ssl_options |= BC_SSL_O_USE_TLSV11;
@@ -5462,9 +5611,13 @@
return ERR_ALERT | ERR_FATAL;
#endif
}
+static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_force_tlsv11(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "force-tlsv12" bind keyword */
-static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#if SSL_OP_NO_TLSv1_2
conf->ssl_options |= BC_SSL_O_USE_TLSV12;
@@ -5475,46 +5628,68 @@
return ERR_ALERT | ERR_FATAL;
#endif
}
-
+static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_force_tlsv12(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "no-tls-tickets" bind keyword */
-static int bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_NO_TLS_TICKETS;
return 0;
}
-
+static int bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_no_tls_tickets(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "no-sslv3" bind keyword */
-static int bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_NO_SSLV3;
return 0;
}
+static int bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_no_sslv3(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "no-tlsv10" bind keyword */
-static int bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_NO_TLSV10;
return 0;
}
+static int bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_no_tlsv10(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "no-tlsv11" bind keyword */
-static int bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_NO_TLSV11;
return 0;
}
+static int bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_no_tlsv11(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "no-tlsv12" bind keyword */
-static int bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
conf->ssl_options |= BC_SSL_O_NO_TLSV12;
return 0;
}
+static int bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_no_tlsv12(args, cur_arg, px, &conf->ssl_conf, err);
+}
/* parse the "npn" bind keyword */
-static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#ifdef OPENSSL_NPN_NEGOTIATED
char *p1, *p2;
@@ -5564,8 +5739,13 @@
#endif
}
+static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_npn(args, cur_arg, px, &conf->ssl_conf, err);
+}
+
/* parse the "alpn" bind keyword */
-static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
char *p1, *p2;
@@ -5615,15 +5795,20 @@
#endif
}
+static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_alpn(args, cur_arg, px, &conf->ssl_conf, err);
+}
+
/* parse the "ssl" bind keyword */
static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
conf->xprt = &ssl_sock;
conf->is_ssl = 1;
- if (global_ssl.listen_default_ciphers && !conf->ciphers)
- conf->ciphers = strdup(global_ssl.listen_default_ciphers);
- conf->ssl_options |= global_ssl.listen_default_ssloptions;
+ if (global_ssl.listen_default_ciphers && !conf->ssl_conf.ciphers)
+ conf->ssl_conf.ciphers = strdup(global_ssl.listen_default_ciphers);
+ conf->ssl_conf.ssl_options |= global_ssl.listen_default_ssloptions;
return 0;
}
@@ -5723,7 +5908,7 @@
}
/* parse the "verify" bind keyword */
-static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
{
if (!*args[cur_arg + 1]) {
if (err)
@@ -5746,6 +5931,10 @@
return 0;
}
+static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+ return ssl_bind_parse_verify(args, cur_arg, px, &conf->ssl_conf, err);
+}
/************** "server" keywords ****************/
@@ -6620,6 +6809,26 @@
* the config parser can report an appropriate error when a known keyword was
* not enabled.
*/
+static struct ssl_bind_kw ssl_bind_kws[] = {
+ { "alpn", ssl_bind_parse_alpn, 1 }, /* set ALPN supported protocols */
+ { "ca-file", ssl_bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */
+ { "ciphers", ssl_bind_parse_ciphers, 1 }, /* set SSL cipher suite */
+ { "crl-file", ssl_bind_parse_crl_file, 1 }, /* set certificat revocation list file use on client cert verify */
+ { "ecdhe", ssl_bind_parse_ecdhe, 1 }, /* defines named curve for elliptic curve Diffie-Hellman */
+ { "force-sslv3", ssl_bind_parse_force_sslv3, 0 }, /* force SSLv3 */
+ { "force-tlsv10", ssl_bind_parse_force_tlsv10, 0 }, /* force TLSv10 */
+ { "force-tlsv11", ssl_bind_parse_force_tlsv11, 0 }, /* force TLSv11 */
+ { "force-tlsv12", ssl_bind_parse_force_tlsv12, 0 }, /* force TLSv12 */
+ { "no-sslv3", ssl_bind_parse_no_sslv3, 0 }, /* disable SSLv3 */
+ { "no-tlsv10", ssl_bind_parse_no_tlsv10, 0 }, /* disable TLSv10 */
+ { "no-tlsv11", ssl_bind_parse_no_tlsv11, 0 }, /* disable TLSv11 */
+ { "no-tlsv12", ssl_bind_parse_no_tlsv12, 0 }, /* disable TLSv12 */
+ { "no-tls-tickets", ssl_bind_parse_no_tls_tickets, 0 }, /* disable session resumption tickets */
+ { "npn", ssl_bind_parse_npn, 1 }, /* set NPN supported protocols */
+ { "verify", ssl_bind_parse_verify, 1 }, /* set SSL verify method */
+ { NULL, NULL, 0 },
+};
+
static struct bind_kw_list bind_kws = { "SSL", { }, {
{ "alpn", bind_parse_alpn, 1 }, /* set ALPN supported protocols */
{ "ca-file", bind_parse_ca_file, 1 }, /* set CAfile to process verify on client cert */