[Help-gnutls] Parsing certificate extensions and issuer alt names

Brad Hards bradh at frogmouth.net
Tue Jul 14 11:46:10 CEST 2009


On Monday 13 July 2009 16:33:48 Nikos Mavrogiannopoulos wrote:
> Actually I think it might be much easier to do that inside gnutls by
> extending get_subject_alt_name() to be able to accept the OID as
> parameter to parse the 2.5.29.18 extension as well. Then would be easy
> to submit a gnutls_x509_crt_get_issuer_alt_name that can be added to
> gnutls.
I had a first cut at this. See attached patch.

Thoughts / comments?

Brad

-------------- next part --------------
commit a46a2b6388b96bb4541ee9e1d0515430f6afe618
Author: Brad Hards <bradh at conferta.cuneata.net>
Date:   Tue Jul 14 14:22:49 2009 +1000

    Initial version of issuer altname handling.
    
    Heavily based on the existing subject altname handling, this adds
    some minor refactoring to allow the issuer altname OID to use the
    same basic code.
    
    The changes in the printing code are a bit larger - we don't have
    issuer altname in the certificate request, so it wasn't practical
    to share the code.

diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h
index 2a87cba..e61ef25 100644
--- a/lib/includes/gnutls/x509.h
+++ b/lib/includes/gnutls/x509.h
@@ -198,6 +198,21 @@ extern "C"
 						     void *ret,
 						     size_t * ret_size);
 
+  int gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert,
+					    unsigned int seq, void *ret,
+					    size_t * ret_size,
+					    unsigned int *critical);
+  int gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert,
+					     unsigned int seq, void *ret,
+					     size_t * ret_size,
+					     unsigned int *ret_type,
+					     unsigned int *critical);
+
+  int gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert,
+						     unsigned int seq,
+						     void *ret,
+						     size_t * ret_size);
+
   int gnutls_x509_crt_get_ca_status (gnutls_x509_crt_t cert,
 				     unsigned int *critical);
   int gnutls_x509_crt_get_basic_constraints (gnutls_x509_crt_t cert,
diff --git a/lib/x509/output.c b/lib/x509/output.c
index 38200bb..27617d3 100644
--- a/lib/x509/output.c
+++ b/lib/x509/output.c
@@ -655,6 +655,148 @@ print_san (gnutls_string * str, const char *prefix, int type,
 }
 
 static void
+print_ian (gnutls_string * str, const char *prefix, int type,
+	   cert_type_t cert)
+{
+  unsigned int ian_idx;
+  char str_ip[64];
+  char *p;
+
+  for (ian_idx = 0;; ian_idx++)
+    {
+      char *buffer = NULL;
+      size_t size = 0;
+      int err;
+
+      if (type == TYPE_CRT)
+	err =
+	  gnutls_x509_crt_get_issuer_alt_name (cert.crt, ian_idx, buffer,
+						&size, NULL);
+      else
+	return;
+
+      if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+	break;
+      if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+	{
+	  addf (str, "error: get_issuer_alt_name: %s\n",
+		gnutls_strerror (err));
+	  return;
+	}
+
+      buffer = gnutls_malloc (size);
+      if (!buffer)
+	{
+	  addf (str, "error: malloc: %s\n",
+		gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
+	  return;
+	}
+
+      if (type == TYPE_CRT)
+	err =
+	  gnutls_x509_crt_get_issuer_alt_name (cert.crt, ian_idx, buffer,
+						&size, NULL);
+
+      if (err < 0)
+	{
+	  gnutls_free (buffer);
+	  addf (str, "error: get_issuer_alt_name2: %s\n",
+		gnutls_strerror (err));
+	  return;
+	}
+
+      switch (err)
+	{
+	case GNUTLS_SAN_DNSNAME:
+	  addf (str, "%s\t\t\tDNSname: %.*s\n", prefix, (int) size, buffer);
+	  break;
+
+	case GNUTLS_SAN_RFC822NAME:
+	  addf (str, "%s\t\t\tRFC822name: %.*s\n", prefix, (int) size, buffer);
+	  break;
+
+	case GNUTLS_SAN_URI:
+	  addf (str, "%s\t\t\tURI: %.*s\n", prefix, (int) size, buffer);
+	  break;
+
+	case GNUTLS_SAN_IPADDRESS:
+	  p = ip_to_string (buffer, size, str_ip, sizeof (str_ip));
+	  if (p == NULL)
+	    p = ERROR_STR;
+	  addf (str, "%s\t\t\tIPAddress: %s\n", prefix, p);
+	  break;
+
+	case GNUTLS_SAN_DN:
+	  addf (str, "%s\t\t\tdirectoryName: %.*s\n", prefix,
+		(int) size, buffer);
+	  break;
+
+	case GNUTLS_SAN_OTHERNAME:
+	  {
+	    char *oid = NULL;
+	    size_t oidsize;
+
+	    oidsize = 0;
+	    if (type == TYPE_CRT)
+	      err = gnutls_x509_crt_get_issuer_alt_othername_oid
+		(cert.crt, ian_idx, oid, &oidsize);
+
+	    if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+	      {
+		gnutls_free (buffer);
+		addf (str, "error: get_issuer_alt_othername_oid: %s\n",
+		      gnutls_strerror (err));
+		return;
+	      }
+
+	    oid = gnutls_malloc (oidsize);
+	    if (!oid)
+	      {
+		gnutls_free (buffer);
+		addf (str, "error: malloc: %s\n",
+		      gnutls_strerror (GNUTLS_E_MEMORY_ERROR));
+		return;
+	      }
+
+	    if (type == TYPE_CRT)
+	      err = gnutls_x509_crt_get_issuer_alt_othername_oid
+		(cert.crt, ian_idx, oid, &oidsize);
+	    if (err < 0)
+	      {
+		gnutls_free (buffer);
+		gnutls_free (oid);
+		addf (str, "error: get_issuer_alt_othername_oid2: %s\n",
+		      gnutls_strerror (err));
+		return;
+	      }
+
+	    if (err == GNUTLS_SAN_OTHERNAME_XMPP)
+	      addf (str, _("%s\t\t\tXMPP Address: %.*s\n"), prefix,
+		    (int) size, buffer);
+	    else
+	      {
+		addf (str, _("%s\t\t\totherName OID: %.*s\n"), prefix,
+		      (int) oidsize, oid);
+		addf (str, _("%s\t\t\totherName DER: "), prefix);
+		hexprint (str, buffer, size);
+		addf (str, _("\n%s\t\t\totherName ASCII: "), prefix);
+		asciiprint (str, buffer, size);
+		addf (str, "\n");
+	      }
+	    gnutls_free (oid);
+	  }
+	  break;
+
+	default:
+	  addf (str, "error: unknown Issuer AltName\n");
+	  break;
+	}
+
+      gnutls_free (buffer);
+    }
+}
+
+static void
 print_extensions (gnutls_string * str, const char *prefix, int type,
 		  cert_type_t cert)
 {
@@ -666,6 +808,7 @@ print_extensions (gnutls_string * str, const char *prefix, int type,
       size_t sizeof_oid = sizeof (oid);
       int critical;
       size_t san_idx = 0;
+      size_t ian_idx = 0;
       size_t proxy_idx = 0;
       size_t basic_idx = 0;
       size_t keyusage_idx = 0;
@@ -796,6 +939,21 @@ print_extensions (gnutls_string * str, const char *prefix, int type,
 
 	  san_idx++;
 	}
+      else if (strcmp (oid, "2.5.29.18") == 0)
+	{
+	  if (ian_idx)
+	    {
+	      addf (str, "error: more than one Issuer AltName extension\n");
+	      continue;
+	    }
+
+	  addf (str, _("%s\t\tIssuer Alternative Name (%s):\n"), prefix,
+		critical ? _("critical") : _("not critical"));
+
+	  print_ian (str, prefix, type, cert);
+
+	  ian_idx++;
+	}
       else if (strcmp (oid, "2.5.29.31") == 0)
 	{
 	  if (crldist_idx)
diff --git a/lib/x509/x509.c b/lib/x509/x509.c
index 048ff89..ade73e3 100644
--- a/lib/x509/x509.c
+++ b/lib/x509/x509.c
@@ -1079,10 +1079,10 @@ _gnutls_parse_general_name (ASN1_TYPE src, const char *src_name,
 }
 
 static int
-get_subject_alt_name (gnutls_x509_crt_t cert,
-		      unsigned int seq, void *ret,
-		      size_t * ret_size, unsigned int *ret_type,
-		      unsigned int *critical, int othername_oid)
+get_alt_name(gnutls_x509_crt_t cert, const char *extension_id,
+             unsigned int seq, void *ret,
+	     size_t * ret_size, unsigned int *ret_type,
+	     unsigned int *critical, int othername_oid)
 {
   int result;
   gnutls_datum_t dnsname;
@@ -1101,7 +1101,7 @@ get_subject_alt_name (gnutls_x509_crt_t cert,
     *ret_size = 0;
 
   if ((result =
-       _gnutls_x509_crt_get_extension (cert, "2.5.29.17", 0, &dnsname,
+       _gnutls_x509_crt_get_extension (cert, extension_id, 0, &dnsname,
 				       critical)) < 0)
     {
       return result;
@@ -1113,8 +1113,20 @@ get_subject_alt_name (gnutls_x509_crt_t cert,
       return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
     }
 
-  result = asn1_create_element
-    (_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2);
+  if (strncmp("2.5.29.18", extension_id, 9) == 0) 
+    {
+      result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.IssuerAltName", &c2);
+    }
+  else if (strncmp("2.5.29.17", extension_id, 9) == 0)
+    {
+      result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2);
+    }
+  else
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+
   if (result != ASN1_SUCCESS)
     {
       gnutls_assert ();
@@ -1188,7 +1200,49 @@ gnutls_x509_crt_get_subject_alt_name (gnutls_x509_crt_t cert,
 				      size_t * ret_size,
 				      unsigned int *critical)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, NULL, critical, 0);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, critical, 0);
+}
+
+/**
+  * gnutls_x509_crt_get_issuer_alt_name - Get certificate's issuer alternative name, if any
+  * @cert: should contain a #gnutls_x509_crt_t structure
+  * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+  * @ret: is the place where the alternative name will be copied to
+  * @ret_size: holds the size of ret.
+  * @critical: will be non zero if the extension is marked as critical (may be null)
+  *
+  * This function will return the issuer alternative names, contained in the
+  * given certificate.
+  *
+  * This is specified in X509v3 Certificate Extensions.  GNUTLS will
+  * return the Isssuer Alternative name (2.5.29.18), or a negative error code.
+  *
+  * When the SAN type is otherName, it will extract the data in the
+  * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned.
+  * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get
+  * the corresponding OID and the "virtual" SAN types (e.g.,
+  * %GNUTLS_SAN_OTHERNAME_XMPP).
+  *
+  * If an otherName OID is known, the data will be decoded.  Otherwise
+  * the returned data will be DER encoded, and you will have to decode
+  * it yourself.  Currently, only the RFC 3920 id-on-xmppAddr Issuer AltName
+  * is recognized.
+  *
+  * Returns: the alternative issuer name type on success, one of the
+  *   enumerated #gnutls_x509_subject_alt_name_t.  It will return
+  *   %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough
+  *   to hold the value.  In that case @ret_size will be updated with
+  *   the required size.  If the certificate does not have an
+  *   Alternative name with the specified sequence number then
+  *   %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+  **/
+int
+gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert,
+				      unsigned int seq, void *ret,
+				      size_t * ret_size,
+				      unsigned int *critical)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, critical, 0);
 }
 
 /**
@@ -1222,8 +1276,41 @@ gnutls_x509_crt_get_subject_alt_name2 (gnutls_x509_crt_t cert,
 				       unsigned int *ret_type,
 				       unsigned int *critical)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, ret_type, critical,
-			       0);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, ret_type, critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_name2 - Get certificate issuer's alternative name, if any
+ * @cert: should contain a #gnutls_x509_crt_t structure
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the alternative name will be copied to
+ * @ret_size: holds the size of ret.
+ * @ret_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @critical: will be non zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the alternative names, contained in the
+ * given certificate. It is the same as
+ * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it
+ * will return the type of the alternative name in @ret_type even if
+ * the function fails for some reason (i.e.  the buffer provided is
+ * not enough).
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ *   enumerated #gnutls_x509_subject_alt_name_t.  It will return
+ *   %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough
+ *   to hold the value.  In that case @ret_size will be updated with
+ *   the required size.  If the certificate does not have an
+ *   Alternative name with the specified sequence number then
+ *   %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert,
+				       unsigned int seq, void *ret,
+				       size_t * ret_size,
+				       unsigned int *ret_type,
+				       unsigned int *critical)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, ret_type, critical, 0);
 }
 
 /**
@@ -1257,7 +1344,41 @@ gnutls_x509_crt_get_subject_alt_othername_oid (gnutls_x509_crt_t cert,
 					       unsigned int seq,
 					       void *ret, size_t * ret_size)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, NULL, NULL, 1);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, NULL, 1);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_othername_oid - Get Issuer AltName otherName OID
+ * @cert: should contain a #gnutls_x509_crt_t structure
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the otherName OID will be copied to
+ * @ret_size: holds the size of ret.
+ *
+ * This function will extract the type OID of an otherName Subject
+ * Alternative Name, contained in the given certificate, and return
+ * the type as an enumerated element.
+ *
+ * This function is only useful if
+ * gnutls_x509_crt_get_issuer_alt_name() returned
+ * %GNUTLS_SAN_OTHERNAME.
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ * enumerated gnutls_x509_subject_alt_name_t.  For supported OIDs, it
+ * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types,
+ * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for
+ * unknown OIDs.  It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if
+ * @ret_size is not large enough to hold the value.  In that case
+ * @ret_size will be updated with the required size.  If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number and with the otherName type then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert,
+					       unsigned int seq,
+					       void *ret, size_t * ret_size)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, NULL, 1);
 }
 
 /**


More information about the Gnutls-help mailing list