diff --git a/compat.c b/compat.c index 1246730b..ce5a7038 100644 --- a/compat.c +++ b/compat.c @@ -19,6 +19,25 @@ #include #include +#ifdef _WIN32 +#include /* errno_t, size_t */ +errno_t getenv_s( + size_t *ret_required_buf_size, + char *buf, + size_t buf_size_in_bytes, + const char *name +); +errno_t _putenv_s( + const char *varname, + const char *value_string +); + +/* XX: needed to get _putenv_s, getenv_s from stdlib.h with MinGW, + * but only works on newer versions. + * https://stackoverflow.com/a/51977723 + */ +/* #define MINGW_HAS_SECURE_API 1 */ +#endif #include #include #include @@ -196,6 +215,18 @@ int openconnect__inet_aton(const char *cp, struct in_addr *addr) #endif #ifdef _WIN32 +int openconnect__win32_setenv(const char *name, const char *value, int overwrite) +{ + /* https://stackoverflow.com/a/23616164 */ + int errcode = 0; + if(!overwrite) { + size_t envsize = 0; + errcode = getenv_s(&envsize, NULL, 0, name); + if(errcode || envsize) return errcode; + } + return _putenv_s(name, value); +} + char *openconnect__win32_strerror(DWORD err) { wchar_t *msgw; diff --git a/gnutls.c b/gnutls.c index db1e4470..6738d04a 100644 --- a/gnutls.c +++ b/gnutls.c @@ -78,12 +78,23 @@ const char *openconnect_get_tls_library_version() int can_enable_insecure_crypto() { + int ret = 0; + + if (setenv("GNUTLS_SYSTEM_PRIORITY_FILE", DEVNULL, 1) < 0) + return -errno; + + gnutls_global_deinit(); + ret = openconnect_init_ssl(); + if (ret) + return ret; + /* XX: As of GnuTLS 3.6.13, no released version has (yet) removed 3DES/RC4 from default builds, * but like OpenSSL (removed in 1.1.0) it may happen. */ if (gnutls_cipher_get_id("3DES-CBC") == GNUTLS_CIPHER_UNKNOWN || gnutls_cipher_get_id("ARCFOUR-128") == GNUTLS_CIPHER_UNKNOWN) - return -ENOENT; - return 0; + ret = -ENOENT; + + return ret; } /* Helper functions for reading/writing lines over TLS/DTLS. */ diff --git a/library.c b/library.c index 643aa25d..1f8a2221 100644 --- a/library.c +++ b/library.c @@ -757,10 +757,8 @@ void openconnect_set_pfs(struct openconnect_info *vpninfo, unsigned val) int openconnect_set_allow_insecure_crypto(struct openconnect_info *vpninfo, unsigned val) { int ret = can_enable_insecure_crypto(); - if (ret) - return ret; vpninfo->allow_insecure_crypto = val; - return 0; + return ret; } void openconnect_set_cancel_fd(struct openconnect_info *vpninfo, int fd) diff --git a/main.c b/main.c index f477a4c6..31bd5c1d 100644 --- a/main.c +++ b/main.c @@ -928,6 +928,7 @@ static void usage(void) printf(" --no-http-keepalive %s\n", _("Disable HTTP connection re-use")); printf(" --no-xmlpost %s\n", _("Do not attempt XML POST authentication")); printf(" --allow-insecure-crypto %s\n", _("Allow use of the ancient, insecure 3DES and RC4 ciphers")); + printf(" %s\n", _("(and attempt to override OS crypto policies)")); printf("\n"); @@ -1685,9 +1686,12 @@ int main(int argc, char **argv) openconnect_set_pfs(vpninfo, 1); break; case OPT_ALLOW_INSECURE_CRYPTO: - if (openconnect_set_allow_insecure_crypto(vpninfo, 1)) { - fprintf(stderr, _("Cannot enable insecure 3DES or RC4 ciphers, because the library\n" + ret = openconnect_set_allow_insecure_crypto(vpninfo, 1); + if (ret == -ENOENT) + fprintf(stderr, _("WARNING: cannot enable insecure 3DES and/or RC4 ciphers, because the library\n" "%s no longer supports them.\n"), openconnect_get_tls_library_version()); + else if (ret < 0) { + fprintf(stderr, _("Unknown error while enabling insecure crypto.\n")); exit(1); } break; diff --git a/openconnect-internal.h b/openconnect-internal.h index af11c366..429aa6f8 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -41,6 +41,15 @@ #include "openconnect.h" +/* Equivalent of "/dev/null" on Windows. + * See https://stackoverflow.com/a/44163934 + */ +#ifdef _WIN32 +#define DEVNULL "NUL" +#else +#define DEVNULL "/dev/null" +#endif + #if defined(OPENCONNECT_OPENSSL) #include #include @@ -825,6 +834,9 @@ static inline int tun_is_up(struct openconnect_info *vpninfo) #define pipe(fds) _pipe(fds, 4096, O_BINARY) int openconnect__win32_sock_init(); char *openconnect__win32_strerror(DWORD err); +#undef setenv +#define setenv openconnect__win32_setenv +int openconnect__win32_setenv(const char *name, const char *value, int overwrite); #undef inet_pton #define inet_pton openconnect__win32_inet_pton int openconnect__win32_inet_pton(int af, const char *src, void *dst); @@ -1066,7 +1078,7 @@ int do_gen_hotp_code(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt); -int set_oidc_token(struct openconnect_info *vpninfo, +int set_oidc_token(struct openconnect_info *vpninfo, const char *token_str); /* stoken.c */ diff --git a/openconnect.8.in b/openconnect.8.in index 55d43a06..374ea1c5 100644 --- a/openconnect.8.in +++ b/openconnect.8.in @@ -469,8 +469,11 @@ The ancient, broken 3DES and RC4 ciphers are insecure; we explicitly disable them by default. However, some still-in-use VPN servers can't do any better. -This option enables use of these insecure ciphers, as well as the use -of SHA1 for server certificate validation. +This option +.B attempts +to enable use of these insecure ciphers, as well as +the use of SHA1 for server certificate validation, and to override any +other system policies regarding minimum crypto requirements. .TP .B \-\-non\-inter Do not expect user input; exit if it is required. diff --git a/openssl.c b/openssl.c index ac0d919a..8fb03b92 100644 --- a/openssl.c +++ b/openssl.c @@ -63,10 +63,23 @@ const char *openconnect_get_tls_library_version() int can_enable_insecure_crypto() { + int ret = 0; + + if (setenv("OPENSSL_CONF", DEVNULL, 1) < 0) + return -errno; + + /* FIXME: deinitialize and reinitialize library, as is done for GnuTLS, + * to ensure that updated value is used. + * + * Cleaning up and reinitalizing OpenSSL appears to be complex: + * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup + */ + if (EVP_des_ede3_cbc() == NULL || EVP_rc4() == NULL) - return -ENOENT; - return 0; + ret = -ENOENT; + + return ret; } int openconnect_sha1(unsigned char *result, void *data, int len) @@ -1698,6 +1711,19 @@ int openconnect_open_https(struct openconnect_info *vpninfo) SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_NO_TICKET); #endif +#if OPENSSL_VERSION_NUMBER >= 0x010100000L + if (vpninfo->allow_insecure_crypto) { + /* OpenSSL versions after 1.1.0 added the notion of a "security level" + * that enforces checks on certificates and ciphers. + * These security levels overlap in functionality with the ciphersuite + * priority/allow-strings. + * + * For now we will set the security level to 0, thus reverting + * to the functionality seen in versions before 1.1.0. */ + SSL_CTX_set_security_level(vpninfo->https_ctx, 0); + } +#endif + if (vpninfo->cert) { err = load_certificate(vpninfo); if (!err && !SSL_CTX_check_private_key(vpninfo->https_ctx)) { diff --git a/tests/obsolete-server-crypto b/tests/obsolete-server-crypto index fff723d4..90d93ef6 100755 --- a/tests/obsolete-server-crypto +++ b/tests/obsolete-server-crypto @@ -25,13 +25,6 @@ top_builddir=${top_builddir:-..} . `dirname $0`/common.sh -######################################## -# Need to override mandatory system-wide crypto policy on Fedora 31+, -# for both ocserv and openconnect. -######################################## - -export GNUTLS_SYSTEM_PRIORITY_FILE=/dev/null - ######################################## # Verify that we cannot connect to a server offering only obsolete, insecure # crypto UNLESS --allow-insecure-crypto is specified. @@ -43,7 +36,16 @@ echo "Testing against server with insecure crypto (3DES and RC4 only)... " PORT=4568 TLS_PRIORITIES="LEGACY:%SERVER_PRECEDENCE:%COMPAT:-VERS-TLS-ALL:+VERS-TLS1.0:-CIPHER-ALL:+3DES-CBC:+ARCFOUR-128:+MD5:+SHA1" update_config test-obsolete-server-crypto.config -launch_simple_sr_server -d 1 -f -c $CONFIG + +######################################## +# Need to override mandatory system-wide crypto policy on Fedora 31+, in +# order for ocserv to offer 3DES and RC4. +# +# However, we want to leave this policy in place for openconnect, +# in order to verify the client's ability to disable it on its own. +######################################## +GNUTLS_SYSTEM_PRIORITY_FILE=/dev/null launch_simple_sr_server -d 1 -f -c $CONFIG + PID=$! wait_server $PID diff --git a/www/changelog.xml b/www/changelog.xml index 4e4a9bba..b7f79e78 100644 --- a/www/changelog.xml +++ b/www/changelog.xml @@ -18,8 +18,9 @@
  • Make tncc-emulate.py work with Python 3.7+. (!152, !120)
  • Emulated a newer version of GlobalProtect official clients, 5.1.5-8; was 4.0.2-19 (!131)
  • Support Juniper login forms containing both password and 2FA token (!121)
  • -
  • Explicitly disable 3DES and RC4, unless enabled with --allow-insecure-crypto (!114)
  • -
  • Add obsolete-server-crypto test (!114)
  • +
  • Explicitly disable 3DES and RC4, unless enabled with --allow-insecure-crypto (!114)
  • +
  • With --allow-insecure-crypto, additionally try to disable system-wide and library minimum crypto policies (!158, #132)
  • +
  • Add obsolete-server-crypto test (!114)
  • Allow protocols to delay tunnel setup and shutdown (!117)
  • Incomplete, speculative support for GlobalProtect IPv6 (!155; previous work in d6db0ec)
  • SIGUSR1 causes OpenConnect to log detailed connection information and statistics (!154)