示例#1
0
文件: validate.c 项目: 0ndorio/gnupg
/* Basic check for supported policies.  */
static gpg_error_t
check_cert_policy (ksba_cert_t cert)
{
  static const char *allowed[] = {
    "2.289.9.9",
    NULL
  };
  gpg_error_t err;
  int idx;
  char *p, *haystack;
  char *policies;
  int any_critical;

  err = ksba_cert_get_cert_policies (cert, &policies);
  if (gpg_err_code (err) == GPG_ERR_NO_DATA)
    return 0; /* No policy given. */
  if (err)
    return err;

  /* STRING is a line delimited list of certifiate policies as stored
     in the certificate.  The line itself is colon delimited where the
     first field is the OID of the policy and the second field either
     N or C for normal or critical extension */
  if (opt.verbose > 1)
    log_info ("certificate's policy list: %s\n", policies);

  /* The check is very minimal but won't give false positives */
  any_critical = !!strstr (policies, ":C");

  /* See whether we find ALLOWED (which is an OID) in POLICIES */
  for (idx=0; allowed[idx]; idx++)
    {
      for (haystack=policies; (p=strstr (haystack, allowed[idx]));
           haystack = p+1)
        {
          if ( !(p == policies || p[-1] == '\n') )
            continue; /* Does not match the begin of a line. */
          if (p[strlen (allowed[idx])] != ':')
            continue; /* The length does not match. */
          /* Yep - it does match: Return okay. */
          ksba_free (policies);
          return 0;
        }
    }

  if (!any_critical)
    {
      log_info (_("Note: non-critical certificate policy not allowed"));
      err = 0;
    }
  else
    {
      log_info (_("certificate policy not allowed"));
      err = gpg_error (GPG_ERR_NO_POLICY_MATCH);
    }

  ksba_free (policies);
  return err;
}
示例#2
0
文件: certdump.c 项目: FMayzek/gnupg
/* Create a key description for the CERT, this may be passed to the
   pinentry.  The caller must free the returned string.  NULL may be
   returned on error. */
char *
gpgsm_format_keydesc (ksba_cert_t cert)
{
  char *name, *subject, *buffer;
  ksba_isotime_t t;
  char created[20];
  char expires[20];
  char *sn;
  ksba_sexp_t sexp;
  char *orig_codeset;

  name = ksba_cert_get_subject (cert, 0);
  subject = name? gpgsm_format_name2 (name, 0) : NULL;
  ksba_free (name); name = NULL;

  sexp = ksba_cert_get_serial (cert);
  sn = sexp? gpgsm_format_serial (sexp) : NULL;
  ksba_free (sexp);

  ksba_cert_get_validity (cert, 0, t);
  if (*t)
    sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
  else
    *created = 0;
  ksba_cert_get_validity (cert, 1, t);
  if (*t)
    sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
  else
    *expires = 0;

  orig_codeset = i18n_switchto_utf8 ();

  name = xtryasprintf (_("Please enter the passphrase to unlock the"
                         " secret key for the X.509 certificate:\n"
                         "\"%s\"\n"
                         "S/N %s, ID 0x%08lX,\n"
                         "created %s, expires %s.\n" ),
                       subject? subject:"?",
                       sn? sn: "?",
                       gpgsm_get_short_fingerprint (cert, NULL),
                       created, expires);

  i18n_switchback (orig_codeset);

  if (!name)
    {
      xfree (subject);
      xfree (sn);
      return NULL;
    }

  xfree (subject);
  xfree (sn);

  buffer = percent_plus_escape (name);
  xfree (name);
  return buffer;
}
示例#3
0
文件: certdump.c 项目: FMayzek/gnupg
/* Log the certificate's name in "#SN/ISSUERDN" format along with
   TEXT. */
void
gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
{
  log_info ("%s", text? text:"certificate" );
  if (cert)
    {
      ksba_sexp_t sn;
      char *p;

      p = ksba_cert_get_issuer (cert, 0);
      sn = ksba_cert_get_serial (cert);
      if (p && sn)
        {
          log_printf (" #");
          gpgsm_dump_serial (sn);
          log_printf ("/");
          gpgsm_dump_string (p);
        }
      else
        log_printf (" [invalid]");
      ksba_free (sn);
      xfree (p);
    }
  log_printf ("\n");
}
示例#4
0
文件: certdump.c 项目: FMayzek/gnupg
/* This simple dump function is mainly used for debugging purposes. */
void
gpgsm_dump_cert (const char *text, ksba_cert_t cert)
{
  ksba_sexp_t sexp;
  char *p;
  char *dn;
  ksba_isotime_t t;

  log_debug ("BEGIN Certificate '%s':\n", text? text:"");
  if (cert)
    {
      sexp = ksba_cert_get_serial (cert);
      log_debug ("     serial: ");
      gpgsm_dump_serial (sexp);
      ksba_free (sexp);
      log_printf ("\n");

      ksba_cert_get_validity (cert, 0, t);
      log_debug ("  notBefore: ");
      dump_isotime (t);
      log_printf ("\n");
      ksba_cert_get_validity (cert, 1, t);
      log_debug ("   notAfter: ");
      dump_isotime (t);
      log_printf ("\n");

      dn = ksba_cert_get_issuer (cert, 0);
      log_debug ("     issuer: ");
      gpgsm_dump_string (dn);
      ksba_free (dn);
      log_printf ("\n");

      dn = ksba_cert_get_subject (cert, 0);
      log_debug ("    subject: ");
      gpgsm_dump_string (dn);
      ksba_free (dn);
      log_printf ("\n");

      log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));

      p = gpgsm_get_fingerprint_string (cert, 0);
      log_debug ("  SHA1 Fingerprint: %s\n", p);
      xfree (p);
    }
  log_debug ("END Certificate\n");
}
示例#5
0
文件: certcache.c 项目: 0ndorio/gnupg
/* Cleanup one slot.  This releases all resourses but keeps the actual
   slot in the cache marked for reuse. */
static void
clean_cache_slot (cert_item_t ci)
{
  ksba_cert_t cert;

  if (!ci->cert)
    return; /* Already cleaned.  */

  ksba_free (ci->sn);
  ci->sn = NULL;
  ksba_free (ci->issuer_dn);
  ci->issuer_dn = NULL;
  ksba_free (ci->subject_dn);
  ci->subject_dn = NULL;
  cert = ci->cert;
  ci->cert = NULL;

  ksba_cert_release (cert);
}
示例#6
0
文件: certdump.c 项目: FMayzek/gnupg
/* Return fingerprint and a percent escaped name in a human readable
   format suitable for status messages like GOODSIG.  May return NULL
   on error (out of core). */
char *
gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
{
  char *fpr, *name, *p;
  char *buffer;

  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
  if (!fpr)
    return NULL;

  name = ksba_cert_get_subject (cert, 0);
  if (!name)
    {
      xfree (fpr);
      return NULL;
    }

  p = gpgsm_format_name2 (name, 0);
  ksba_free (name);
  name = p;
  if (!name)
    {
      xfree (fpr);
      return NULL;
    }

  buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
  if (buffer)
    {
      const char *s;

      p = stpcpy (stpcpy (buffer, fpr), " ");
      for (s = name; *s; s++)
        {
          if (*s < ' ')
            {
              sprintf (p, "%%%02X", *(const unsigned char*)s);
              p += 3;
            }
          else
            *p++ = *s;
        }
      *p = 0;
    }
  xfree (fpr);
  xfree (name);
  return buffer;
}
示例#7
0
文件: validate.c 项目: 0ndorio/gnupg
/* Check the signature on CERT using the ISSUER_CERT.  This function
   does only test the cryptographic signature and nothing else.  It is
   assumed that the ISSUER_CERT is valid. */
static gpg_error_t
check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
{
  gpg_error_t err;
  const char *algoid;
  gcry_md_hd_t md;
  int i, algo;
  ksba_sexp_t p;
  size_t n;
  gcry_sexp_t s_sig, s_hash, s_pkey;
  const char *s;
  char algo_name[16+1]; /* hash algorithm name converted to lower case. */
  int digestlen;
  unsigned char *digest;

  /* Hash the target certificate using the algorithm from that certificate.  */
  algoid = ksba_cert_get_digest_algo (cert);
  algo = gcry_md_map_name (algoid);
  if (!algo)
    {
      log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?");
      return gpg_error (GPG_ERR_GENERAL);
    }
  s = gcry_md_algo_name (algo);
  for (i=0; *s && i < sizeof algo_name - 1; s++, i++)
    algo_name[i] = tolower (*s);
  algo_name[i] = 0;

  err = gcry_md_open (&md, algo, 0);
  if (err)
    {
      log_error ("md_open failed: %s\n", gpg_strerror (err));
      return err;
    }
  if (DBG_HASHING)
    gcry_md_debug (md, "hash.cert");

  err = ksba_cert_hash (cert, 1, HASH_FNC, md);
  if (err)
    {
      log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      return err;
    }
  gcry_md_final (md);

  /* Get the signature value out of the target certificate.  */
  p = ksba_cert_get_sig_val (cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      return gpg_error (GPG_ERR_BUG);
    }
  if (DBG_CRYPTO)
    {
      int j;
      log_debug ("signature value:");
      for (j=0; j < n; j++)
        log_printf (" %02X", p[j]);
      log_printf ("\n");
    }

  err = gcry_sexp_sscan ( &s_sig, NULL, p, n);
  ksba_free (p);
  if (err)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      return err;
    }

  /* Get the public key from the issuer certificate.  */
  p = ksba_cert_get_public_key (issuer_cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      gcry_sexp_release (s_sig);
      return gpg_error (GPG_ERR_BUG);
    }
  err = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
  ksba_free (p);
  if (err)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err));
      gcry_md_close (md);
      gcry_sexp_release (s_sig);
      return err;
    }


  /* Prepare the values for signature verification. At this point we
     have these values:

     S_PKEY    - S-expression with the issuer's public key.
     S_SIG     - Signature value as given in the certrificate.
     MD        - Finalized hash context with hash of the certificate.
     ALGO_NAME - Lowercase hash algorithm name
   */
  digestlen = gcry_md_get_algo_dlen (algo);
  digest = gcry_md_read (md, algo);
  if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA)
    {
      if (digestlen != 20)
        {
          log_error (_("DSA requires the use of a 160 bit hash algorithm\n"));
          gcry_md_close (md);
          gcry_sexp_release (s_sig);
          gcry_sexp_release (s_pkey);
          return gpg_error (GPG_ERR_INTERNAL);
        }
      if ( gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))",
                            (int)digestlen, digest) )
        BUG ();
    }
  else /* Not DSA.  */
    {
      if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))",
                            algo_name, (int)digestlen, digest) )
        BUG ();

    }

  err = gcry_pk_verify (s_sig, s_hash, s_pkey);
  if (DBG_X509)
    log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err));
  gcry_md_close (md);
  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_hash);
  gcry_sexp_release (s_pkey);
  return err;
}
示例#8
0
文件: validate.c 项目: 0ndorio/gnupg
/* Validate the certificate CHAIN up to the trust anchor. Optionally
   return the closest expiration time in R_EXPTIME (this is useful for
   caching issues).  MODE is one of the VALIDATE_MODE_* constants.

   If R_TRUST_ANCHOR is not NULL and the validation would fail only
   because the root certificate is not trusted, the hexified
   fingerprint of that root certificate is stored at R_TRUST_ANCHOR
   and success is returned.  The caller needs to free the value at
   R_TRUST_ANCHOR; in all other cases NULL is stored there.  */
gpg_error_t
validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
                     int mode, char **r_trust_anchor)
{
  gpg_error_t err = 0;
  int depth, maxdepth;
  char *issuer = NULL;
  char *subject = NULL;
  ksba_cert_t subject_cert = NULL, issuer_cert = NULL;
  ksba_isotime_t current_time;
  ksba_isotime_t exptime;
  int any_expired = 0;
  int any_no_policy_match = 0;
  chain_item_t chain;


  if (r_exptime)
    *r_exptime = 0;
  *exptime = 0;

  if (r_trust_anchor)
    *r_trust_anchor = NULL;

  if (!opt.system_daemon)
    {
      /* For backward compatibility we only do this in daemon mode.  */
      log_info (_("running in compatibility mode - "
                  "certificate chain not checked!\n"));
      return 0; /* Okay. */
    }

  if (DBG_X509)
    dump_cert ("subject", cert);

  /* May the target certificate be used for this purpose?  */
  switch (mode)
    {
    case VALIDATE_MODE_OCSP:
      err = cert_use_ocsp_p (cert);
      break;
    case VALIDATE_MODE_CRL:
    case VALIDATE_MODE_CRL_RECURSIVE:
      err = cert_use_crl_p (cert);
      break;
    default:
      err = 0;
      break;
    }
  if (err)
    return err;

  /* If we already validated the certificate not too long ago, we can
     avoid the excessive computations and lookups unless the caller
     asked for the expiration time.  */
  if (!r_exptime)
    {
      size_t buflen;
      time_t validated_at;

      err = ksba_cert_get_user_data (cert, "validated_at",
                                     &validated_at, sizeof (validated_at),
                                     &buflen);
      if (err || buflen != sizeof (validated_at) || !validated_at)
        err = 0; /* Not available or other error. */
      else
        {
          /* If the validation is not older than 30 minutes we are ready. */
          if (validated_at < gnupg_get_time () + (30*60))
            {
              if (opt.verbose)
                log_info ("certificate is good (cached)\n");
              /* Note, that we can't jump to leave here as this would
                 falsely updated the validation timestamp.  */
              return 0;
            }
        }
    }

  /* Get the current time. */
  gnupg_get_isotime (current_time);

  /* We walk up the chain until we find a trust anchor. */
  subject_cert = cert;
  maxdepth = 10;
  chain = NULL;
  depth = 0;
  for (;;)
    {
      /* Get the subject and issuer name from the current
         certificate.  */
      ksba_free (issuer);
      ksba_free (subject);
      issuer = ksba_cert_get_issuer (subject_cert, 0);
      subject = ksba_cert_get_subject (subject_cert, 0);

      if (!issuer)
        {
          log_error (_("no issuer found in certificate\n"));
          err = gpg_error (GPG_ERR_BAD_CERT);
          goto leave;
        }

      /* Handle the notBefore and notAfter timestamps.  */
      {
        ksba_isotime_t not_before, not_after;

        err = ksba_cert_get_validity (subject_cert, 0, not_before);
        if (!err)
          err = ksba_cert_get_validity (subject_cert, 1, not_after);
        if (err)
          {
            log_error (_("certificate with invalid validity: %s"),
                       gpg_strerror (err));
            err = gpg_error (GPG_ERR_BAD_CERT);
            goto leave;
          }

        /* Keep track of the nearest expiration time in EXPTIME.  */
        if (*not_after)
          {
            if (!*exptime)
              gnupg_copy_time (exptime, not_after);
            else if (strcmp (not_after, exptime) < 0 )
              gnupg_copy_time (exptime, not_after);
          }

        /* Check whether the certificate is already valid.  */
        if (*not_before && strcmp (current_time, not_before) < 0 )
          {
            log_error (_("certificate not yet valid"));
            log_info ("(valid from ");
            dump_isotime (not_before);
            log_printf (")\n");
            err = gpg_error (GPG_ERR_CERT_TOO_YOUNG);
            goto leave;
          }

        /* Now check whether the certificate has expired.  */
        if (*not_after && strcmp (current_time, not_after) > 0 )
          {
            log_error (_("certificate has expired"));
            log_info ("(expired at ");
            dump_isotime (not_after);
            log_printf (")\n");
            any_expired = 1;
          }
      }

      /* Do we have any critical extensions in the certificate we
         can't handle? */
      err = unknown_criticals (subject_cert);
      if (err)
        goto leave; /* yes. */

      /* Check that given policies are allowed.  */
      err = check_cert_policy (subject_cert);
      if (gpg_err_code (err) == GPG_ERR_NO_POLICY_MATCH)
        {
          any_no_policy_match = 1;
          err = 0;
        }
      else if (err)
        goto leave;

      /* Is this a self-signed certificate? */
      if (is_root_cert ( subject_cert, issuer, subject))
        {
          /* Yes, this is our trust anchor.  */
          if (check_cert_sig (subject_cert, subject_cert) )
            {
              log_error (_("selfsigned certificate has a BAD signature"));
              err = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN
                                    : GPG_ERR_BAD_CERT);
              goto leave;
            }

          /* Is this certificate allowed to act as a CA.  */
          err = allowed_ca (subject_cert, NULL);
          if (err)
            goto leave;  /* No. */

          err = is_trusted_cert (subject_cert);
          if (!err)
            ; /* Yes we trust this cert.  */
          else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)
            {
              char *fpr;

              log_error (_("root certificate is not marked trusted"));
              fpr = get_fingerprint_hexstring (subject_cert);
              log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
              dump_cert ("issuer", subject_cert);
              if (r_trust_anchor)
                {
                  /* Caller wants to do another trustiness check.  */
                  *r_trust_anchor = fpr;
                  err = 0;
                }
              else
                xfree (fpr);
            }
          else
            {
              log_error (_("checking trustworthiness of "
                           "root certificate failed: %s\n"),
                         gpg_strerror (err));
            }
          if (err)
            goto leave;

          /* Prepend the certificate to our list.  */
          {
            chain_item_t ci;

            ci = xtrycalloc (1, sizeof *ci);
            if (!ci)
              {
                err = gpg_error_from_errno (errno);
                goto leave;
              }
            ksba_cert_ref (subject_cert);
            ci->cert = subject_cert;
            cert_compute_fpr (subject_cert, ci->fpr);
            ci->next = chain;
            chain = ci;
          }

          if (opt.verbose)
            {
              if (r_trust_anchor && *r_trust_anchor)
                log_info ("root certificate is good but not trusted\n");
              else
                log_info ("root certificate is good and trusted\n");
            }

          break;  /* Okay: a self-signed certicate is an end-point. */
        }

      /* To avoid loops, we use an arbitrary limit on the length of
         the chain. */
      depth++;
      if (depth > maxdepth)
        {
          log_error (_("certificate chain too long\n"));
          err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
          goto leave;
        }

      /* Find the next cert up the tree. */
      ksba_cert_release (issuer_cert); issuer_cert = NULL;
      err = find_issuing_cert (ctrl, subject_cert, &issuer_cert);
      if (err)
        {
          if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
            {
              log_error (_("issuer certificate not found"));
              log_info ("issuer certificate: #/");
              dump_string (issuer);
              log_printf ("\n");
            }
          else
            log_error (_("issuer certificate not found: %s\n"),
                         gpg_strerror (err));
          /* Use a better understandable error code.  */
          err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
          goto leave;
        }

/*     try_another_cert: */
      if (DBG_X509)
        {
          log_debug ("got issuer's certificate:\n");
          dump_cert ("issuer", issuer_cert);
        }

      /* Now check the signature of the certificate.  Well, we
         should delay this until later so that faked certificates
         can't be turned into a DoS easily.  */
      err = check_cert_sig (issuer_cert, subject_cert);
      if (err)
        {
          log_error (_("certificate has a BAD signature"));
#if 0
          if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE)
            {
              /* We now try to find other issuer certificates which
                 might have been used.  This is required because some
                 CAs are reusing the issuer and subject DN for new
                 root certificates without using a  authorityKeyIdentifier. */
              rc = find_up (kh, subject_cert, issuer, 1);
              if (!rc)
                {
                  ksba_cert_t tmp_cert;

                  rc = keydb_get_cert (kh, &tmp_cert);
                  if (rc || !compare_certs (issuer_cert, tmp_cert))
                    {
                      /* The find next did not work or returned an
                         identical certificate.  We better stop here
                         to avoid infinite checks. */
                      rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
                      ksba_cert_release (tmp_cert);
                    }
                  else
                    {
                      do_list (0, lm, fp, _("found another possible matching "
                                            "CA certificate - trying again"));
                      ksba_cert_release (issuer_cert);
                      issuer_cert = tmp_cert;
                      goto try_another_cert;
                    }
                }
            }
#endif
          /* We give a more descriptive error code than the one
             returned from the signature checking. */
          err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
          goto leave;
        }

      /* Check that the length of the chain is not longer than allowed
         by the CA.  */
      {
        int chainlen;

        err = allowed_ca (issuer_cert, &chainlen);
        if (err)
          goto leave;
        if (chainlen >= 0 && (depth - 1) > chainlen)
          {
            log_error (_("certificate chain longer than allowed by CA (%d)"),
                       chainlen);
            err = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
            goto leave;
          }
      }

      /* May that certificate be used for certification? */
      err = cert_use_cert_p (issuer_cert);
      if (err)
        goto leave;  /* No.  */

      /* Prepend the certificate to our list.  */
      {
        chain_item_t ci;

        ci = xtrycalloc (1, sizeof *ci);
        if (!ci)
          {
            err = gpg_error_from_errno (errno);
            goto leave;
          }
        ksba_cert_ref (subject_cert);
        ci->cert = subject_cert;
        cert_compute_fpr (subject_cert, ci->fpr);
        ci->next = chain;
        chain = ci;
      }

      if (opt.verbose)
        log_info (_("certificate is good\n"));

      /* Now to the next level up.  */
      subject_cert = issuer_cert;
      issuer_cert = NULL;
    }

  if (!err)
    { /* If we encountered an error somewhere during the checks, set
         the error code to the most critical one */
      if (any_expired)
        err = gpg_error (GPG_ERR_CERT_EXPIRED);
      else if (any_no_policy_match)
        err = gpg_error (GPG_ERR_NO_POLICY_MATCH);
    }

  if (!err && opt.verbose)
    {
      chain_item_t citem;

      log_info (_("certificate chain is good\n"));
      for (citem = chain; citem; citem = citem->next)
        cert_log_name ("  certificate", citem->cert);
    }

  if (!err && mode != VALIDATE_MODE_CRL)
    { /* Now that everything is fine, walk the chain and check each
         certificate for revocations.

         1. item in the chain  - The root certificate.
         2. item               - the CA below the root
         last item             - the target certificate.

         Now for each certificate in the chain check whether it has
         been included in a CRL and thus be revoked.  We don't do OCSP
         here because this does not seem to make much sense.  This
         might become a recursive process and we should better cache
         our validity results to avoid double work.  Far worse a
         catch-22 may happen for an improper setup hierarchy and we
         need a way to break up such a deadlock. */
      err = check_revocations (ctrl, chain);
    }

  if (!err && opt.verbose)
    {
      if (r_trust_anchor && *r_trust_anchor)
        log_info ("target certificate may be valid\n");
      else
        log_info ("target certificate is valid\n");
    }
  else if (err && opt.verbose)
    log_info ("target certificate is NOT valid\n");


 leave:
  if (!err && !(r_trust_anchor && *r_trust_anchor))
    {
      /* With no error we can update the validation cache.  We do this
         for all certificates in the chain.  Note that we can't use
         the cache if the caller requested to check the trustiness of
         the root certificate himself.  Adding such a feature would
         require us to also store the fingerprint of root
         certificate.  */
      chain_item_t citem;
      time_t validated_at = gnupg_get_time ();

      for (citem = chain; citem; citem = citem->next)
        {
          err = ksba_cert_set_user_data (citem->cert, "validated_at",
                                         &validated_at, sizeof (validated_at));
          if (err)
            {
              log_error ("set_user_data(validated_at) failed: %s\n",
                         gpg_strerror (err));
              err = 0;
            }
        }
    }

  if (r_exptime)
    gnupg_copy_time (r_exptime, exptime);
  ksba_free (issuer);
  ksba_free (subject);
  ksba_cert_release (issuer_cert);
  if (subject_cert != cert)
    ksba_cert_release (subject_cert);
  while (chain)
    {
      chain_item_t ci_next = chain->next;
      if (chain->cert)
        ksba_cert_release (chain->cert);
      xfree (chain);
      chain = ci_next;
    }
  if (err && r_trust_anchor && *r_trust_anchor)
    {
      xfree (*r_trust_anchor);
      *r_trust_anchor = NULL;
    }
  return err;
}
示例#9
0
文件: validate.c 项目: 0ndorio/gnupg
/* Check whether CERT is a root certificate.  ISSUERDN and SUBJECTDN
   are the DNs already extracted by the caller from CERT.  Returns
   True if this is the case. */
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;
}
示例#10
0
文件: certcheck.c 项目: 0ndorio/gnupg
/* Check the signature on CERT using the ISSUER-CERT.  This function
   does only test the cryptographic signature and nothing else.  It is
   assumed that the ISSUER_CERT is valid. */
int
gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
{
  const char *algoid;
  gcry_md_hd_t md;
  int rc, algo;
  gcry_mpi_t frame;
  ksba_sexp_t p;
  size_t n;
  gcry_sexp_t s_sig, s_hash, s_pkey;

  algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert)));
  if (!algo)
    {
      log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?");
      if (algoid
          && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
                ||!strcmp (algoid, "1.2.840.113549.2.2")))
        log_info (_("(this is the MD2 algorithm)\n"));
      return gpg_error (GPG_ERR_GENERAL);
    }
  rc = gcry_md_open (&md, algo, 0);
  if (rc)
    {
      log_error ("md_open failed: %s\n", gpg_strerror (rc));
      return rc;
    }
  if (DBG_HASHING)
    gcry_md_debug (md, "hash.cert");

  rc = ksba_cert_hash (cert, 1, HASH_FNC, md);
  if (rc)
    {
      log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
      gcry_md_close (md);
      return rc;
    }
  gcry_md_final (md);

  p = ksba_cert_get_sig_val (cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      return gpg_error (GPG_ERR_BUG);
    }
  if (DBG_CRYPTO)
    {
      int j;
      log_debug ("signature value:");
      for (j=0; j < n; j++)
        log_printf (" %02X", p[j]);
      log_printf ("\n");
    }

  rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n);
  ksba_free (p);
  if (rc)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
      gcry_md_close (md);
      return rc;
    }

  p = ksba_cert_get_public_key (issuer_cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      gcry_md_close (md);
      ksba_free (p);
      gcry_sexp_release (s_sig);
      return gpg_error (GPG_ERR_BUG);
    }
  rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
  ksba_free (p);
  if (rc)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
      gcry_md_close (md);
      gcry_sexp_release (s_sig);
      return rc;
    }

  rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey),
                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
  if (rc)
    {
      gcry_md_close (md);
      gcry_sexp_release (s_sig);
      gcry_sexp_release (s_pkey);
      return rc;
    }

  /* put hash into the S-Exp s_hash */
  if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
    BUG ();
  gcry_mpi_release (frame);


  rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
  if (DBG_X509)
      log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
  gcry_md_close (md);
  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_hash);
  gcry_sexp_release (s_pkey);
  return rc;
}
示例#11
0
文件: certcache.c 项目: 0ndorio/gnupg
/* Return the certificate matching ISSUER_DN and SERIALNO; if it is
   not already in the cache, try to find it from other resources.  */
ksba_cert_t
find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno)
{
  gpg_error_t err;
  ksba_cert_t cert;
  cert_fetch_context_t context = NULL;
  char *hexsn, *buf;

  /* First check whether it has already been cached.  */
  cert = get_cert_bysn (issuer_dn, serialno);
  if (cert)
    return cert;

  /* Ask back to the service requester to return the certificate.
     This is because we can assume that he already used the
     certificate while checking for the CRL. */
  hexsn = serial_hex (serialno);
  if (!hexsn)
    {
      log_error ("serial_hex() failed\n");
      return NULL;
    }
  buf = xtrymalloc (1 + strlen (hexsn) + 1 + strlen (issuer_dn) + 1);
  if (!buf)
    {
      log_error ("can't allocate enough memory: %s\n", strerror (errno));
      xfree (hexsn);
      return NULL;
    }
  strcpy (stpcpy (stpcpy (stpcpy (buf, "#"), hexsn),"/"), issuer_dn);
  xfree (hexsn);
  cert = get_cert_local (ctrl, buf);
  xfree (buf);
  if (cert)
    {
      cache_cert (cert);
      return cert; /* Done. */
    }

  if (DBG_LOOKUP)
    log_debug ("find_cert_bysn: certificate not returned by caller"
               " - doing lookup\n");

  /* Retrieve the certificate from external resources. */
  while (!cert)
    {
      ksba_sexp_t sn;
      char *issdn;

      if (!context)
        {
          err = ca_cert_fetch (ctrl, &context, issuer_dn);
          if (err)
            {
              log_error (_("error fetching certificate by S/N: %s\n"),
                         gpg_strerror (err));
              break;
            }
        }

      err = fetch_next_ksba_cert (context, &cert);
      if (err)
        {
          log_error (_("error fetching certificate by S/N: %s\n"),
                     gpg_strerror (err) );
          break;
        }

      issdn = ksba_cert_get_issuer (cert, 0);
      if (strcmp (issuer_dn, issdn))
        {
          log_debug ("find_cert_bysn: Ooops: issuer DN does not match\n");
          ksba_cert_release (cert);
          cert = NULL;
          ksba_free (issdn);
          break;
        }

      sn = ksba_cert_get_serial (cert);

      if (DBG_LOOKUP)
        {
          log_debug ("   considering certificate (#");
          dump_serial (sn);
          log_printf ("/");
          dump_string (issdn);
          log_printf (")\n");
        }

      if (!compare_serialno (serialno, sn))
        {
          ksba_free (sn);
          ksba_free (issdn);
          cache_cert (cert);
          if (DBG_LOOKUP)
            log_debug ("   found\n");
          break; /* Ready.  */
        }

      ksba_free (sn);
      ksba_free (issdn);
      ksba_cert_release (cert);
      cert = NULL;
    }

  end_cert_fetch (context);
  return cert;
}
示例#12
0
文件: certcache.c 项目: 0ndorio/gnupg
/* Return the certificate matching SUBJECT_DN and (if not NULL)
   KEYID. If it is not already in the cache, try to find it from other
   resources.  Note, that the external search does not work for user
   certificates because the LDAP lookup is on the caCertificate
   attribute. For our purposes this is just fine.  */
ksba_cert_t
find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid)
{
  gpg_error_t err;
  int seq;
  ksba_cert_t cert = NULL;
  cert_fetch_context_t context = NULL;
  ksba_sexp_t subj;

  /* If we have certificates from an OCSP request we first try to use
     them.  This is because these certificates will really be the
     required ones and thus even in the case that they can't be
     uniquely located by the following code we can use them.  This is
     for example required by Telesec certificates where a keyId is
     used but the issuer certificate comes without a subject keyId! */
  if (ctrl->ocsp_certs && subject_dn)
    {
      cert_item_t ci;
      cert_ref_t cr;
      int i;

      /* For efficiency reasons we won't use get_cert_bysubject here. */
      acquire_cache_read_lock ();
      for (i=0; i < 256; i++)
        for (ci=cert_cache[i]; ci; ci = ci->next)
          if (ci->cert && ci->subject_dn
              && !strcmp (ci->subject_dn, subject_dn))
            for (cr=ctrl->ocsp_certs; cr; cr = cr->next)
              if (!memcmp (ci->fpr, cr->fpr, 20))
                {
                  ksba_cert_ref (ci->cert);
                  release_cache_lock ();
                  return ci->cert; /* We use this certificate. */
                }
      release_cache_lock ();
      if (DBG_LOOKUP)
        log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n");
    }


  /* First we check whether the certificate is cached.  */
  for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++)
    {
      if (!keyid)
        break; /* No keyid requested, so return the first one found. */
      if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)
          && !cmp_simple_canon_sexp (keyid, subj))
        {
          xfree (subj);
          break; /* Found matching cert. */
        }
      xfree (subj);
      ksba_cert_release (cert);
    }
  if (cert)
    return cert; /* Done.  */

  if (DBG_LOOKUP)
    log_debug ("find_cert_bysubject: certificate not in cache\n");

  /* Ask back to the service requester to return the certificate.
     This is because we can assume that he already used the
     certificate while checking for the CRL. */
  if (keyid)
    cert = get_cert_local_ski (ctrl, subject_dn, keyid);
  else
    {
      /* In contrast to get_cert_local_ski, get_cert_local uses any
         passed pattern, so we need to make sure that an exact subject
         search is done. */
      char *buf;

      buf = xtrymalloc (1 + strlen (subject_dn) + 1);
      if (!buf)
        {
          log_error ("can't allocate enough memory: %s\n", strerror (errno));
          return NULL;
        }
      strcpy (stpcpy (buf, "/"), subject_dn);
      cert = get_cert_local (ctrl, buf);
      xfree (buf);
    }
  if (cert)
    {
      cache_cert (cert);
      return cert; /* Done. */
    }

  if (DBG_LOOKUP)
    log_debug ("find_cert_bysubject: certificate not returned by caller"
               " - doing lookup\n");

  /* Locate the certificate using external resources. */
  while (!cert)
    {
      char *subjdn;

      if (!context)
        {
          err = ca_cert_fetch (ctrl, &context, subject_dn);
          if (err)
            {
              log_error (_("error fetching certificate by subject: %s\n"),
                         gpg_strerror (err));
              break;
            }
        }

      err = fetch_next_ksba_cert (context, &cert);
      if (err)
        {
          log_error (_("error fetching certificate by subject: %s\n"),
                     gpg_strerror (err) );
          break;
        }

      subjdn = ksba_cert_get_subject (cert, 0);
      if (strcmp (subject_dn, subjdn))
        {
          log_info ("find_cert_bysubject: subject DN does not match\n");
          ksba_cert_release (cert);
          cert = NULL;
          ksba_free (subjdn);
          continue;
        }


      if (DBG_LOOKUP)
        {
          log_debug ("   considering certificate (/");
          dump_string (subjdn);
          log_printf (")\n");
        }
      ksba_free (subjdn);

      /* If no key ID has been provided, we return the first match.  */
      if (!keyid)
        {
          cache_cert (cert);
          if (DBG_LOOKUP)
            log_debug ("   found\n");
          break; /* Ready.  */
        }

      /* With the key ID given we need to compare it.  */
      if (!ksba_cert_get_subj_key_id (cert, NULL, &subj))
        {
          if (!cmp_simple_canon_sexp (keyid, subj))
            {
              ksba_free (subj);
              cache_cert (cert);
              if (DBG_LOOKUP)
                log_debug ("   found\n");
              break; /* Ready.  */
            }
        }

      ksba_free (subj);
      ksba_cert_release (cert);
      cert = NULL;
    }

  end_cert_fetch (context);
  return cert;
}
示例#13
0
文件: ocsp.c 项目: FMayzek/gnupg
/* Check whether the certificate either given by fingerprint CERT_FPR
   or directly through the CERT object is valid by running an OCSP
   transaction.  With FORCE_DEFAULT_RESPONDER set only the configured
   default responder is used. */
gpg_error_t
ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr,
              int force_default_responder)
{
  gpg_error_t err;
  ksba_ocsp_t ocsp = NULL;
  ksba_cert_t issuer_cert = NULL;
  ksba_sexp_t sigval = NULL;
  gcry_sexp_t s_sig = NULL;
  ksba_isotime_t current_time;
  ksba_isotime_t this_update, next_update, revocation_time, produced_at;
  ksba_isotime_t tmp_time;
  ksba_status_t status;
  ksba_crl_reason_t reason;
  char *url_buffer = NULL;
  const char *url;
  gcry_md_hd_t md = NULL;
  int i, idx;
  char *oid;
  ksba_name_t name;
  fingerprint_list_t default_signer = NULL;

  /* Get the certificate.  */
  if (cert)
    {
      ksba_cert_ref (cert);

      err = find_issuing_cert (ctrl, cert, &issuer_cert);
      if (err)
        {
          log_error (_("issuer certificate not found: %s\n"),
                     gpg_strerror (err));
          goto leave;
        }
    }
  else
    {
      cert = get_cert_local (ctrl, cert_fpr);
      if (!cert)
        {
          log_error (_("caller did not return the target certificate\n"));
          err = gpg_error (GPG_ERR_GENERAL);
          goto leave;
        }
      issuer_cert = get_issuing_cert_local (ctrl, NULL);
      if (!issuer_cert)
        {
          log_error (_("caller did not return the issuing certificate\n"));
          err = gpg_error (GPG_ERR_GENERAL);
          goto leave;
        }
    }

  /* Create an OCSP instance.  */
  err = ksba_ocsp_new (&ocsp);
  if (err)
    {
      log_error (_("failed to allocate OCSP context: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }



  /* Figure out the OCSP responder to use.
     1. Try to get the reponder from the certificate.
        We do only take http and https style URIs into account.
     2. If this fails use the default responder, if any.
   */
  url = NULL;
  for (idx=0; !url && !opt.ignore_ocsp_service_url && !force_default_responder
         && !(err=ksba_cert_get_authority_info_access (cert, idx,
                                                       &oid, &name)); idx++)
    {
      if ( !strcmp (oid, oidstr_ocsp) )
        {
          for (i=0; !url && ksba_name_enum (name, i); i++)
            {
              char *p = ksba_name_get_uri (name, i);
              if (p && (!ascii_strncasecmp (p, "http:", 5)
                        || !ascii_strncasecmp (p, "https:", 6)))
                url = url_buffer = p;
              else
                xfree (p);
            }
        }
      ksba_name_release (name);
      ksba_free (oid);
    }
  if (err && gpg_err_code (err) != GPG_ERR_EOF)
    {
      log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err));
      goto leave;
    }
  if (!url)
    {
      if (!opt.ocsp_responder || !*opt.ocsp_responder)
        {
          log_info (_("no default OCSP responder defined\n"));
          err = gpg_error (GPG_ERR_CONFIGURATION);
          goto leave;
        }
      if (!opt.ocsp_signer)
        {
          log_info (_("no default OCSP signer defined\n"));
          err = gpg_error (GPG_ERR_CONFIGURATION);
          goto leave;
        }
      url = opt.ocsp_responder;
      default_signer = opt.ocsp_signer;
      if (opt.verbose)
        log_info (_("using default OCSP responder '%s'\n"), url);
    }
  else
    {
      if (opt.verbose)
        log_info (_("using OCSP responder '%s'\n"), url);
    }

  /* Ask the OCSP responder. */
  err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
  if (err)
    {
      log_error (_("failed to establish a hashing context for OCSP: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }
  err = do_ocsp_request (ctrl, ocsp, md, url, cert, issuer_cert);
  if (err)
    goto leave;

  /* We got a useful answer, check that the answer has a valid signature. */
  sigval = ksba_ocsp_get_sig_val (ocsp, produced_at);
  if (!sigval || !*produced_at)
    {
      err = gpg_error (GPG_ERR_INV_OBJ);
      goto leave;
    }
  if ( (err = canon_sexp_to_gcry (sigval, &s_sig)) )
    goto leave;
  xfree (sigval);
  sigval = NULL;
  err = check_signature (ctrl, ocsp, s_sig, md, default_signer);
  if (err)
    goto leave;

  /* We only support one certificate per request.  Check that the
     answer matches the right certificate. */
  err = ksba_ocsp_get_status (ocsp, cert,
                              &status, this_update, next_update,
                              revocation_time, &reason);
  if (err)
    {
      log_error (_("error getting OCSP status for target certificate: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }

  /* In case the certificate has been revoked, we better invalidate
     our cached validation status. */
  if (status == KSBA_STATUS_REVOKED)
    {
      time_t validated_at = 0; /* That is: No cached validation available. */
      err = ksba_cert_set_user_data (cert, "validated_at",
                                     &validated_at, sizeof (validated_at));
      if (err)
        {
          log_error ("set_user_data(validated_at) failed: %s\n",
                     gpg_strerror (err));
          err = 0; /* The certificate is anyway revoked, and that is a
                      more important message than the failure of our
                      cache. */
        }
    }


  if (opt.verbose)
    {
      log_info (_("certificate status is: %s  (this=%s  next=%s)\n"),
                status == KSBA_STATUS_GOOD? _("good"):
                status == KSBA_STATUS_REVOKED? _("revoked"):
                status == KSBA_STATUS_UNKNOWN? _("unknown"):
                status == KSBA_STATUS_NONE? _("none"): "?",
                this_update, next_update);
      if (status == KSBA_STATUS_REVOKED)
        log_info (_("certificate has been revoked at: %s due to: %s\n"),
                  revocation_time,
                  reason == KSBA_CRLREASON_UNSPECIFIED?   "unspecified":
                  reason == KSBA_CRLREASON_KEY_COMPROMISE? "key compromise":
                  reason == KSBA_CRLREASON_CA_COMPROMISE?   "CA compromise":
                  reason == KSBA_CRLREASON_AFFILIATION_CHANGED?
                                                      "affiliation changed":
                  reason == KSBA_CRLREASON_SUPERSEDED?   "superseeded":
                  reason == KSBA_CRLREASON_CESSATION_OF_OPERATION?
                                                  "cessation of operation":
                  reason == KSBA_CRLREASON_CERTIFICATE_HOLD?
                                                  "certificate on hold":
                  reason == KSBA_CRLREASON_REMOVE_FROM_CRL?
                                                  "removed from CRL":
                  reason == KSBA_CRLREASON_PRIVILEGE_WITHDRAWN?
                                                  "privilege withdrawn":
                  reason == KSBA_CRLREASON_AA_COMPROMISE? "AA compromise":
                  reason == KSBA_CRLREASON_OTHER?   "other":"?");

    }


  if (status == KSBA_STATUS_REVOKED)
    err = gpg_error (GPG_ERR_CERT_REVOKED);
  else if (status == KSBA_STATUS_UNKNOWN)
    err = gpg_error (GPG_ERR_NO_DATA);
  else if (status != KSBA_STATUS_GOOD)
    err = gpg_error (GPG_ERR_GENERAL);

  /* Allow for some clock skew. */
  gnupg_get_isotime (current_time);
  add_seconds_to_isotime (current_time, opt.ocsp_max_clock_skew);

  if (strcmp (this_update, current_time) > 0 )
    {
      log_error (_("OCSP responder returned a status in the future\n"));
      log_info ("used now: %s  this_update: %s\n", current_time, this_update);
      if (!err)
        err = gpg_error (GPG_ERR_TIME_CONFLICT);
    }

  /* Check that THIS_UPDATE is not too far back in the past. */
  gnupg_copy_time (tmp_time, this_update);
  add_seconds_to_isotime (tmp_time,
                          opt.ocsp_max_period+opt.ocsp_max_clock_skew);
  if (!*tmp_time || strcmp (tmp_time, current_time) < 0 )
    {
      log_error (_("OCSP responder returned a non-current status\n"));
      log_info ("used now: %s  this_update: %s\n",
                current_time, this_update);
      if (!err)
        err = gpg_error (GPG_ERR_TIME_CONFLICT);
    }

  /* Check that we are not beyound NEXT_UPDATE  (plus some extra time). */
  if (*next_update)
    {
      gnupg_copy_time (tmp_time, next_update);
      add_seconds_to_isotime (tmp_time,
                              opt.ocsp_current_period+opt.ocsp_max_clock_skew);
      if (!*tmp_time && strcmp (tmp_time, current_time) < 0 )
        {
          log_error (_("OCSP responder returned an too old status\n"));
          log_info ("used now: %s  next_update: %s\n",
                    current_time, next_update);
          if (!err)
            err = gpg_error (GPG_ERR_TIME_CONFLICT);
        }
    }


 leave:
  gcry_md_close (md);
  gcry_sexp_release (s_sig);
  xfree (sigval);
  ksba_cert_release (issuer_cert);
  ksba_cert_release (cert);
  ksba_ocsp_release (ocsp);
  xfree (url_buffer);
  return err;
}
示例#14
0
文件: ocsp.c 项目: FMayzek/gnupg
/* Check the signature of an OCSP repsonse.  OCSP is the context,
   S_SIG the signature value and MD the handle of the hash we used for
   the response.  This function automagically finds the correct public
   key.  If SIGNER_FPR_LIST is not NULL, the default OCSP reponder has been
   used and thus the certificate is one of those identified by
   the fingerprints. */
static gpg_error_t
check_signature (ctrl_t ctrl,
                 ksba_ocsp_t ocsp, gcry_sexp_t s_sig, gcry_md_hd_t md,
                 fingerprint_list_t signer_fpr_list)
{
  gpg_error_t err;
  int algo, cert_idx;
  gcry_sexp_t s_hash;
  ksba_cert_t cert;

  /* Create a suitable S-expression with the hash value of our response. */
  gcry_md_final (md);
  algo = gcry_md_get_algo (md);
  if (algo != GCRY_MD_SHA1 )
    {
      log_error (_("only SHA-1 is supported for OCSP responses\n"));
      return gpg_error (GPG_ERR_DIGEST_ALGO);
    }
  err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash sha1 %b))",
                         gcry_md_get_algo_dlen (algo),
                         gcry_md_read (md, algo));
  if (err)
    {
      log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err));
      return err;
    }

  /* Get rid of old OCSP specific certificate references. */
  release_ctrl_ocsp_certs (ctrl);

  if (signer_fpr_list && !signer_fpr_list->next)
    {
      /* There is exactly one signer fingerprint given. Thus we use
         the default OCSP responder's certificate and instantly know
         the certificate to use.  */
      cert = get_cert_byhexfpr (signer_fpr_list->hexfpr);
      if (!cert)
        cert = get_cert_local (ctrl, signer_fpr_list->hexfpr);
      if (cert)
        {
          err = check_signature_core (ctrl, cert, s_sig, s_hash,
                                      signer_fpr_list);
          ksba_cert_release (cert);
          cert = NULL;
          if (!err)
            {
              gcry_sexp_release (s_hash);
              return 0; /* Successfully verified the signature. */
            }
        }
    }
  else
    {
      char *name;
      ksba_sexp_t keyid;

      /* Put all certificates included in the response into the cache
         and setup a list of those certificate which will later be
         preferred used when locating certificates.  */
      for (cert_idx=0; (cert = ksba_ocsp_get_cert (ocsp, cert_idx));
           cert_idx++)
        {
          cert_ref_t cref;

          cref = xtrymalloc (sizeof *cref);
          if (!cref)
            log_error (_("allocating list item failed: %s\n"),
                       gcry_strerror (err));
          else if (!cache_cert_silent (cert, &cref->fpr))
            {
              cref->next = ctrl->ocsp_certs;
              ctrl->ocsp_certs = cref;
            }
          else
            xfree (cref);
        }

      /* Get the certificate by means of the responder ID. */
      err = ksba_ocsp_get_responder_id (ocsp, &name, &keyid);
      if (err)
        {
          log_error (_("error getting responder ID: %s\n"),
                     gcry_strerror (err));
          return err;
        }
      cert = find_cert_bysubject (ctrl, name, keyid);
      if (!cert)
        {
          log_error ("responder certificate ");
          if (name)
            log_printf ("'/%s' ", name);
          if (keyid)
            {
              log_printf ("{");
              dump_serial (keyid);
              log_printf ("} ");
            }
          log_printf ("not found\n");
        }
      ksba_free (name);
      ksba_free (keyid);

      if (cert)
        {
          err = check_signature_core (ctrl, cert, s_sig, s_hash,
                                      signer_fpr_list);
          ksba_cert_release (cert);
          if (!err)
            {
              gcry_sexp_release (s_hash);
              return 0; /* Successfully verified the signature. */
            }
        }
    }

  gcry_sexp_release (s_hash);
  log_error (_("no suitable certificate found to verify the OCSP response\n"));
  return gpg_error (GPG_ERR_NO_PUBKEY);
}
示例#15
0
void 
one_response (const char *cert_fname, const char *issuer_cert_fname,
              char *response_fname)
{
  gpg_error_t err;
  ksba_ocsp_t ocsp;
  unsigned char *request, *response;
  size_t requestlen, responselen;
  ksba_cert_t cert = get_one_cert (cert_fname);
  ksba_cert_t issuer_cert = get_one_cert (issuer_cert_fname);
  ksba_ocsp_response_status_t response_status;
  const char *t;

  err = ksba_ocsp_new (&ocsp);
  fail_if_err (err);

  /* We need to build a request, so that the context is properly
     prepared for the response. */
  err = ksba_ocsp_add_target (ocsp, cert, issuer_cert);
  fail_if_err (err);
  ksba_cert_release (issuer_cert);

  if (!no_nonce)
    ksba_ocsp_set_nonce (ocsp, "ABCDEFGHIJKLMNOP", 16);

  err = ksba_ocsp_build_request (ocsp, &request, &requestlen);
  fail_if_err (err);
  xfree (request);

  /* Now for the response. */
  response = read_file (response_fname, &responselen);
  if (!response)
    fail ("file error");

  err = ksba_ocsp_parse_response (ocsp, response, responselen,
                                  &response_status);
  fail_if_err (err);
  switch (response_status)
    {
    case KSBA_OCSP_RSPSTATUS_SUCCESS:      t = "success"; break;
    case KSBA_OCSP_RSPSTATUS_MALFORMED:    t = "malformed"; break;  
    case KSBA_OCSP_RSPSTATUS_INTERNAL:     t = "internal error"; break;  
    case KSBA_OCSP_RSPSTATUS_TRYLATER:     t = "try later"; break;      
    case KSBA_OCSP_RSPSTATUS_SIGREQUIRED:  t = "must sign request"; break;  
    case KSBA_OCSP_RSPSTATUS_UNAUTHORIZED: t = "unauthorized"; break;  
    case KSBA_OCSP_RSPSTATUS_REPLAYED:     t = "replay detected"; break;  
    case KSBA_OCSP_RSPSTATUS_OTHER:        t = "other (unknown)"; break;  
    case KSBA_OCSP_RSPSTATUS_NONE:         t = "no status"; break;
    default: fail ("impossible response_status"); break;
    }
  printf ("response status ..: %s\n", t);

  if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS
      || response_status == KSBA_OCSP_RSPSTATUS_REPLAYED)
    {
      ksba_status_t status;
      ksba_crl_reason_t reason;
      ksba_isotime_t this_update, next_update, revocation_time, produced_at;
      ksba_sexp_t sigval;
      char *name;
      ksba_sexp_t keyid;

      err = ksba_ocsp_get_responder_id (ocsp, &name, &keyid);
      fail_if_err (err);
      printf ("responder id .....: ");
      if (name)
        printf ("`%s'", name);
      else
        print_sexp (keyid);
      putchar ('\n');
      ksba_free (name);
      ksba_free (keyid);

      sigval = ksba_ocsp_get_sig_val (ocsp, produced_at);
      printf ("signature value ..: ");
      print_sexp (sigval);
      printf ("\nproduced at ......: ");
      print_time (produced_at);
      putchar ('\n');
      err = ksba_ocsp_get_status (ocsp, cert,
                                  &status, this_update, next_update,
                                  revocation_time, &reason);
      fail_if_err (err);
      printf ("certificate status: %s\n",
              status == KSBA_STATUS_GOOD? "good":
              status == KSBA_STATUS_REVOKED? "revoked":
              status == KSBA_STATUS_UNKNOWN? "unknown":
              status == KSBA_STATUS_NONE? "none": "?");
      if (status == KSBA_STATUS_REVOKED)
        {
          printf ("revocation time ..: ");
          print_time (revocation_time);
          printf ("\nrevocation reason : %s\n",
                  reason == KSBA_CRLREASON_UNSPECIFIED?   "unspecified":        
                  reason == KSBA_CRLREASON_KEY_COMPROMISE? "key compromise":
                  reason == KSBA_CRLREASON_CA_COMPROMISE?   "CA compromise":
                  reason == KSBA_CRLREASON_AFFILIATION_CHANGED?
                                                       "affiliation changed":
                  reason == KSBA_CRLREASON_SUPERSEDED?   "superseeded":
                  reason == KSBA_CRLREASON_CESSATION_OF_OPERATION?
                                                      "cessation of operation": 
                  reason == KSBA_CRLREASON_CERTIFICATE_HOLD?
                                                      "certificate on hold":   
                  reason == KSBA_CRLREASON_REMOVE_FROM_CRL? "removed from CRL":
                  reason == KSBA_CRLREASON_PRIVILEGE_WITHDRAWN?
                                                       "privilege withdrawn":
                  reason == KSBA_CRLREASON_AA_COMPROMISE? "AA compromise":
                  reason == KSBA_CRLREASON_OTHER?   "other":"?");
        }
      printf ("this update ......: ");
      print_time (this_update);
      printf ("\nnext update ......: ");
      print_time (next_update);
      putchar ('\n');
      {
        int cert_idx;
        ksba_cert_t acert;

        for (cert_idx=0; (acert = ksba_ocsp_get_cert (ocsp, cert_idx));
             cert_idx++)
          ksba_cert_release (acert);
        printf ("extra certificates: %d\n", cert_idx );
      }

      {
        int idx, crit;
        const char *oid;
        const unsigned char *der;
        size_t derlen;
        
        for (idx=0; !(err=ksba_ocsp_get_extension (ocsp, NULL, idx,
                                                   &oid, &crit,
                                                   &der, &derlen)); idx++)
          {
            const char *s = get_oid_desc (oid);
            printf ("%sresp-extn ..%s: %s%s%s%s  (",
                    crit? "crit. ":"", 
                    crit?"":"......", 
                    s?"(":"", s?s:"", s?") ":"", oid);
            print_hex (der, derlen);
            putchar (')');
            putchar ('\n');
          }
        if (err && gpg_err_code (err) != GPG_ERR_EOF)
          fail_if_err (err);

        for (idx=0; !(err=ksba_ocsp_get_extension (ocsp, cert, idx,
                                                   &oid, &crit,
                                                   &der, &derlen)); idx++)
          {
            const char *s = get_oid_desc (oid);
            printf ("%ssngl-extn ..%s: %s%s%s%s  (",
                    crit? "crit. ":"", 
                    crit?"":"......", 
                    s?"(":"", s?s:"", s?") ":"", oid);
            print_hex (der, derlen);
            putchar (')');
            putchar ('\n');
          }
        if (err && gpg_err_code (err) != GPG_ERR_EOF)
          fail_if_err (err);
      }
    }
  

  ksba_cert_release (cert);
  ksba_ocsp_release (ocsp);
  xfree (response);
}
示例#16
0
文件: certcheck.c 项目: 0ndorio/gnupg
int
gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
                           gcry_md_hd_t md, int mdalgo, int *r_pkalgo)
{
  int rc;
  ksba_sexp_t p;
  gcry_mpi_t frame;
  gcry_sexp_t s_sig, s_hash, s_pkey;
  size_t n;
  int pkalgo;

  if (r_pkalgo)
    *r_pkalgo = 0;

  n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      return gpg_error (GPG_ERR_BUG);
    }
  rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n);
  if (rc)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
      return rc;
    }

  p = ksba_cert_get_public_key (cert);
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    {
      log_error ("libksba did not return a proper S-Exp\n");
      ksba_free (p);
      gcry_sexp_release (s_sig);
      return gpg_error (GPG_ERR_BUG);
    }
  if (DBG_CRYPTO)
    log_printhex ("public key: ", p, n);

  rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
  ksba_free (p);
  if (rc)
    {
      log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
      gcry_sexp_release (s_sig);
      return rc;
    }

  pkalgo = pk_algo_from_sexp (s_pkey);
  if (r_pkalgo)
    *r_pkalgo = pkalgo;
  rc = do_encode_md (md, mdalgo, pkalgo,
                     gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
  if (rc)
    {
      gcry_sexp_release (s_sig);
      gcry_sexp_release (s_pkey);
      return rc;
    }
  /* put hash into the S-Exp s_hash */
  if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
    BUG ();
  gcry_mpi_release (frame);

  rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
  if (DBG_X509)
      log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc));
  gcry_sexp_release (s_sig);
  gcry_sexp_release (s_hash);
  gcry_sexp_release (s_pkey);
  return rc;
}
示例#17
0
文件: validate.c 项目: 0ndorio/gnupg
/* Return 0 if the cert is usable for encryption.  A MODE of 0 checks
   for signing, a MODE of 1 checks for encryption, a MODE of 2 checks
   for verification and a MODE of 3 for decryption (just for
   debugging).  MODE 4 is for certificate signing, MODE 5 for OCSP
   response signing, MODE 6 is for CRL signing. */
static int
cert_usage_p (ksba_cert_t cert, int mode)
{
  gpg_error_t err;
  unsigned int use;
  char *extkeyusages;
  int have_ocsp_signing = 0;

  err = ksba_cert_get_ext_key_usages (cert, &extkeyusages);
  if (gpg_err_code (err) == GPG_ERR_NO_DATA)
    err = 0; /* No policy given. */
  if (!err)
    {
      unsigned int extusemask = ~0; /* Allow all. */

      if (extkeyusages)
        {
          char *p, *pend;
          int any_critical = 0;

          extusemask = 0;

          p = extkeyusages;
          while (p && (pend=strchr (p, ':')))
            {
              *pend++ = 0;
              /* Only care about critical flagged usages. */
              if ( *pend == 'C' )
                {
                  any_critical = 1;
                  if ( !strcmp (p, oid_kp_serverAuth))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_clientAuth))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_codeSigning))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE);
                  else if ( !strcmp (p, oid_kp_emailProtection))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_NON_REPUDIATION
                                   | KSBA_KEYUSAGE_KEY_ENCIPHERMENT
                                   | KSBA_KEYUSAGE_KEY_AGREEMENT);
                  else if ( !strcmp (p, oid_kp_timeStamping))
                    extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE
                                   | KSBA_KEYUSAGE_NON_REPUDIATION);
                }

              /* This is a hack to cope with OCSP.  Note that we do
                 not yet fully comply with the requirements and that
                 the entire CRL/OCSP checking thing should undergo a
                 thorough review and probably redesign. */
              if ( !strcmp (p, oid_kp_ocspSigning))
                have_ocsp_signing = 1;

              if ((p = strchr (pend, '\n')))
                p++;
            }
          ksba_free (extkeyusages);
          extkeyusages = NULL;

          if (!any_critical)
            extusemask = ~0; /* Reset to the don't care mask. */
        }


      err = ksba_cert_get_key_usage (cert, &use);
      if (gpg_err_code (err) == GPG_ERR_NO_DATA)
        {
          err = 0;
          if (opt.verbose && mode < 2)
            log_info (_("no key usage specified - assuming all usages\n"));
          use = ~0;
        }

      /* Apply extKeyUsage. */
      use &= extusemask;

    }
  if (err)
    {
      log_error (_("error getting key usage information: %s\n"),
                 gpg_strerror (err));
      ksba_free (extkeyusages);
      return err;
    }

  if (mode == 4)
    {
      if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN)))
        return 0;
      log_info (_("certificate should not have "
                  "been used for certification\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if (mode == 5)
    {
      if (use != ~0
          && (have_ocsp_signing
              || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN
                         |KSBA_KEYUSAGE_CRL_SIGN))))
        return 0;
      log_info (_("certificate should not have "
                  "been used for OCSP response signing\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if (mode == 6)
    {
      if ((use & (KSBA_KEYUSAGE_CRL_SIGN)))
        return 0;
      log_info (_("certificate should not have "
                  "been used for CRL signing\n"));
      return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
    }

  if ((use & ((mode&1)?
              (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT):
              (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
      )
    return 0;

  log_info (mode==3? _("certificate should not have been used "
                       "for encryption\n"):
            mode==2? _("certificate should not have been used for signing\n"):
            mode==1? _("certificate is not usable for encryption\n"):
                     _("certificate is not usable for signing\n"));
  return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
示例#18
0
/* Perform a verify operation.  To verify detached signatures, data_fd
   must be different than -1.  With OUT_FP given and a non-detached
   signature, the signed material is written to that stream. */
int
gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
{
  int i, rc;
  Base64Context b64reader = NULL;
  Base64Context b64writer = NULL;
  ksba_reader_t reader;
  ksba_writer_t writer = NULL;
  ksba_cms_t cms = NULL;
  ksba_stop_reason_t stopreason;
  ksba_cert_t cert;
  KEYDB_HANDLE kh;
  gcry_md_hd_t data_md = NULL;
  int signer;
  const char *algoid;
  int algo;
  int is_detached;
  FILE *fp = NULL;
  char *p;

  audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY);

  kh = keydb_new (0);
  if (!kh)
    {
      log_error (_("failed to allocated keyDB handle\n"));
      rc = gpg_error (GPG_ERR_GENERAL);
      goto leave;
    }


  fp = fdopen ( dup (in_fd), "rb");
  if (!fp)
    {
      rc = gpg_error (gpg_err_code_from_errno (errno));
      log_error ("fdopen() failed: %s\n", strerror (errno));
      goto leave;
    }

  rc = gpgsm_create_reader (&b64reader, ctrl, fp, 0, &reader);
  if (rc)
    {
      log_error ("can't create reader: %s\n", gpg_strerror (rc));
      goto leave;
    }

  if (out_fp)
    {
      rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer);
      if (rc)
        {
          log_error ("can't create writer: %s\n", gpg_strerror (rc));
          goto leave;
        }
    }

  rc = ksba_cms_new (&cms);
  if (rc)
    goto leave;

  rc = ksba_cms_set_reader_writer (cms, reader, writer);
  if (rc)
    {
      log_error ("ksba_cms_set_reader_writer failed: %s\n",
                 gpg_strerror (rc));
      goto leave;
    }

  rc = gcry_md_open (&data_md, 0, 0);
  if (rc)
    {
      log_error ("md_open failed: %s\n", gpg_strerror (rc));
      goto leave;
    }
  if (DBG_HASHING)
    gcry_md_start_debug (data_md, "vrfy.data");

  audit_log (ctrl->audit, AUDIT_SETUP_READY);

  is_detached = 0;
  do 
    {
      rc = ksba_cms_parse (cms, &stopreason);
      if (rc)
        {
          log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
          goto leave;
        }

      if (stopreason == KSBA_SR_NEED_HASH)
        {
          is_detached = 1;
          audit_log (ctrl->audit, AUDIT_DETACHED_SIGNATURE);
          if (opt.verbose)
            log_info ("detached signature\n");
        }

      if (stopreason == KSBA_SR_NEED_HASH
          || stopreason == KSBA_SR_BEGIN_DATA)
        { 
          audit_log (ctrl->audit, AUDIT_GOT_DATA);

          /* We are now able to enable the hash algorithms */
          for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
            {
              algo = gcry_md_map_name (algoid);
              if (!algo)
                {
                  log_error ("unknown hash algorithm `%s'\n",
                             algoid? algoid:"?");
                  if (algoid
                      && (  !strcmp (algoid, "1.2.840.113549.1.1.2")
                          ||!strcmp (algoid, "1.2.840.113549.2.2")))
                    log_info (_("(this is the MD2 algorithm)\n"));
                  audit_log_s (ctrl->audit, AUDIT_BAD_DATA_HASH_ALGO, algoid);
                }
              else
                {
                  if (DBG_X509)
                    log_debug ("enabling hash algorithm %d (%s)\n",
                               algo, algoid? algoid:"");
                  gcry_md_enable (data_md, algo);
                  audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
                }
            }
          if (opt.extra_digest_algo)
            {
              if (DBG_X509)
                log_debug ("enabling extra hash algorithm %d\n", 
                           opt.extra_digest_algo);
              gcry_md_enable (data_md, opt.extra_digest_algo);
              audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO,
                           opt.extra_digest_algo);
            }
          if (is_detached)
            {
              if (data_fd == -1)
                {
                  log_info ("detached signature w/o data "
                            "- assuming certs-only\n");
                  audit_log (ctrl->audit, AUDIT_CERT_ONLY_SIG);
                }
              else
                audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING,
                              hash_data (data_fd, data_md));
            }
          else
            {
              ksba_cms_set_hash_function (cms, HASH_FNC, data_md);
            }
        }
      else if (stopreason == KSBA_SR_END_DATA)
        { /* The data bas been hashed */
          audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, 0);
        }
    }
  while (stopreason != KSBA_SR_READY);   

  if (b64writer)
    {
      rc = gpgsm_finish_writer (b64writer);
      if (rc) 
        {
          log_error ("write failed: %s\n", gpg_strerror (rc));
          audit_log_ok (ctrl->audit, AUDIT_WRITE_ERROR, rc);
          goto leave;
        }
    }

  if (data_fd != -1 && !is_detached)
    {
      log_error ("data given for a non-detached signature\n");
      rc = gpg_error (GPG_ERR_CONFLICT);
      audit_log (ctrl->audit, AUDIT_USAGE_ERROR);
      goto leave;
    }

  for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
    {
      /* Fixme: it might be better to check the validity of the
         certificate first before entering it into the DB.  This way
         we would avoid cluttering the DB with invalid
         certificates. */
      audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 
                      keydb_store_cert (cert, 0, NULL));
      ksba_cert_release (cert);
    }

  cert = NULL;
  for (signer=0; ; signer++)
    {
      char *issuer = NULL;
      ksba_sexp_t sigval = NULL;
      ksba_isotime_t sigtime, keyexptime;
      ksba_sexp_t serial;
      char *msgdigest = NULL;
      size_t msgdigestlen;
      char *ctattr;
      int sigval_hash_algo;
      int info_pkalgo;
      unsigned int verifyflags;

      rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
      if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA
          && data_fd == -1 && is_detached)
        {
          log_info ("certs-only message accepted\n");
          rc = 0;
          break;
        }
      if (rc)
        {
          if (signer && rc == -1)
            rc = 0;
          break;
        }

      gpgsm_status (ctrl, STATUS_NEWSIG, NULL);
      audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);

      if (DBG_X509)
        {
          log_debug ("signer %d - issuer: `%s'\n",
                     signer, issuer? issuer:"[NONE]");
          log_debug ("signer %d - serial: ", signer);
          gpgsm_dump_serial (serial);
          log_printf ("\n");
        }
      if (ctrl->audit)
        {
          char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
          audit_log_s (ctrl->audit, AUDIT_SIG_NAME, tmpstr);
          xfree (tmpstr);
        }

      rc = ksba_cms_get_signing_time (cms, signer, sigtime);
      if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
        *sigtime = 0;
      else if (rc)
        {
          log_error ("error getting signing time: %s\n", gpg_strerror (rc));
          *sigtime = 0; /* (we can't encode an error in the time string.) */
        }

      rc = ksba_cms_get_message_digest (cms, signer,
                                        &msgdigest, &msgdigestlen);
      if (!rc)
        {
          size_t is_enabled;

          algoid = ksba_cms_get_digest_algo (cms, signer);
          algo = gcry_md_map_name (algoid);
          if (DBG_X509)
            log_debug ("signer %d - digest algo: %d\n", signer, algo);
          is_enabled = sizeof algo;
          if ( gcry_md_info (data_md, GCRYCTL_IS_ALGO_ENABLED,
                             &algo, &is_enabled)
               || !is_enabled)
            {
              log_error ("digest algo %d (%s) has not been enabled\n", 
                         algo, algoid?algoid:"");
              audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported");
              goto next_signer;
            }
        }
      else if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
        {
          assert (!msgdigest);
          rc = 0;
          algoid = NULL;
          algo = 0; 
        }
      else /* real error */
        {
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
          break;
        }

      rc = ksba_cms_get_sigattr_oids (cms, signer,
                                      "1.2.840.113549.1.9.3", &ctattr);
      if (!rc) 
        {
          const char *s;

          if (DBG_X509)
            log_debug ("signer %d - content-type attribute: %s",
                       signer, ctattr);

          s = ksba_cms_get_content_oid (cms, 1);
          if (!s || strcmp (ctattr, s))
            {
              log_error ("content-type attribute does not match "
                         "actual content-type\n");
              ksba_free (ctattr);
              ctattr = NULL;
              audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
              goto next_signer;
            }
          ksba_free (ctattr);
          ctattr = NULL;
        }
      else if (rc != -1)
        {
          log_error ("error getting content-type attribute: %s\n",
                     gpg_strerror (rc));
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
          goto next_signer;
        }
      rc = 0;


      sigval = ksba_cms_get_sig_val (cms, signer);
      if (!sigval)
        {
          log_error ("no signature value available\n");
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
          goto next_signer;
        }
      sigval_hash_algo = hash_algo_from_sigval (sigval);
      if (DBG_X509)
        {
          log_debug ("signer %d - signature available (sigval hash=%d)",
                     signer, sigval_hash_algo);
/*           log_printhex ("sigval    ", sigval, */
/*                         gcry_sexp_canon_len (sigval, 0, NULL, NULL)); */
        }
      if (!sigval_hash_algo)
        sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */

      /* Find the certificate of the signer */
      keydb_search_reset (kh);
      rc = keydb_search_issuer_sn (kh, issuer, serial);
      if (rc)
        {
          if (rc == -1)
            {
              log_error ("certificate not found\n");
              rc = gpg_error (GPG_ERR_NO_PUBKEY);
            }
          else
            log_error ("failed to find the certificate: %s\n",
                       gpg_strerror(rc));
          {
            char numbuf[50];
            sprintf (numbuf, "%d", rc);

            gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey",
                           numbuf, NULL);
          }
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "no-cert");
          goto next_signer;
        }

      rc = keydb_get_cert (kh, &cert);
      if (rc)
        {
          log_error ("failed to get cert: %s\n", gpg_strerror (rc));
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
          goto next_signer;
        }

      log_info (_("Signature made "));
      if (*sigtime)
        dump_isotime (sigtime);
      else
        log_printf (_("[date not given]"));
      log_printf (_(" using certificate ID 0x%08lX\n"),
                  gpgsm_get_short_fingerprint (cert, NULL));

      audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);

      if (msgdigest)
        { /* Signed attributes are available. */
          gcry_md_hd_t md;
          unsigned char *s;

          /* Check that the message digest in the signed attributes
             matches the one we calculated on the data.  */
          s = gcry_md_read (data_md, algo);
          if ( !s || !msgdigestlen
               || gcry_md_get_algo_dlen (algo) != msgdigestlen
               || !s || memcmp (s, msgdigest, msgdigestlen) )
            {
              char *fpr;

              log_error (_("invalid signature: message digest attribute "
                           "does not match computed one\n"));
              if (DBG_X509)
                {
                  if (msgdigest)
                    log_printhex ("message:  ", msgdigest, msgdigestlen);
                  if (s)
                    log_printhex ("computed: ",
                                  s, gcry_md_get_algo_dlen (algo));
                }
              fpr = gpgsm_fpr_and_name_for_status (cert);
              gpgsm_status (ctrl, STATUS_BADSIG, fpr);
              xfree (fpr);
              audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
              goto next_signer; 
            }
            
          audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, sigval_hash_algo);
          rc = gcry_md_open (&md, sigval_hash_algo, 0);
          if (rc)
            {
              log_error ("md_open failed: %s\n", gpg_strerror (rc));
              audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
              goto next_signer;
            }
          if (DBG_HASHING)
            gcry_md_start_debug (md, "vrfy.attr");

          ksba_cms_set_hash_function (cms, HASH_FNC, md);
          rc = ksba_cms_hash_signed_attrs (cms, signer);
          if (rc)
            {
              log_error ("hashing signed attrs failed: %s\n",
                         gpg_strerror (rc));
              gcry_md_close (md);
              audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
              goto next_signer;
            }
          rc = gpgsm_check_cms_signature (cert, sigval, md, 
                                          sigval_hash_algo, &info_pkalgo);
          gcry_md_close (md);
        }
      else
        {
          rc = gpgsm_check_cms_signature (cert, sigval, data_md, 
                                          algo, &info_pkalgo);
        }

      if (rc)
        {
          char *fpr;

          log_error ("invalid signature: %s\n", gpg_strerror (rc));
          fpr = gpgsm_fpr_and_name_for_status (cert);
          gpgsm_status (ctrl, STATUS_BADSIG, fpr);
          xfree (fpr);
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
          goto next_signer;
        }
      rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/
      if (rc)
        {
          gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage",
                                      gpg_err_code (rc));
          rc = 0;
        }

      if (DBG_X509)
        log_debug ("signature okay - checking certs\n");
      audit_log (ctrl->audit, AUDIT_VALIDATE_CHAIN);
      rc = gpgsm_validate_chain (ctrl, cert,
                                 *sigtime? sigtime : "19700101T000000",
                                 keyexptime, 0, 
                                 NULL, 0, &verifyflags);
      {
        char *fpr, *buf, *tstr;

        fpr = gpgsm_fpr_and_name_for_status (cert);
        if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
          {
            gpgsm_status (ctrl, STATUS_EXPKEYSIG, fpr);
            rc = 0;
          }
        else
          gpgsm_status (ctrl, STATUS_GOODSIG, fpr);
        
        xfree (fpr);

        fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
        tstr = strtimestamp_r (sigtime);
        buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr,
                         *sigtime? sigtime : "0",
                         *keyexptime? keyexptime : "0",
                         info_pkalgo, algo);
        xfree (tstr);
        xfree (fpr);
        gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
        xfree (buf);
      }

      audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc);
      if (rc) /* of validate_chain */
        {
          log_error ("invalid certification chain: %s\n", gpg_strerror (rc));
          if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN
              || gpg_err_code (rc) == GPG_ERR_BAD_CERT
              || gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT
              || gpg_err_code (rc) == GPG_ERR_CERT_REVOKED)
            gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL,
                                        gpg_err_code (rc));
          else
            gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL, 
                                        gpg_err_code (rc));
          audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
          goto next_signer;
        }

      audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "good");

      for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
        {
          log_info (!i? _("Good signature from")
                      : _("                aka"));
          log_printf (" \"");
          gpgsm_print_name (log_get_stream (), p);
          log_printf ("\"\n");
          ksba_free (p);
        }

      /* Print a note if this is a qualified signature.  */
      {
        size_t qualbuflen;
        char qualbuffer[1];
        
        rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer,
                                      sizeof (qualbuffer), &qualbuflen);
        if (!rc && qualbuflen)
          {
            if (*qualbuffer)
              {
                log_info (_("This is a qualified signature\n"));
                if (!opt.qualsig_approval)
                  log_info 
                    (_("Note, that this software is not officially approved "
                       "to create or verify such signatures.\n"));
              }
          }    
        else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
          log_error ("get_user_data(is_qualified) failed: %s\n",
                     gpg_strerror (rc)); 
      }

      gpgsm_status (ctrl, STATUS_TRUST_FULLY, 
                    (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)?
                    "0 chain": "0 shell");
          

    next_signer:
      rc = 0;
      xfree (issuer);
      xfree (serial);
      xfree (sigval);
      xfree (msgdigest);
      ksba_cert_release (cert);
      cert = NULL;
    }
  rc = 0;

 leave:
  ksba_cms_release (cms);
  gpgsm_destroy_reader (b64reader);
  gpgsm_destroy_writer (b64writer);
  keydb_release (kh); 
  gcry_md_close (data_md);
  if (fp)
    fclose (fp);

  if (rc)
    {
      char numbuf[50];
      sprintf (numbuf, "%d", rc );
      gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave",
                     numbuf, NULL);
    }

  return rc;
}