Add Kludge for RegTP sillyness.
authorWerner Koch <wk@gnupg.org>
Tue, 21 Mar 2006 09:56:47 +0000 (09:56 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 21 Mar 2006 09:56:47 +0000 (09:56 +0000)
NEWS
TODO
sm/ChangeLog
sm/certchain.c
sm/gpgsm.h
sm/qualified.c

diff --git a/NEWS b/NEWS
index 38dee1e..0d520f3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,9 @@ Noteworthy changes in version 1.9.21
 
  * Cards are not anymore reseted at the end of a connection. 
 
+ * [gpgsm] Kludge to allow use of Bundesnetzagentur issued
+   certificates.
+
 
 Noteworthy changes in version 1.9.20 (2005-12-20)
 -------------------------------------------------
diff --git a/TODO b/TODO
index 85e08ed..6033d91 100644 (file)
--- a/TODO
+++ b/TODO
@@ -18,7 +18,9 @@ might want to have an agent context for each service request
 
 * sm/certchain.c
 ** When a certificate chain was sucessfully verified, make ephemeral certs used  in this chain permanent.
-
+** Try to keep certificate references somewhere
+  This will help with some of our caching code.  We also need to test
+  that cachining; in particular "regtp_ca_chainlen".
 
 * sm/decrypt.c
 ** replace leading zero in integer hack by a cleaner solution
index 764faa6..0250596 100644 (file)
@@ -1,3 +1,12 @@
+2006-03-21  Werner Koch  <wk@g10code.com>
+
+       * certchain.c (get_regtp_ca_info): New.
+       (allowed_ca): Use it.
+
+2006-03-20  Werner Koch  <wk@g10code.com>
+
+       * qualified.c (gpgsm_is_in_qualified_list): New optional arg COUNTRY.
+
 2006-02-17  Werner Koch  <wk@g10code.com>
 
        * call-dirmngr.c (start_dirmngr): Print name of dirmngr to be started.
index 0c14fc8..44d72ef 100644 (file)
@@ -1,5 +1,6 @@
 /* certchain.c - certificate chain validation
- * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "i18n.h"
 
 
+static int get_regtp_ca_info (ksba_cert_t cert, int *chainlen);
+
+
+
 /* If LISTMODE is true, print FORMAT using LISTMODE to FP.  If
    LISTMODE is false, use the string to print an log_info or, if
    IS_ERROR is true, and log_error. */
@@ -128,6 +133,11 @@ allowed_ca (ksba_cert_t cert, int *chainlen, int listmode, FILE *fp)
     return err;
   if (!flag)
     {
+      if (get_regtp_ca_info (cert, chainlen))
+        {
+          return 0; /* RegTP issued certificate. */
+        }
+
       do_list (1, listmode, fp,_("issuer certificate is not marked as a CA"));
       return gpg_error (GPG_ERR_BAD_CA_CERT);
     }
@@ -267,7 +277,7 @@ check_cert_policy (ksba_cert_t cert, int listmode, FILE *fplist)
 }
 
 
-/* Helper fucntion for find_up.  This resets the key handle and search
+/* Helper function for find_up.  This resets the key handle and search
    for an issuer ISSUER with a subjectKeyIdentifier of KEYID.  Returns
    0 obn success or -1 when not found. */
 static int
@@ -796,7 +806,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
                 {
                   /* Need to consult the list of root certificates for
                      qualified signatures. */
-                  err = gpgsm_is_in_qualified_list (ctrl, subject_cert);
+                  err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL);
                   if (!err)
                     is_qualified = 1;
                   else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@@ -807,8 +817,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
                                gpg_strerror (err));
                   if ( is_qualified != -1 )
                     {
-                      /* Cache the result but don't care toomuch about
-                         an error. */
+                      /* Cache the result but don't care too much
+                         about an error. */
                       buf[0] = !!is_qualified;
                       err = ksba_cert_set_user_data (subject_cert,
                                                      "is_qualified", buf, 1);
@@ -1181,3 +1191,110 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
   return rc;
 }
 
+
+
+/* Check whether the certificate CERT has been issued by the German
+   authority for qualified signature.  They do not set the
+   basicConstraints and thus we need this workaround.  It works by
+   looking up the root certificate and checking whether that one is
+   listed as a qualified certificate for Germany. 
+
+   We also try to cache this data but as long as don't keep a
+   reference to the certificate this won't be used.
+
+   Returns: True if CERT is a RegTP issued CA cert (i.e. the root
+   certificate itself or one of the CAs).  In that case CHAINLEN will
+   receive the length of the chain which is either 0 or 1.
+*/
+static int
+get_regtp_ca_info (ksba_cert_t cert, int *chainlen)
+{
+  gpg_error_t err;
+  ksba_cert_t next;
+  int rc = 0;
+  int i, depth;
+  char country[3];
+  ksba_cert_t array[4];
+  char buf[2];
+  size_t buflen;
+  int dummy_chainlen;
+
+  if (!chainlen)
+    chainlen = &dummy_chainlen;
+
+  *chainlen = 0;
+  err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen", 
+                                 &buf, sizeof (buf), &buflen);
+  if (!err)
+    {
+      /* Got info. */
+      if (buflen < 2 || !*buf)
+        return 0; /* Nothing found. */
+      *chainlen = buf[1];
+      return 1; /* This is a regtp CA. */
+    }
+  else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+    {
+      log_error ("ksba_cert_get_user_data(%s) failed: %s\n",
+                 "regtp_ca_chainlen", gpg_strerror (err));
+      return 0; /* Nothing found.  */
+    }
+
+  /* Need to gather the info.  This requires to walk up the chain
+     until we have found the root.  Because we are only interested in
+     German Bundesnetzagentur (former RegTP) derived certificates 3
+     levels are enough.  (The German signature law demands a 3 tier
+     hierachy; thus there is only one CA between the EE and the Root
+     CA.)  */
+  memset (&array, 0, sizeof array);
+
+  depth = 0;
+  ksba_cert_ref (cert);
+  array[depth++] = cert;
+  ksba_cert_ref (cert);
+  while (depth < DIM(array) && !(rc=gpgsm_walk_cert_chain (cert, &next)))
+    {
+      ksba_cert_release (cert);
+      ksba_cert_ref (next);
+      array[depth++] = next;
+      cert = next;
+    }
+  ksba_cert_release (cert);
+  if (rc != -1 || !depth || depth == DIM(array) )
+    {
+      /* We did not reached the root. */
+      goto leave;
+    }
+
+  /* If this is a German signature law issued certificate, we store
+     additional additional information. */
+  if (!gpgsm_is_in_qualified_list (NULL, array[depth-1], country)
+      && !strcmp (country, "de"))
+    {
+      /* Setting the pathlen for the root CA and the CA flag for the
+         next one is all what we need to do. */
+      err = ksba_cert_set_user_data (array[depth-1], "regtp_ca_chainlen",
+                                     "\x01\x01", 2);
+      if (!err && depth > 1)
+        err = ksba_cert_set_user_data (array[depth-2], "regtp_ca_chainlen",
+                                       "\x01\x00", 2);
+      if (err)
+        log_error ("ksba_set_user_data(%s) failed: %s\n",
+                   "regtp_ca_chainlen", gpg_strerror (err)); 
+      for (i=0; i < depth; i++)
+        ksba_cert_release (array[i]);
+      *chainlen = (depth>1? 0:1);
+      return 1;
+    }
+
+ leave:
+  /* Nothing special with this certificate. Mark the target
+     certificate anyway to avoid duplicate lookups. */ 
+  err = ksba_cert_set_user_data (cert, "regtp_ca_chainlen", "", 1);
+  if (err)
+    log_error ("ksba_set_user_data(%s) failed: %s\n",
+               "regtp_ca_chainlen", gpg_strerror (err)); 
+  for (i=0; i < depth; i++)
+    ksba_cert_release (array[i]);
+  return 0;
+}
index dc863f6..4382520 100644 (file)
@@ -296,7 +296,8 @@ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp);
 int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp);
 
 /*-- qualified.c --*/
-gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert);
+gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert,
+                                        char *country);
 gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
 gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert);
 
index a522697..07abaad 100644 (file)
@@ -145,24 +145,29 @@ read_list (char *key, char *country, int *lnr)
    per user because it is not a decision of the user. 
 
    Returns: 0 if the certificate is included.  GPG_ERR_NOT_FOUND if it
-   is not in the liost or any other error (e.g. if no list of
-   qualified signatures is available. */
+   is not in the list or any other error (e.g. if no list of
+   qualified signatures is available.  If COUNTRY has not been passed
+   as NULL a string witha maximum length of 2 will be copied into it;
+   thus the caller needs to provide a buffer of length 3. */
 gpg_error_t
-gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert)
+gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country)
 {
   gpg_error_t err;
   char *fpr;
   char key[41];
-  char country[2];
+  char mycountry[3];
   int lnr = 0;
 
+  if (country)
+    *country = 0;
+
   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
   if (!fpr)
     return gpg_error (GPG_ERR_GENERAL);
 
   if (listfp)
     rewind (listfp);
-  while (!(err = read_list (key, country, &lnr)))
+  while (!(err = read_list (key, mycountry, &lnr)))
     {
       if (!strcmp (key, fpr))
         break;
@@ -170,6 +175,9 @@ gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert)
   if (gpg_err_code (err) == GPG_ERR_EOF)
     err = gpg_error (GPG_ERR_NOT_FOUND);
 
+  if (!err && country)
+    strcpy (country, mycountry);
+
   xfree (fpr);
   return err;
 }