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
+
+- 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
#include
#include
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= 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
+#include
+#include
+
+#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; itype == 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
#include
@@ -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", // :
@@ -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