mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 22:01:31 +02:00
MEDIUM: ssl: improve crt-list format to support negation
Improve the crt-list file format to allow a rule to negate a certain SNI : <crtfile> [[!]<snifilter> ...] This can be useful when a domain supports a wildcard but you don't want to deliver the wildcard cert for certain specific domains.
This commit is contained in:
parent
2b5702030d
commit
7c41a1b59b
@ -7224,17 +7224,19 @@ crt-ignore-err <errors>
|
|||||||
|
|
||||||
crt-list <file>
|
crt-list <file>
|
||||||
This setting is only available when support for OpenSSL was built in. It
|
This setting is only available when support for OpenSSL was built in. It
|
||||||
designates a list of PEM file with an optional SNI filter per certificate,
|
designates a list of PEM file with an optional list of SNI filter per
|
||||||
with the following format for each line :
|
certificate, with the following format for each line :
|
||||||
|
|
||||||
<crtfile> [<snifilter>]
|
<crtfile> [[!]<snifilter> ...]
|
||||||
|
|
||||||
Wildcards are supported in the SNI filter. The certificates will be presented
|
Wildcards are supported in the SNI filter. Negative filter are also supported,
|
||||||
to clients who provide a valid TLS Server Name Indication field matching one
|
only useful in combination with a wildcard filter to exclude a particular SNI.
|
||||||
of SNI filter. If no SNI filter is specified the CN and alt subjects are
|
The certificates will be presented to clients who provide a valid TLS Server
|
||||||
used. This directive may be specified multiple times. See the "crt" option
|
Name Indication field matching one of the SNI filters. If no SNI filter is
|
||||||
for more information. The default certificate is still needed to meet OpenSSL
|
specified, the CN and alt subjects are used. This directive may be specified
|
||||||
expectations. If it is not used, the strict-sni option may be used.
|
multiple times. See the "crt" option for more information. The default
|
||||||
|
certificate is still needed to meet OpenSSL expectations. If it is not used,
|
||||||
|
the 'strict-sni' option may be used.
|
||||||
|
|
||||||
defer-accept
|
defer-accept
|
||||||
Is an optional keyword which is supported only on certain Linux kernels. It
|
Is an optional keyword which is supported only on certain Linux kernels. It
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
struct sni_ctx {
|
struct sni_ctx {
|
||||||
SSL_CTX *ctx; /* context associated to the certificate */
|
SSL_CTX *ctx; /* context associated to the certificate */
|
||||||
int order; /* load order for the certificate */
|
int order; /* load order for the certificate */
|
||||||
|
int neg; /* reject if match */
|
||||||
struct ebmb_node name; /* node holding the servername value */
|
struct ebmb_node name; /* node holding the servername value */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,16 +189,15 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
|
|||||||
{
|
{
|
||||||
const char *servername;
|
const char *servername;
|
||||||
const char *wildp = NULL;
|
const char *wildp = NULL;
|
||||||
struct ebmb_node *node;
|
struct ebmb_node *node, *n;
|
||||||
int i;
|
int i;
|
||||||
(void)al; /* shut gcc stupid warning */
|
(void)al; /* shut gcc stupid warning */
|
||||||
|
|
||||||
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
||||||
if (!servername) {
|
if (!servername) {
|
||||||
if (s->strict_sni)
|
return (s->strict_sni ?
|
||||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
SSL_TLSEXT_ERR_ALERT_FATAL :
|
||||||
else
|
SSL_TLSEXT_ERR_ALERT_WARNING);
|
||||||
return SSL_TLSEXT_ERR_NOACK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < trash.size; i++) {
|
for (i = 0; i < trash.size; i++) {
|
||||||
@ -212,21 +211,23 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
|
|||||||
|
|
||||||
/* lookup in full qualified names */
|
/* lookup in full qualified names */
|
||||||
node = ebst_lookup(&s->sni_ctx, trash.str);
|
node = ebst_lookup(&s->sni_ctx, trash.str);
|
||||||
if (!node) {
|
|
||||||
if (!wildp) {
|
/* lookup a not neg filter */
|
||||||
if (s->strict_sni)
|
for (n = node; n; n = ebmb_next_dup(n)) {
|
||||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
if (!container_of(n, struct sni_ctx, name)->neg) {
|
||||||
else
|
node = n;
|
||||||
return SSL_TLSEXT_ERR_ALERT_WARNING;
|
break;
|
||||||
}
|
}
|
||||||
/* lookup in full wildcards names */
|
wildp = NULL; /* never match a wildcard after matching a neg */
|
||||||
|
}
|
||||||
|
if (!node && wildp) {
|
||||||
|
/* lookup in wildcards names */
|
||||||
node = ebst_lookup(&s->sni_w_ctx, wildp);
|
node = ebst_lookup(&s->sni_w_ctx, wildp);
|
||||||
if (!node) {
|
|
||||||
if (s->strict_sni)
|
|
||||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
||||||
else
|
|
||||||
return SSL_TLSEXT_ERR_ALERT_WARNING;
|
|
||||||
}
|
}
|
||||||
|
if (!node || container_of(node, struct sni_ctx, name)->neg) {
|
||||||
|
return (s->strict_sni ?
|
||||||
|
SSL_TLSEXT_ERR_ALERT_FATAL :
|
||||||
|
SSL_TLSEXT_ERR_ALERT_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* switch ctx */
|
/* switch ctx */
|
||||||
@ -309,24 +310,32 @@ end:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int len, int order)
|
static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int order)
|
||||||
{
|
{
|
||||||
struct sni_ctx *sc;
|
struct sni_ctx *sc;
|
||||||
int wild = 0;
|
int wild = 0, neg = 0;
|
||||||
int j;
|
|
||||||
|
|
||||||
if (len) {
|
if (*name == '!') {
|
||||||
|
neg = 1;
|
||||||
|
name++;
|
||||||
|
}
|
||||||
if (*name == '*') {
|
if (*name == '*') {
|
||||||
wild = 1;
|
wild = 1;
|
||||||
name++;
|
name++;
|
||||||
len--;
|
|
||||||
}
|
}
|
||||||
|
/* !* filter is a nop */
|
||||||
|
if (neg && wild)
|
||||||
|
return order;
|
||||||
|
if (*name) {
|
||||||
|
int j, len;
|
||||||
|
len = strlen(name);
|
||||||
sc = malloc(sizeof(struct sni_ctx) + len + 1);
|
sc = malloc(sizeof(struct sni_ctx) + len + 1);
|
||||||
for (j = 0; j < len; j++)
|
for (j = 0; j < len; j++)
|
||||||
sc->name.key[j] = tolower(name[j]);
|
sc->name.key[j] = tolower(name[j]);
|
||||||
sc->name.key[len] = 0;
|
sc->name.key[len] = 0;
|
||||||
sc->order = order++;
|
|
||||||
sc->ctx = ctx;
|
sc->ctx = ctx;
|
||||||
|
sc->order = order++;
|
||||||
|
sc->neg = neg;
|
||||||
if (wild)
|
if (wild)
|
||||||
ebst_insert(&s->sni_w_ctx, &sc->name);
|
ebst_insert(&s->sni_w_ctx, &sc->name);
|
||||||
else
|
else
|
||||||
@ -338,11 +347,11 @@ int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int len
|
|||||||
/* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
|
/* 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.
|
* an early error happens and the caller must call SSL_CTX_free() by itelf.
|
||||||
*/
|
*/
|
||||||
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, char **sni_filter, int fcount)
|
||||||
{
|
{
|
||||||
BIO *in;
|
BIO *in;
|
||||||
X509 *x = NULL, *ca;
|
X509 *x = NULL, *ca;
|
||||||
int i, len, err;
|
int i, err;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
int order = 0;
|
int order = 0;
|
||||||
X509_NAME *xname;
|
X509_NAME *xname;
|
||||||
@ -364,7 +373,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
|
|||||||
|
|
||||||
if (fcount) {
|
if (fcount) {
|
||||||
while (fcount--)
|
while (fcount--)
|
||||||
order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], strlen(sni_filter[fcount]), order);
|
order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], order);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
@ -374,8 +383,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
|
|||||||
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
|
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
|
||||||
if (name->type == GEN_DNS) {
|
if (name->type == GEN_DNS) {
|
||||||
if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
|
if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
|
||||||
len = strlen(str);
|
order = ssl_sock_add_cert_sni(ctx, s, str, order);
|
||||||
order = ssl_sock_add_cert_sni(ctx, s, str, len, order);
|
|
||||||
OPENSSL_free(str);
|
OPENSSL_free(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,8 +396,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
|
|||||||
while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1) {
|
while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1) {
|
||||||
X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
|
X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
|
||||||
if (ASN1_STRING_to_UTF8((unsigned char **)&str, entry->value) >= 0) {
|
if (ASN1_STRING_to_UTF8((unsigned char **)&str, entry->value) >= 0) {
|
||||||
len = strlen(str);
|
order = ssl_sock_add_cert_sni(ctx, s, str, order);
|
||||||
order = ssl_sock_add_cert_sni(ctx, s, str, len, order);
|
|
||||||
OPENSSL_free(str);
|
OPENSSL_free(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,7 +545,7 @@ static int ssl_initialize_random()
|
|||||||
|
|
||||||
int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
|
int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
|
||||||
{
|
{
|
||||||
char thisline[65536];
|
char thisline[LINESIZE];
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int linenum = 0;
|
int linenum = 0;
|
||||||
int cfgerr = 0;
|
int cfgerr = 0;
|
||||||
@ -591,6 +598,8 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
|
|||||||
}
|
}
|
||||||
line++;
|
line++;
|
||||||
}
|
}
|
||||||
|
if (cfgerr)
|
||||||
|
break;
|
||||||
|
|
||||||
/* empty line */
|
/* empty line */
|
||||||
if (!arg)
|
if (!arg)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user