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 <d...@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-de...@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-de...@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:dw...@infradead.org">dw...@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"