MEDIUM: ssl: Set verify 'required' as global default for servers side.

If no CA file specified on a server line, the config parser will show an error.

Adds an cmdline option '-dV' to re-set verify 'none' as global default on
servers side (previous behavior).

Also adds 'ssl-server-verify' global statement to set global default to
'none' or 'required'.

WARNING: this changes the default verify mode from "none" to "required" on
the server side, and it *will* break insecure setups.
This commit is contained in:
Emeric Brun 2014-01-29 12:24:34 +01:00 committed by Willy Tarreau
parent 59ad1a2e75
commit 850efd5149
5 changed files with 95 additions and 12 deletions

View File

@ -455,6 +455,7 @@ The following keywords are supported in the "global" section :
- ulimit-n
- user
- stats
- ssl-server-verify
- node
- description
- unix-bind
@ -625,6 +626,11 @@ stats bind-process [ all | odd | even | <number 1-32>[-<number 1-32>] ] ...
warning will automatically be disabled when this setting is used, whatever
the number of processes used.
ssl-server-verify [none|required]
The default behavior for SSL verify on servers side. If specified to 'none',
servers certificates are not verified. The default is 'required' except if
forced using cmdline option '-dV'.
stats socket [<address:port>|<path>] [param*]
Binds a UNIX socket to <path> or a TCPv4/v6 address to <address:port>.
Connections to this socket will return various statistics outputs and even
@ -8519,9 +8525,10 @@ track [<proxy>/]<server>
verify [none|required]
This setting is only available when support for OpenSSL was built in. If set
to 'none', server certificate is not verified. This is the default. In the
other case, The certificate provided by the server is verified using CAs from
'ca-file' and optional CRLs from 'crl-file'. On verify failure the handshake
to 'none', server certificate is not verified. In the other case, The
certificate provided by the server is verified using CAs from 'ca-file'
and optional CRLs from 'crl-file'. If 'ssl_server_verify' is not specified
in global section, this is the default. On verify failure the handshake
is aborted. It is critically important to verify server certificates when
using SSL to connect to servers, otherwise the communication is prone to
trivial man-in-the-middle attacks rendering SSL totally useless.

View File

@ -64,6 +64,12 @@
#define ACCESS_LVL_OPER 2
#define ACCESS_LVL_ADMIN 3
/* SSL server verify mode */
enum {
SSL_SERVER_VERIFY_NONE = 0,
SSL_SERVER_VERIFY_REQUIRED = 1,
};
/* FIXME : this will have to be redefined correctly */
struct global {
#ifdef USE_OPENSSL
@ -79,6 +85,7 @@ struct global {
char *listen_default_ciphers;
char *connect_default_ciphers;
#endif
unsigned int ssl_server_verify; /* default verify mode on servers side */
struct freq_ctr conn_per_sec;
struct freq_ctr sess_per_sec;
struct freq_ctr ssl_per_sec;

View File

@ -862,6 +862,22 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
goto out;
#endif
}
else if (!strcmp(args[0], "ssl-server-verify")) {
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;
}
if (strcmp(args[1],"none") == 0)
global.ssl_server_verify = SSL_SERVER_VERIFY_NONE;
else if (strcmp(args[1],"required") == 0)
global.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED;
else {
Alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
}
else if (!strcmp(args[0], "maxconnrate")) {
if (global.cps_lim != 0) {
Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);

View File

@ -128,6 +128,7 @@ struct global global = {
.maxzlibmem = 0,
#endif
.comp_rate_lim = 0,
.ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED,
.unix_bind = {
.ux = {
.uid = -1,
@ -383,6 +384,7 @@ void usage(char *name)
#if defined(CONFIG_HAP_LINUX_SPLICE)
" -dS disables splice usage (broken on old kernels)\n"
#endif
" -dV disables SSL verify on servers side\n"
" -sf/-st [pid ]* finishes/terminates old pids. Must be last arguments.\n"
"\n",
name, DEFAULT_MAXCONN, cfg_maxpconn);
@ -587,6 +589,8 @@ void init(int argc, char **argv)
else if (*flag == 'd' && flag[1] == 'S')
global.tune.options &= ~GTUNE_USE_SPLICE;
#endif
else if (*flag == 'd' && flag[1] == 'V')
global.ssl_server_verify = SSL_SERVER_VERIFY_NONE;
else if (*flag == 'V')
arg_mode |= MODE_VERBOSE;
else if (*flag == 'd' && flag[1] == 'b')

View File

@ -86,6 +86,14 @@
#define SSL_SOCK_ST_TO_CAEDEPTH(s) ((s >> (6+16)) & 15)
#define SSL_SOCK_ST_TO_CRTERROR(s) ((s >> (4+6+16)) & 63)
/* server and bind verify method, it uses a global value as default */
enum {
SSL_SOCK_VERIFY_DEFAULT = 0,
SSL_SOCK_VERIFY_REQUIRED = 1,
SSL_SOCK_VERIFY_OPTIONAL = 2,
SSL_SOCK_VERIFY_NONE = 3,
};
int sslconns = 0;
int totalsslconns = 0;
@ -651,6 +659,7 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *curproxy)
{
int cfgerr = 0;
int verify = SSL_VERIFY_NONE;
int ssloptions =
SSL_OP_ALL | /* all known workarounds for bugs */
SSL_OP_NO_SSLv2 |
@ -695,8 +704,19 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy
SSL_CTX_set_options(ctx, ssloptions);
SSL_CTX_set_mode(ctx, sslmode);
SSL_CTX_set_verify(ctx, bind_conf->verify ? bind_conf->verify : SSL_VERIFY_NONE, ssl_sock_bind_verifycbk);
if (bind_conf->verify & SSL_VERIFY_PEER) {
switch (bind_conf->verify) {
case SSL_SOCK_VERIFY_NONE:
verify = SSL_VERIFY_NONE;
break;
case SSL_SOCK_VERIFY_OPTIONAL:
verify = SSL_VERIFY_PEER;
break;
case SSL_SOCK_VERIFY_REQUIRED:
verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
break;
}
SSL_CTX_set_verify(ctx, verify, ssl_sock_bind_verifycbk);
if (verify & SSL_VERIFY_PEER) {
if (bind_conf->ca_file) {
/* load CAfile to verify */
if (!SSL_CTX_load_verify_locations(ctx, bind_conf->ca_file, NULL)) {
@ -707,6 +727,11 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy
/* 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));
}
else {
Alert("Proxy '%s': verify is enabled but no CA file specified for bind '%s' at [%s:%d].\n",
curproxy->id, bind_conf->arg, bind_conf->file, bind_conf->line);
cfgerr++;
}
#ifdef X509_V_FLAG_CRL_CHECK
if (bind_conf->crl_file) {
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
@ -906,6 +931,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *curproxy)
SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
SSL_MODE_RELEASE_BUFFERS;
int verify = SSL_VERIFY_NONE;
/* Make sure openssl opens /dev/urandom before the chroot */
if (!ssl_initialize_random()) {
@ -974,10 +1000,22 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *curproxy)
SSL_CTX_set_options(srv->ssl_ctx.ctx, options);
SSL_CTX_set_mode(srv->ssl_ctx.ctx, mode);
if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
verify = SSL_VERIFY_PEER;
switch (srv->ssl_ctx.verify) {
case SSL_SOCK_VERIFY_NONE:
verify = SSL_VERIFY_NONE;
break;
case SSL_SOCK_VERIFY_REQUIRED:
verify = SSL_VERIFY_PEER;
break;
}
SSL_CTX_set_verify(srv->ssl_ctx.ctx,
srv->ssl_ctx.verify ? srv->ssl_ctx.verify : SSL_VERIFY_NONE,
verify,
srv->ssl_ctx.verify_host ? ssl_sock_srv_verifycbk : NULL);
if (srv->ssl_ctx.verify & SSL_VERIFY_PEER) {
if (verify & SSL_VERIFY_PEER) {
if (srv->ssl_ctx.ca_file) {
/* load CAfile to verify */
if (!SSL_CTX_load_verify_locations(srv->ssl_ctx.ctx, srv->ssl_ctx.ca_file, NULL)) {
@ -987,6 +1025,17 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *curproxy)
cfgerr++;
}
}
else {
if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
Alert("Proxy '%s', server '%s' |%s:%d] verify is enabled by default but no CA file specified. If you're running on a LAN where you're certain to trust the server's certificate, please set an explicit 'verify none' statement on the 'server' line, or use 'ssl-server-verify none' in the global section to disable server-side verifications by default.\n",
curproxy->id, srv->id,
srv->conf.file, srv->conf.line);
else
Alert("Proxy '%s', server '%s' |%s:%d] verify is enabled but no CA file specified.\n",
curproxy->id, srv->id,
srv->conf.file, srv->conf.line);
cfgerr++;
}
#ifdef X509_V_FLAG_CRL_CHECK
if (srv->ssl_ctx.crl_file) {
X509_STORE *store = SSL_CTX_get_cert_store(srv->ssl_ctx.ctx);
@ -3190,11 +3239,11 @@ static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct
}
if (strcmp(args[cur_arg + 1], "none") == 0)
conf->verify = SSL_VERIFY_NONE;
conf->verify = SSL_SOCK_VERIFY_NONE;
else if (strcmp(args[cur_arg + 1], "optional") == 0)
conf->verify = SSL_VERIFY_PEER;
conf->verify = SSL_SOCK_VERIFY_OPTIONAL;
else if (strcmp(args[cur_arg + 1], "required") == 0)
conf->verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
conf->verify = SSL_SOCK_VERIFY_REQUIRED;
else {
if (err)
memprintf(err, "'%s' : unknown verify method '%s', only 'none', 'optional', and 'required' are supported\n",
@ -3380,9 +3429,9 @@ static int srv_parse_verify(char **args, int *cur_arg, struct proxy *px, struct
}
if (strcmp(args[*cur_arg + 1], "none") == 0)
newsrv->ssl_ctx.verify = SSL_VERIFY_NONE;
newsrv->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
else if (strcmp(args[*cur_arg + 1], "required") == 0)
newsrv->ssl_ctx.verify = SSL_VERIFY_PEER;
newsrv->ssl_ctx.verify = SSL_SOCK_VERIFY_REQUIRED;
else {
if (err)
memprintf(err, "'%s' : unknown verify method '%s', only 'none' and 'required' are supported\n",