From: Jan Just Keijser <janj...@nikhef.nl> Add extended client certificate verification support.
Replace --client-cert-not-required with a more flexible option, that allows for no, optional or mandatory client certificate verification. Signed-off-by: Jan Just Keijser <janj...@nikhef.nl> --- doc/openvpn.8 | 49 +++++++++++++++++++++++++++++++++++++++++++- src/openvpn/options.c | 28 +++++++++++++++++++++++- src/openvpn/ssl_common.h | 5 ++- src/openvpn/ssl_openssl.c | 15 +++++++++---- 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/doc/openvpn.8 b/doc/openvpn.8 index 3eb2493..6ff2b4e 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -3549,18 +3549,63 @@ to empty strings (""). The authentication module/script MUST have logic to detect this condition and respond accordingly. .\"********************************************************* .TP -.B \-\-client\-cert\-not\-required +.B \-\-client\-cert\-not\-required (DEPRECATED) Don't require client certificate, client will authenticate using username/password only. Be aware that using this directive is less secure than requiring certificates from all clients. + +.B Please note: +This option is now deprecated and will be removed in OpenVPN v2.5. +It is replaced by +.B \-\-verify\-client\-cert +which allows for more flexibility. The option +.B \-\-verify\-client\-cert none +is functionally equivalent to +.B \-\-client\-cert\-not\-required +. + +.\"********************************************************* +.TP +.B \-\-verify\-client\-cert none|optional|require +Specify whether the client is required to supply a valid certificate. + +Possible options are + +.B none +: a client certificate is not required. the client need to authenticate +using username/password only. Be aware that using this directive +is less secure than requiring certificates from all clients. + If you use this directive, the entire responsibility of authentication will rest on your .B \-\-auth\-user\-pass\-verify script, so keep in mind that bugs in your script could potentially compromise the security of your VPN. -If you don't use this directive, but you also specify an +.B \-\-verify\-client\-cert none +is functionally equivalent to +.B \-\-client\-cert\-not\-required. + +.B optional +: a client may present a certificate but it is not required to do so. +When using this directive, you should also use a +.B \-\-auth\-user\-pass\-verify +script to ensure that clients are authenticated using a +certificate, a username and password, or possibly even both. + +Again, the entire responsibility of authentication will rest on your +.B \-\-auth\-user\-pass\-verify +script, so keep in mind that bugs in your script +could potentially compromise the security of your VPN. + +.B require +: this is the default option. A client is required to present a +certificate, otherwise VPN access is refused. + +If you don't use this directive (or use +.B \-\-verify\-client\-cert require +) but you also specify an .B \-\-auth\-user\-pass\-verify script, then OpenVPN will perform double authentication. The client certificate verification AND the diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 74276d4..65e4658 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -437,6 +437,9 @@ static const char usage_message[] = " Only valid in a client-specific config file.\n" "--client-cert-not-required : Don't require client certificate, client\n" " will authenticate using username/password.\n" + "--verify-client-cert [none|optional|require] : perform no, optional or\n" + " mandatory client certificate verification.\n" + " Default is to require the client to supply a certificate.\n" "--username-as-common-name : For auth-user-pass authentication, use\n" " the authenticated username as the common name,\n" " rather than the common name from the client cert.\n" @@ -2091,8 +2094,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--duplicate-cn requires --mode server"); if (options->cf_max || options->cf_per) msg (M_USAGE, "--connect-freq requires --mode server"); - if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) - msg (M_USAGE, "--client-cert-not-required requires --mode server"); + if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED || options->ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) + msg (M_USAGE, "--client-cert-not-required and --verify-client-cert require --mode server"); if (options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) msg (M_USAGE, "--username-as-common-name requires --mode server"); if (options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) @@ -5658,6 +5661,27 @@ add_option (struct options *options, { VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_CLIENT_CERT_NOT_REQUIRED; + msg (M_WARN, "DEPRECATED OPTION: --client-cert-not-required, use --verify-client-cert instead"); + } + else if (streq (p[0], "verify-client-cert") && !p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + + /* Reset any existing flags */ + options->ssl_flags &= ~SSLF_CLIENT_CERT_OPTIONAL; + options->ssl_flags &= ~SSLF_CLIENT_CERT_NOT_REQUIRED; + if (p[1]) + { + if (streq (p[1], "none")) + options->ssl_flags |= SSLF_CLIENT_CERT_NOT_REQUIRED; + else if (streq (p[1], "optional")) + options->ssl_flags |= SSLF_CLIENT_CERT_OPTIONAL; + else if (!streq (p[1], "require")) + { + msg (msglevel, "parameter to --verify-client-cert must be 'none', 'optional' or 'require'"); + goto err; + } + } } else if (streq (p[0], "username-as-common-name") && !p[1]) { diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 95cd2f7..8fab21f 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -293,8 +293,9 @@ struct tls_options /* configuration file SSL-related boolean and low-permutation options */ # define SSLF_CLIENT_CERT_NOT_REQUIRED (1<<0) -# define SSLF_USERNAME_AS_COMMON_NAME (1<<1) -# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2) +# define SSLF_CLIENT_CERT_OPTIONAL (1<<1) +# define SSLF_USERNAME_AS_COMMON_NAME (1<<2) +# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<3) # define SSLF_OPT_VERIFY (1<<4) # define SSLF_CRL_VERIFY_DIR (1<<5) # define SSLF_TLS_VERSION_MIN_SHIFT 6 diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index df9fa87..6e29af4 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -180,6 +180,9 @@ void tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) { ASSERT(NULL != ctx); + + /* default certificate verification flags */ + int flags = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; /* process SSL options including minimum TLS version we will accept from peer */ { @@ -221,13 +224,15 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags) if (ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) { msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION " - "--client-cert-not-required may accept clients which do not present " - "a certificate"); + "--client-cert-not-required and --verify-client-cert none " + "may accept clients which do not present a certificate"); + + flags = 0; } - else + else if (ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) + flags = SSL_VERIFY_PEER; #endif - SSL_CTX_set_verify (ctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - verify_callback); + SSL_CTX_set_verify (ctx->ctx, flags, verify_callback); SSL_CTX_set_info_callback (ctx->ctx, info_callback); } -- 1.7.1