gpgsm: Allow arbitrary extensions for cert creation.
[gnupg.git] / sm / certchain.c
index 04b7e05..1a26325 100644 (file)
@@ -1,6 +1,6 @@
 /* certchain.c - certificate chain validation
  * Copyright (C) 2001, 2002, 2003, 2004, 2005,
- *               2006, 2007 Free Software Foundation, Inc.
+ *               2006, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <stdarg.h>
 #include <assert.h>
@@ -60,6 +60,8 @@ struct chain_item_s
 typedef struct chain_item_s *chain_item_t;
 
 
+static int is_root_cert (ksba_cert_t cert,
+                         const char *issuerdn, const char *subjectdn);
 static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen);
 
 
@@ -191,7 +193,7 @@ has_validation_model_chain (ksba_cert_t cert, int listmode, estream_t listfp)
 
   if (opt.verbose)
     do_list (0, listmode, listfp,
-             _("validation model requested by certificate: %s"), 
+             _("validation model requested by certificate: %s"),
               !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1")? _("chain") :
               !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.2")? _("shell") :
               /* */                                       oidbuf);
@@ -227,6 +229,8 @@ unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
   int rc = 0, i, idx, crit;
   const char *oid;
   gpg_error_t err;
+  int unsupported;
+  strlist_t sl;
 
   for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
                                              &oid, &crit, NULL, NULL));idx++)
@@ -235,7 +239,20 @@ unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
         continue;
       for (i=0; known[i] && strcmp (known[i],oid); i++)
         ;
-      if (!known[i])
+      unsupported = !known[i];
+
+      /* If this critical extension is not supported.  Check the list
+         of to be ignored extensions to see whether we claim that it
+         is supported.  */
+      if (unsupported && opt.ignored_cert_extensions)
+        {
+          for (sl=opt.ignored_cert_extensions;
+               sl && strcmp (sl->d, oid); sl = sl->next)
+            ;
+          if (sl)
+            unsupported = 0;
+        }
+      if (unsupported)
         {
           do_list (1, listmode, fp,
                    _("critical certificate extension %s is not supported"),
@@ -257,9 +274,9 @@ unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
 /* Check whether CERT is an allowed certificate.  This requires that
    CERT matches all requirements for such a CA, i.e. the
    BasicConstraints extension.  The function returns 0 on success and
-   the awlloed length of the chain at CHAINLEN. */
+   the allowed length of the chain at CHAINLEN. */
 static int
-allowed_ca (ctrl_t ctrl, 
+allowed_ca (ctrl_t ctrl,
             ksba_cert_t cert, int *chainlen, int listmode, estream_t fp)
 {
   gpg_error_t err;
@@ -310,7 +327,7 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
   any_critical = !!strstr (policies, ":C");
 
   if (!opt.policy_file)
-    { 
+    {
       xfree (policies);
       if (any_critical)
         {
@@ -331,8 +348,9 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
       /* With no critical policies this is only a warning */
       if (!any_critical)
         {
-          do_list (0, listmode, fplist,
-                   _("note: non-critical certificate policy not allowed"));
+          if (!opt.quiet)
+            do_list (0, listmode, fplist,
+                     _("note: non-critical certificate policy not allowed"));
           return 0;
         }
       do_list (1, listmode, fplist,
@@ -340,7 +358,7 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
       return gpg_error (GPG_ERR_NO_POLICY_MATCH);
     }
 
-  for (;;) 
+  for (;;)
     {
       int c;
       char *p, line[256];
@@ -371,7 +389,7 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
               fclose (fp);
               return tmperr;
             }
-      
+
           if (!*line || line[strlen(line)-1] != '\n')
             {
               /* eat until end of line */
@@ -382,13 +400,13 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
               return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                                      : GPG_ERR_INCOMPLETE_LINE);
             }
-          
+
           /* Allow for empty lines and spaces */
           for (p=line; spacep (p); p++)
             ;
         }
       while (!*p || *p == '\n' || *p == '#');
-  
+
       /* parse line */
       for (allowed=line; spacep (allowed); allowed++)
         ;
@@ -445,7 +463,7 @@ find_up_search_by_keyid (KEYDB_HANDLE kh,
             break; /* Found matching cert. */
         }
     }
-  
+
   ksba_cert_release (cert);
   xfree (subj);
   return rc? -1:0;
@@ -475,7 +493,7 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh,
   int count = 0;
   char *pattern;
   const char *s;
-      
+
   if (opt.verbose)
     log_info (_("looking up issuer at external location\n"));
   /* The Dirmngr process is confused about unknown attributes.  As a
@@ -497,7 +515,7 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh,
 
   if (opt.verbose)
     log_info (_("number of issuers matching: %d\n"), count);
-  if (rc) 
+  if (rc)
     {
       log_error ("external key lookup failed: %s\n", gpg_strerror (rc));
       rc = -1;
@@ -536,7 +554,9 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh,
   strlist_t names = NULL;
   int count = 0;
   char *pattern;
-      
+
+  (void)kh;
+
   if (opt.verbose)
     log_info (_("looking up issuer from the Dirmngr cache\n"));
   if (subject_mode)
@@ -563,7 +583,7 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh,
 
   if (opt.verbose)
     log_info (_("number of matching certificates: %d\n"), count);
-  if (rc
+  if (rc && !opt.quiet)
     log_info (_("dirmngr cache-only key lookup failed: %s\n"),
               gpg_strerror (rc));
   return (!rc && count)? 0 : -1;
@@ -578,7 +598,7 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh,
    keydb_get_cert on the keyDb context KH will return it.  Returns 0
    on success, -1 if not found or an error code.  */
 static int
-find_up (ctrl_t ctrl, KEYDB_HANDLE kh, 
+find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
          ksba_cert_t cert, const char *issuer, int find_next)
 {
   ksba_name_t authid;
@@ -593,9 +613,9 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
         {
           rc = keydb_search_issuer_sn (kh, s, authidno);
           if (rc)
-              keydb_search_reset (kh);
-          
-          /* In case of an error, try to get the certifcate from the
+            keydb_search_reset (kh);
+
+          /* In case of an error, try to get the certificate from the
              dirmngr.  That is done by trying to put that certifcate
              into the ephemeral DB and let the code below do the
              actual retrieve.  Thus there is no error checking.
@@ -607,7 +627,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
              that in find_next mode because we can't keep the search
              state then. */
           if (rc == -1 && !find_next)
-            { 
+            {
               int old = keydb_set_ephemeral (kh, 1);
               if (!old)
                 {
@@ -617,7 +637,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
                 }
               keydb_set_ephemeral (kh, old);
             }
-          if (rc) 
+          if (rc)
             rc = -1; /* Need to make sure to have this error code. */
         }
 
@@ -636,7 +656,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
                 rc = find_up_search_by_keyid (kh, issuer, keyid);
               keydb_set_ephemeral (kh, old);
             }
-          if (rc) 
+          if (rc)
             rc = -1; /* Need to make sure to have this error code. */
         }
 
@@ -656,7 +676,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
                 }
               keydb_set_ephemeral (kh, old);
             }
-          if (rc) 
+          if (rc)
             rc = -1; /* Need to make sure to have this error code. */
         }
 
@@ -667,7 +687,9 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
       /* Print a note so that the user does not feel too helpless when
          an issuer certificate was found and gpgsm prints BAD
          signature because it is not the correct one. */
-      if (rc == -1)
+      if (rc == -1 && opt.quiet)
+        ;
+      else if (rc == -1)
         {
           log_info ("%sissuer certificate ", find_next?"next ":"");
           if (keyid)
@@ -692,17 +714,19 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
       ksba_name_release (authid);
       xfree (authidno);
     }
-  
+
   if (rc) /* Not found via authorithyKeyIdentifier, try regular issuer name. */
     rc = keydb_search_subject (kh, issuer);
   if (rc == -1 && !find_next)
     {
+      int old;
+
       /* Also try to get it from the Dirmngr cache.  The function
          merely puts it into the ephemeral database.  */
       find_up_dirmngr (ctrl, kh, NULL, issuer, 0);
 
       /* Not found, let us see whether we have one in the ephemeral key DB. */
-      int old = keydb_set_ephemeral (kh, 1);
+      old = keydb_set_ephemeral (kh, 1);
       if (!old)
         {
           keydb_search_reset (kh);
@@ -724,7 +748,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh,
 int
 gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
 {
-  int rc = 0; 
+  int rc = 0;
   char *issuer = NULL;
   char *subject = NULL;
   KEYDB_HANDLE kh = keydb_new (0);
@@ -752,10 +776,10 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
       goto leave;
     }
 
-  if (!strcmp (issuer, subject))
+  if (is_root_cert (start, issuer, subject))
     {
       rc = -1; /* we are at the root */
-      goto leave; 
+      goto leave;
     }
 
   rc = find_up (ctrl, kh, start, issuer, 0);
@@ -765,7 +789,7 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
          print an error here.  */
       if (rc != -1 && opt.verbose > 1)
         log_error ("failed to find issuer's certificate: rc=%d\n", rc);
-      rc = gpg_error (GPG_ERR_MISSING_CERT);
+      rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
       goto leave;
     }
 
@@ -779,11 +803,80 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next)
  leave:
   xfree (issuer);
   xfree (subject);
-  keydb_release (kh); 
+  keydb_release (kh);
   return rc;
 }
 
 
+/* Helper for gpgsm_is_root_cert.  This one is used if the subject and
+   issuer DNs are already known.  */
+static int
+is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn)
+{
+  gpg_error_t err;
+  int result = 0;
+  ksba_sexp_t serialno;
+  ksba_sexp_t ak_keyid;
+  ksba_name_t ak_name;
+  ksba_sexp_t ak_sn;
+  const char *ak_name_str;
+  ksba_sexp_t subj_keyid = NULL;
+
+  if (!issuerdn || !subjectdn)
+    return 0;  /* No.  */
+
+  if (strcmp (issuerdn, subjectdn))
+    return 0;  /* No.  */
+
+  err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn);
+  if (err)
+    {
+      if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+        return 1; /* Yes. Without a authorityKeyIdentifier this needs
+                     to be the Root certifcate (our trust anchor).  */
+      log_error ("error getting authorityKeyIdentifier: %s\n",
+                 gpg_strerror (err));
+      return 0; /* Well, it is broken anyway.  Return No. */
+    }
+
+  serialno = ksba_cert_get_serial (cert);
+  if (!serialno)
+    {
+      log_error ("error getting serialno: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Check whether the auth name's matches the issuer name+sn.  If
+     that is the case this is a root certificate.  */
+  ak_name_str = ksba_name_enum (ak_name, 0);
+  if (ak_name_str
+      && !strcmp (ak_name_str, issuerdn)
+      && !cmp_simple_canon_sexp (ak_sn, serialno))
+    {
+      result = 1;  /* Right, CERT is self-signed.  */
+      goto leave;
+    }
+
+  /* Similar for the ak_keyid. */
+  if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid)
+      && !cmp_simple_canon_sexp (ak_keyid, subj_keyid))
+    {
+      result = 1;  /* Right, CERT is self-signed.  */
+      goto leave;
+    }
+
+
+ leave:
+  ksba_free (subj_keyid);
+  ksba_free (ak_keyid);
+  ksba_name_release (ak_name);
+  ksba_free (ak_sn);
+  ksba_free (serialno);
+  return result;
+}
+
+
+
 /* Check whether the CERT is a root certificate.  Returns True if this
    is the case. */
 int
@@ -795,7 +888,7 @@ gpgsm_is_root_cert (ksba_cert_t cert)
 
   issuer = ksba_cert_get_issuer (cert, 0);
   subject = ksba_cert_get_subject (cert, 0);
-  yes = (issuer && subject && !strcmp (issuer, subject));
+  yes = is_root_cert (cert, issuer, subject);
   xfree (issuer);
   xfree (subject);
   return yes;
@@ -803,7 +896,7 @@ gpgsm_is_root_cert (ksba_cert_t cert)
 
 
 /* This is a helper for gpgsm_validate_chain. */
-static gpg_error_t 
+static gpg_error_t
 is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
                      ksba_cert_t subject_cert, ksba_cert_t issuer_cert,
                      int *any_revoked, int *any_no_crl, int *any_crl_too_old)
@@ -811,11 +904,17 @@ is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
   gpg_error_t err;
 
   if (opt.no_crl_check && !ctrl->use_ocsp)
-    return 0;
+    {
+      audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK,
+                    gpg_error (GPG_ERR_NOT_ENABLED));
+      return 0;
+    }
 
   err = gpgsm_dirmngr_isvalid (ctrl,
-                               subject_cert, issuer_cert, 
+                               subject_cert, issuer_cert,
                                force_ocsp? 2 : !!ctrl->use_ocsp);
+  audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, err);
+
   if (err)
     {
       if (!lm)
@@ -849,7 +948,7 @@ is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
                         "\"dirmngr\" is properly installed\n"));
           *any_crl_too_old = 1;
           break;
-          
+
         default:
           do_list (1, lm, fp, _("checking the CRL failed: %s"),
                    gpg_strerror (err));
@@ -864,7 +963,7 @@ is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
    SUBJECT_CERT.  The caller needs to pass EXPTIME which will be
    updated to the nearest expiration time seen.  A DEPTH of 0 indicates
    the target certifciate, -1 the final root certificate and other
-   values intermediate certificates. */ 
+   values intermediate certificates. */
 static gpg_error_t
 check_validity_period (ksba_isotime_t current_time,
                        ksba_cert_t subject_cert,
@@ -894,19 +993,19 @@ check_validity_period (ksba_isotime_t current_time,
 
   if (*not_before && strcmp (current_time, not_before) < 0 )
     {
-      do_list (1, listmode, listfp, 
+      do_list (1, listmode, listfp,
                depth ==  0 ? _("certificate not yet valid") :
                depth == -1 ? _("root certificate not yet valid") :
                /* other */   _("intermediate certificate not yet valid"));
       if (!listmode)
         {
           log_info ("  (valid from ");
-          gpgsm_dump_time (not_before);
+          dump_isotime (not_before);
           log_printf (")\n");
         }
       return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
-    } 
-           
+    }
+
   if (*not_after && strcmp (current_time, not_after) > 0 )
     {
       do_list (opt.ignore_expiration?0:1, listmode, listfp,
@@ -916,15 +1015,15 @@ check_validity_period (ksba_isotime_t current_time,
       if (!listmode)
         {
           log_info ("  (expired at ");
-          gpgsm_dump_time (not_after);
+          dump_isotime (not_after);
           log_printf (")\n");
         }
       if (opt.ignore_expiration)
         log_info ("WARNING: ignoring expiration\n");
       else
         return gpg_error (GPG_ERR_CERT_EXPIRED);
-    }      
-      
+    }
+
   return 0;
 }
 
@@ -965,13 +1064,13 @@ check_validity_period_cm (ksba_isotime_t current_time,
       do_list (1, listmode, listfp,
                _("certificate with invalid validity"));
       log_info ("  (valid from ");
-      gpgsm_dump_time (not_before);
+      dump_isotime (not_before);
       log_printf (" expired at ");
-      gpgsm_dump_time (not_after);
+      dump_isotime (not_after);
       log_printf (")\n");
       return gpg_error (GPG_ERR_BAD_CERT);
     }
-  
+
   if (!*exptime)
     gnupg_copy_time (exptime, not_after);
   else if (strcmp (not_after, exptime) < 0 )
@@ -979,27 +1078,27 @@ check_validity_period_cm (ksba_isotime_t current_time,
 
   if (strcmp (current_time, not_before) < 0 )
     {
-      do_list (1, listmode, listfp, 
+      do_list (1, listmode, listfp,
                depth ==  0 ? _("certificate not yet valid") :
                depth == -1 ? _("root certificate not yet valid") :
                /* other */   _("intermediate certificate not yet valid"));
       if (!listmode)
         {
           log_info ("  (valid from ");
-          gpgsm_dump_time (not_before);
+          dump_isotime (not_before);
           log_printf (")\n");
         }
       return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
-    } 
+    }
 
   if (*check_time
-      && (strcmp (check_time, not_before) < 0 
+      && (strcmp (check_time, not_before) < 0
           || strcmp (check_time, not_after) > 0))
     {
       /* Note that we don't need a case for the root certificate
          because its own consitency has already been checked.  */
       do_list(opt.ignore_expiration?0:1, listmode, listfp,
-              depth == 0 ? 
+              depth == 0 ?
               _("signature not created during lifetime of certificate") :
               depth == 1 ?
               _("certificate not created during lifetime of issuer") :
@@ -1009,13 +1108,13 @@ check_validity_period_cm (ksba_isotime_t current_time,
         {
           log_info (depth== 0? _("  (  signature created at ") :
                     /* */      _("  (certificate created at ") );
-          gpgsm_dump_time (check_time);
+          dump_isotime (check_time);
           log_printf (")\n");
           log_info (depth==0? _("  (certificate valid from ") :
                     /* */     _("  (     issuer valid from ") );
-          gpgsm_dump_time (not_before);
+          dump_isotime (not_before);
           log_info (" to ");
-          gpgsm_dump_time (not_after);
+          dump_isotime (not_after);
           log_printf (")\n");
         }
       if (opt.ignore_expiration)
@@ -1036,7 +1135,7 @@ check_validity_period_cm (ksba_isotime_t current_time,
 static int
 ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode)
 {
-  static int no_more_questions; 
+  static int no_more_questions;
   int rc;
   char *fpr;
   int success = 0;
@@ -1044,7 +1143,7 @@ ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode)
   fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
   log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
   xfree (fpr);
-  
+
   if (no_more_questions)
     rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
   else
@@ -1126,7 +1225,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
     {
       if (!strcmp (checktime_arg, "19700101T000000"))
         {
-          do_list (1, listmode, listfp, 
+          do_list (1, listmode, listfp,
                    _("WARNING: creation time of signature not known - "
                      "assuming current time"));
           gnupg_copy_time (check_time, current_time);
@@ -1197,11 +1296,8 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
         }
 
 
-      /* Is this a self-issued certificate (i.e. the root
-         certificate)?  This is actually the same test as done by
-         gpgsm_is_root_cert but here we want to keep the issuer and
-         subject for later use.  */
-      is_root = (subject && !strcmp (issuer, subject));
+      /* Is this a self-issued certificate (i.e. the root certificate)?  */
+      is_root = is_root_cert (subject_cert, issuer, subject);
       if (is_root)
         {
           chain->is_root = 1;
@@ -1209,7 +1305,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
              We used to do this only later but changed it to call the
              check right here so that we can access special flags
              associated with that specific root certificate.  */
-          istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert,
+          istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, NULL,
                                                 rootca_flags);
           audit_log_cert (ctrl->audit, AUDIT_ROOT_TRUSTED,
                           subject_cert, istrusted_rc);
@@ -1218,7 +1314,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
           if (has_validation_model_chain (subject_cert, listmode, listfp))
             rootca_flags->chain_model = 1;
         }
-      
+
 
       /* Check the validity period. */
       if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
@@ -1236,7 +1332,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
         }
       else if (rc)
         goto leave;
-        
+
 
       /* Assert that we understand all critical extensions. */
       rc = unknown_criticals (subject_cert, listmode, listfp);
@@ -1259,7 +1355,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
 
       /* If this is the root certificate we are at the end of the chain.  */
       if (is_root)
-        { 
+        {
           if (!istrusted_rc)
             ; /* No need to check the certificate for a trusted one. */
           else if (gpgsm_check_cert_sig (subject_cert, subject_cert) )
@@ -1282,8 +1378,8 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
               if (rc)
                 goto leave;
             }
-              
-          
+
+
           /* Set the flag for qualified signatures.  This flag is
              deduced from a list of root certificates allowed for
              qualified signatures. */
@@ -1292,15 +1388,15 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
               gpg_error_t err;
               size_t buflen;
               char buf[1];
-              
-              if (!ksba_cert_get_user_data (cert, "is_qualified", 
+
+              if (!ksba_cert_get_user_data (cert, "is_qualified",
                                             &buf, sizeof (buf),
                                             &buflen) && buflen)
                 {
                   /* We already checked this for this certificate,
                      thus we simply take it from the user data. */
                   is_qualified = !!*buf;
-                }    
+                }
               else
                 {
                   /* Need to consult the list of root certificates for
@@ -1323,7 +1419,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
                                                      "is_qualified", buf, 1);
                       if (err)
                         log_error ("set_user_data(is_qualified) failed: %s\n",
-                                   gpg_strerror (err)); 
+                                   gpg_strerror (err));
                     }
                 }
             }
@@ -1335,7 +1431,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
             ;
           else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
             {
-              do_list (0, listmode, listfp, 
+              do_list (0, listmode, listfp,
                        _("root certificate is not marked trusted"));
               /* If we already figured out that the certificate is
                  expired it does not make much sense to ask the user
@@ -1347,12 +1443,12 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
                    && ask_marktrusted (ctrl, subject_cert, listmode) )
                 rc = 0;
             }
-          else 
+          else
             {
               log_error (_("checking the trust list failed: %s\n"),
                          gpg_strerror (rc));
             }
-          
+
           if (rc)
             goto leave;
 
@@ -1360,9 +1456,9 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
           if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
             ;
           else if (opt.no_trusted_cert_crl_check || rootca_flags->relax)
-            ; 
+            ;
           else
-            rc = is_cert_still_valid (ctrl, 
+            rc = is_cert_still_valid (ctrl,
                                       (flags & VALIDATE_FLAG_CHAIN_MODEL),
                                       listmode, listfp,
                                       subject_cert, subject_cert,
@@ -1374,7 +1470,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
           break;  /* Okay: a self-signed certicate is an end-point. */
         } /* End is_root.  */
 
-      
+
       /* Take care that the chain does not get too long. */
       if ((depth+1) > maxdepth)
         {
@@ -1400,7 +1496,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
             }
           else
             log_error ("failed to find issuer's certificate: rc=%d\n", rc);
-          rc = gpg_error (GPG_ERR_MISSING_CERT);
+          rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
           goto leave;
         }
 
@@ -1456,7 +1552,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
                       do_list (0, listmode, listfp,
                                _("found another possible matching "
                                  "CA certificate - trying again"));
-                      ksba_cert_release (issuer_cert); 
+                      ksba_cert_release (issuer_cert);
                       issuer_cert = tmp_cert;
                       goto try_another_cert;
                     }
@@ -1490,7 +1586,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
                performance reasons. */
             if (is_root)
               {
-                istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert,
+                istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, NULL,
                                                       rootca_flags);
                 if (!istrusted_rc && rootca_flags->relax)
                   {
@@ -1533,9 +1629,9 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
         rc = 0;
       else if (is_root && (opt.no_trusted_cert_crl_check
                            || (!istrusted_rc && rootca_flags->relax)))
-        rc = 0; 
+        rc = 0;
       else
-        rc = is_cert_still_valid (ctrl, 
+        rc = is_cert_still_valid (ctrl,
                                   (flags & VALIDATE_FLAG_CHAIN_MODEL),
                                   listmode, listfp,
                                   subject_cert, issuer_cert,
@@ -1570,7 +1666,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
       depth++;
     } /* End chain traversal. */
 
-  if (!listmode)
+  if (!listmode && !opt.quiet)
     {
       if (opt.no_policy_check)
         log_info ("policies not checked due to %s option\n",
@@ -1594,7 +1690,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
       else if (any_no_policy_match)
         rc = gpg_error (GPG_ERR_NO_POLICY_MATCH);
     }
-  
+
  leave:
   /* If we have traversed a complete chain up to the root we will
      reset the ephemeral flag for all these certificates.  This is done
@@ -1604,7 +1700,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
     {
       gpg_error_t err;
       chain_item_t ci;
-      
+
       for (ci = chain; ci; ci = ci->next)
         {
           /* Note that it is possible for the last certificate in the
@@ -1618,7 +1714,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
             ;
           else if (err)
             log_error ("clearing ephemeral flag failed: %s\n",
-                       gpg_strerror (err)); 
+                       gpg_strerror (err));
         }
     }
 
@@ -1633,14 +1729,14 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
       char buf[1];
 
       buf[0] = !!is_qualified;
-      
+
       for (ci = chain; ci; ci = ci->next)
         {
           err = ksba_cert_set_user_data (ci->cert, "is_qualified", buf, 1);
           if (err)
             {
               log_error ("set_user_data(is_qualified) failed: %s\n",
-                         gpg_strerror (err)); 
+                         gpg_strerror (err));
               if (!rc)
                 rc = err;
             }
@@ -1666,7 +1762,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
     gnupg_copy_time (r_exptime, exptime);
   xfree (issuer);
   xfree (subject);
-  keydb_release (kh); 
+  keydb_release (kh);
   while (chain)
     {
       chain_item_t ci_next = chain->next;
@@ -1711,7 +1807,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
   *retflags = (flags & VALIDATE_FLAG_CHAIN_MODEL);
   memset (&rootca_flags, 0, sizeof rootca_flags);
 
-  rc = do_validate_chain (ctrl, cert, checktime, 
+  rc = do_validate_chain (ctrl, cert, checktime,
                           r_exptime, listmode, listfp, flags,
                           &rootca_flags);
   if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED
@@ -1720,17 +1816,17 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
     {
       do_list (0, listmode, listfp, _("switching to chain model"));
       rc = do_validate_chain (ctrl, cert, checktime,
-                              r_exptime, listmode, listfp, 
+                              r_exptime, listmode, listfp,
                               (flags |= VALIDATE_FLAG_CHAIN_MODEL),
                               &rootca_flags);
       *retflags |= VALIDATE_FLAG_CHAIN_MODEL;
     }
 
   if (opt.verbose)
-    do_list (0, listmode, listfp, _("validation model used: %s"), 
+    do_list (0, listmode, listfp, _("validation model used: %s"),
              (*retflags & VALIDATE_FLAG_CHAIN_MODEL)?
              _("chain"):_("shell"));
-  
+
   return rc;
 }
 
@@ -1747,7 +1843,7 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
   char *subject = NULL;
   KEYDB_HANDLE kh;
   ksba_cert_t issuer_cert = NULL;
-  
+
   if (opt.no_chain_validation)
     {
       log_info ("WARNING: bypassing basic certificate checks\n");
@@ -1771,7 +1867,7 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
       goto leave;
     }
 
-  if (subject && !strcmp (issuer, subject))
+  if (is_root_cert (cert, issuer, subject))
     {
       rc = gpgsm_check_cert_sig (cert, cert);
       if (rc)
@@ -1801,10 +1897,10 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
             }
           else
             log_error ("failed to find issuer's certificate: rc=%d\n", rc);
-          rc = gpg_error (GPG_ERR_MISSING_CERT);
+          rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
           goto leave;
         }
-      
+
       ksba_cert_release (issuer_cert); issuer_cert = NULL;
       rc = keydb_get_cert (kh, &issuer_cert);
       if (rc)
@@ -1834,7 +1930,7 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
  leave:
   xfree (issuer);
   xfree (subject);
-  keydb_release (kh); 
+  keydb_release (kh);
   ksba_cert_release (issuer_cert);
   return rc;
 }
@@ -1845,7 +1941,7 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert)
    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. 
+   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.
@@ -1871,7 +1967,7 @@ get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen)
     chainlen = &dummy_chainlen;
 
   *chainlen = 0;
-  err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen", 
+  err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen",
                                  &buf, sizeof (buf), &buflen);
   if (!err)
     {
@@ -1928,7 +2024,7 @@ get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen)
                                        "\x01\x00", 2);
       if (err)
         log_error ("ksba_set_user_data(%s) failed: %s\n",
-                   "regtp_ca_chainlen", gpg_strerror (err)); 
+                   "regtp_ca_chainlen", gpg_strerror (err));
       for (i=0; i < depth; i++)
         ksba_cert_release (array[i]);
       *chainlen = (depth>1? 0:1);
@@ -1937,11 +2033,11 @@ get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen)
 
  leave:
   /* Nothing special with this certificate. Mark the target
-     certificate anyway to avoid duplicate lookups. */ 
+     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)); 
+               "regtp_ca_chainlen", gpg_strerror (err));
   for (i=0; i < depth; i++)
     ksba_cert_release (array[i]);
   return 0;