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 &mdash; 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 &mdash; 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> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; 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> &mdash; all versions from 0.9.7 onwards will work for 
basic connectivity, but see note on DTLS compatibility below.</LI>
+  <LI><B>OpenSSL</B> &mdash; 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 &mdash; 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 &lt;<A 
HREF="mailto:dw...@infradead.org";>dw...@infradead.org</A>&gt;</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 via email to