Re: Bug#590873: openconnect < 2.25 does not verify SSL server certificates
On Sat, Aug 28, 2010 at 01:16:29PM +0200, Julien Cristau wrote:
> On Sat, Aug 28, 2010 at 11:50:49 +0100, Dominic Hargreaves wrote:
>
> > On Sun, Aug 15, 2010 at 08:56:46PM +0100, Adam D. Barratt wrote:
> > > On Sun, 2010-08-15 at 16:13 +0100, Dominic Hargreaves wrote:
> > > > To the untrained eye, the diff between
> > > > 6732c0e8ccb4d57d6a970973f994a9d2d3509def
> > > > and
> > > > 3b2738befa7fe934d0d55b77fe1fcf28aafbe424
> > > >
> > > > in upstream git is what's required for this, but the patch would need
> > > > a bit of work to apply cleanly. Note also that there
> > > > are some memory leaks fixed in 2.25 which might be a good idea to fix
> > > > too.
> > > >
> > > > Given all this, might the best idea be allow an exception for the
> > > > new upstream? The full changelog is:
> > >
> > > Most of the changes sound potentially worthy of inclusion. What does
> > > the debdiff look like?
> >
> > File lists identical (after any substitutions)
> >
> > Control files: lines which differ (wdiff format)
> > ------------------------------------------------
> > Installed-Size: [-196-] {+208+}
> > Version: [-2.22-1.1-] {+2.25-0.1+}
> >
> The debdiff between both .dscs, not between the .debs.
Ah, it wasn't clear what was required.
> > Trivial interdiff (including reverted patch included upstream)
> > attached.
> >
> This doesn't seem to be the full story, it has no upstream changes...
The upstream changes are visible at
<http://git.infradead.org/users/dwmw2/openconnect.git>
and also in the attached debdiff.
Cheers,
Dominic.
--
Dominic Hargreaves | http://www.larted.org.uk/~dom/
PGP key 5178E2A5 from the.earth.li (keyserver,web,email)
diff -Nru openconnect-2.22/auth.c openconnect-2.25/auth.c
--- openconnect-2.22/auth.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/auth.c 2010-05-15 09:23:37.000000000 +0100
@@ -523,6 +523,7 @@
if (vpninfo->password &&
!strcmp(opt->name, "password")) {
opt->value = strdup(vpninfo->password);
+ vpninfo->password = NULL;
if (!opt->value) {
ret = -ENOMEM;
goto out_ui;
diff -Nru openconnect-2.22/auth-dlg-settings.h openconnect-2.25/auth-dlg-settings.h
--- openconnect-2.22/auth-dlg-settings.h 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/auth-dlg-settings.h 2010-05-15 09:23:37.000000000 +0100
@@ -31,15 +31,11 @@
#define NM_OPENCONNECT_KEY_GATEWAY "gateway"
#define NM_OPENCONNECT_KEY_COOKIE "cookie"
#define NM_OPENCONNECT_KEY_GWCERT "gwcert"
-#define NM_OPENCONNECT_KEY_AUTHTYPE "authtype"
#define NM_OPENCONNECT_KEY_USERCERT "usercert"
#define NM_OPENCONNECT_KEY_CACERT "cacert"
#define NM_OPENCONNECT_KEY_PRIVKEY "userkey"
#define NM_OPENCONNECT_KEY_USERNAME "username"
#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig"
-#define NM_OPENCONNECT_AUTHTYPE_CERT "cert"
-#define NM_OPENCONNECT_AUTHTYPE_CERT_TPM "cert-tpm"
-#define NM_OPENCONNECT_AUTHTYPE_PASSWORD "password"
#endif /* __OPENCONNECT_AUTH_DLG_SETTINGS_H */
diff -Nru openconnect-2.22/cstp.c openconnect-2.25/cstp.c
--- openconnect-2.22/cstp.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/cstp.c 2010-05-15 09:23:37.000000000 +0100
@@ -84,6 +84,7 @@
vpninfo->vpn_addr6 = vpninfo->vpn_netmask6 = NULL;
vpninfo->cstp_options = vpninfo->dtls_options = NULL;
vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
+ vpninfo->banner = NULL;
for (i=0; i<3; i++)
vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
@@ -248,6 +249,8 @@
vpninfo->vpn_domain = new_option->value;
} else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
vpninfo->vpn_proxy_pac = new_option->value;
+ } else if (!strcmp(buf + 7, "Banner")) {
+ vpninfo->banner = new_option->value;
} else if (!strcmp(buf + 7, "Split-Include")) {
struct split_include *inc = malloc(sizeof(*inc));
if (!inc)
diff -Nru openconnect-2.22/debian/changelog openconnect-2.25/debian/changelog
--- openconnect-2.22/debian/changelog 2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/debian/changelog 2010-08-28 12:58:23.000000000 +0100
@@ -1,3 +1,11 @@
+openconnect (2.25-0.1) unstable; urgency=low
+
+ * Non-maintainer upload.
+ * New upstream release (Closes: #566188)
+ - always verify SSL server certificates (Closes: #590873)
+
+ -- Dominic Hargreaves <dom@earth.li> Sat, 28 Aug 2010 11:21:16 +0100
+
openconnect (2.22-1.1) unstable; urgency=low
* Non-maintainer upload.
diff -Nru openconnect-2.22/dtls.c openconnect-2.25/dtls.c
--- openconnect-2.22/dtls.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/dtls.c 2010-05-15 09:23:37.000000000 +0100
@@ -35,6 +35,19 @@
#include "openconnect.h"
+static unsigned char nybble(unsigned char n)
+{
+ if (n >= '0' && n <= '9') return n - '0';
+ else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
+ else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
+ return 0;
+}
+
+unsigned char unhex(const char *data)
+{
+ return (nybble(data[0]) << 4) | nybble(data[1]);
+}
+
#ifdef SSL_F_DTLS1_CONNECT
#if 0
/*
@@ -90,19 +103,6 @@
* their clients use anyway.
*/
-static unsigned char nybble(unsigned char n)
-{
- if (n >= '0' && n <= '9') return n - '0';
- else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
- else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
- return 0;
-}
-
-static unsigned char hex(const char *data)
-{
- return (nybble(data[0]) << 4) | nybble(data[1]);
-}
-
int connect_dtls_socket(struct openconnect_info *vpninfo)
{
STACK_OF(SSL_CIPHER) *ciphers;
@@ -211,7 +211,7 @@
if (!SSL_set_session(dtls_ssl, vpninfo->dtls_session)) {
vpninfo->progress(vpninfo, PRG_ERR,
"SSL_set_session() failed with old protocol version 0x%x\n"
- "Your OpenSSL may lack Cisco compatibility support\n"
+ "Are you using a version of OpenSSL older than 0.9.8m?\n"
"See http://rt.openssl.org/Ticket/Display.html?id=1751\n"
"Use the --no-dtls command line option to avoid this message\n",
vpninfo->dtls_session->ssl_version);
@@ -346,7 +346,7 @@
return -EINVAL;
}
for (i = 0; i < 64; i += 2)
- vpninfo->dtls_session_id[i/2] = hex(dtls_opt->value + i);
+ vpninfo->dtls_session_id[i/2] = unhex(dtls_opt->value + i);
sessid_found = 1;
} else if (!strcmp(dtls_opt->option + 7, "Port")) {
dtls_port = atol(dtls_opt->value);
diff -Nru openconnect-2.22/http.c openconnect-2.25/http.c
--- openconnect-2.22/http.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/http.c 2010-05-15 09:23:37.000000000 +0100
@@ -51,7 +51,8 @@
* provided by their caller.
*/
-int http_add_cookie(struct openconnect_info *vpninfo, const char *option, const char *value)
+static int http_add_cookie(struct openconnect_info *vpninfo,
+ const char *option, const char *value)
{
struct vpn_option *new, **this;
@@ -124,7 +125,8 @@
return -EINVAL;
}
- vpninfo->progress(vpninfo, PRG_TRACE, "Got HTTP response: %s\n", buf);
+ vpninfo->progress(vpninfo, (*result==200)?PRG_TRACE:PRG_INFO,
+ "Got HTTP response: %s\n", buf);
/* Eat headers... */
while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
@@ -289,7 +291,7 @@
}
}
- if (closeconn) {
+ if (closeconn || vpninfo->no_http_keepalive) {
SSL_free(vpninfo->https_ssl);
vpninfo->https_ssl = NULL;
close(vpninfo->ssl_fd);
@@ -365,10 +367,10 @@
int fd, ret;
if (!vpninfo->uid_csd_given) {
- vpninfo->progress(vpninfo, PRG_ERR, "Error: You are trying to "
- "run insecure CSD code without specifying the CSD user.\n"
- " Use command line option \"--csd-user\"\n");
- exit(1);
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
+ "This facility is disabled by default for security reasons, so you may wish to enable it.");
+ return -EPERM;
}
#ifndef __linux__
@@ -427,7 +429,11 @@
"CSD code with root privileges\n"
"\t Use command line option \"--csd-user\"\n");
}
-
+ if (vpninfo->uid_csd_given == 2) {
+ /* The NM tool really needs not to get spurious output
+ on stdout, which the CSD trojan spews. */
+ dup2(2, 1);
+ }
csd_argv[i++] = fname;
csd_argv[i++] = "-ticket";
if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
@@ -532,11 +538,8 @@
}
path = strchr(host, '/');
- if (path) {
+ if (path)
*(path++) = 0;
- if (!*path)
- path = NULL;
- }
port_str = strrchr(host, ':');
if (port_str) {
@@ -556,7 +559,13 @@
if (res_port)
*res_port = port;
if (res_path)
- *res_path = path ? strdup(path) : NULL;
+ *res_path = (path && *path) ? strdup(path) : NULL;
+
+ /* Undo the damage we did to the original string */
+ if (path)
+ *(path - 1) = '/';
+ if (proto)
+ *(host - 3) = ':';
return 0;
}
@@ -712,7 +721,13 @@
goto retry;
}
}
-
+ if (!form_buf || result != 200) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Unexpected %d result from server\n",
+ result);
+ free(form_buf);
+ return -EINVAL;
+ }
if (vpninfo->csd_stuburl) {
/* This is the CSD stub script, which we now need to run */
result = run_csd_script(vpninfo, form_buf, buflen);
diff -Nru openconnect-2.22/main.c openconnect-2.25/main.c
--- openconnect-2.22/main.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/main.c 2010-05-15 09:23:37.000000000 +0100
@@ -37,8 +37,9 @@
#include <sys/utsname.h>
#include <sys/types.h>
#include <openssl/rand.h>
+#include <openssl/ui.h>
#ifdef OPENCONNECT_LIBPROXY
-#include <libproxy/proxy.h>
+#include LIBPROXY_HDR
#endif
#define _GNU_SOURCE
@@ -49,10 +50,12 @@
static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen);
static void write_progress(struct openconnect_info *info, int level, const char *fmt, ...);
static void syslog_progress(struct openconnect_info *info, int level, const char *fmt, ...);
+static int validate_peer_cert(struct openconnect_info *info, X509 *peer_cert, const char *reason);
int verbose = PRG_INFO;
int background;
int do_passphrase_from_fsid;
+int nocertcheck;
static struct option long_options[] = {
{"background", 0, 0, 'b'},
@@ -95,6 +98,8 @@
{"disable-ipv6", 0, 0, 0x05},
{"no-proxy", 0, 0, 0x06},
{"libproxy", 0, 0, 0x07},
+ {"no-http-keepalive", 0, 0, 0x08},
+ {"no-cert-check", 0, 0, 0x09},
{NULL, 0, 0, 0},
};
@@ -140,7 +145,9 @@
printf(" --disable-ipv6 Do not ask for IPv6 connectivity\n");
printf(" --dtls-ciphers=LIST OpenSSL ciphers to support for DTLS\n");
printf(" --no-dtls Disable DTLS\n");
+ printf(" --no-http-keepalive Disable HTTP connection re-use\n");
printf(" --no-passwd Disable password/SecurID authentication\n");
+ printf(" --no-cert-check Do not require server SSL cert to be valid\n");
printf(" --passwd-on-stdin Read password from standard input\n");
printf(" --reconnect-timeout Connection retry timeout in seconds\n");
printf(" --servercert Server's certificate SHA1 fingerprint\n");
@@ -206,6 +213,7 @@
vpninfo->reconnect_timeout = 300;
vpninfo->uid_csd = 0;
vpninfo->uid_csd_given = 0;
+ vpninfo->validate_peer_cert = validate_peer_cert;
if (RAND_bytes(vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)) != 1) {
fprintf(stderr, "Failed to initialise DTLS secret\n");
@@ -327,6 +335,14 @@
autoproxy = 1;
proxy = NULL;
break;
+ case 0x08:
+ fprintf(stderr, "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
+ "If this helps, please report to <openconnect-devel@lists.infradead.org>.\n");
+ vpninfo->no_http_keepalive = 1;
+ break;
+ case 0x09:
+ nocertcheck = 1;
+ break;
case 's':
vpninfo->vpnc_script = optarg;
break;
@@ -501,6 +517,10 @@
(vpninfo->deflate ? "SSL + deflate" : "SSL")
: "DTLS");
+ if (!vpninfo->vpnc_script)
+ vpninfo->progress(vpninfo, PRG_INFO,
+ "No --script argument provided; DNS and routing are not configured\n");
+
if (background) {
int pid;
if ((pid = fork())) {
@@ -563,3 +583,68 @@
va_end(args);
}
}
+
+
+struct accepted_cert {
+ struct accepted_cert *next;
+ char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
+ char host[0];
+} *accepted_certs;
+
+static int validate_peer_cert(struct openconnect_info *vpninfo, X509 *peer_cert,
+ const char *reason)
+{
+ char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
+ struct accepted_cert *this;
+ int ret;
+
+ if (nocertcheck)
+ return 0;
+
+ ret = get_cert_sha1_fingerprint(vpninfo, peer_cert, fingerprint);
+ if (ret)
+ return ret;
+
+ for (this = accepted_certs; this; this = this->next) {
+ if (!strcasecmp(this->host, vpninfo->hostname) &&
+ !strcasecmp(this->fingerprint, fingerprint))
+ return 0;
+ }
+
+ while (1) {
+ UI *ui;
+ char buf[6];
+ int ret;
+
+ fprintf(stderr, "\nCertificate from VPN server \"%s\" failed verification.\n"
+ "Reason: %s\n", vpninfo->hostname, reason);
+ fflush(stderr);
+
+ ui = UI_new();
+ UI_add_input_string(ui, "Enter 'yes' to accept, 'no' to abort; anything else to view: ",
+ UI_INPUT_FLAG_ECHO, buf, 2, 5);
+ ret = UI_process(ui);
+ UI_free(ui);
+ if (ret == -2)
+ return -EINVAL;
+ if (ret == -1)
+ buf[0] = 0;
+
+ if (!strcasecmp(buf, "yes")) {
+ struct accepted_cert *newcert = malloc(sizeof(*newcert) +
+ strlen(vpninfo->hostname) + 1);
+ if (newcert) {
+ newcert->next = accepted_certs;
+ accepted_certs = newcert;
+ strcpy(newcert->fingerprint, fingerprint);
+ strcpy(newcert->host, vpninfo->hostname);
+ }
+ return 0;
+ }
+ if (!strcasecmp(buf, "no"))
+ return -EINVAL;
+
+ X509_print_fp(stderr, peer_cert);
+ }
+
+}
diff -Nru openconnect-2.22/mainloop.c openconnect-2.25/mainloop.c
--- openconnect-2.22/mainloop.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/mainloop.c 2010-05-15 09:23:37.000000000 +0100
@@ -119,7 +119,7 @@
continue;
vpninfo->progress(vpninfo, PRG_TRACE,
- "Did no work; sleeping for %d ms...\n", timeout);
+ "No work to do; sleeping for %d ms...\n", timeout);
memcpy(&rfds, &vpninfo->select_rfds, sizeof(rfds));
memcpy(&wfds, &vpninfo->select_wfds, sizeof(wfds));
memcpy(&efds, &vpninfo->select_efds, sizeof(efds));
diff -Nru openconnect-2.22/Makefile openconnect-2.25/Makefile
--- openconnect-2.22/Makefile 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/Makefile 2010-05-15 09:23:37.000000000 +0100
@@ -15,7 +15,7 @@
# dir; there's no need to install it anywhere (we link it statically).
ifdef OPENSSL
SSL_CFLAGS += -I$(OPENSSL)/include
-SSL_LDFLAGS += -lz $(OPENSSL)/libssl.a $(OPENSSL)/libcrypto.a -ldl
+SSL_LDFLAGS += $(OPENSSL)/libssl.a $(OPENSSL)/libcrypto.a
else
ifeq ($(wildcard /usr/include/openssl),)
$(error "No OpenSSL in /usr/include/openssl. Cannot continue");
@@ -43,7 +43,7 @@
endif
CFLAGS := $(OPT_FLAGS) $(SSL_CFLAGS) $(XML2_CFLAGS) $(EXTRA_CFLAGS)
-LDFLAGS := $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
+LDFLAGS := -lz $(SSL_LDFLAGS) $(XML2_LDFLAGS) $(EXTRA_LDFLAGS)
ifdef SSL_UI
CFLAGS += -DSSL_UI
@@ -58,7 +58,7 @@
endif
ifneq ($(LIBPROXY_HDR),)
-CFLAGS += -DOPENCONNECT_LIBPROXY
+CFLAGS += -DOPENCONNECT_LIBPROXY -DLIBPROXY_HDR=\"$(LIBPROXY_HDR)\"
LDFLAGS += -lproxy
endif
@@ -73,11 +73,14 @@
all: openconnect maybe-auth-dialog
+libopenconnect.a: ${AUTH_OBJECTS}
+ $(AR) rcs $@ $^
+
version.c: $(patsubst %.o,%.c,$(VERSION_OBJS)) Makefile openconnect.h \
$(wildcard .git/index .git/refs/tags) version.sh
@./version.sh
-openconnect: $(OPENCONNECT_OBJS) $(CONNECTION_OBJS) $(AUTH_OBJECTS)
+openconnect: $(OPENCONNECT_OBJS) $(CONNECTION_OBJS) libopenconnect.a
$(CC) -o $@ $^ $(LDFLAGS)
ifeq ($(MISSINGPKGS),)
@@ -87,7 +90,7 @@
$(warning Missing pkg-config packages: $(MISSINGPKGS))
endif
-nm-openconnect-auth-dialog: nm-auth-dialog.o $(AUTH_OBJECTS)
+nm-openconnect-auth-dialog: nm-auth-dialog.o libopenconnect.a
$(CC) -o $@ $^ $(LDFLAGS) $(GTK_LDFLAGS) $(GCONF_LDFLAGS) $(XML2_LDFLAGS)
%.o: %.c
@@ -114,7 +117,7 @@
HDRTEST = for a in $2 ; do if echo "\#include <$$a>" | $(CC) -o/dev/null -xc - -M -MF $1 -MP -MT Make.config 2>/dev/null; then \
echo $$a; break ; fi; done
-Make.config: LIBPROXY_H = $(shell $(call HDRTEST,.libproxy.h.dep,libproxy/proxy.h))
+Make.config: LIBPROXY_H = $(shell $(call HDRTEST,.libproxy.h.dep,proxy.h libproxy/proxy.h))
Make.config: IF_TUN_H = $(shell $(call HDRTEST,.if_tun.h.dep, linux/if_tun.h net/if_tun.h net/tun/if_tun.h))
Make.config: Makefile
( echo "IF_TUN_HDR := $(IF_TUN_H)"; echo "LIBPROXY_HDR := $(LIBPROXY_H)" ) > $@
diff -Nru openconnect-2.22/nm-auth-dialog.c openconnect-2.25/nm-auth-dialog.c
--- openconnect-2.22/nm-auth-dialog.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/nm-auth-dialog.c 2010-05-15 09:23:37.000000000 +0100
@@ -616,6 +616,7 @@
typedef struct cert_data {
auth_ui_data *ui_data;
X509 *peer_cert;
+ const char *reason;
} cert_data;
@@ -637,8 +638,9 @@
BIO_get_mem_ptr(bp, &certinfo);
title = get_title(data->ui_data->vpninfo->vpn_name);
- msg = g_strdup_printf("Unknown certificate from VPN server \"%s\".\n"
- "Do you want to accept it?", data->ui_data->vpninfo->hostname);
+ msg = g_strdup_printf("Certificate from VPN server \"%s\" failed verification.\n"
+ "Reason: %s\nDo you want to accept it?",
+ data->ui_data->vpninfo->hostname, data->reason);
dlg = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_QUESTION,
GTK_BUTTONS_OK_CANCEL,
@@ -683,7 +685,7 @@
/* runs in worker thread */
static int validate_peer_cert(struct openconnect_info *vpninfo,
- X509 *peer_cert)
+ X509 *peer_cert, const char *reason)
{
char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
char *certs_data;
@@ -714,6 +716,7 @@
data = g_slice_new(cert_data);
data->ui_data = ui_data; /* FIXME uses global */
data->peer_cert = peer_cert;
+ data->reason = reason;
g_mutex_lock(ui_data->form_mutex);
@@ -884,10 +887,11 @@
static int get_config(char *vpn_uuid, struct openconnect_info *vpninfo)
{
- char *authtype;
+ char *proxy;
char *xmlconfig;
char *hostname;
char *group;
+ char *csd;
char *pem_passphrase_fsid;
gcl = gconf_client_get_default();
@@ -950,37 +954,25 @@
vpninfo->cafile = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_CACERT);
- authtype = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_AUTHTYPE);
- if (!authtype) {
- fprintf(stderr, "No authentication type configured\n");
- return -EINVAL;
+ csd = get_gconf_setting(gcl, config_path, "enable_csd_trojan");
+ if (csd && !strcmp(csd, "yes")) {
+ /* We're not running as root; we can't setuid(). */
+ vpninfo->uid_csd = getuid();
+ vpninfo->uid_csd_given = 2;
}
+ g_free(csd);
- if (!strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_PASSWORD)) {
- vpninfo->username = get_gconf_setting(gcl, config_path,
- NM_OPENCONNECT_KEY_USERNAME);
- return 0;
- }
- if (!strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_CERT_TPM))
- vpninfo->cert_type = CERT_TYPE_TPM;
- else if (strcmp(authtype, NM_OPENCONNECT_AUTHTYPE_CERT)) {
- fprintf(stderr, "Unknown authentication type '%s'\n", authtype);
+ proxy = get_gconf_setting(gcl, config_path, "proxy");
+ if (proxy && proxy[0] && set_http_proxy(vpninfo, proxy))
return -EINVAL;
- }
- /* It's a certificate */
vpninfo->cert = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_USERCERT);
- if (!vpninfo->cert) {
- fprintf(stderr, "No user certificate configured\n");
- return -EINVAL;
- }
-
vpninfo->sslkey = get_gconf_setting(gcl, config_path, NM_OPENCONNECT_KEY_PRIVKEY);
if (!vpninfo->sslkey)
vpninfo->sslkey = vpninfo->cert;
pem_passphrase_fsid = get_gconf_setting(gcl, config_path, "pem_passphrase_fsid");
- if (pem_passphrase_fsid && !strcmp(pem_passphrase_fsid, "yes"))
+ if (pem_passphrase_fsid && vpninfo->sslkey && !strcmp(pem_passphrase_fsid, "yes"))
passphrase_from_fsid(vpninfo);
g_free(pem_passphrase_fsid);
diff -Nru openconnect-2.22/openconnect.8 openconnect-2.25/openconnect.8
--- openconnect-2.22/openconnect.8 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.8 2010-05-15 09:23:37.000000000 +0100
@@ -131,9 +131,15 @@
.I LIST
]
[
+.B --no-cert-check
+]
+[
.B --no-dtls
]
[
+.B --no-http-keepalive
+]
+[
.B --no-passwd
]
[
@@ -295,9 +301,38 @@
.B --dtls-ciphers=LIST
Set OpenSSL ciphers to support for DTLS
.TP
+.B --no-cert-check
+Do not require server SSL certificate to be valid. Checks will still happen
+and failures will cause a warning message, but the connection will continue
+anyway. You should not need to use this option -- if your servers have SSL
+certificates which are not signed by a trusted Certificate Authority, you can
+still add them (or your private CA) to a local file and use that file with the
+.B --cafile
+option.
+
+.TP
.B --no-dtls
Disable DTLS
.TP
+.B --no-http-keepalive
+Version 8.2.2.5 of the Cisco ASA software has a bug where it will forget
+the client's SSL certificate when HTTP connections are being re-used for
+multiple requests. So far, this has only been seen on the initial connection,
+where the server gives an HTTP/1.0 redirect response with an explicit
+.B Connection: Keep-Alive
+directive. OpenConnect as of v2.22 has an unconditional workaround for this,
+which is never to obey that directive after an HTTP/1.0 response.
+
+However, Cisco's support team has failed to give any competent
+response to the bug report and we don't know under what other
+circumstances their bug might manifest itself. So this option exists
+to disable ALL re-use of HTTP sessions and cause a new connection to be
+made for each request. If your server seems not to be recognising your
+certificate, try this option. If it makes a difference, please report
+this information to the
+.B openconnect-devel@lists.infradead.org
+mailing list.
+.TP
.B --no-passwd
Never attempt password (or SecurID) authentication
.TP
diff -Nru openconnect-2.22/openconnect.h openconnect-2.25/openconnect.h
--- openconnect-2.22/openconnect.h 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.h 2010-05-15 09:23:37.000000000 +0100
@@ -35,7 +35,7 @@
#include <sys/types.h>
#include <unistd.h>
#ifdef OPENCONNECT_LIBPROXY
-#include <libproxy/proxy.h>
+#include LIBPROXY_HDR
#endif
@@ -173,6 +173,7 @@
char *dtls_ciphers;
uid_t uid_csd;
int uid_csd_given;
+ int no_http_keepalive;
char *cookie;
struct vpn_option *cookies;
@@ -210,6 +211,7 @@
char *ifname;
int mtu;
+ const char *banner;
const char *vpn_addr;
const char *vpn_netmask;
const char *vpn_addr6;
@@ -249,7 +251,7 @@
char *quit_reason;
- int (*validate_peer_cert) (struct openconnect_info *vpninfo, X509 *cert);
+ int (*validate_peer_cert) (struct openconnect_info *vpninfo, X509 *cert, const char *reason);
int (*write_new_config) (struct openconnect_info *vpninfo, char *buf, int buflen);
int (*process_auth_form) (struct openconnect_info *vpninfo, struct oc_auth_form *form);
@@ -273,7 +275,7 @@
#define AC_PKT_TERM_SERVER 9 /* Server kick */
/* Ick */
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
#define method_const const
#else
#define method_const
@@ -287,6 +289,7 @@
void shutdown_tun(struct openconnect_info *vpninfo);
/* dtls.c */
+unsigned char unhex(const char *data);
int setup_dtls(struct openconnect_info *vpninfo);
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout);
int dtls_try_handshake(struct openconnect_info *vpninfo);
diff -Nru openconnect-2.22/openconnect.html openconnect-2.25/openconnect.html
--- openconnect-2.22/openconnect.html 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/openconnect.html 2010-05-15 09:23:37.000000000 +0100
@@ -8,7 +8,7 @@
<body>
<h1>OpenConnect</h1>
-<P>OpenConnect is a client for Cisco's <A HREF="http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6586/ps6657/product_data_sheet0900aecd80405e25.html">AnyConnect SSL VPN</A>, which is supported by IOS 12.4(9)T or later on Cisco SR500, 870, 880, 1800, 2800, 3800, 7200 Series and Cisco 7301 Routers.</P>
+<P>OpenConnect is a client for Cisco's <A HREF="http://www.cisco.com/web/go/sslvpn">AnyConnect SSL VPN</A>, which is supported by the ASA5500 Series, by IOS 12.4(9)T or later on Cisco SR500, 870, 880, 1800, 2800, 3800, 7200 Series and Cisco 7301 Routers, and probably others.</P>
<P>OpenConnect is released under the GNU Lesser Public License, version 2.1.</P>
@@ -49,8 +49,8 @@
</LI>
<LI>Install a <TT>vpnc-script</TT>.<BR>
This script is what sets up all the addresses and routes for you; it's the
- same as <TT>vpnc</TT>'s. You can get one from <A HREF="http://git.infradead.org/users/dwmw2/vpnc-scripts.git/blob_plain/HEAD:/vpnc-script">here</A> if you don't have one — or if you need IPv6 or Solaris support, which the <TT>vpnc</TT> version lacks.</LI>
- <LI>Connect to your server:<BR>
+ same as <TT>vpnc</TT>'s. You can get one from <A HREF="http://git.infradead.org/users/dwmw2/vpnc-scripts.git/blob_plain/HEAD:/vpnc-script">here</A> if you don't have one — or if you need IPv6 or Solaris support, which the <TT>vpnc</TT> version lacks. <I>(Note that the script needs to be executable, and stored somewhere where SELinux or similar security setups won't prevent the root user from accessing it.)</I></LI>
+ <LI>Connect to your server, running as root:<BR>
<TT>openconnect --script /etc/vpnc/vpnc-script https://vpn.mycompany.com/</TT></LI>
</OL>
@@ -69,7 +69,7 @@
<H2>Supported Platforms</H2>
-OpenConnect is known to work on Linux, OpenBSD, FreeBSD, OpenSolaris
+OpenConnect is known to work on Linux, OpenBSD, FreeBSD, NetBSD, DragonFly BSD, OpenSolaris
and Mac OS X platforms, and should be trivially portable to any other platform
supporting <A HREF="http://en.wikipedia.org/wiki/TUN/TAP">TUN/TAP</a>
devices and on which <A HREF="http://www.openssl.org/">OpenSSL</a> runs.
@@ -178,6 +178,34 @@
<LI><I>No changelog entries yet</I></LI>
</UL><BR>
</LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.25.tar.gz">OpenConnect v2.25</a></B> — 2010-05-15<BR>
+ <UL>
+ <LI>Always validate server certificate, even when no extra <TT>--cafile</TT> is provided.</LI>
+ <LI>Add <TT>--no-cert-check</TT> option to avoid certificate validation.</LI>
+ <LI>Check server hostname against its certificate.</LI>
+ <LI>Provide text-mode function for reviewing and accepting "invalid" certificates.</LI>
+ <LI>Fix libproxy detection on NetBSD.</LI>
+ </UL><BR>
+ </LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.24.tar.gz">OpenConnect v2.24</a></B> — 2010-05-07<BR>
+ <UL>
+ <LI>Forget preconfigured password after a single attempt; don't retry infinitely if it's failing.</LI>
+ <LI>Set <TT>$CISCO_BANNER</TT> environment variable when running script.</I></LI>
+ <LI>Better handling of passphrase failure on certificate files.</LI>
+ <LI>Fix NetBSD build (thanks to Pouya D. Tafti).</LI>
+ <LI>Fix DragonFly BSD build.</LI>
+ </UL><BR>
+ </LI>
+ <LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.23.tar.gz">OpenConnect v2.23</a></B> — 2010-04-09<BR>
+ <UL>
+ <LI>Support "Cisco Secure Desktop" trojan in NetworkManager auth-dialog.</LI>
+ <LI>Support proxy in NetworkManager auth-dialog.</LI>
+ <LI>Add <TT>--no-http-keepalive</TT> option to work around Cisco's incompetence.</LI>
+ <LI>Fix build on Debian/kFreeBSD.</LI>
+ <LI>Fix crash on receiving HTTP 404 error.</LI>
+ <LI>Improve workaround for server certificates lacking SSL_SERVER purpose, so that it also works with OpenSSL older than 0.9.8k.</LI>
+ </UL><BR>
+ </LI>
<LI><B><A HREF="ftp://ftp.infradead.org/pub/openconnect/openconnect-2.22.tar.gz">OpenConnect v2.22</a></B> — 2010-03-07<BR>
<UL>
<LI>Fix bug handling port numbers above 9999.</LI>
@@ -318,10 +346,10 @@
<H2>Requirements</H2>
The basic text-mode client uses the following libraries:
<UL>
- <LI><B>OpenSSL</B> — all versions from 0.9.7 onwards will work for basic connectivity, but see note on DTLS compatibility below.</LI>
+ <LI><B>OpenSSL</B> — ideally at least 0.9.8m, although all versions from 0.9.7 onwards will work for basic connectivity. See note on DTLS compatibility below.</LI>
<LI><B>libxml2</B></LI>
<LI><B>zlib</B></LI>
- <LI><B><A HREF="http://code.google.com/p/libproxy/">libproxy</A></B></LI>
+ <LI><B><A HREF="http://code.google.com/p/libproxy/">libproxy</A></B> <I>(optionally)</I></LI>
</UL>
Mac OS X users will also need to install the
<A HREF="http://tuntaposx.sourceforge.net/">Mac OS X tun/tap driver</A>, and Solaris users will need the <A HREF="http://www.whiteboard.ne.jp/~admin2/tuntap/">Solaris one</A>. Note that for IPv6 support, the Solaris tun/tap driver from 16th Nov 2009 or newer is required.<P>
@@ -365,22 +393,7 @@
implementation of DTLS.
<P>
Compatibility support for their "speshul" version of the protocol is
-in the 1.0.0-beta2 and later releases, and is in CVS for the 0.9.8 branch,
-but has not yet been part of an official 0.9.8 release.
-<P>
-It was originally committed to the 0.9.8 branch after the 0.9.8k release,
-and therefore expected to be in the 0.9.8l release — but 0.9.8l
-actually turned out to be an "emergency" release to address a <A
-HREF="http://extendedsubset.com/?p=8">serious protocol flaw</A>, and
-was identical to 0.9.8k except for a patch to disable SSL
-renegotiation. It's quite likely that 0.9.8m will have a <A
-HREF="https://svn.resiprocate.org/rep/ietf-drafts/ekr/draft-rescorla-tls-renegotiate.txt">more
-refined fix</A> for the renegotiation problem, and therefore the Cisco
-DTLS compatibility and all the other things which went into OpenSSL
-CVS after 0.9.8k should finally see the light of day in a 0.9.8n
-release.
-
-
+in the 0.9.8m and later releases of OpenSSL (and 1.0.0-beta2 and later).
<P>
If you are using an older version of OpenSSL, DTLS will
@@ -406,8 +419,12 @@
<H3>Debian</H3>
The <TT>openconnect</TT> and <TT>network-manager-openconnect</TT> packages are available in unstable and testing.<BR>
<A HREF="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=524982">Debian bug #524982</A> has been filed, requesting that the required patches be included in Debian's OpenSSL package.<P>
+<H3>Ubuntu</H3>
+Reasonably current versions of the required packages are finally included in Ubuntu 10.04 "Lucid". Older releases still have <A HREF="https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/516318">out of date OpenSSL</A> and <A HREF="https://bugs.launchpad.net/ubuntu/+source/openconnect/+bug/516324">out of date OpenConnect which doesn't work around the latest Cisco bugs</A>.
<H3>Gentoo</H3>
<A HREF="http://bugs.gentoo.org/show_bug.cgi?id=263097">Gentoo bug #263097</A> has been filed, asking for <TT>openconnect</TT> to be packaged.
+<H3>NetBSD, DragonFly BSD, etc. <i>(pkgsrc)</i></H3>
+There are packages for <A HREF="http://pkgsrc-wip.cvs.sourceforge.net/viewvc/pkgsrc-wip/wip/vpnc-script/">vpnc-script</A> and <A HREF="http://pkgsrc-wip.cvs.sourceforge.net/viewvc/pkgsrc-wip/wip/openconnect/">openconnect</A> in the pkgsrc-wip repository <I>(<A HREF="http://pkgsrc-wip.sourceforge.net/">pkgsrc-wip.sf.net</A>)</I>.
<H3>FreeBSD</H3>
An <TT>openconnect</TT> <A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/ports/security/openconnect/">port</A> is available for FreeBSD. FreeBSD does not yet ship a version of OpenSSL which supports Cisco's "speshul" version of DTLS.
@@ -415,6 +432,6 @@
<hr>
<address>David Woodhouse <<A HREF="mailto:dwmw2@infradead.org">dwmw2@infradead.org</A>></address>
<!-- hhmts start -->
-Last modified: Sun Mar 7 14:10:55 PST 2010
+Last modified: Sat May 15 09:23:37 BST 2010
<!-- hhmts end -->
</body> </html>
diff -Nru openconnect-2.22/README.DTLS openconnect-2.25/README.DTLS
--- openconnect-2.22/README.DTLS 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/README.DTLS 2010-05-15 09:23:37.000000000 +0100
@@ -1,22 +1,24 @@
Cisco's implementation of the DTLS protocol unfortunately does not
-comply with the relevant standards. We need some patches to OpenSSL to
-be compatible with it.
+comply with the relevant standards. OpenSSL 0.9.8m or newer, and
+1.0.0-beta2 or newer, contain a compatibility mode which allows
+interoperation with Cisco's servers.
-For the 0.9.8 branch of OpenSSL, the required patch is
- http://cvs.openssl.org/chngview?cn=18037
+As long as you are using a current version of OpenSSL, you have nothing
+to worry about -- everything should work optimally.
+
+Without a suitable OpenSSL, the openconnect client will fall back to
+passing packets over the HTTPS connection. This will still work OK, but
+will suffer quite a lot if your connection has packet loss. For details
+of why that happens, see http://sites.inka.de/~W1011/devel/tcp-tcp.html
+
+If you insist on using ancient buggy versions of OpenSSL, these are the
+patches you require if you want DTLS to work:
-This was included in OpenSSL CVS in April 2009 and should be in the
-next release from the 0.9.8 branch, which will presumably be 0.9.8l.
+For versions of OpenSSL earlier than 0.9.8m, you'll need the Cisco
+compatibility support:
+ http://cvs.openssl.org/chngview?cn=18037
For versions of OpenSSL earlier than 0.9.8j, a couple of other DTLS
bug-fixes are also required:
http://cvs.openssl.org/chngview?cn=17500
http://cvs.openssl.org/chngview?cn=17505
-
-OpenSSL 1.0.0-beta2 and later require no patching; all the required
-support is already present.
-
-Without a suitable OpenSSL, the openconnect client will fall back to
-passing packets over the HTTPS connection. This will work, but will
-suffer quite a lot if your connection has packet loss. For details of
-why that happens, see http://sites.inka.de/~W1011/devel/tcp-tcp.html
diff -Nru openconnect-2.22/ssl.c openconnect-2.25/ssl.c
--- openconnect-2.22/ssl.c 2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/ssl.c 2010-05-15 09:23:37.000000000 +0100
@@ -31,12 +31,14 @@
#include <string.h>
#include <ctype.h>
#include <stdio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#if defined(__linux__)
#include <sys/vfs.h>
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <sys/param.h>
#include <sys/mount.h>
-#elif defined (__sun__)
+#elif defined (__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
#include <sys/statvfs.h>
#endif
@@ -138,18 +140,35 @@
{
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
- STACK_OF(X509) *ca = sk_X509_new_null();
+ STACK_OF(X509) *ca;
int ret = 0;
char pass[PEM_BUFSIZE];
+ retrypass:
+ /* We do this every time round the loop, to work around a bug in
+ OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed
+ when PKCS12_parse() returns an error, but *ca is left pointing
+ to the freed memory. */
+ ca = NULL;
if (!vpninfo->cert_password) {
if (EVP_read_pw_string(pass, PEM_BUFSIZE,
"Enter PKCS#12 pass phrase:", 0))
return -EINVAL;
}
if (!PKCS12_parse(p12, vpninfo->cert_password?:pass, &pkey, &cert, &ca)) {
- vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed\n");
+ unsigned long err = ERR_peek_error();
+
report_ssl_errors(vpninfo);
+
+ if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
+ ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
+ ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (wrong passphrase?)\n");
+ vpninfo->cert_password = NULL;
+ goto retrypass;
+ }
+
+ vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (see above errors)\n");
PKCS12_free(p12);
return -EINVAL;
}
@@ -187,12 +206,13 @@
buf, sizeof(buf));
vpninfo->progress(vpninfo, PRG_DEBUG,
"Extra cert from PKCS#12: '%s'\n", buf);
+ CRYPTO_add(&cert2->references, 1, CRYPTO_LOCK_X509);
SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
cert = cert2;
goto next;
}
}
- sk_X509_free(ca);
+ sk_X509_pop_free(ca, X509_free);
}
PKCS12_free(p12);
@@ -333,7 +353,6 @@
SSL_FILETYPE_PEM)) {
unsigned long err = ERR_peek_error();
- vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed\n");
report_ssl_errors(vpninfo);
#ifndef EVP_F_EVP_DECRYPTFINAL_EX
@@ -342,9 +361,12 @@
/* If the user fat-fingered the passphrase, try again */
if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
- ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT)
+ ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (wrong passphrase?)\n");
goto again;
+ }
+ vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (see above errors)\n");
return -EINVAL;
}
return 0;
@@ -411,35 +433,310 @@
return 0;
}
-static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
+static int match_hostname_elem(const char *hostname, int helem_len,
+ const char *match, int melem_len)
{
- X509 *peer_cert;
+ if (!helem_len && !melem_len)
+ return 0;
- if (vpninfo->cafile) {
- int vfy = SSL_get_verify_result(https_ssl);
+ if (!helem_len || !melem_len)
+ return -1;
- if (vfy != X509_V_OK) {
- vpninfo->progress(vpninfo, PRG_ERR, "Server certificate verify failed: %s\n",
- X509_verify_cert_error_string(vfy));
- return -EINVAL;
+
+ if (match[0] == '*') {
+ int i;
+
+ for (i = 1 ; i <= helem_len; i++) {
+ if (!match_hostname_elem(hostname + i, helem_len - i,
+ match + 1, melem_len - 1))
+ return 0;
}
- return 0;
+ return -1;
}
- peer_cert = SSL_get_peer_certificate(https_ssl);
+ if (toupper(hostname[0]) == toupper(match[0]))
+ return match_hostname_elem(hostname + 1, helem_len - 1,
+ match + 1, melem_len - 1);
- if (vpninfo->servercert)
- return check_server_cert(vpninfo, peer_cert);
+ return -1;
+}
- if (vpninfo->validate_peer_cert)
- return vpninfo->validate_peer_cert(vpninfo, peer_cert);
+static int match_hostname(const char *hostname, const char *match)
+{
+ while (*match) {
+ const char *h_dot, *m_dot;
+ int helem_len, melem_len;
+
+ h_dot = strchr(hostname, '.');
+ m_dot = strchr(match, '.');
+
+ if (h_dot && m_dot) {
+ helem_len = h_dot - hostname + 1;
+ melem_len = m_dot - match + 1;
+ } else if (!h_dot && !m_dot) {
+ helem_len = strlen(hostname);
+ melem_len = strlen(match);
+ } else
+ return -1;
+
+
+ if (match_hostname_elem(hostname, helem_len,
+ match, melem_len))
+ return -1;
+
+ hostname += helem_len;
+ match += melem_len;
+ }
+ if (*hostname)
+ return -1;
- /* If no validation function, just let it succeed */
return 0;
}
-void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
- SSL *ssl)
+/* cf. RFC2818 and RFC2459 */
+int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert)
+{
+ STACK_OF(GENERAL_NAME) *altnames;
+ X509_NAME *subjname;
+ ASN1_STRING *subjasn1;
+ char *subjstr = NULL;
+ int addrlen = 0;
+ int i, altdns = 0;
+ char addrbuf[sizeof(struct in6_addr)];
+ int ret;
+
+ /* Allow GEN_IP in the certificate only if we actually connected
+ by IP address rather than by name. */
+ if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0)
+ addrlen = 4;
+ else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0)
+ addrlen = 16;
+ else if (vpninfo->hostname[0] == '[' &&
+ vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') {
+ char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1];
+ *p = 0;
+ if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0)
+ addrlen = 16;
+ *p = ']';
+ }
+
+ altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name,
+ NULL, NULL);
+ for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) {
+ const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i);
+
+ if (this->type == GEN_DNS) {
+ char *str;
+
+ int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
+ if (len < 0)
+ continue;
+
+ altdns = 1;
+
+ /* We don't like names with embedded NUL */
+ if (strlen(str) != len)
+ continue;
+
+ if (!match_hostname(vpninfo->hostname, str)) {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched DNS altname '%s'\n",
+ str);
+ GENERAL_NAMES_free(altnames);
+ OPENSSL_free(str);
+ return 0;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for altname '%s'\n",
+ str);
+ }
+ OPENSSL_free(str);
+ } else if (this->type == GEN_IPADD && addrlen) {
+ char host[80];
+ int family;
+
+ if (this->d.ip->length == 4) {
+ family = AF_INET;
+ } else if (this->d.ip->length == 16) {
+ family = AF_INET6;
+ } else {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Certificate has GEN_IPADD altname with bogus length %d\n",
+ this->d.ip->length);
+ continue;
+ }
+
+ /* We only do this for the debug messages */
+ inet_ntop(family, this->d.ip->data, host, sizeof(host));
+
+ if (this->d.ip->length == addrlen &&
+ !memcmp(addrbuf, this->d.ip->data, addrlen)) {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched IP%s address '%s'\n",
+ (family == AF_INET6)?"v6":"",
+ host);
+ GENERAL_NAMES_free(altnames);
+ return 0;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for IP%s address '%s'\n",
+ (family == AF_INET6)?"v6":"",
+ host);
+ }
+ } else if (this->type == GEN_URI) {
+ char *str;
+ char *url_proto, *url_host, *url_path, *url_host2;
+ int url_port;
+ int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
+
+ if (len < 0)
+ continue;
+
+ /* We don't like names with embedded NUL */
+ if (strlen(str) != len)
+ continue;
+
+ if (parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) {
+ OPENSSL_free(str);
+ continue;
+ }
+
+ if (!url_proto || strcasecmp(url_proto, "https"))
+ goto no_uri_match;
+
+ if (url_port != vpninfo->port)
+ goto no_uri_match;
+
+ /* Leave url_host as it was so that it can be freed */
+ url_host2 = url_host;
+ if (addrlen == 16 && vpninfo->hostname[0] != '[' &&
+ url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') {
+ /* Cope with https://[IPv6]/ when the hostname is bare IPv6 */
+ url_host[strlen(url_host)-1] = 0;
+ url_host2++;
+ }
+
+ if (strcasecmp(vpninfo->hostname, url_host2))
+ goto no_uri_match;
+
+ if (url_path) {
+ vpninfo->progress(vpninfo, PRG_TRACE, "URI '%s' has non-empty path; ignoring\n",
+ str);
+ goto no_uri_match_silent;
+ }
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched URI '%s'\n",
+ str);
+ free(url_proto);
+ free(url_host);
+ free(url_path);
+ OPENSSL_free(str);
+ GENERAL_NAMES_free(altnames);
+ return 0;
+
+ no_uri_match:
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "No match for URI '%s'\n",
+ str);
+ no_uri_match_silent:
+ free(url_proto);
+ free(url_host);
+ free(url_path);
+ OPENSSL_free(str);
+ }
+ }
+ GENERAL_NAMES_free(altnames);
+
+ /* According to RFC2818, we don't use the legacy subject name if
+ there was an altname with DNS type. */
+ if (altdns) {
+ vpninfo->progress(vpninfo, PRG_ERR, "No altname in peer cert matched '%s'\n",
+ vpninfo->hostname);
+ return -EINVAL;
+ }
+
+ subjname = X509_get_subject_name(peer_cert);
+ if (!subjname) {
+ vpninfo->progress(vpninfo, PRG_ERR, "No subject name in peer cert!\n");
+ return -EINVAL;
+ }
+
+ /* Find the _last_ (most specific) commonName */
+ i = -1;
+ while (1) {
+ int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i);
+ if (j >= 0)
+ i = j;
+ else
+ break;
+ }
+
+ subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i));
+
+ i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1);
+
+ if (!subjstr || strlen(subjstr) != i) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Failed to parse subject name in peer cert\n");
+ return -EINVAL;
+ }
+ ret = 0;
+
+ if (match_hostname(vpninfo->hostname, subjstr)) {
+ vpninfo->progress(vpninfo, PRG_ERR, "Peer cert subject mismatch ('%s' != '%s')\n",
+ subjstr, vpninfo->hostname);
+ ret = -EINVAL;
+ } else {
+ vpninfo->progress(vpninfo, PRG_TRACE,
+ "Matched peer certificate subject name '%s'\n",
+ subjstr);
+ }
+
+ OPENSSL_free(subjstr);
+ return ret;
+}
+
+static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
+{
+ X509 *peer_cert;
+ int ret;
+
+ peer_cert = SSL_get_peer_certificate(https_ssl);
+
+ if (vpninfo->servercert) {
+ /* If given a cert fingerprint on the command line, that's
+ all we look for */
+ ret = check_server_cert(vpninfo, peer_cert);
+ } else {
+ int vfy = SSL_get_verify_result(https_ssl);
+ const char *err_string = NULL;
+
+ if (vfy != X509_V_OK)
+ err_string = X509_verify_cert_error_string(vfy);
+ else if (match_cert_hostname(vpninfo, peer_cert))
+ err_string = "certificate does not match hostname";
+
+ if (err_string) {
+ vpninfo->progress(vpninfo, PRG_ERR,
+ "Server certificate verify failed: %s\n",
+ err_string);
+
+ if (vpninfo->validate_peer_cert)
+ ret = vpninfo->validate_peer_cert(vpninfo, peer_cert,
+ err_string);
+ else
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+ }
+ X509_free(peer_cert);
+
+ return ret;
+}
+
+static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
+ SSL *ssl)
{
/* OpenSSL has problems with certificate chains -- if there are
multiple certs with the same name, it doesn't necessarily
@@ -474,6 +771,16 @@
X509_STORE_CTX_cleanup(&ctx);
}
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
+{
+ /* We've seen certificates in the wild which don't have the
+ purpose fields filled in correctly */
+ X509_VERIFY_PARAM_set_purpose(ctx->param, X509_PURPOSE_ANY);
+ return X509_verify_cert(ctx);
+}
+#endif
+
int openconnect_open_https(struct openconnect_info *vpninfo)
{
method_const SSL_METHOD *ssl3_method;
@@ -581,8 +888,8 @@
free(hostname);
if (err) {
- vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed: %s\n",
- gai_strerror(err));
+ vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed for host '%s': %s\n",
+ hostname, gai_strerror(err));
return -EINVAL;
}
@@ -649,9 +956,18 @@
}
}
- /* We've seen certificates in the wild which don't have the
- purpose fields filled in correctly */
- SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY);
+ /* We just want to do:
+ SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY);
+ ... but it doesn't work with OpenSSL < 0.9.8k because of
+ problems with inheritance (fixed in v1.1.4.6 of
+ crypto/x509/x509_vpm.c) so we have to play silly buggers
+ instead. This trick doesn't work _either_ in < 0.9.7 but
+ I don't know of _any_ workaround which will, and can't
+ be bothered to find out either. */
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+ SSL_CTX_set_cert_verify_callback(vpninfo->https_ctx,
+ ssl_app_verify_callback, NULL);
+#endif
SSL_CTX_set_default_verify_paths(vpninfo->https_ctx);
if (vpninfo->cafile)
@@ -709,7 +1025,7 @@
OpenSSL_add_all_algorithms ();
}
-#ifdef __sun__
+#if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
int passphrase_from_fsid(struct openconnect_info *vpninfo)
{
struct statvfs buf;
diff -Nru openconnect-2.22/TODO openconnect-2.25/TODO
--- openconnect-2.22/TODO 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/TODO 2010-05-15 09:23:37.000000000 +0100
@@ -1,7 +1,6 @@
openconnect:
- IPv6 support
- Port to/test on Windows/Solaris/*BSD
+ Port to/test on Windows, Symbian, etc.
Proper SecurID support
nm-auth-dialog:
diff -Nru openconnect-2.22/tun.c openconnect-2.25/tun.c
--- openconnect-2.22/tun.c 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/tun.c 2010-05-15 09:23:37.000000000 +0100
@@ -37,9 +37,14 @@
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
+#include <ctype.h>
#if defined(__sun__)
#include <stropts.h>
#include <sys/sockio.h>
+#include <net/if_tun.h>
+#ifndef TUNNEWPPA
+#error "Install TAP driver from http://www.whiteboard.ne.jp/~admin2/tuntap/"
+#endif
#endif
#include "openconnect.h"
@@ -232,6 +237,31 @@
free(env_buf);
}
+static void set_banner(struct openconnect_info *vpninfo)
+{
+ char *banner, *q;
+ const char *p;
+
+ if (!vpninfo->banner || !(banner = malloc(strlen(vpninfo->banner)))) {
+ unsetenv("CISCO_BANNER");
+ return;
+ }
+ p = vpninfo->banner;
+ q = banner;
+
+ while (*p) {
+ if (*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
+ *(q++) = unhex(p + 1);
+ p += 3;
+ } else
+ *(q++) = *(p++);
+ }
+ *q = 0;
+ setenv("CISCO_BANNER", banner, 1);
+
+ free(banner);
+}
+
static void set_script_env(struct openconnect_info *vpninfo)
{
char host[80];
@@ -241,7 +271,7 @@
setenv("VPNGATEWAY", host, 1);
setenv("reason", "connect", 1);
- unsetenv("CISCO_BANNER");
+ set_banner(vpninfo);
unsetenv("CISCO_SPLIT_INC");
unsetenv("CISCO_SPLIT_EXC");
diff -Nru openconnect-2.22/version.c openconnect-2.25/version.c
--- openconnect-2.22/version.c 2010-08-28 12:58:22.000000000 +0100
+++ openconnect-2.25/version.c 2010-05-15 09:23:37.000000000 +0100
@@ -1 +1 @@
-char openconnect_version[] = "v2.22-unknown";
+char openconnect_version[] = "v2.25";
diff -Nru openconnect-2.22/version.sh openconnect-2.25/version.sh
--- openconnect-2.22/version.sh 2010-03-07 22:10:55.000000000 +0000
+++ openconnect-2.25/version.sh 2010-05-15 09:23:37.000000000 +0100
@@ -1,6 +1,6 @@
#!/bin/sh
-v="v2.22"
+v="v2.25"
if tag=`git describe --tags`; then
v="$tag"
Reply to: