From patchwork Wed Sep 26 03:44:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arne Schwabe X-Patchwork-Id: 470 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director12.mail.ord1d.rsapps.net ([172.31.255.6]) by backend30.mail.ord1d.rsapps.net with LMTP id cNndN7eNq1ttQQAAIUCqbw for ; Wed, 26 Sep 2018 09:46:32 -0400 Received: from proxy9.mail.iad3b.rsapps.net ([172.31.255.6]) by director12.mail.ord1d.rsapps.net with LMTP id CJSxNLeNq1vmXQAAIasKDg ; Wed, 26 Sep 2018 09:46:31 -0400 Received: from smtp35.gate.iad3b ([172.31.255.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy9.mail.iad3b.rsapps.net with LMTP id aPmJLbeNq1uVXAAAC4PSzw ; Wed, 26 Sep 2018 09:46:31 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp35.gate.iad3b.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=rfc2549.org X-Suspicious-Flag: YES X-Classification-ID: 927e858a-c192-11e8-99e4-525400503131-1-1 Received: from [216.105.38.7] ([216.105.38.7:7752] helo=lists.sourceforge.net) by smtp35.gate.iad3b.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id B0/85-01855-6BD8BAB5; Wed, 26 Sep 2018 09:46:31 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1g5A7z-00032h-RE; Wed, 26 Sep 2018 13:45:19 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1g5A7z-00032U-0D for openvpn-devel@lists.sourceforge.net; Wed, 26 Sep 2018 13:45:19 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=BzViTzADQ0Bo9fOUmPI3jWalqMvPqim3YkgvMpQSg5M=; b=YAkbZzZj7QE8O/7EsDez/zpcr+ ZpnYy/bIhCKrL4cXe7/NXJxQ8SCnQUrnfrCERhF0/JZghKyG+O/6BQ67lsIY38W1MWVTupfGqmA06 VATUEOYYjYByOvPfJsNbj+nRPdcj0udgaM2jQCP4MDAMbTZtS5Kp3c1qk5WmLlyqP+bQ=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:MIME-Version: Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=BzViTzADQ0Bo9fOUmPI3jWalqMvPqim3YkgvMpQSg5M=; b=e38jWdex/sOt6XmBUM0Fz0nC3q b0E8hsyTjY8qFZBf6d8ZmnQ2tdoTKrAHCvAMVNu01ITkTvt/aipbnrjKyVbMJfzxv1OlP31plb0yn gptzU6iwuOiwwvimC+6OZxQ4EYZIpTLi24i5l7wCr0YkYFB6kMj7kAbggusuFIXXtnSA=; Received: from [192.26.174.232] (helo=mail.blinkt.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) id 1g5A7t-004UaO-19 for openvpn-devel@lists.sourceforge.net; Wed, 26 Sep 2018 13:45:18 +0000 Received: from kamera.blinkt.de ([2001:638:502:390:20c:29ff:fec8:535c]) by mail.blinkt.de with smtp (Exim 4.91 (FreeBSD)) (envelope-from ) id 1g5A7e-000MUK-OD for openvpn-devel@lists.sourceforge.net; Wed, 26 Sep 2018 15:44:58 +0200 Received: (nullmailer pid 20964 invoked by uid 10006); Wed, 26 Sep 2018 13:44:58 -0000 From: Arne Schwabe To: openvpn-devel@lists.sourceforge.net Date: Wed, 26 Sep 2018 15:44:58 +0200 Message-Id: <20180926134458.20920-1-arne@rfc2549.org> X-Mailer: git-send-email 2.17.1 X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.2 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different 1.0 RDNS_NONE Delivered to internal network by a host with no rDNS -0.5 AWL AWL: Adjusted score from AWL reputation of From: address X-Headers-End: 1g5A7t-004UaO-19 Subject: [Openvpn-devel] [PATCH] Add support for tls-ciphersuites for TLS 1.3 X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox OpenSSL 1.1.1 introduces a seperate list for TLS 1.3 ciphers. As these interfaces are meant to be user facing or not exposed at all and we expose the tls-cipher interface, we should also expose tls-cipherlist. Combining both settings into tls-cipher would add a lot of glue logic that needs to be maintained and is error prone. On top of that, users should not set either settings unless absolutely required. OpenSSL's own s_client/s_server also expose both settings and I believe most other software will too: -cipher val Specify TLSv1.2 and below cipher list to be used -ciphersuites val Specify TLSv1.3 ciphersuites to be used For mbed TLS only the future can tell if we will see a combined or also two seperate lists. --- doc/openvpn.8 | 19 ++++- src/openvpn/options.c | 7 ++ src/openvpn/options.h | 1 + src/openvpn/ssl.c | 3 +- src/openvpn/ssl_backend.h | 13 ++- src/openvpn/ssl_mbedtls.c | 12 +++ src/openvpn/ssl_openssl.c | 167 +++++++++++++++++++++++--------------- 7 files changed, 151 insertions(+), 71 deletions(-) diff --git a/doc/openvpn.8 b/doc/openvpn.8 index 15a10296..0b44a29d 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -5001,11 +5001,13 @@ determines the derivation of the tunnel session keys. .\"********************************************************* .TP .B \-\-tls\-cipher l +.TQ +.B \-\-tls\-ciphersuites l A list .B l of allowable TLS ciphers delimited by a colon (":"). -This setting can be used to ensure that certain cipher suites are used (or +These setting can be used to ensure that certain cipher suites are used (or not used) for the TLS connection. OpenVPN uses TLS to secure the control channel, over which the keys that are used to protect the actual VPN traffic are exchanged. @@ -5014,13 +5016,24 @@ The supplied list of ciphers is (after potential OpenSSL/IANA name translation) simply supplied to the crypto library. Please see the OpenSSL and/or mbed TLS documentation for details on the cipher list interpretation. +For OpenSSL the +.B \-\-tls-cipher +is used for TLS 1.2 and below. For TLS 1.3 and up +the +.B \-\-tls\-ciphersuites +setting is used. mbed TLS has no TLS 1.3 support yet and only the +.B \-\-tls-cipher +setting is used. + Use .B \-\-show\-tls to see a list of TLS ciphers supported by your crypto library. Warning! .B \-\-tls\-cipher -is an expert feature, which \- if used correcly \- can improve the security of +and +.B \-\-tls\-ciphersuites +are expert features, which \- if used correcly \- can improve the security of your VPN connection. But it is also easy to unwittingly use it to carefully align a gun with your foot, or just break your connection. Use with care! @@ -5028,6 +5041,8 @@ The default for \-\-tls\-cipher is to use mbed TLS's default cipher list when using mbed TLS or "DEFAULT:!EXP:!LOW:!MEDIUM:!kDH:!kECDH:!DSS:!PSK:!SRP:!kRSA" when using OpenSSL. + +The default for \-\-tls\-ciphersuites is to use the crypto library's default. .\"********************************************************* .TP .B \-\-tls\-cert\-profile profile diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 03550c1e..a574c9f9 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -1766,6 +1766,7 @@ show_settings(const struct options *o) #endif SHOW_STR(cipher_list); SHOW_STR(tls_cert_profile); + SHOW_STR(cipher_list_tls13); SHOW_STR(tls_verify); SHOW_STR(tls_export_cert); SHOW_INT(verify_x509_type); @@ -2759,6 +2760,7 @@ options_postprocess_verify_ce(const struct options *options, const struct connec MUST_BE_UNDEF(pkcs12_file); #endif MUST_BE_UNDEF(cipher_list); + MUST_BE_UNDEF(cipher_list_tls13); MUST_BE_UNDEF(tls_cert_profile); MUST_BE_UNDEF(tls_verify); MUST_BE_UNDEF(tls_export_cert); @@ -7948,6 +7950,11 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_GENERAL); options->tls_cert_profile = p[1]; } + else if (streq(p[0], "tls-ciphersuites") && p[1] && !p[2]) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + options->cipher_list_tls13 = p[1]; + } else if (streq(p[0], "crl-verify") && p[1] && ((p[2] && streq(p[2], "dir")) || (p[2] && streq(p[1], INLINE_FILE_TAG) ) || !p[2]) && !p[3]) { diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 4c3bc4fb..3e7ef4f8 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -508,6 +508,7 @@ struct options const char *priv_key_file; const char *pkcs12_file; const char *cipher_list; + const char *cipher_list_tls13; const char *tls_cert_profile; const char *ecdh_curve; const char *tls_verify; diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index e5e4aac2..616c2696 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -626,9 +626,10 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx) tls_ctx_set_cert_profile(new_ctx, options->tls_cert_profile); /* Allowable ciphers */ - /* Since @SECLEVEL also influces loading of certificates, set the + /* Since @SECLEVEL also influences loading of certificates, set the * cipher restrictions before loading certificates */ tls_ctx_restrict_ciphers(new_ctx, options->cipher_list); + tls_ctx_restrict_ciphers_tls13(new_ctx, options->cipher_list_tls13); if (!tls_ctx_set_options(new_ctx, options->ssl_flags)) { diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index 5023c02a..0995bb4c 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -169,7 +169,8 @@ bool tls_ctx_initialised(struct tls_root_ctx *ctx); bool tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags); /** - * Restrict the list of ciphers that can be used within the TLS context. + * Restrict the list of ciphers that can be used within the TLS context for TLS 1.2 + * and below * * @param ctx TLS context to restrict, must be valid. * @param ciphers String containing : delimited cipher names, or NULL to use @@ -177,6 +178,16 @@ bool tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags); */ void tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers); +/** + * Restrict the list of ciphers that can be used within the TLS context for TLS 1.3 + * and higher + * + * @param ctx TLS context to restrict, must be valid. + * @param ciphers String containing : delimited cipher names, or NULL to use + * sane defaults. + */ +void tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers); + /** * Set the TLS certificate profile. The profile defines which crypto * algorithms may be used in the supplied certificate. diff --git a/src/openvpn/ssl_mbedtls.c b/src/openvpn/ssl_mbedtls.c index e4850cb6..7c578a90 100644 --- a/src/openvpn/ssl_mbedtls.c +++ b/src/openvpn/ssl_mbedtls.c @@ -222,6 +222,18 @@ tls_translate_cipher_name(const char *cipher_name) return pair->iana_name; } +void +tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers) +{ + if (ciphers == NULL) + { + /* Nothing to do, return without warning message */ + return; + } + + msg(M_WARN, "mbed TLS does not support setting tls-ciphersuites. Ignoring TLS 1.3 cipher list: %s", ciphers); +} + void tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) { diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 51bb6843..e3fb097f 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -322,6 +322,105 @@ tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) return true; } +void +convert_tls_list_to_openssl(char* openssl_ciphers, size_t len,const char *ciphers) +{ + /* Parse supplied cipher list and pass on to OpenSSL */ + size_t begin_of_cipher, end_of_cipher; + + const char *current_cipher; + size_t current_cipher_len; + + const tls_cipher_name_pair *cipher_pair; + + size_t openssl_ciphers_len = 0; + openssl_ciphers[0] = '\0'; + + /* Translate IANA cipher suite names to OpenSSL names */ + begin_of_cipher = end_of_cipher = 0; + for (; begin_of_cipher < strlen(ciphers); begin_of_cipher = end_of_cipher) + { + end_of_cipher += strcspn(&ciphers[begin_of_cipher], ":"); + cipher_pair = tls_get_cipher_name_pair(&ciphers[begin_of_cipher], end_of_cipher - begin_of_cipher); + + if (NULL == cipher_pair) + { + /* No translation found, use original */ + current_cipher = &ciphers[begin_of_cipher]; + current_cipher_len = end_of_cipher - begin_of_cipher; + + /* Issue warning on missing translation */ + /* %.*s format specifier expects length of type int, so guarantee */ + /* that length is small enough and cast to int. */ + msg(D_LOW, "No valid translation found for TLS cipher '%.*s'", + constrain_int(current_cipher_len, 0, 256), current_cipher); + } + else + { + /* Use OpenSSL name */ + current_cipher = cipher_pair->openssl_name; + current_cipher_len = strlen(current_cipher); + + if (end_of_cipher - begin_of_cipher == current_cipher_len + && 0 != memcmp(&ciphers[begin_of_cipher], cipher_pair->iana_name, + end_of_cipher - begin_of_cipher)) + { + /* Non-IANA name used, show warning */ + msg(M_WARN, "Deprecated TLS cipher name '%s', please use IANA name '%s'", cipher_pair->openssl_name, cipher_pair->iana_name); + } + } + + /* Make sure new cipher name fits in cipher string */ + if ((SIZE_MAX - openssl_ciphers_len) < current_cipher_len + || (len - 1) < (openssl_ciphers_len + current_cipher_len)) + { + msg(M_FATAL, + "Failed to set restricted TLS cipher list, too long (>%d).", + (int)(len - 1)); + } + + /* Concatenate cipher name to OpenSSL cipher string */ + memcpy(&openssl_ciphers[openssl_ciphers_len], current_cipher, current_cipher_len); + openssl_ciphers_len += current_cipher_len; + openssl_ciphers[openssl_ciphers_len] = ':'; + openssl_ciphers_len++; + + end_of_cipher++; + } + + if (openssl_ciphers_len > 0) + { + openssl_ciphers[openssl_ciphers_len-1] = '\0'; + } +} + +void +tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers) +{ + if (ciphers == NULL) + { + /* default cipher list of OpenSSL 1.1.1 is sane, do not set own own + * default as we do with tls-cipher */ + return; + } + +#if (OPENSSL_VERSION_NUMBER < 0x1010100fL) + crypto_msg(M_WARN, "Not compiled with OpenSSL 1.1.1 or higher. " + "Ignoring TLS 1.3 only tls-ciphersuites '%s' setting.", + ciphers); +#else + ASSERT(NULL != ctx); + + char openssl_ciphers[4096]; + convert_tls_list_to_openssl(openssl_ciphers, sizeof(openssl_ciphers), ciphers); + + if (!SSL_CTX_set_ciphersuites(ctx->ctx, openssl_ciphers)) + { + crypto_msg(M_FATAL, "Failed to set restricted TLS 1.3 cipher list: %s", openssl_ciphers); + } +#endif +} + void tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) { @@ -345,77 +444,11 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) return; } - /* Parse supplied cipher list and pass on to OpenSSL */ - size_t begin_of_cipher, end_of_cipher; - - const char *current_cipher; - size_t current_cipher_len; - - const tls_cipher_name_pair *cipher_pair; - char openssl_ciphers[4096]; - size_t openssl_ciphers_len = 0; - openssl_ciphers[0] = '\0'; + convert_tls_list_to_openssl(openssl_ciphers, sizeof(openssl_ciphers), ciphers); ASSERT(NULL != ctx); - /* Translate IANA cipher suite names to OpenSSL names */ - begin_of_cipher = end_of_cipher = 0; - for (; begin_of_cipher < strlen(ciphers); begin_of_cipher = end_of_cipher) - { - end_of_cipher += strcspn(&ciphers[begin_of_cipher], ":"); - cipher_pair = tls_get_cipher_name_pair(&ciphers[begin_of_cipher], end_of_cipher - begin_of_cipher); - - if (NULL == cipher_pair) - { - /* No translation found, use original */ - current_cipher = &ciphers[begin_of_cipher]; - current_cipher_len = end_of_cipher - begin_of_cipher; - - /* Issue warning on missing translation */ - /* %.*s format specifier expects length of type int, so guarantee */ - /* that length is small enough and cast to int. */ - msg(D_LOW, "No valid translation found for TLS cipher '%.*s'", - constrain_int(current_cipher_len, 0, 256), current_cipher); - } - else - { - /* Use OpenSSL name */ - current_cipher = cipher_pair->openssl_name; - current_cipher_len = strlen(current_cipher); - - if (end_of_cipher - begin_of_cipher == current_cipher_len - && 0 != memcmp(&ciphers[begin_of_cipher], cipher_pair->iana_name, - end_of_cipher - begin_of_cipher)) - { - /* Non-IANA name used, show warning */ - msg(M_WARN, "Deprecated TLS cipher name '%s', please use IANA name '%s'", cipher_pair->openssl_name, cipher_pair->iana_name); - } - } - - /* Make sure new cipher name fits in cipher string */ - if ((SIZE_MAX - openssl_ciphers_len) < current_cipher_len - || ((sizeof(openssl_ciphers)-1) < openssl_ciphers_len + current_cipher_len)) - { - msg(M_FATAL, - "Failed to set restricted TLS cipher list, too long (>%d).", - (int)sizeof(openssl_ciphers)-1); - } - - /* Concatenate cipher name to OpenSSL cipher string */ - memcpy(&openssl_ciphers[openssl_ciphers_len], current_cipher, current_cipher_len); - openssl_ciphers_len += current_cipher_len; - openssl_ciphers[openssl_ciphers_len] = ':'; - openssl_ciphers_len++; - - end_of_cipher++; - } - - if (openssl_ciphers_len > 0) - { - openssl_ciphers[openssl_ciphers_len-1] = '\0'; - } - /* Set OpenSSL cipher list */ if (!SSL_CTX_set_cipher_list(ctx->ctx, openssl_ciphers)) {