commit openfortivpn for openSUSE:Factory
Hello community, here is the log from the commit of package openfortivpn for openSUSE:Factory checked in at 2020-02-29 21:24:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openfortivpn (Old) and /work/SRC/openSUSE:Factory/.openfortivpn.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "openfortivpn" Sat Feb 29 21:24:45 2020 rev:8 rq:780376 version:1.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/openfortivpn/openfortivpn.changes 2019-12-03 12:43:05.346121004 +0100 +++ /work/SRC/openSUSE:Factory/.openfortivpn.new.26092/openfortivpn.changes 2020-02-29 21:25:01.666577445 +0100 @@ -1,0 +2,19 @@ +Thu Feb 27 15:14:15 UTC 2020 - Martin Hauke <mardnh@gmx.de> + +- Update to version 1.12.0 + * fix CVE-2020-7043: TLS Certificate CommonName NULL Byte + Vulnerability + * fix CVE-2020-7042: use of uninitialized memory in + X509_check_host + * fix CVE-2020-7041: incorrect use of X509_check_host + (regarding return value). + * always hide cleartest password in -vv output + * add a clear warning about sensitive information in the debug + output + * add a hint in debug output when password is read from config + file + * fix segfault when connecting with empty password + * use resolvconf if available to update resolv.conf file + * replace semicolon by space in dns-suffix string + +------------------------------------------------------------------- Old: ---- openfortivpn-1.11.0.tar.gz New: ---- openfortivpn-1.12.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openfortivpn.spec ++++++ --- /var/tmp/diff_new_pack.F08wRw/_old 2020-02-29 21:25:02.666579424 +0100 +++ /var/tmp/diff_new_pack.F08wRw/_new 2020-02-29 21:25:02.670579432 +0100 @@ -1,7 +1,7 @@ # # spec file for package openfortivpn # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: openfortivpn -Version: 1.11.0 +Version: 1.12.0 Release: 0 Summary: Client for PPP+SSL VPN tunnel services License: GPL-3.0-or-later ++++++ openfortivpn-1.11.0.tar.gz -> openfortivpn-1.12.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/.github/SECURITY.md new/openfortivpn-1.12.0/.github/SECURITY.md --- old/openfortivpn-1.11.0/.github/SECURITY.md 1970-01-01 01:00:00.000000000 +0100 +++ new/openfortivpn-1.12.0/.github/SECURITY.md 2020-02-26 17:02:52.000000000 +0100 @@ -0,0 +1,14 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ----------------------- | ------------------ | +| latest stable release | :white_check_mark: | + +## Reporting a Vulnerability + +Send an email to security@openfortivpn.org. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/CHANGELOG.md new/openfortivpn-1.12.0/CHANGELOG.md --- old/openfortivpn-1.11.0/CHANGELOG.md 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/CHANGELOG.md 2020-02-26 17:02:52.000000000 +0100 @@ -14,6 +14,18 @@ This high level changelog is usually updated when a release is tagged. On the master branch there may be changes that are not (yet) described here. +### 1.12.0 + +* [-] fix CVE-2020-7043: TLS Certificate CommonName NULL Byte Vulnerability +* [-] fix CVE-2020-7042: use of uninitialized memory in X509_check_host +* [-] fix CVE-2020-7041: incorrect use of X509_check_host (regarding return value). +* [-] always hide cleartest password in -vv output +* [+] add a clear warning about sensitive information in the debug output +* [+] add a hint in debug output when password is read from config file +* [-] fix segfault when connecting with empty password +* [+] use resolvconf if available to update resolv.conf file +* [~] replace semicolon by space in dns-suffix string + ### 1.11.0 * [+] allow to connect with empty password (and with smartcard instead of username) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/Makefile.am new/openfortivpn-1.12.0/Makefile.am --- old/openfortivpn-1.11.0/Makefile.am 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/Makefile.am 2020-02-26 17:02:52.000000000 +0100 @@ -5,11 +5,14 @@ src/http.c src/http.h src/io.c src/io.h src/ipv4.c \ src/ipv4.h src/log.c src/log.h src/tunnel.c \ src/tunnel.h src/main.c src/ssl.h src/xml.c \ - src/xml.h src/userinput.c src/userinput.h + src/xml.h src/userinput.c src/userinput.h \ + src/openssl_hostname_validation.c \ + src/openssl_hostname_validation.h openfortivpn_CFLAGS = -Wall -pedantic -std=gnu99 openfortivpn_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" \ -DPPP_PATH=\"@PPP_PATH@\" \ - -DNETSTAT_PATH=\"@NETSTAT_PATH@\" + -DNETSTAT_PATH=\"@NETSTAT_PATH@\" \ + -DRESOLVCONF_PATH=\"@RESOLVCONF_PATH@\" openfortivpn_CPPFLAGS += $(OPENSSL_CFLAGS) $(LIBSYSTEMD_CFLAGS) openfortivpn_LDADD = $(OPENSSL_LIBS) $(LIBSYSTEMD_LIBS) @@ -26,7 +29,8 @@ mkdir -p $(DESTDIR)$(sysconfdir)/openfortivpn ; \ cp -a $(DESTDIR)$(datadir)/config.template \ $(DESTDIR)$(sysconfdir)/openfortivpn/config ; \ - fi + sed -i 's/^/# /' $(DESTDIR)$(sysconfdir)/openfortivpn/config ; \ + fi dist_man_MANS = doc/openfortivpn.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/README.md new/openfortivpn-1.12.0/README.md --- old/openfortivpn-1.11.0/README.md 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/README.md 2020-02-26 17:02:52.000000000 +0100 @@ -86,10 +86,10 @@ * [openSUSE / SLE](https://software.opensuse.org/package/openfortivpn) * [Gentoo](https://packages.gentoo.org/packages/net-vpn/openfortivpn) * [NixOS](https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/networking/openforti...) -* [Arch Linux](https://aur.archlinux.org/packages/openfortivpn) +* [Arch Linux](https://www.archlinux.org/packages/community/x86_64/openfortivpn) * [Debian (testing)](https://packages.debian.org/buster/openfortivpn) * [Ubuntu (bionic and later)](https://packages.ubuntu.com/search?keywords=openfortivpn) and [pre-bionic (ppa)](https://launchpad.net/~ar-lex/+archive/ubuntu/fortisslvpn) -* [Solus](https://packages.solus-project.com/unstable/o/openfortivpn/) +* [Solus](https://dev.getsol.us/source/openfortivpn/) On macOS both [Homebrew](http://brewformulas.org/Openfortivpn) and [MacPorts](https://www.macports.org/ports.php?by=name&substr=openfortivpn) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/configure.ac new/openfortivpn-1.12.0/configure.ac --- old/openfortivpn-1.11.0/configure.ac 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/configure.ac 2020-02-26 17:02:52.000000000 +0100 @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) -AC_INIT([openfortivpn], [1.11.0]) +AC_INIT([openfortivpn], [1.12.0]) AC_CONFIG_SRCDIR([src/main.c]) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -226,20 +226,19 @@ ]) ) AS_IF([test "x$with_rt_dst" = "x" ], [ -AC_MSG_CHECKING(whether rtentry is available and has rt_dst ) -AC_TRY_RUN([ +AC_MSG_CHECKING(whether rtentry is available and has rt_dst) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include <sys/types.h> #include <sys/socket.h> #include <net/route.h> static inline struct sockaddr_in *cast_addr(struct sockaddr *addr) { - return (struct sockaddr_in *) addr; + return (struct sockaddr_in *) addr; } +],[ struct rtentry route; -int main() { - cast_addr(&(&route)->rt_dst)->sin_family = AF_INET; - return (cast_addr(&(&route)->rt_dst)->sin_family == AF_INET) ? 0 : 1; } -], [ +cast_addr(&(&route)->rt_dst)->sin_family = AF_INET; +])],[ AC_DEFINE(HAVE_RT_ENTRY_WITH_RT_DST) AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no) ) @@ -247,6 +246,7 @@ NETSTAT_PATH="" PPP_PATH="" +RESOLVCONF_PATH="" # prepare possibility to override default locations AC_ARG_WITH([netstat], @@ -274,6 +274,12 @@ with_pppd="no" ]) ) +# resolvconf if present +AC_ARG_WITH([resolvconf], + AS_HELP_STRING([--with-resolvconf], + [Set the path to the resolvconf executable]), + RESOLVCONF_PATH="$withval" +) # override for /proc/net/route detection AC_ARG_ENABLE([proc], @@ -350,6 +356,8 @@ with_ppp="no" ]) +AC_PATH_PROG(RESOLVCONF_PATH, [resolvconf], [/sbin/resolvconf], "$PATH:/sbin:/usr/sbin") + AS_IF([test "x$with_ppp" = "xyes"], [ AC_DEFINE(HAVE_USR_SBIN_PPP, 1) AC_MSG_NOTICE([HAVE_USR_SBIN_PPP... 1]) @@ -380,5 +388,10 @@ AC_MSG_NOTICE([NETSTAT_PATH...] $NETSTAT_PATH) ]) +AC_SUBST(RESOLVCONF_PATH) +AS_IF([test "x$RESOLVCONF_PATH" != "x" ], [ + AC_MSG_NOTICE([RESOLVCONF_PATH...] $RESOLVCONF_PATH) +]) + AC_CONFIG_COMMANDS([timestamp], [touch src/.dirstamp]) AC_OUTPUT(Makefile) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/doc/openfortivpn.1.in new/openfortivpn-1.12.0/doc/openfortivpn.1.in --- old/openfortivpn-1.11.0/doc/openfortivpn.1.in 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/doc/openfortivpn.1.in 2020-02-26 17:02:52.000000000 +0100 @@ -1,4 +1,4 @@ -.TH OPENFORTIVPN 1 "November 27, 2019" "" +.TH OPENFORTIVPN 1 "December 11, 2019" "" .SH NAME openfortivpn \- Client for PPP+SSL VPN tunnel services @@ -92,13 +92,14 @@ .TP \fB\-\-set\-dns=\fI<bool>\fR, \fB\-\-no\-dns\fR Set if openfortivpn should add DNS name servers in /etc/resolv.conf when -tunnel is up. If used multiple times, the last one takes priority. +tunnel is up. Also a dns\-suffix may be received from the peer and added +to /etc/resolv.conf in the turn of adding the name servers. +resolvconf is instructed to do the update of the resolv.conf file +if it is installed, otherwise openfortivpn prepends its changes +to the existing content of the resolv.conf file. Note that there may be other mechanisms to update /etc/resolv.conf, e.g., \fB\-\-pppd\-use\-peerdns\fR in conjunction with an ip-up-script, which may require that openfortivpn is called with \fB\-\-no\-dns\fR. -Also a dns\-suffix may be received from the peer and added to -/etc/resolv.conf in the turn of adding the name servers. - \fB\-\-no\-dns\fR is the same as \fB\-\-set\-dns=\fI0\fR. .TP \fB\-\-ca\-file=\fI<file>\fR @@ -131,8 +132,8 @@ \fB\-\-trusted\-cert=\fI<digest>\fR Trust a given gateway. If classical SSL certificate validation fails, the gateway certificate will be matched against this value. \fI<digest>\fR is the -X509 certificate's sha256 sum. This option can be used multiple times to trust -several certificates. +X509 certificate's sha256 sum. The certificate has to be encoded in DER form. +This option can be used multiple times to trust several certificates. .TP \fB\-\-insecure\-ssl\fR Do not disable insecure SSL protocols/ciphers. @@ -150,7 +151,8 @@ .TP \fB\-\-use\-peer\-dns=\fI<bool>\fR, \fB\-\-pppd\-no\-peerdns\fR Whether to ask peer ppp server for DNS server addresses and let pppd -rewrite /etc/resolv.conf. If the DNS server addresses are requested, +rewrite /etc/resolv.conf. There is no mechanism to tell the dns\-suffix +to pppd. If the DNS server addresses are requested, also \fB\-\-set\-dns=\fI1\fR may race with the mechanisms in pppd. \fB\-\-pppd\-no\-peerdns\fR is the same as \fB\-\-pppd\-use\-peerdns=\fI0\fR. @@ -264,13 +266,17 @@ .br # otp\-prompt = Please .br +# pinentry = pinentry program +.br user\-cert = @SYSCONFDIR@/openfortivpn/user\-cert.pem .br +# user\-cert = pkcs1: # use smartcard as client certificate +.br user\-key = @SYSCONFDIR@/openfortivpn/user\-key.pem .br # the sha256 digest of the trusted host certs obtained by .br -# openssl dgst -sha256 server\-cert.pem: +# openssl dgst -sha256 server\-cert.crt: .br trusted\-cert = certificatedigest4daa8c5fe6c... .br @@ -315,3 +321,5 @@ cipher\-list = HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4 .br persistent = 0 +.br +seclevel-1 = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/etc/openfortivpn/config.template new/openfortivpn-1.12.0/etc/openfortivpn/config.template --- old/openfortivpn-1.11.0/etc/openfortivpn/config.template 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/etc/openfortivpn/config.template 2020-02-26 17:02:52.000000000 +0100 @@ -1,5 +1,6 @@ -# config file for openfortivpn, see man openfortivpn(1) -host = -port = -username = -password = +### config file for openfortivpn, see man openfortivpn(1) ### + +host = vpn.example.org +port = 443 +username = vpnuser +password = VPNpassw0rd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/config.c new/openfortivpn-1.12.0/src/config.c --- old/openfortivpn-1.11.0/src/config.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/config.c 2020-02-26 17:02:52.000000000 +0100 @@ -108,7 +108,7 @@ else if (isdigit(str[0]) == 0) return -1; - long int i = strtol(str, NULL, 0); + long i = strtol(str, NULL, 0); if (i < 0 || i > 1) return -1; return i; @@ -122,9 +122,8 @@ */ int parse_min_tls(const char *str) { - if (str[0] != '1' || str[1] != '.' || str[2] == 0 || str[3] != 0) { + if (str[0] != '1' || str[1] != '.' || str[2] == 0 || str[3] != 0) return -1; - } switch (str[2]) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L case '0': @@ -230,8 +229,8 @@ strncpy(cfg->gateway_host, val, FIELD_SIZE); cfg->gateway_host[FIELD_SIZE] = '\0'; } else if (strcmp(key, "port") == 0) { - unsigned long int port = strtoul(val, NULL, 0); - if (port <= 0 || port > 65535) { + unsigned long port = strtoul(val, NULL, 0); + if (port == 0 || port > 65535) { log_warn("Bad port in config file: \"%lu\".\n", port); continue; @@ -249,7 +248,7 @@ free(cfg->otp_prompt); cfg->otp_prompt = strdup(val); } else if (strcmp(key, "otp-delay") == 0) { - long int otp_delay = strtol(val, NULL, 0); + long otp_delay = strtol(val, NULL, 0); if (otp_delay < 0 || otp_delay > UINT_MAX) { log_warn("Bad value for otp-delay in config file: \"%s\".\n", val); @@ -287,7 +286,7 @@ } cfg->half_internet_routes = half_internet_routes; } else if (strcmp(key, "persistent") == 0) { - unsigned long int persistent = strtoul(val, NULL, 0); + unsigned long persistent = strtoul(val, NULL, 0); if (persistent > UINT_MAX) { log_warn("Bad value for persistent in config file: \"%s\".\n", val); @@ -508,9 +507,8 @@ free(dst->cipher_list); dst->cipher_list = src->cipher_list; } - if (src->min_tls > 0) { + if (src->min_tls > 0) dst->min_tls = src->min_tls; - } if (src->seclevel_1 != invalid_cfg.seclevel_1) dst->seclevel_1 = src->seclevel_1; if (src->cert_whitelist) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/http.c new/openfortivpn-1.12.0/src/http.c --- old/openfortivpn-1.11.0/src/http.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/http.c 2020-02-26 17:02:52.000000000 +0100 @@ -76,16 +76,20 @@ va_start(args, request); length = vsnprintf(buffer, BUFSZ, request, args); va_end(args); - strcpy(logbuffer,buffer); - if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0]!='\0') { - char* pwstart; - pwstart = strstr(logbuffer, tunnel->config->password); + strcpy(logbuffer, buffer); + if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0] != '\0') { + char *pwstart; + char password[3 * FIELD_SIZE + 1]; + + url_encode(password, tunnel->config->password); + pwstart = strstr(logbuffer, password); + if (pwstart != NULL) { int pos, pwlen, i; pos = pwstart - logbuffer; pwlen = strlen(tunnel->config->password); - for (i=pos; i<pos+pwlen; i++) - logbuffer[i]='*'; + for (i = pos; i < pos + pwlen; i++) + logbuffer[i] = '*'; } } @@ -94,7 +98,7 @@ else if (length >= BUFSZ) return ERR_HTTP_TOO_LONG; - log_debug_details("%s: \n%s\n", __func__, logbuffer); + log_debug_details("%s:\n%s\n", __func__, logbuffer); while (n == 0) n = safe_ssl_write(tunnel->ssl_handle, (uint8_t *) buffer, @@ -162,7 +166,7 @@ (uint8_t *) buffer + bytes_read, BUFSZ - 1 - bytes_read); if (n > 0) { - log_debug_details("%s: \n%s\n", __func__, buffer); + log_debug_details("%s:\n%s\n", __func__, buffer); const char *eoh; bytes_read += n; @@ -287,15 +291,13 @@ uint32_t *response_size ) { - int ret = do_http_request( - tunnel, method, uri, data, response, response_size); + int ret = do_http_request(tunnel, method, uri, data, + response, response_size); if (ret == ERR_HTTP_SSL) { ssl_connect(tunnel); - ret = do_http_request( - tunnel, method, uri, data, response, - response_size - ); + ret = do_http_request(tunnel, method, uri, data, + response, response_size); } if (ret != 1) @@ -573,19 +575,20 @@ tunnel->cookie[0] = '\0'; - if (tunnel->config->use_engine) { + if (tunnel->config->use_engine + || (username[0] == '\0' && tunnel->config->password[0] == '\0')) { snprintf(data, sizeof(data), "cert=&nup=1"); ret = http_request(tunnel, "GET", "/remote/login", data, &res, &response_size); } else { - if (tunnel->config->password == '\0') { - snprintf(data, sizeof(data), "username=%s&realm=%s&ajax=1" - "&redir=%%2Fremote%%2Findex&just_logged_in=1", + if (tunnel->config->password[0] == '\0') { + snprintf(data, sizeof(data), + "username=%s&realm=%s&ajax=1&redir=%%2Fremote%%2Findex&just_logged_in=1", username, realm); } else { url_encode(password, tunnel->config->password); - snprintf(data, sizeof(data), "username=%s&credential=%s&realm=%s&ajax=1" - "&redir=%%2Fremote%%2Findex&just_logged_in=1", + snprintf(data, sizeof(data), + "username=%s&credential=%s&realm=%s&ajax=1&redir=%%2Fremote%%2Findex&just_logged_in=1", username, password, realm); } ret = http_request(tunnel, "POST", "/remote/logincheck", @@ -650,9 +653,8 @@ } url_encode(tokenresponse, cfg->otp); - snprintf(data, sizeof(data), "username=%s&realm=%s&reqid=%s" - "&polid=%s&grp=%s&code=%s&code2=" - "&redir=%%2Fremote%%2Findex&just_logged_in=1", + snprintf(data, sizeof(data), + "username=%s&realm=%s&reqid=%s&polid=%s&grp=%s&code=%s&code2=&redir=%%2Fremote%%2Findex&just_logged_in=1", username, realm, reqid, polid, group, tokenresponse); delay_otp(tunnel); @@ -722,7 +724,7 @@ if (xml_find(' ', "domain=", val, 1)) { tunnel->ipv4.dns_suffix = xml_get(xml_find(' ', "domain=", val, 1)); - log_debug("found dns suffix %s in xml config", + log_debug("found dns suffix %s in xml config\n", tunnel->ipv4.dns_suffix); break; } @@ -733,7 +735,7 @@ while ((val = xml_find('<', "dns", val, 2))) { if (xml_find(' ', "ip=", val, 1)) { dns_server = xml_get(xml_find(' ', "ip=", val, 1)); - log_debug("found dns server %s in xml config", dns_server); + log_debug("found dns server %s in xml config\n", dns_server); if (!tunnel->ipv4.ns1_addr.s_addr) tunnel->ipv4.ns1_addr.s_addr = inet_addr(dns_server); else if (!tunnel->ipv4.ns2_addr.s_addr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/io.c new/openfortivpn-1.12.0/src/io.c --- old/openfortivpn-1.11.0/src/io.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/io.c 2020-02-26 17:02:52.000000000 +0100 @@ -360,16 +360,16 @@ && pkt_data(packet)[6] == 0x03) #define packet_is_end_negociation(packet) \ - ((packet)->len == 6 \ - && pkt_data(packet)[0] == 0x80 \ - && pkt_data(packet)[1] == 0x21 \ - && pkt_data(packet)[2] == 0x01 \ - && pkt_data(packet)[4] == 0x00 \ - && pkt_data(packet)[5] == 0x04) || \ - ((packet)-> len >= 12 \ - && pkt_data(packet)[0] == 0x80 \ - && pkt_data(packet)[1] == 0x21 \ - && pkt_data(packet)[2] == 0x02) + (((packet)->len == 6 \ + && pkt_data(packet)[0] == 0x80 \ + && pkt_data(packet)[1] == 0x21 \ + && pkt_data(packet)[2] == 0x01 \ + && pkt_data(packet)[4] == 0x00 \ + && pkt_data(packet)[5] == 0x04) || \ + ((packet)->len >= 12 \ + && pkt_data(packet)[0] == 0x80 \ + && pkt_data(packet)[1] == 0x21 \ + && pkt_data(packet)[2] == 0x02)) static inline void set_tunnel_ips(struct tunnel *tunnel, struct ppp_packet *packet) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/ipv4.c new/openfortivpn-1.12.0/src/ipv4.c --- old/openfortivpn-1.11.0/src/ipv4.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/ipv4.c 2020-02-26 17:02:52.000000000 +0100 @@ -196,7 +196,7 @@ } #else - FILE *fp; + FILE * fp; uint32_t total_bytes_read = 0; char *saveptr3 = NULL; @@ -317,7 +317,7 @@ #ifdef RTF_STATIC // Manually added flag_table['S'] = RTF_STATIC & USHRT_MAX; #endif -#ifdef RTF_UP // Route usable +#ifdef RTF_UP // Route usable flag_table['U'] = RTF_UP & USHRT_MAX; #endif #ifdef RTF_WASCLONED // Route was generated as a result of cloning @@ -576,7 +576,7 @@ if (err) return err; - if (rtfound==0) { + if (rtfound == 0) { // should not occur anymore unless there is no default route log_debug("Route not found.\n"); // at least restore input values @@ -741,7 +741,7 @@ log_debug("ip route show %s\n", ipv4_show_route(gtw_rt)); ipv4_del_route(gtw_rt); } - sprintf(route_iface(gtw_rt),"!%s",tunnel->ppp_iface); + sprintf(route_iface(gtw_rt), "!%s", tunnel->ppp_iface); ret = ipv4_get_route(gtw_rt); if (ret != 0) { log_warn("Could not get route to gateway (%s).\n", @@ -779,8 +779,8 @@ const char *mask, const char *gw) { size_t l0, l1; - const char fmt[] = ",%s/%s/%s"; - const char trigger[] = "openfortivpn"; + static const char fmt[] = ",%s/%s/%s"; + static const char trigger[] = "openfortivpn"; char **target = &tunnel->config->pppd_ipparam; char *ptr; @@ -791,7 +791,8 @@ log_info("Registering route %s/%s via %s\n", dest, mask, gw); l0 = strlen(*target); l1 = strlen(fmt) + strlen(dest) + strlen(mask) + strlen(gw) + 1; - if ((ptr = realloc(*target, l0 + l1))) { + ptr = realloc(*target, l0 + l1); + if (ptr) { *target = ptr; snprintf(*target + l0, l1, fmt, dest, mask, gw); } else { @@ -1006,6 +1007,16 @@ return 0; } +static inline char *replace_char(char *str, char find, char replace) +{ + int i; + + for (i = 0; i < strlen(str); i++) + if (str[i] == find) + str[i] = replace; + return str; +} + int ipv4_add_nameservers_to_resolv_conf(struct tunnel *tunnel) { int ret = -1; @@ -1013,7 +1024,8 @@ struct stat stat; char ns1[28], ns2[28]; // 11 + 15 + 1 + 1 char dns_suffix[MAX_DOMAIN_LENGTH+8]; // 7 + MAX_DOMAIN_LENGTH + 1 - char *buffer; + char *buffer = NULL; + int use_resolvconf = 0; tunnel->ipv4.ns1_was_there = 0; tunnel->ipv4.ns2_was_there = 0; @@ -1025,40 +1037,68 @@ if (tunnel->ipv4.ns2_addr.s_addr == 0) tunnel->ipv4.ns2_was_there = -1; - file = fopen("/etc/resolv.conf", "r+"); - if (file == NULL) { - log_warn("Could not open /etc/resolv.conf (%s).\n", - strerror(errno)); - return 1; - } + if (access(RESOLVCONF_PATH, F_OK) == 0) { + int resolvconf_call_len + = strlen(RESOLVCONF_PATH) + + 20 + + strlen(tunnel->ppp_iface); + char *resolvconf_call = malloc(resolvconf_call_len); + if (resolvconf_call == NULL) { + log_warn("Could not create command to run resolvconf (%s).\n", + strerror(errno)); + return 1; + } - if (fstat(fileno(file), &stat) == -1) { - log_warn("Could not stat /etc/resolv.conf (%s).\n", - strerror(errno)); - goto err_close; - } + snprintf(resolvconf_call, resolvconf_call_len, + "%s -a \"%s.openfortivpn\"", + RESOLVCONF_PATH, + tunnel->ppp_iface); + + use_resolvconf = 1; + log_debug("resolvconf_call: %s\n", resolvconf_call); + file = popen(resolvconf_call, "w"); + free(resolvconf_call); + if (file == NULL) { + log_warn("Could not open pipe %s (%s).\n", + resolvconf_call, + strerror(errno)); + return 1; + } + } else { + file = fopen("/etc/resolv.conf", "r+"); + if (file == NULL) { + log_warn("Could not open /etc/resolv.conf (%s).\n", + strerror(errno)); + return 1; + } - if (stat.st_size == 0) { - log_warn("Could not read /etc/resolv.conf (%s).\n", - "Empty file"); - goto err_close; - } + if (fstat(fileno(file), &stat) == -1) { + log_warn("Could not stat /etc/resolv.conf (%s).\n", + strerror(errno)); + goto err_close; + } - buffer = malloc(stat.st_size + 1); - if (buffer == NULL) { - log_warn("Could not read /etc/resolv.conf (%s).\n", - strerror(errno)); - goto err_close; - } + if (stat.st_size == 0) { + log_warn("Could not read /etc/resolv.conf (%s).\n", + "Empty file"); + goto err_close; + } - // Copy all file contents at once - if (fread(buffer, stat.st_size, 1, file) != 1) { - log_warn("Could not read /etc/resolv.conf.\n"); - goto err_free; - } + buffer = malloc(stat.st_size + 1); + if (buffer == NULL) { + log_warn("Could not read /etc/resolv.conf (%s).\n", + strerror(errno)); + goto err_close; + } - buffer[stat.st_size] = '\0'; + // Copy all file contents at once + if (fread(buffer, stat.st_size, 1, file) != 1) { + log_warn("Could not read /etc/resolv.conf.\n"); + goto err_free; + } + buffer[stat.st_size] = '\0'; + } if (tunnel->ipv4.ns1_addr.s_addr != 0) { strcpy(ns1, "nameserver "); strncat(ns1, inet_ntoa(tunnel->ipv4.ns1_addr), 15); @@ -1076,59 +1116,63 @@ if (tunnel->ipv4.dns_suffix != NULL) { strcpy(dns_suffix, "search "); strncat(dns_suffix, tunnel->ipv4.dns_suffix, MAX_DOMAIN_LENGTH); + replace_char(dns_suffix, ';', ' '); } else { dns_suffix[0] = '\0'; } - for (const char *line = strtok(buffer, "\n"); - line != NULL; - line = strtok(NULL, "\n")) { - if (strcmp(line, ns1) == 0) { - tunnel->ipv4.ns1_was_there = 1; - log_debug("ns1 already present in /etc/resolv.conf.\n"); - } - } - - if (tunnel->ipv4.ns1_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns1); - - for (const char *line = strtok(buffer, "\n"); - line != NULL; - line = strtok(NULL, "\n")) { - if (strcmp(line, ns2) == 0) { - tunnel->ipv4.ns2_was_there = 1; - log_debug("ns2 already present in /etc/resolv.conf.\n"); + if (use_resolvconf == 0) { + for (const char *line = strtok(buffer, "\n"); + line != NULL; + line = strtok(NULL, "\n")) { + if (strcmp(line, ns1) == 0) { + tunnel->ipv4.ns1_was_there = 1; + log_debug("ns1 already present in /etc/resolv.conf.\n"); + } } - } - if (tunnel->ipv4.ns2_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns2); + if (tunnel->ipv4.ns1_was_there == 0) + log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns1); - if (dns_suffix[0] == '\0') { - tunnel->ipv4.dns_suffix_was_there = -1; - } else { for (const char *line = strtok(buffer, "\n"); line != NULL; line = strtok(NULL, "\n")) { - if (dns_suffix[0] != '\0' && strcmp(line, dns_suffix) == 0) { - tunnel->ipv4.dns_suffix_was_there = 1; - log_debug("dns_suffix already present in /etc/resolv.conf.\n"); + if (strcmp(line, ns2) == 0) { + tunnel->ipv4.ns2_was_there = 1; + log_debug("ns2 already present in /etc/resolv.conf.\n"); } } - } - if (tunnel->ipv4.dns_suffix_was_there == 0) - log_debug("Adding \"%s\", to /etc/resolv.conf.\n", dns_suffix); + if (tunnel->ipv4.ns2_was_there == 0) + log_debug("Adding \"%s\", to /etc/resolv.conf.\n", ns2); - rewind(file); - if (fread(buffer, stat.st_size, 1, file) != 1) { - log_warn("Could not read /etc/resolv.conf.\n"); - goto err_free; - } + if (dns_suffix[0] == '\0') { + tunnel->ipv4.dns_suffix_was_there = -1; + } else { + for (const char *line = strtok(buffer, "\n"); + line != NULL; + line = strtok(NULL, "\n")) { + if (dns_suffix[0] != '\0' + && strcmp(line, dns_suffix) == 0) { + tunnel->ipv4.dns_suffix_was_there = 1; + log_debug("dns_suffix already present in /etc/resolv.conf.\n"); + } + } + } - buffer[stat.st_size] = '\0'; + if (tunnel->ipv4.dns_suffix_was_there == 0) + log_debug("Adding \"%s\", to /etc/resolv.conf.\n", dns_suffix); - rewind(file); + rewind(file); + if (fread(buffer, stat.st_size, 1, file) != 1) { + log_warn("Could not read /etc/resolv.conf.\n"); + goto err_free; + } + + buffer[stat.st_size] = '\0'; + + rewind(file); + } if (tunnel->ipv4.ns1_was_there == 0) { strcat(ns1, "\n"); fputs(ns1, file); @@ -1141,14 +1185,18 @@ strcat(dns_suffix, "\n"); fputs(dns_suffix, file); } - fwrite(buffer, stat.st_size, 1, file); + if (use_resolvconf == 0) + fwrite(buffer, stat.st_size, 1, file); ret = 0; err_free: free(buffer); err_close: - fclose(file); + if (use_resolvconf == 0) + fclose(file); + else + pclose(file); return ret; } @@ -1160,7 +1208,35 @@ struct stat stat; char ns1[27], ns2[27]; // 11 + 15 + 1 char dns_suffix[MAX_DOMAIN_LENGTH+8]; // 7 + MAX_DOMAIN_LENGTH + 1 - char *buffer; + char *buffer = NULL; + + + if (access(RESOLVCONF_PATH, F_OK) == 0) { + int resolvconf_call_len + = strlen(RESOLVCONF_PATH) + + 20 + + strlen(tunnel->ppp_iface); + char *resolvconf_call = malloc(resolvconf_call_len); + if (resolvconf_call == NULL) { + log_warn("Could not create command to run resolvconf (%s).\n", + strerror(errno)); + return ERR_IPV4_SEE_ERRNO; + } + + snprintf(resolvconf_call, + resolvconf_call_len, + "%s -d \"%s.openfortivpn\"", + RESOLVCONF_PATH, + tunnel->ppp_iface + ); + + log_debug("resolvconf_call: %s\n", resolvconf_call); + ret = system(resolvconf_call); + free(resolvconf_call); + if (ret == -1) + return ERR_IPV4_SEE_ERRNO; + return 0; + } file = fopen("/etc/resolv.conf", "r+"); if (file == NULL) { @@ -1190,18 +1266,18 @@ buffer[stat.st_size] = '\0'; - ns1[0]='\0'; + ns1[0] = '\0'; if (tunnel->ipv4.ns1_addr.s_addr != 0) { strcpy(ns1, "nameserver "); strncat(ns1, inet_ntoa(tunnel->ipv4.ns1_addr), 15); } - ns2[0]='\0'; + ns2[0] = '\0'; if (tunnel->ipv4.ns2_addr.s_addr != 0) { strcpy(ns2, "nameserver "); strncat(ns2, inet_ntoa(tunnel->ipv4.ns2_addr), 15); } - dns_suffix[0]='\0'; - if (tunnel->ipv4.dns_suffix != NULL && tunnel->ipv4.dns_suffix[0]!='\0') { + dns_suffix[0] = '\0'; + if (tunnel->ipv4.dns_suffix != NULL && tunnel->ipv4.dns_suffix[0] != '\0') { strcpy(dns_suffix, "search "); strncat(dns_suffix, tunnel->ipv4.dns_suffix, MAX_DOMAIN_LENGTH); } @@ -1216,13 +1292,13 @@ for (const char *line = strtok(buffer, "\n"); line != NULL; line = strtok(NULL, "\n")) { - if (ns1[0]!='\0' && strcmp(line, ns1) == 0 + if (ns1[0] != '\0' && strcmp(line, ns1) == 0 && (tunnel->ipv4.ns1_was_there == 0)) { log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", ns1); - } else if (ns2[0]!='\0' && strcmp(line, ns2) == 0 + } else if (ns2[0] != '\0' && strcmp(line, ns2) == 0 && (tunnel->ipv4.ns2_was_there == 0)) { log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", ns2); - } else if (dns_suffix[0]!='\0' && strcmp(line, dns_suffix) == 0 + } else if (dns_suffix[0] != '\0' && strcmp(line, dns_suffix) == 0 && (tunnel->ipv4.dns_suffix_was_there == 0)) { log_debug("Deleting \"%s\" from /etc/resolv.conf.\n", dns_suffix); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/main.c new/openfortivpn-1.12.0/src/main.c --- old/openfortivpn-1.11.0/src/main.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/main.c 2020-02-26 17:02:52.000000000 +0100 @@ -99,17 +99,17 @@ " -p <pass>, --password=<pass> VPN account password.\n" \ " -o <otp>, --otp=<otp> One-Time-Password.\n" \ " --otp-prompt=<prompt> Search for the OTP prompt starting with this string\n" \ -" --otp-delay=<delay> Wait <delay> seconds before sending the OTP.\n" \ +" --otp-delay=<delay> Wait <delay> seconds before sending the OTP.\n" \ " --pinentry=<program> Use the program to supply a secret instead of asking for it\n" \ -" --realm=<realm> Use specified authentication realm on VPN gateway\n" \ +" --realm=<realm> Use specified authentication realm.\n" \ +" --set-routes=[01] Set if openfortivpn should configure routes\n" \ " when tunnel is up.\n" \ -" --set-routes=[01] Set if openfortivpn should configure output routes through\n" \ -" the VPN when tunnel is up.\n" \ " --no-routes Do not configure routes, same as --set-routes=0.\n" \ " --half-internet-routes=[01] Add two 0.0.0.0/1 and 128.0.0.0/1 routes with higher\n" \ " priority instead of replacing the default route.\n" \ -" --set-dns=[01] Set if openfortivpn should add DNS name servers \n" \ -" and domain seach list in /etc/resolv.conf.\n" \ +" --set-dns=[01] Set if openfortivpn should add DNS name servers\n" \ +" and domain search list in /etc/resolv.conf.\n" \ +" If installed resolvconf is used for the update.\n" \ " --no-dns Do not reconfigure DNS, same as --set-dns=0\n" \ " --ca-file=<file> Use specified PEM-encoded certificate bundle\n" \ " instead of system-wide store to verify the gateway\n" \ @@ -407,7 +407,7 @@ } if (strcmp(long_options[option_index].name, "otp-delay") == 0) { - long int otp_delay = strtol(optarg, NULL, 0); + long otp_delay = strtol(optarg, NULL, 0); if (otp_delay < 0 || otp_delay > UINT_MAX) { log_warn("Bad otp-delay option: \"%s\"\n", optarg); @@ -418,7 +418,7 @@ } if (strcmp(long_options[option_index].name, "persistent") == 0) { - long int persistent = strtol(optarg, NULL, 0); + long persistent = strtol(optarg, NULL, 0); if (persistent < 0 || persistent > UINT_MAX) { log_warn("Bad persistent option: \"%s\"\n", optarg); @@ -474,6 +474,8 @@ if (cli_cfg.password != NULL && cli_cfg.password[0] != '\0') log_warn("You should not pass the password on the command line. Type it interactively or use a config file instead.\n"); + log_debug_all("ATTENTION: the output contains sensitive information such as the THE CLEAR TEXT PASSWORD.\n"); + log_debug("openfortivpn " VERSION "\n"); // Load config file @@ -485,6 +487,14 @@ log_warn("Could not load config file \"%s\" (%s).\n", config_file, err_cfg_str(ret)); } + if (cfg.password != NULL && cli_cfg.password == NULL) { + if (cfg.password[0] == '\0') + log_debug("Disabled password due to empty entry in config file \"%s\"\n", + config_file); + else + log_debug("Loaded password from config file \"%s\"\n", + config_file); + } // Then apply CLI config merge_config(&cfg, &cli_cfg); set_syslog(cfg.use_syslog); @@ -497,7 +507,7 @@ port_str[0] = '\0'; port_str++; cfg.gateway_port = strtol(port_str, NULL, 0); - if (cfg.gateway_port <= 0 || cfg.gateway_port > 65535) { + if (cfg.gateway_port == 0 || cfg.gateway_port > 65535) { log_error("Specify a valid port.\n"); goto user_error; } @@ -512,12 +522,12 @@ goto user_error; } // Check username - if (cfg.username[0] == '\0' && cfg.use_engine != 1) { - log_error("Specify an username.\n"); + if (cfg.username[0] == '\0' && cfg.user_cert == NULL) { + log_error("Specify a username.\n"); goto user_error; } // If username but no password given, interactively ask user - if (cfg.password == NULL && cfg.username[0] != '\0' ) { + if (cfg.password == NULL && cfg.username[0] != '\0') { char *tmp_password = malloc(PWD_BUFSIZ); // allocate large buffer read_password(cfg.pinentry, "password", "VPN account password: ", tmp_password, PWD_BUFSIZ); @@ -531,6 +541,8 @@ log_debug("Config username = \"%s\"\n", cfg.username); if (cfg.password != NULL) log_debug_all("Config password = \"%s\"\n", cfg.password); + else + cfg.password = strdup(""); if (cfg.otp[0] != '\0') log_debug("One-time password = \"%s\"\n", cfg.otp); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/openssl_hostname_validation.c new/openfortivpn-1.12.0/src/openssl_hostname_validation.c --- old/openfortivpn-1.11.0/src/openssl_hostname_validation.c 1970-01-01 01:00:00.000000000 +0100 +++ new/openfortivpn-1.12.0/src/openssl_hostname_validation.c 2020-02-26 17:02:52.000000000 +0100 @@ -0,0 +1,165 @@ +/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ + +/* + * Helper functions to perform basic hostname validation using OpenSSL. + * + * Author: Alban Diquet + * + * Copyright (C) 2012, iSEC Partners. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + + +#include <strings.h> +#include <openssl/x509v3.h> +#include <openssl/ssl.h> + +#define HOSTNAME_MAX_SIZE 255 + +#ifndef HAVE_X509_CHECK_HOST + +#include "openssl_hostname_validation.h" + + +/** +* Tries to find a match for hostname in the certificate's Common Name field. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if the Common Name had a NUL character embedded in it. +* Returns Error if the Common Name could not be extracted. +*/ +static HostnameValidationResult matches_common_name(const char *hostname, const X509 *server_cert) { + int common_name_loc = -1; + X509_NAME_ENTRY *common_name_entry = NULL; + ASN1_STRING *common_name_asn1 = NULL; + char *common_name_str = NULL; + + // Find the position of the CN field in the Subject field of the certificate + common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1); + if (common_name_loc < 0) { + return Error; + } + + // Extract the CN field + common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc); + if (common_name_entry == NULL) { + return Error; + } + + // Convert the CN field to a C string + common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) { + return Error; + } + common_name_str = (char *) ASN1_STRING_data(common_name_asn1); + + // Make sure there isn't an embedded NUL character in the CN + if (ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { + return MalformedCertificate; + } + + // Compare expected hostname with the CN + if (strcasecmp(hostname, common_name_str) == 0) { + return MatchFound; + } + else { + return MatchNotFound; + } +} + + +/** +* Tries to find a match for hostname in the certificate's Subject Alternative Name extension. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns NoSANPresent if the SAN extension was not present in the certificate. +*/ +static HostnameValidationResult matches_subject_alternative_name(const char *hostname, const X509 *server_cert) { + HostnameValidationResult result = MatchNotFound; + int i; + int san_names_nb = -1; + STACK_OF(GENERAL_NAME) *san_names = NULL; + + // Try to extract the names within the SAN extension from the certificate + san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL); + if (san_names == NULL) { + return NoSANPresent; + } + san_names_nb = sk_GENERAL_NAME_num(san_names); + + // Check each name within the extension + for (i=0; i<san_names_nb; i++) { + const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); + + if (current_name->type == GEN_DNS) { + // Current name is a DNS name, let's check it + char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); + + // Make sure there isn't an embedded NUL character in the DNS name + if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { + result = MalformedCertificate; + break; + } + else { // Compare expected hostname with the DNS name + if (strcasecmp(hostname, dns_name) == 0) { + result = MatchFound; + break; + } + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + + return result; +} + + +/** +* Validates the server's identity by looking for the expected hostname in the +* server's certificate. As described in RFC 6125, it first tries to find a match +* in the Subject Alternative Name extension. If the extension is not present in +* the certificate, it checks the Common Name instead. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns Error if there was an error. +*/ +HostnameValidationResult validate_hostname(const char *hostname, const X509 *server_cert) { + HostnameValidationResult result; + + if((hostname == NULL) || (server_cert == NULL)) + return Error; + + // First try the Subject Alternative Names extension + result = matches_subject_alternative_name(hostname, server_cert); + if (result == NoSANPresent) { + // Extension was not found: try the Common Name + result = matches_common_name(hostname, server_cert); + } + + return result; +} + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/openssl_hostname_validation.h new/openfortivpn-1.12.0/src/openssl_hostname_validation.h --- old/openfortivpn-1.11.0/src/openssl_hostname_validation.h 1970-01-01 01:00:00.000000000 +0100 +++ new/openfortivpn-1.12.0/src/openssl_hostname_validation.h 2020-02-26 17:02:52.000000000 +0100 @@ -0,0 +1,53 @@ +/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ + +/* + * Helper functions to perform basic hostname validation using OpenSSL. + * + * Author: Alban Diquet + * + * Copyright (C) 2012, iSEC Partners. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef HAVE_X509_CHECK_HOST + +typedef enum { + MatchFound, + MatchNotFound, + NoSANPresent, + MalformedCertificate, + Error +} HostnameValidationResult; + +/** +* Validates the server's identity by looking for the expected hostname in the +* server's certificate. As described in RFC 6125, it first tries to find a match +* in the Subject Alternative Name extension. If the extension is not present in +* the certificate, it checks the Common Name instead. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns Error if there was an error. +*/ +HostnameValidationResult validate_hostname(const char *hostname, const X509 *server_cert); + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/tunnel.c new/openfortivpn-1.12.0/src/tunnel.c --- old/openfortivpn-1.11.0/src/tunnel.c 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/tunnel.c 2020-02-26 17:02:52.000000000 +0100 @@ -29,6 +29,9 @@ #include "tunnel.h" #include "http.h" #include "log.h" +#ifndef HAVE_X509_CHECK_HOST +#include "openssl_hostname_validation.h" +#endif #include <unistd.h> #include <fcntl.h> @@ -61,8 +64,8 @@ #endif struct ofv_varr { - unsigned cap; // current capacity - unsigned off; // next slot to write, always < max(cap - 1, 1) + unsigned int cap; // current capacity + unsigned int off; // next slot to write, always < max(cap - 1, 1) const char **data; // NULL terminated }; @@ -70,9 +73,9 @@ { if (p->off + 1 >= p->cap) { const char **ndata; - unsigned ncap = (p->off + 1) * 2; + unsigned int ncap = (p->off + 1) * 2; if (p->off + 1 >= ncap) { - log_error("ofv_append_varr: ncap exceeded\n"); + log_error("%s: ncap exceeded\n", __func__); return 1; }; ndata = realloc(p->data, ncap * sizeof(const char *)); @@ -85,17 +88,16 @@ } } if (p->data == NULL) { - log_error("ofv_append_varr: NULL data\n"); + log_error("%s: NULL data\n", __func__); return 1; } - if (p->off + 1 < p->cap) { - p->data[p->off] = x; - p->data[++p->off] = NULL; - return 0; - } else { - log_error("ofv_append_varr: cap exceeded in p\n"); + if (p->off + 1 >= p->cap) { + log_error("%s: cap exceeded in p\n", __func__); return 1; } + p->data[p->off] = x; + p->data[++p->off] = NULL; + return 0; } static int on_ppp_if_up(struct tunnel *tunnel) @@ -109,9 +111,8 @@ ret = ipv4_set_tunnel_routes(tunnel); - if (ret != 0) { + if (ret != 0) log_warn("Adding route table is incomplete. Please check route table.\n"); - } } if (tunnel->config->set_dns) { @@ -193,11 +194,11 @@ * e.g. the name of the configuration or options * to send interactively to ppp will be added later */ - const char *v[] = { + static const char *const v[] = { ppp_path, "-direct" }; - for (unsigned i = 0; i < ARRAY_SIZE(v); i++) + for (unsigned int i = 0; i < ARRAY_SIZE(v); i++) if (ofv_append_varr(&pppd_args, v[i])) return 1; #endif @@ -210,7 +211,7 @@ if (ofv_append_varr(&pppd_args, tunnel->config->pppd_call)) return 1; } else { - const char *v[] = { + static const char *const v[] = { ppp_path, "115200", // speed ":192.0.2.1", // <local_IP_address>:<remote_IP_address> @@ -225,7 +226,7 @@ "lcp-max-configure", "40", "mru", "1354" }; - for (unsigned i = 0; i < ARRAY_SIZE(v); i++) + for (unsigned int i = 0; i < ARRAY_SIZE(v); i++) if (ofv_append_varr(&pppd_args, v[i])) return 1; } @@ -408,12 +409,12 @@ (tunnel->config->pppd_ifname && strstr(ifa->ifa_name, tunnel->config->pppd_ifname) != NULL) - || strstr(ifa->ifa_name, "ppp") != NULL) + || strstr(ifa->ifa_name, "ppp") != NULL #endif #if HAVE_USR_SBIN_PPP - strstr(ifa->ifa_name, "tun") != NULL) + strstr(ifa->ifa_name, "tun") != NULL #endif - && ifa->ifa_flags& IFF_UP) { + ) && ifa->ifa_flags & IFF_UP) { if (&(ifa->ifa_addr->sa_family) != NULL && ifa->ifa_addr->sa_family == AF_INET) { struct in_addr if_ip_addr = @@ -589,7 +590,7 @@ } // detect "200" - const char HTTP_STATUS_200[] = "200"; + static const char HTTP_STATUS_200[] = "200"; response = strstr(request, HTTP_STATUS_200); // detect end-of-line after "200" @@ -611,7 +612,7 @@ * recognize a single LF as a line terminator * and ignore the leading CR. */ - static const char *HTTP_EOL[] = { + static const char *const HTTP_EOL[] = { "\r\n\r\n", "\n\n" }; @@ -654,7 +655,6 @@ char *line; int i; X509_NAME *subj; - char common_name[FIELD_SIZE + 1]; SSL_set_verify(tunnel->ssl_handle, SSL_VERIFY_PEER, NULL); @@ -668,16 +668,15 @@ #ifdef HAVE_X509_CHECK_HOST // Use OpenSSL native host validation if v >= 1.0.2. - if (X509_check_host(cert, common_name, FIELD_SIZE, 0, NULL)) + // compare against gateway_host and correctly check return value + // to fix piror Incorrect use of X509_check_host + if (X509_check_host(cert, tunnel->config->gateway_host, + 0, 0, NULL) == 1) cert_valid = 1; #else - // Use explicit Common Name check if native validation not available. - // Note: this will ignore Subject Alternative Name fields. - if (subj - && X509_NAME_get_text_by_NID(subj, NID_commonName, common_name, - FIELD_SIZE) > 0 - && strncasecmp(common_name, tunnel->config->gateway_host, - FIELD_SIZE) == 0) + // Use validate_hostname form iSECPartners if native validation not available + // in order to avoid TLS Certificate CommonName NULL Byte Vulnerability + if (validate_hostname(tunnel->config->gateway_host, cert) == MatchFound) cert_valid = 1; #endif @@ -831,8 +830,8 @@ return 1; } - EVP_PKEY *privkey = ENGINE_load_private_key( - e, parms.uri, UI_OpenSSL(), NULL); + EVP_PKEY * privkey = ENGINE_load_private_key( + e, parms.uri, UI_OpenSSL(), NULL); if (!privkey) { log_error("PKCS11 ENGINE_load_private_key: %s\n", ERR_error_string(ERR_peek_last_error(), NULL)); @@ -905,17 +904,15 @@ if (!tunnel->config->cipher_list) { const char *cipher_list; if (tunnel->config->seclevel_1) - cipher_list = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4" - "@SECLEVEL=1"; + cipher_list = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4@SECLEVEL=1"; else cipher_list = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4"; tunnel->config->cipher_list = strdup(cipher_list); } } else { #if OPENSSL_VERSION_NUMBER >= 0x10100000L - if (tunnel->config->min_tls <= 0) { + if (tunnel->config->min_tls <= 0) tunnel->config->min_tls = TLS1_VERSION; - } #endif if (!tunnel->config->cipher_list && tunnel->config->seclevel_1) { const char *cipher_list = "DEFAULT@SECLEVEL=1"; @@ -924,7 +921,7 @@ } if (tunnel->config->cipher_list) { - log_debug("Setting cipher list to: %s", tunnel->config->cipher_list); + log_debug("Setting cipher list to: %s\n", tunnel->config->cipher_list); if (!SSL_set_cipher_list(tunnel->ssl_handle, tunnel->config->cipher_list)) { log_error("SSL_set_cipher_list failed: %s\n", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/src/tunnel.h new/openfortivpn-1.12.0/src/tunnel.h --- old/openfortivpn-1.11.0/src/tunnel.h 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/src/tunnel.h 2020-02-26 17:02:52.000000000 +0100 @@ -75,8 +75,8 @@ struct ipv4_config ipv4; - int (*on_ppp_if_up)(struct tunnel *); - int (*on_ppp_if_down)(struct tunnel *); + int (*on_ppp_if_up)(struct tunnel *tunnel); + int (*on_ppp_if_down)(struct tunnel *tunnel); }; struct token { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/tests/lint/astyle.sh new/openfortivpn-1.12.0/tests/lint/astyle.sh --- old/openfortivpn-1.11.0/tests/lint/astyle.sh 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/tests/lint/astyle.sh 2020-02-26 17:02:52.000000000 +0100 @@ -2,7 +2,7 @@ # Copyright (C) 2015 Adrien Vergé # Check that astyle is installed -if ! which astyle &>/dev/null; then +if ! command -v astyle &>/dev/null; then echo "error: astyle is not installed" >&2 exit -1 fi @@ -17,15 +17,15 @@ --indent=tab=8 \ --pad-header \ --align-reference=type \ - <"$file" >$tmp + <"$file" >"$tmp" - if ! cmp -s "$file" $tmp; then - echo "error: $file does not comply with coding style" - git --no-pager diff --no-index -U0 "$file" $tmp + if ! cmp -s "$file" "$tmp"; then + echo "error: $file does not comply with coding style" >&2 + git --no-pager diff --no-index -U0 "$file" "$tmp" rc=1 fi - rm $tmp + rm "$tmp" done exit $rc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/tests/lint/eol-at-eof.sh new/openfortivpn-1.12.0/tests/lint/eol-at-eof.sh --- old/openfortivpn-1.11.0/tests/lint/eol-at-eof.sh 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/tests/lint/eol-at-eof.sh 2020-02-26 17:02:52.000000000 +0100 @@ -5,12 +5,12 @@ for file in "$@"; do if [ "$(sed -n '$p' "$file")" = "" ]; then - echo "$file: too many newlines at end of file" + echo "$file: too many newlines at end of file" >&2 rc=1 fi if [ "$(tail -c 1 "$file")" != "" ]; then - echo "$file: no newline at end of file" + echo "$file: no newline at end of file" >&2 rc=1 fi done diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/tests/lint/line-length.py new/openfortivpn-1.12.0/tests/lint/line-length.py --- old/openfortivpn-1.11.0/tests/lint/line-length.py 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/tests/lint/line-length.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,53 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -# Copyright (C) 2015 Adrien Vergé - -import sys - -# Guidelines say 80, let's tolerate a bit more -MAX = 90 - - -def endswithstring(line): - """Detect lines from C source code ending with a string. - - Parameters - ---------- - line : str - Line of C source code. - - Returns - ------- - bool - True if line ends with string, False otherwise. - - """ - for end in ('"', '",', '");', '" \\'): - if line.endswith(end): - return True - return False - - -def main(): - exit_status = 0 - - for arg in sys.argv[1:]: - with open(arg, "r") as source_file: - for i, line in enumerate(source_file): - line = line.rstrip() - # Lines that end with a string are exempted - if endswithstring(line): - continue - # Replace tabs with 8 spaces - line = line.replace("\t", " ") - # Lines longer than MAX are reported as an error - if len(line) > MAX: - print("{}: {}: line too long ({} characters)" - .format(arg, i, len(line))) - exit_status = 1 - - sys.exit(exit_status) - - -if __name__ == "__main__": - main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/tests/lint/line_length.py new/openfortivpn-1.12.0/tests/lint/line_length.py --- old/openfortivpn-1.11.0/tests/lint/line_length.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openfortivpn-1.12.0/tests/lint/line_length.py 2020-02-26 17:02:52.000000000 +0100 @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Copyright (C) 2015 Adrien Vergé + +import sys + +# Guidelines say 80, let's tolerate a bit more +MAX = 90 + + +def endswithstring(line): + """Detect lines from C source code ending with a string. + + Parameters + ---------- + line : str + Line of C source code. + + Returns + ------- + bool + True if line ends with string, False otherwise. + + """ + for end in ('"', '",', '");', '";', '" \\'): + if line.endswith(end): + return True + return False + + +def main(): + exit_status = 0 + + for arg in sys.argv[1:]: + with open(arg, "r") as source_file: + for i, line in enumerate(source_file): + line = line.rstrip() + # Lines that end with a string are exempted + if endswithstring(line): + continue + # Replace tabs with 8 spaces + line = line.replace("\t", " ") + # Lines longer than MAX are reported as an error + if len(line) > MAX: + print("{}: {}: line too long ({} characters)" + .format(arg, i, len(line))) + exit_status = 1 + + sys.exit(exit_status) + + +if __name__ == "__main__": + main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openfortivpn-1.11.0/tests/lint/run.sh new/openfortivpn-1.12.0/tests/lint/run.sh --- old/openfortivpn-1.11.0/tests/lint/run.sh 2019-11-28 17:08:40.000000000 +0100 +++ new/openfortivpn-1.12.0/tests/lint/run.sh 2020-02-26 17:02:52.000000000 +0100 @@ -3,13 +3,10 @@ rc=0 -bash tests/lint/eol-at-eof.sh $(git ls-files) -[ $? -ne 0 ] && rc=1 +./tests/lint/eol-at-eof.sh $(git ls-files | grep -v openssl_hostname_validation) || rc=1 -python3 tests/lint/line-length.py $(git ls-files '*.[ch]') -[ $? -ne 0 ] && rc=1 +./tests/lint/line_length.py $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) || rc=1 -bash tests/lint/astyle.sh $(git ls-files '*.[ch]') -[ $? -ne 0 ] && rc=1 +./tests/lint/astyle.sh $(git ls-files '*.[ch]' | grep -v openssl_hostname_validation) || rc=1 exit $rc
participants (1)
-
root