]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added function to verify and extract the username
authorAdriaan de Jong <dejong@fox-it.com>
Thu, 30 Jun 2011 09:43:38 +0000 (11:43 +0200)
committerDavid Sommerseth <davids@redhat.com>
Fri, 21 Oct 2011 12:51:45 +0000 (14:51 +0200)
Signed-off-by: Adriaan de Jong <dejong@fox-it.com>
Acked-by: James Yonan <james@openvpn.net>
Signed-off-by: David Sommerseth <davids@redhat.com>
ssl.c
ssl_verify_backend.h
ssl_verify_openssl.c

diff --git a/ssl.c b/ssl.c
index e600ca7bbe6b285ab6f77a5f5e5e2d0b60a706bd..17ef478fdf077ea7267df078b361859d3d7afca4 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -296,114 +296,6 @@ ssl_put_auth_challenge (const char *cr_str)
 
 #endif
 
-/*
- * Extract a field from an X509 subject name.
- *
- * Example:
- *
- * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net
- *
- * The common name is 'Test-CA'
- *
- * Return true on success, false on error (insufficient buffer size in 'out'
- * to contain result is grounds for error).
- */
-static bool
-extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int size)
-{
-  int lastpos = -1;
-  int tmp = -1;
-  X509_NAME_ENTRY *x509ne = 0;
-  ASN1_STRING *asn1 = 0;
-  unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
-  int nid = OBJ_txt2nid((char *)field_name);
-
-  ASSERT (size > 0);
-  *out = '\0';
-  do {
-    lastpos = tmp;
-    tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos);
-  } while (tmp > -1);
-
-  /* Nothing found */
-  if (lastpos == -1)
-    return false;
-
-  x509ne = X509_NAME_get_entry(x509, lastpos);
-  if (!x509ne)
-    return false;
-
-  asn1 = X509_NAME_ENTRY_get_data(x509ne);
-  if (!asn1)
-    return false;
-  tmp = ASN1_STRING_to_UTF8(&buf, asn1);
-  if (tmp <= 0)
-    return false;
-
-  strncpynt(out, (char *)buf, size);
-
-  {
-    const bool ret = (strlen ((char *)buf) < size);
-    OPENSSL_free (buf);
-    return ret;
-  }
-}
-
-#ifdef ENABLE_X509ALTUSERNAME
-static
-bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size)
-{
-  bool retval = false;
-  X509_EXTENSION *pExt;
-  char *buf = 0;
-  int length = 0;
-  GENERAL_NAMES *extensions;
-  int nid = OBJ_txt2nid(fieldname);
-
-  extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL);
-  if ( extensions )
-    {
-      int numalts;
-      int i;
-      /* get amount of alternatives,
-       * RFC2459 claims there MUST be at least
-       * one, but we don't depend on it...
-       */
-
-      numalts = sk_GENERAL_NAME_num(extensions);
-
-      /* loop through all alternatives */
-      for (i=0; i<numalts; i++)
-        {
-          /* get a handle to alternative name number i */
-          const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i );
-
-          switch (name->type)
-            {
-              case GEN_EMAIL:
-                ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5);
-                if ( strlen (buf) != name->d.ia5->length )
-                  {
-                    msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero");
-                    OPENSSL_free (buf);
-                  } else {
-                    strncpynt(out, buf, size);
-                    OPENSSL_free(buf);
-                    retval = true;
-                  }
-                break;
-              default:
-                msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i",
-                     name->type);
-                break;
-            }
-          }
-        sk_GENERAL_NAME_free (extensions);
-    }
-  return retval;
-}
-#endif /* ENABLE_X509ALTUSERNAME */
-
 #ifdef ENABLE_X509_TRACK
 /*
  * setenv_x509_track function -- save X509 fields to environment,
@@ -740,21 +632,7 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth)
   string_replace_leading (subject, '-', '_');
 
   /* extract the username (default is CN) */
-#ifdef ENABLE_X509ALTUSERNAME
-  if (strncmp("ext:",x509_username_field,4) == 0)
-    {
-      if (!extract_x509_extension (ctx->current_cert, x509_username_field+4, common_name, sizeof(common_name)))
-        {
-          msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s extension from X509 subject string ('%s') "
-                             "-- note that the username length is limited to %d characters",
-                             x509_username_field+4,
-                             subject,
-                             TLS_USERNAME_LEN);
-          goto err;
-        }
-    } else
-#endif
-  if (!extract_x509_field_ssl (X509_get_subject_name (cert), x509_username_field, common_name, sizeof(common_name)))
+  if (verify_get_username (common_name, TLS_USERNAME_LEN, x509_username_field, cert))
     {
       if (!cert_depth)
         {
@@ -768,7 +646,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth)
         }
     }
 
-
   string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags);
 
 #if 0 /* print some debugging info */
index 31b52104578cf663379e12f925f5bdf2ac332db5..82109c86d6d614918e9cd55f5b708190f7fbabec 100644 (file)
@@ -84,4 +84,20 @@ void cert_hash_remember (struct tls_session *session, const int cert_depth,
  */
 bool verify_get_subject (char **subject, x509_cert_t *cert);
 
+/*
+ * Retrieve the certificate's username from the specified field.
+ *
+ * If the field is prepended with ext: and ENABLE_X509ALTUSERNAME is enabled,
+ * it will be loaded from an X.509 extension
+ *
+ * @param cn                   Buffer to return the common name in.
+ * @param cn_len               Length of the cn buffer.
+ * @param x509_username_field  Name of the field to load from
+ * @param cert                 Certificate to retrieve the common name from.
+ *
+ * @return             \c 1 on failure, \c 0 on success
+ */
+bool verify_get_username (char *common_name, int cn_len,
+    char * x509_username_field, X509 *peer_cert);
+
 #endif /* SSL_VERIFY_BACKEND_H_ */
index 64b71c398b8a0825e113ee13219b7bb2bc42907b..10374740ef0a3742cc7355e5676d0f5adc10ef4c 100644 (file)
@@ -82,3 +82,130 @@ verify_get_subject (char **subject, X509 *cert)
 
   return 0;
 }
+
+#ifdef ENABLE_X509ALTUSERNAME
+static
+bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size)
+{
+  bool retval = false;
+  X509_EXTENSION *pExt;
+  char *buf = 0;
+  int length = 0;
+  GENERAL_NAMES *extensions;
+  int nid = OBJ_txt2nid(fieldname);
+
+  extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL);
+  if ( extensions )
+    {
+      int numalts;
+      int i;
+      /* get amount of alternatives,
+       * RFC2459 claims there MUST be at least
+       * one, but we don't depend on it...
+       */
+
+      numalts = sk_GENERAL_NAME_num(extensions);
+
+      /* loop through all alternatives */
+      for (i=0; i<numalts; i++)
+        {
+          /* get a handle to alternative name number i */
+          const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i );
+
+          switch (name->type)
+            {
+              case GEN_EMAIL:
+                ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5);
+                if ( strlen (buf) != name->d.ia5->length )
+                  {
+                    msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero");
+                    OPENSSL_free (buf);
+                  } else {
+                    strncpynt(out, buf, size);
+                    OPENSSL_free(buf);
+                    retval = true;
+                  }
+                break;
+              default:
+                msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i",
+                     name->type);
+                break;
+            }
+          }
+        sk_GENERAL_NAME_free (extensions);
+    }
+  return retval;
+}
+#endif /* ENABLE_X509ALTUSERNAME */
+
+/*
+ * Extract a field from an X509 subject name.
+ *
+ * Example:
+ *
+ * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net
+ *
+ * The common name is 'Test-CA'
+ *
+ * Return true on success, false on error (insufficient buffer size in 'out'
+ * to contain result is grounds for error).
+ */
+static bool
+extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out,
+    int size)
+{
+  int lastpos = -1;
+  int tmp = -1;
+  X509_NAME_ENTRY *x509ne = 0;
+  ASN1_STRING *asn1 = 0;
+  unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
+  int nid = OBJ_txt2nid((char *)field_name);
+
+  ASSERT (size > 0);
+  *out = '\0';
+  do {
+    lastpos = tmp;
+    tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos);
+  } while (tmp > -1);
+
+  /* Nothing found */
+  if (lastpos == -1)
+    return false;
+
+  x509ne = X509_NAME_get_entry(x509, lastpos);
+  if (!x509ne)
+    return false;
+
+  asn1 = X509_NAME_ENTRY_get_data(x509ne);
+  if (!asn1)
+    return false;
+  tmp = ASN1_STRING_to_UTF8(&buf, asn1);
+  if (tmp <= 0)
+    return false;
+
+  strncpynt(out, (char *)buf, size);
+
+  {
+    const bool ret = (strlen ((char *)buf) < size);
+    OPENSSL_free (buf);
+    return ret;
+  }
+}
+
+bool
+verify_get_username (char *common_name, int cn_len,
+    char * x509_username_field, X509 *peer_cert)
+{
+#ifdef ENABLE_X509ALTUSERNAME
+  if (strncmp("ext:",x509_username_field,4) == 0)
+    {
+      if (!extract_x509_extension (peer_cert, x509_username_field+4, common_name, cn_len))
+       return true;
+    } else
+#endif
+  if (!extract_x509_field_ssl (X509_get_subject_name (peer_cert),
+      x509_username_field, common_name, cn_len))
+      return true;
+
+  return false;
+}