Simon Josefsson simon at
Wed Aug 5 13:21:24 CEST 2009

Having had some time to read the code, here are some observations:

* The old _gnutls_x509_oid_data2string in lib/x509/common.c is buggy
  since it truncates the string after a NUL (it copies the string using
  strcpy instead of memcpy).  The RES_SIZE length output variable is
  correct though, but output data beyond the NUL will be garbage.

* I can see a few ways to solve the problem:

  1) Make _gnutls_x509_oid_data2string escape NULs as \00 following RFC

  2) Use memcpy instead of strcpy and change the documentation of the
     function to say that the returned string may contain embedded NULs,
     and fix the callers of that function.

  3) Return a RFC 2253 #-style string for these strings.

  4) Return an error when a NUL is encountered.

* Solution 1) and 3) appear to break
  gnutls_x509_crq_get_challenge_password which is used to extract the
  certificate request challenge password.  Password strings are not DNs
  and should not be escaped as per RFC 2253.  If any of the escaped
  characters in RFC 2253 is used in a password (e.g., plus or comma),
  and if _gnutls_x509_oid_data2string escapes them, things will break.
  Further, the gnutls_x509_crq_get_challenge_password API does not use
  zero-terminated strings, so it can handle embedded NULs in password.

  The same problem could apply to DN SAN's and CRL distribution points,
  they are not documented in GnuTLS to be in LDAP escape format
  (although not documented to be in any other format either).

  However, if we really want to use 1) or 3) as the solution, the
  gnutls_x509_crq_get_challenge_password function could be modified to
  use some other code path to yield correct results.

* Solution 2) could work but require that we investigate and possibly
  fix all callers.  The function is used in three places:

    A) By parse_attribute in lib/x509/crq.c which is used by two other

      A.1.) gnutls_x509_crq_get_challenge_password which is OK as per above.

      A.2) gnutls_x509_crq_get_attribute_by_oid.  That function calls
      parse_attribute with RAW=1 which means that
      _gnutls_x509_oid_data2string is never called.

    B) By _gnutls_x509_parse_dn in lib/x509/dn.c.  That function is
       documented to return a LDAP string.  It performs RFC 2253
       escaping by calling str_escape, which doesn't handle embedded

    C) By _gnutls_x509_parse_dn_oid in lib/x509/dn.c.  That function is
       also documented to return a LDAP string, but does not perform RFC
       2253 escaping so it is arguable broken.

  Both _gnutls_x509_parse_dn and _gnutls_x509_parse_dn_oid are used in
  many places in GnuTLS.  Most of them are from APIs that actually are
  documented to return LDAP escaped strings (yay!) so we are relatively
  safe.  However there is one usages that could be problematic:

    A) _gnutls_parse_general_name uses _gnutls_x509_parse_dn to extract
    a DirectoryName type of GeneralName.  The _gnutls_parse_general_name
    function is also by gnutls_x509_crt_get_crl_dist_points to get the
    CRL distribution point.

  I suspect DN SAN's and use of CRL dist points are quite rare.  Anyone
  with more insight?

I believe 2 is actually the right solution but it may not be worth it,
especially considering that there are not likely to be any non-malicious
uses of NUL in a DNs.

So while I prefer 2) since it allows GnuTLS to handle embedded NULs, I
suspect 4) will result in more robust overall design.  The patch could
be as simple as the one below, but I need to do more testing.


--- common.c~	2009-06-02 20:59:32.000000000 +0200
+++ common.c	2009-08-05 13:18:17.000000000 +0200
@@ -1,5 +1,5 @@
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation
  * Author: Nikos Mavrogiannopoulos
@@ -242,6 +242,9 @@
       str[len] = 0;
+      if (strlen (str) != len)
       if (res)
 	_gnutls_str_cpy (res, *res_size, str);
       *res_size = len;
@@ -296,6 +299,8 @@
 	  if (non_printable == 0)
 	      str[len] = 0;
+	      if (strlen (str) != len)
 	      _gnutls_str_cpy (res, *res_size, str);
 	      *res_size = len;

More information about the Gnutls-devel mailing list