]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Merged --remote-cert-ku, --remote-cert-eku, and
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 12 Nov 2005 08:26:57 +0000 (08:26 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 12 Nov 2005 08:26:57 +0000 (08:26 +0000)
--remote-cert-tls from Alon's branch:
svn merge -r 793:796 $SO/contrib/alon/BETA21/openvpn .

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@797 e7ae566f-a301-0410-adde-c780ea21d3b5

easy-rsa/2.0/openssl.cnf
init.c
openvpn.8
options.c
options.h
ssl.c
ssl.h

index 7fedebe48ecd68a543e2ce92b5aabe3feb54a282..b430b8354e98343d56b7d53ec4353618e2750350 100755 (executable)
@@ -207,6 +207,8 @@ nsCertType                  = server
 nsComment                      = "OpenSSL Generated Server Certificate"
 subjectKeyIdentifier=hash
 authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
 
 [ v3_req ]
 
diff --git a/init.c b/init.c
index bd90b803b64f71904906b9f71016efa580fa985d..0c9a509db7b0f5fe425e5f7e1bf88d71597b3c32 100644 (file)
--- a/init.c
+++ b/init.c
@@ -1445,6 +1445,8 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
   to.verify_x509name = options->tls_remote;
   to.crl_file = options->crl_file;
   to.ns_cert_type = options->ns_cert_type;
+  memmove (to.remote_cert_ku, options->remote_cert_ku, sizeof (to.remote_cert_ku));
+  to.remote_cert_eku = options->remote_cert_eku;
   to.es = c->c2.es;
 
 #ifdef ENABLE_DEBUG
index 0c634a961fde7a04b38b21afdae615652c2ead10..7d145242b64f16b1aff104d6d6553d4c97e2ec67 100644 (file)
--- a/openvpn.8
+++ b/openvpn.8
@@ -225,6 +225,9 @@ openvpn \- secure IP tunnel daemon.
 [\ \fB\-\-remap\-usr1\fR\ \fIsignal\fR\ ]
 [\ \fB\-\-remote\-random\fR\ ]
 [\ \fB\-\-remote\fR\ \fIhost\ [port]\fR\ ]
+[\ \fB\-\-remote\-cert\-ku\ \fIv...\fR\ ]
+[\ \fB\-\-remote\-cert\-eku\ \fIoid\fR\ ]
+[\ \fB\-\-remote\-cert\-tls\ \fIt\fR\ ]
 [\ \fB\-\-reneg\-bytes\fR\ \fIn\fR\ ]
 [\ \fB\-\-reneg\-pkts\fR\ \fIn\fR\ ]
 [\ \fB\-\-reneg\-sec\fR\ \fIn\fR\ ]
@@ -4044,6 +4047,58 @@ or
 .B --tls-verify.
 .\"*********************************************************
 .TP
+.B --remote-cert-ku v...
+Require that peer certificate was signed with an explicit
+.B key usage.
+
+This is useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The key usage should be encoded in hex, more than one key
+usage can be specified.
+.\"*********************************************************
+.TP
+.B --remote-cert-eku oid
+Require that peer certificate was signed with an explicit
+.B extended key usage.
+
+This is useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The extended key usage should be encoded in oid notation, or
+OpenSSL symbolic representation.
+.\"*********************************************************
+.TP
+.B --remote-cert-tls client|server
+Require that peer certificate was signed with an explicit
+.B key usage
+and
+.B extended key usage
+based on TLS rules.
+
+This is a useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+The
+.B --remote-cert-tls client
+option is equivalent to
+.B --remote-cert-ku 80 08 88 --remote-cert-eku \fB"TLS Web Client Authentication" 
+
+The
+.B --remote-cert-tls server
+option is equivalent to
+.B --remote-cert-ku a0 08 --remote-cert-eku \fB"TLS Web Server Authentication"
+
+This is an important security precaution to protect against
+a man-in-the-middle attack where an authorized client
+attempts to connect to another client by impersonating the server.
+The attack is easily prevented by having clients verify
+the server certificate using any one of
+.B --remote-cert-tls, --tls-remote,
+or
+.B --tls-verify.
+.\"*********************************************************
+.TP
 .B --crl-verify crl
 Check peer certificate against the file
 .B crl
index 466ba137e02be46ae3278f4786ed1e2dc5b213a3..97fb13066510993bb1cf00c57013046532ee504f 100644 (file)
--- a/options.c
+++ b/options.c
@@ -463,6 +463,17 @@ static const char usage_message[] =
   "                  of verification.\n"
   "--ns-cert-type t: Require that peer certificate was signed with an explicit\n"
   "                  nsCertType designation t = 'client' | 'server'.\n"
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+  "--remote-cert-ku v ... : Require that the peer certificate was signed with\n"
+  "                  explicit key usage, you can specify more than one value.\n"
+  "                  value should be given in hex format.\n"
+  "--remote-cert-eku oid : Require that the peer certificate was signed with\n"
+  "                  explicit extended key usage. Extended key usage can be encoded\n"
+  "                  as on object identifier or OpenSSL string representation.\n"
+  "--remote-cert-tls t: Require that peer certificate was signed with explicit\n"
+  "                  key usage and extended key usage based on TLS rules.\n"
+  "                  t = 'client | 'server'.\n"
+#endif                         /* OPENSSL_VERSION_NUMBER */
 #endif                         /* USE_SSL */
 #ifdef ENABLE_PKCS11
   "\n"
@@ -1197,6 +1208,12 @@ show_settings (const struct options *o)
   SHOW_STR (tls_remote);
   SHOW_STR (crl_file);
   SHOW_INT (ns_cert_type);
+  {
+    int i;
+    for (i=0;i<MAX_PARMS;i++)
+      SHOW_INT (remote_cert_ku[i]);
+  }
+  SHOW_STR (remote_cert_eku);
 
   SHOW_INT (tls_timeout);
 
@@ -1813,6 +1830,8 @@ options_postprocess (struct options *options, bool first_time)
       MUST_BE_UNDEF (crl_file);
       MUST_BE_UNDEF (key_method);
       MUST_BE_UNDEF (ns_cert_type);
+      MUST_BE_UNDEF (remote_cert_ku[0]);
+      MUST_BE_UNDEF (remote_cert_eku);
 #ifdef ENABLE_PKCS11
       MUST_BE_UNDEF (pkcs11_providers[0]);
       MUST_BE_UNDEF (pkcs11_sign_mode[0]);
@@ -4786,6 +4805,45 @@ add_option (struct options *options,
          goto err;
        }
     }
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+  else if (streq (p[0], "remote-cert-ku"))
+    {
+      int j;
+
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+       sscanf (p[j], "%x", &(options->remote_cert_ku[j-1]));
+    }
+  else if (streq (p[0], "remote-cert-eku") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->remote_cert_eku = p[1];
+    }
+  else if (streq (p[0], "remote-cert-tls") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+
+      if (streq (p[1], "server"))
+       {
+         options->remote_cert_ku[0] = 0xa0;
+         options->remote_cert_ku[1] = 0x08;
+         options->remote_cert_eku = "TLS Web Server Authentication";
+       }
+      else if (streq (p[1], "client"))
+       {
+         options->remote_cert_ku[0] = 0x80;
+         options->remote_cert_ku[1] = 0x08;
+         options->remote_cert_ku[2] = 0x88;
+         options->remote_cert_eku = "TLS Web Client Authentication";
+       }
+      else
+       {
+         msg (msglevel, "--remote-cert-tls must be 'client' or 'server'");
+         goto err;
+       }
+    }
+#endif /* OPENSSL_VERSION_NUMBER */
   else if (streq (p[0], "tls-timeout") && p[1])
     {
       VERIFY_PERMISSION (OPT_P_TLS_PARMS);
index 6ae6aee2e6a6a8ba50095bdd6787499db249e8bd..6d7b7858b3a6d954e8ebba9ab30feb1c49e38aea 100644 (file)
--- a/options.h
+++ b/options.h
@@ -392,6 +392,8 @@ struct options
 #endif
 
   int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
+  unsigned remote_cert_ku[MAX_PARMS];
+  const char *remote_cert_eku;
   const char *pkcs11_providers[MAX_PARMS];
   const char *pkcs11_sign_mode[MAX_PARMS];
   const char *pkcs11_slot_type;
diff --git a/ssl.c b/ssl.c
index 2b78bd033d1f83d1675c420538dd479d4616194a..d76cda6662a70d28f8e56b02d2a776022daec4e0 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -389,6 +389,91 @@ set_common_name (struct tls_session *session, const char *common_name)
     }
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+bool verify_cert_eku (X509 *x509, const char * const expected_oid) {
+
+       EXTENDED_KEY_USAGE *eku = NULL;
+       bool fFound = false;
+
+       if ((eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i (x509, NID_ext_key_usage, NULL, NULL)) == NULL) {
+               msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
+       }
+       else {
+               int i;
+
+               msg (D_HANDSHAKE, "Validating certificate extended key usage");
+               for(i = 0; !fFound && i < sk_ASN1_OBJECT_num (eku); i++) {
+                       ASN1_OBJECT *oid = sk_ASN1_OBJECT_value (eku, i);
+                       char szOid[1024];
+
+                       if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 0) != -1) {
+                               msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", szOid, expected_oid);
+                               if (!strcmp (expected_oid, szOid)) {
+                                       fFound = true;
+                               }
+                       }
+                       if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 1) != -1) {
+                               msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", szOid, expected_oid);
+                               if (!strcmp (expected_oid, szOid)) {
+                                       fFound = true;
+                               }
+                       }
+               }
+       }
+
+       if (eku != NULL) {
+               sk_ASN1_OBJECT_pop_free (eku, ASN1_OBJECT_free);
+       }
+
+       return fFound;
+}
+
+bool verify_cert_ku (X509 *x509, const unsigned * const expected_ku, int expected_len) {
+
+       ASN1_BIT_STRING *ku = NULL;
+       bool fFound = false;
+
+       if ((ku = (ASN1_BIT_STRING *)X509_get_ext_d2i (x509, NID_key_usage, NULL, NULL)) == NULL) {
+               msg (D_HANDSHAKE, "Certificate does not have key usage extension");
+       }
+       else {
+               unsigned nku = 0;
+               int i;
+               for (i=0;i<8;i++) {
+                       if (ASN1_BIT_STRING_get_bit (ku, i)) {
+                               nku |= 1<<(7-i);
+                       }
+               }
+
+               /*
+                * Fixup if no LSB bits
+                */
+               if ((nku & 0xff) == 0) {
+                       nku >>= 8;
+               }
+
+               msg (D_HANDSHAKE, "Validating certificate key usage");
+               for (i=0;!fFound && i<expected_len;i++) {
+                       if (expected_ku[i] != 0) {
+                               msg (D_HANDSHAKE, "++ Certificate has key usage  %04x, expects %04x", nku, expected_ku[i]);
+
+                               if (nku == expected_ku[i]) {
+                                       fFound = true;
+                               }
+                       }
+               }
+       }
+
+       if (ku != NULL) {
+               ASN1_BIT_STRING_free (ku);
+       }
+
+       return fFound;
+}
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
 /*
  * nsCertType checking
  */
@@ -506,6 +591,38 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
        }
     }
 
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+  /* verify certificate ku */
+  if (opt->remote_cert_ku[0] != 0 &&  ctx->error_depth == 0)
+    {
+      if (verify_cert_ku (ctx->current_cert, opt->remote_cert_ku, MAX_PARMS))
+       {
+         msg (D_HANDSHAKE, "VERIFY KU OK");
+       }
+        else
+        {
+         msg (D_HANDSHAKE, "VERIFY KU ERROR");
+          goto err;            /* Reject connection */
+       }
+    }
+
+  /* verify certificate eku */
+  if (opt->remote_cert_eku != NULL && ctx->error_depth == 0)
+    {
+      if (verify_cert_eku (ctx->current_cert, opt->remote_cert_eku))
+        {
+         msg (D_HANDSHAKE, "VERIFY EKU OK");
+       }
+      else
+       {
+         msg (D_HANDSHAKE, "VERIFY EKU ERROR");
+          goto err;            /* Reject connection */
+       }
+    }
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
   /* verify X509 name or common name against --tls-remote */
   if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && ctx->error_depth == 0)
     {
diff --git a/ssl.h b/ssl.h
index 0e41690d312ac1142ef5345ec05d004156ea9222..4acb25922ac6a7c05446bee65e40373538c08683 100644 (file)
--- a/ssl.h
+++ b/ssl.h
@@ -410,6 +410,8 @@ struct tls_options
   const char *verify_x509name;
   const char *crl_file;
   int ns_cert_type;
+  unsigned remote_cert_ku[MAX_PARMS];
+  const char *remote_cert_eku;
 
   /* allow openvpn config info to be
      passed over control channel */