]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
allow specification of multiple certificate fingerprints on command-line via --servercert
authorDaniel Lenski <dlenski@gmail.com>
Mon, 25 Jan 2021 07:39:39 +0000 (23:39 -0800)
committerDaniel Lenski <dlenski@gmail.com>
Fri, 5 Feb 2021 17:24:33 +0000 (09:24 -0800)
Server certificates will be accepted if they match *any* of the provided fingerprints.

Behavior with `--servercert` is otherwise unchanged; it still disables system trust
stores, meaning that _only_ certificates matching the provided fingerprints will be
accepted if it is specified one or more times.

This will allow the use of `--servercert` to non-interactively connect to a server which
has a non-trusted certificate and redirects to one or more other servers with non-trusted
certificates. (See #25 for a real case.)

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
main.c
openconnect.8.in

diff --git a/main.c b/main.c
index 31bd5c1d10cabba5ce55810eac30b49ba134a3fb..502bfc82f20aff56273ee3e91815d8d234b27e2a 100644 (file)
--- a/main.c
+++ b/main.c
@@ -92,7 +92,14 @@ static int cookieonly;
 static int allow_stdin_read;
 
 static char *token_filename;
-static char *server_cert = NULL;
+static int allowed_fingerprints;
+
+struct accepted_cert {
+       struct accepted_cert *next;
+       char *fingerprint;
+       char *host;
+       int port;
+} *accepted_certs;
 
 static char *username;
 static char *password;
@@ -849,7 +856,7 @@ static void usage(void)
 #endif
 
        printf("\n%s:\n", _("Server validation"));
-       printf("      --servercert=FINGERPRINT    %s\n", _("Server's certificate SHA1 fingerprint"));
+       printf("      --servercert=FINGERPRINT    %s\n", _("Accept only server certificate with this fingerprint"));
        printf("      --no-system-trust           %s\n", _("Disable default system certificate authorities"));
        printf("      --cafile=FILE               %s\n", _("Cert file for server verification"));
 
@@ -1526,6 +1533,7 @@ int main(int argc, char **argv)
        struct openconnect_info *vpninfo;
        char *urlpath = NULL;
        struct oc_vpn_option *gai;
+       struct accepted_cert *newcert;
        char *ip;
        char *proxy = getenv("https_proxy");
        char *vpnc_script = NULL;
@@ -1696,8 +1704,19 @@ int main(int argc, char **argv)
                        }
                        break;
                case OPT_SERVERCERT:
-                       server_cert = keep_config_arg();
+                       newcert = malloc(sizeof(*newcert));
+                       if (!newcert) {
+                               fprintf(stderr, _("Failed to allocate memory\n"));
+                               exit(1);
+                       }
+                       newcert->next = accepted_certs;
+                       accepted_certs = newcert;
+                       newcert->fingerprint = keep_config_arg();
+                       newcert->host = NULL;
+                       newcert->port = 0;
+
                        openconnect_set_system_trust(vpninfo, 0);
+                       allowed_fingerprints++;
                        break;
                case OPT_RESOLVE:
                        ip = strchr(config_arg, ':');
@@ -2207,47 +2226,41 @@ static void __attribute__ ((format(printf, 3, 4)))
        }
 }
 
-struct accepted_cert {
-       struct accepted_cert *next;
-       char *fingerprint;
-       char *host;
-       int port;
-} *accepted_certs;
-
 static int validate_peer_cert(void *_vpninfo, const char *reason)
 {
        struct openconnect_info *vpninfo = _vpninfo;
        const char *fingerprint;
        struct accepted_cert *this;
 
-#ifdef INSECURE_DEBUGGING
-       if (server_cert && strcasecmp(server_cert, "ACCEPT")) {
-#else
-       if (server_cert) {
-#endif
-               int err = openconnect_check_peer_cert_hash(vpninfo, server_cert);
-
-               if (!err)
-                       return 0;
-
-               if (err < 0)
-                       vpn_progress(vpninfo, PRG_ERR,
-                                    _("Could not calculate hash of server's certificate\n"));
-               else
-                       vpn_progress(vpninfo, PRG_ERR,
-                                    _("Server SSL certificate didn't match: %s\n"),
-                                    openconnect_get_peer_cert_hash(vpninfo));
-
-               return -EINVAL;
-       }
-
        fingerprint = openconnect_get_peer_cert_hash(vpninfo);
 
        for (this = accepted_certs; this; this = this->next) {
-               if (!strcasecmp(this->host, vpninfo->hostname) &&
-                   this->port == vpninfo->port &&
-                   !openconnect_check_peer_cert_hash(vpninfo, this->fingerprint))
+#ifdef INSECURE_DEBUGGING
+               if (this->port == 0 && this->host == NULL && !strcasecmp(this->fingerprint, "ACCEPT")) {
+                       fprintf(stderr, _("Insecurely accepting certificate from VPN server \"%s\" because you ran with --servercert=ACCEPT.\n"),
+                               vpninfo->hostname);
                        return 0;
+               } else
+#endif
+               /* XX: if set by --servercert argument (port 0 and host NULL), accept for any host/port */
+               if ((this->host == NULL || !strcasecmp(this->host, vpninfo->hostname)) &&
+                   (this->port == 0 || this->port == vpninfo->port)) {
+                       int err = openconnect_check_peer_cert_hash(vpninfo, this->fingerprint);
+                       if (!err)
+                               return 0;
+                       else if (err < 0) {
+                                vpn_progress(vpninfo, PRG_ERR,
+                                             _("Could not check server's certificate against %s\n"),
+                                             this->fingerprint);
+                       }
+               }
+       }
+
+       if (allowed_fingerprints) {
+               vpn_progress(vpninfo, PRG_ERR,
+                            _("None of the %d fingerprint(s) specified via --servercert match server's certificate: %s\n"),
+                            allowed_fingerprints, fingerprint);
+               return -EINVAL;
        }
 
        while (1) {
@@ -2263,12 +2276,6 @@ static int validate_peer_cert(void *_vpninfo, const char *reason)
                if (non_inter)
                        return -EINVAL;
 
-#ifdef INSECURE_DEBUGGING
-               if (!strcasecmp(server_cert, "ACCEPT")) {
-                       fprintf(stderr, _("Insecurely accepting because you ran with --servertcert=ACCEPT.\n"));
-                       goto accepted;
-               }
-#endif
                fprintf(stderr, _("Enter '%s' to accept, '%s' to abort; anything else to view: "),
                       _("yes"), _("no"));
 
@@ -2278,9 +2285,6 @@ static int validate_peer_cert(void *_vpninfo, const char *reason)
 
                if (!strcasecmp(response, _("yes"))) {
                        struct accepted_cert *newcert;
-#ifdef INSECURE_DEBUGGING
-               accepted:
-#endif
                        newcert = malloc(sizeof(*newcert));
                        if (newcert) {
                                newcert->next = accepted_certs;
index 374ea1c576cea37c4275e5040e480fb7f5c7acb8..3588f6de78446561daf1ea60eb622b448ee6ba9d 100644 (file)
@@ -554,7 +554,10 @@ to
 instead of using the normal resolver to look it up.
 .TP
 .B \-\-servercert=HASH
-Accept server's SSL certificate only if the provided fingerprint matches.
+Accept server's SSL certificate only if it matches the provided fingerprint.
+This option implies \-\-no\-system\-trust, and may be specified multiple
+times in order to accept multiple possible fingerprints.
+
 The allowed fingerprint types are
 .IR SHA1 ,
 .IR SHA256 ,