g10: Correctly determine whether a binding has a conflict.
[gnupg.git] / g10 / sig-check.c
index 8f976ba..4d39e09 100644 (file)
@@ -1,7 +1,7 @@
 /* sig-check.c -  Check a signature
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
  *               2004, 2006 Free Software Foundation, Inc.
- * Copyright (C) 2015 g10 Code GmbH
+ * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #include "gpg.h"
 #include "util.h"
@@ -40,6 +39,9 @@ static int check_signature_end (PKT_public_key *pk, PKT_signature *sig,
                                int *r_expired, int *r_revoked,
                                PKT_public_key *ret_pk);
 
+static int check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig,
+                                       gcry_md_hd_t digest);
+
 /* Check a signature.  This is shorthand for check_signature2 with
    the unnamed arguments passed as NULL.  */
 int
@@ -82,23 +84,29 @@ check_signature (PKT_signature *sig, gcry_md_hd_t digest)
  * revoked (0 otherwise).  Note: PK being revoked does not cause this
  * function to fail.
  *
- * If PK is not NULL, the public key is saved in *PK on success.
+ * If R_PK is not NULL, the public key is stored at that address if it
+ * was found; other wise NULL is stored.
  *
  * Returns 0 on success.  An error code otherwise.  */
-int
+gpg_error_t
 check_signature2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
-                 int *r_expired, int *r_revoked, PKT_public_key *pk )
+                 int *r_expired, int *r_revoked, PKT_public_key **r_pk)
 {
     int rc=0;
-    int pk_internal;
+    PKT_public_key *pk;
 
-    if (pk)
-      pk_internal = 0;
-    else
-      {
-       pk_internal = 1;
-       pk = xmalloc_clear( sizeof *pk );
-      }
+    if (r_expiredate)
+      *r_expiredate = 0;
+    if (r_expired)
+      *r_expired = 0;
+    if (r_revoked)
+      *r_revoked = 0;
+    if (r_pk)
+      *r_pk = NULL;
+
+    pk = xtrycalloc (1, sizeof *pk);
+    if (!pk)
+      return gpg_error_from_syserror ();
 
     if ( (rc=openpgp_md_test_algo(sig->digest_algo)) )
       ; /* We don't have this digest. */
@@ -112,14 +120,14 @@ check_signature2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
           header is missing or does not match the actual sig. */
 
         log_info(_("WARNING: signature digest conflict in message\n"));
-       rc = GPG_ERR_GENERAL;
+       rc = gpg_error (GPG_ERR_GENERAL);
       }
     else if( get_pubkey( pk, sig->keyid ) )
-       rc = GPG_ERR_NO_PUBKEY;
-    else if(!pk->flags.valid && !pk->flags.primary)
+      rc = gpg_error (GPG_ERR_NO_PUBKEY);
+    else if(!pk->flags.valid)
       {
-        /* You cannot have a good sig from an invalid subkey.  */
-        rc = GPG_ERR_BAD_PUBKEY;
+        /* You cannot have a good sig from an invalid key.  */
+        rc = gpg_error (GPG_ERR_BAD_PUBKEY);
       }
     else
       {
@@ -134,7 +142,7 @@ check_signature2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
           them as their own.  The attacker couldn't actually use the
           subkey, but they could try and claim ownership of any
           signatures issued by it. */
-       if(rc==0 && !pk->flags.primary && pk->flags.backsig < 2)
+       if (!rc && !pk->flags.primary && pk->flags.backsig < 2)
          {
            if (!pk->flags.backsig)
              {
@@ -146,27 +154,17 @@ check_signature2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
                      error.  TODO: change the default to require this
                      after more keys have backsigs. */
                if(opt.flags.require_cross_cert)
-                 rc = GPG_ERR_GENERAL;
+                 rc = gpg_error (GPG_ERR_GENERAL);
              }
            else if(pk->flags.backsig == 1)
              {
                log_info(_("WARNING: signing subkey %s has an invalid"
                           " cross-certification\n"),keystr_from_pk(pk));
-               rc = GPG_ERR_GENERAL;
+               rc = gpg_error (GPG_ERR_GENERAL);
              }
          }
       }
 
-    if (pk_internal || rc)
-      {
-       release_public_key_parts (pk);
-       if (pk_internal)
-         xfree (pk);
-       else
-         /* Be very sure that the caller doesn't try to use *PK.  */
-         memset (pk, 0, sizeof (*pk));
-      }
-
     if( !rc && sig->sig_class < 2 && is_status_enabled() ) {
        /* This signature id works best with DLP algorithms because
         * they use a random parameter for every signature.  Instead of
@@ -233,6 +231,14 @@ check_signature2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
        xfree (buffer);
     }
 
+    if (r_pk)
+      *r_pk = pk;
+    else
+      {
+       release_public_key_parts (pk);
+        xfree (pk);
+      }
+
     return rc;
 }
 
@@ -268,10 +274,23 @@ check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig,
     if( pk->timestamp > sig->timestamp )
       {
        ulong d = pk->timestamp - sig->timestamp;
-       log_info
-          (ngettext("public key %s is %lu second newer than the signature\n",
-                    "public key %s is %lu seconds newer than the signature\n",
-                    d), keystr_from_pk (pk), d);
+        if ( d < 86400 )
+          {
+            log_info
+              (ngettext
+               ("public key %s is %lu second newer than the signature\n",
+                "public key %s is %lu seconds newer than the signature\n",
+                d), keystr_from_pk (pk), d);
+          }
+        else
+          {
+            d /= 86400;
+            log_info
+              (ngettext
+               ("public key %s is %lu day newer than the signature\n",
+                "public key %s is %lu days newer than the signature\n",
+                d), keystr_from_pk (pk), d);
+          }
        if (!opt.ignore_time_conflict)
          return GPG_ERR_TIME_CONFLICT; /* pubkey newer than signature.  */
       }
@@ -280,12 +299,24 @@ check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig,
     if( pk->timestamp > cur_time )
       {
        ulong d = pk->timestamp - cur_time;
-       log_info (ngettext("key %s was created %lu second"
-                           " in the future (time warp or clock problem)\n",
-                           "key %s was created %lu seconds"
-                           " in the future (time warp or clock problem)\n",
-                           d), keystr_from_pk (pk), d);
-       if( !opt.ignore_time_conflict )
+        if (d < 86400)
+          {
+            log_info (ngettext("key %s was created %lu second"
+                               " in the future (time warp or clock problem)\n",
+                               "key %s was created %lu seconds"
+                               " in the future (time warp or clock problem)\n",
+                               d), keystr_from_pk (pk), d);
+          }
+        else
+          {
+            d /= 86400;
+            log_info (ngettext("key %s was created %lu day"
+                               " in the future (time warp or clock problem)\n",
+                               "key %s was created %lu days"
+                               " in the future (time warp or clock problem)\n",
+                               d), keystr_from_pk (pk), d);
+          }
+       if (!opt.ignore_time_conflict)
          return GPG_ERR_TIME_CONFLICT;
       }
 
@@ -351,14 +382,32 @@ check_signature_end (PKT_public_key *pk, PKT_signature *sig,
                     gcry_md_hd_t digest,
                     int *r_expired, int *r_revoked, PKT_public_key *ret_pk)
 {
-    gcry_mpi_t result = NULL;
     int rc = 0;
-    const struct weakhash *weak;
 
     if ((rc = check_signature_metadata_validity (pk, sig,
                                                 r_expired, r_revoked)))
         return rc;
 
+    if ((rc = check_signature_end_simple (pk, sig, digest)))
+      return rc;
+
+    if(!rc && ret_pk)
+      copy_public_key(ret_pk,pk);
+
+    return rc;
+}
+
+/* This function is similar to check_signature_end, but it only checks
+   whether the signature was generated by PK.  It does not check
+   expiration, revocation, etc.  */
+static int
+check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig,
+                            gcry_md_hd_t digest)
+{
+    gcry_mpi_t result = NULL;
+    int rc = 0;
+    const struct weakhash *weak;
+
     if (!opt.flags.allow_weak_digest_algos)
       for (weak = opt.weak_digests; weak; weak = weak->next)
         if (sig->digest_algo == weak->algo)
@@ -428,9 +477,6 @@ check_signature_end (PKT_public_key *pk, PKT_signature *sig,
        rc = GPG_ERR_BAD_SIGNATURE;
       }
 
-    if(!rc && ret_pk)
-      copy_public_key(ret_pk,pk);
-
     return rc;
 }
 
@@ -438,11 +484,8 @@ check_signature_end (PKT_public_key *pk, PKT_signature *sig,
 /* Add a uid node to a hash context.  See section 5.2.4, paragraph 4
    of RFC 4880.  */
 static void
-hash_uid_node( KBNODE unode, gcry_md_hd_t md, PKT_signature *sig )
+hash_uid_packet (PKT_user_id *uid, gcry_md_hd_t md, PKT_signature *sig )
 {
-    PKT_user_id *uid = unode->pkt->pkt.user_id;
-
-    assert( unode->pkt->pkttype == PKT_USER_ID );
     if( uid->attrib_data ) {
        if( sig->version >=4 ) {
            byte buf[5];
@@ -523,8 +566,8 @@ check_revocation_keys (PKT_public_key *pk, PKT_signature *sig)
   int i;
   int rc = GPG_ERR_GENERAL;
 
-  assert(IS_KEY_REV(sig));
-  assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1]));
+  log_assert (IS_KEY_REV(sig));
+  log_assert ((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1]));
 
   /* Avoid infinite recursion.  Consider the following:
    *
@@ -648,6 +691,237 @@ check_key_signature (KBNODE root, KBNODE node, int *is_selfsig)
 }
 
 
+/* Returns whether SIGNER generated the signature SIG over the packet
+   PACKET, which is a key, subkey or uid, and comes from the key block
+   KB.  (KB is PACKET's corresponding keyblock; we don't assume that
+   SIG has been added to the keyblock.)
+
+   If SIGNER is set, then checks whether SIGNER generated the
+   signature.  Otherwise, uses SIG->KEYID to find the alleged signer.
+   This parameter can be used to effectively override the alleged
+   signer that is stored in SIG.
+
+   KB may be NULL if SIGNER is set.
+
+   Unlike check_key_signature, this function ignores any cached
+   results!  That is, it does not consider SIG->FLAGS.CHECKED and
+   SIG->FLAGS.VALID nor does it set them.
+
+   This doesn't check the signature's semantic mean.  Concretely, it
+   doesn't check whether a non-self signed revocation signature was
+   created by a designated revoker.  In fact, it doesn't return an
+   error for a binding generated by a completely different key!
+
+   Returns 0 if the signature is valid.  Returns GPG_ERR_SIG_CLASS if
+   this signature can't be over PACKET.  Returns GPG_ERR_NOT_FOUND if
+   the key that generated the signature (according to SIG) could not
+   be found.  Returns GPG_ERR_BAD_SIGNATURE if the signature is bad.
+   Other errors codes may be returned if something else goes wrong.
+
+   IF IS_SELFSIG is not NULL, sets *IS_SELFSIG to 1 if this is a
+   self-signature (by the key's primary key) or 0 if not.
+
+   If RET_PK is not NULL, returns a copy of the public key that
+   generated the signature (i.e., the signer) on success.  This must
+   be released by the caller using release_public_key_parts ().  */
+gpg_error_t
+check_signature_over_key_or_uid (PKT_public_key *signer,
+                                 PKT_signature *sig, KBNODE kb, PACKET *packet,
+                                 int *is_selfsig, PKT_public_key *ret_pk)
+{
+  int rc;
+  PKT_public_key *pripk = kb->pkt->pkt.public_key;
+  gcry_md_hd_t md;
+  int signer_alloced = 0;
+
+  rc = openpgp_pk_test_algo (sig->pubkey_algo);
+  if (rc)
+    return rc;
+  rc = openpgp_md_test_algo (sig->digest_algo);
+  if (rc)
+    return rc;
+
+  /* A signature's class indicates the type of packet that it
+     signs.  */
+  if (/* Primary key binding (made by a subkey).  */
+      sig->sig_class == 0x19
+      /* Direct key signature.  */
+      || sig->sig_class == 0x1f
+      /* Primary key revocation.  */
+      || sig->sig_class == 0x20)
+    {
+      if (packet->pkttype != PKT_PUBLIC_KEY)
+        /* Key revocations can only be over primary keys.  */
+        return gpg_error (GPG_ERR_SIG_CLASS);
+    }
+  else if (/* Subkey binding.  */
+           sig->sig_class == 0x18
+           /* Subkey revocation.  */
+           || sig->sig_class == 0x28)
+    {
+      if (packet->pkttype != PKT_PUBLIC_SUBKEY)
+        return gpg_error (GPG_ERR_SIG_CLASS);
+    }
+  else if (/* Certification.  */
+           sig->sig_class == 0x10
+           || sig->sig_class == 0x11
+           || sig->sig_class == 0x12
+           || sig->sig_class == 0x13
+           /* Certification revocation.  */
+           || sig->sig_class == 0x30)
+    {
+      if (packet->pkttype != PKT_USER_ID)
+        return gpg_error (GPG_ERR_SIG_CLASS);
+    }
+  else
+    return gpg_error (GPG_ERR_SIG_CLASS);
+
+  /* PACKET is the right type for SIG.  */
+
+  if (signer)
+    {
+      if (is_selfsig)
+        {
+          if (signer->keyid[0] == pripk->keyid[0]
+              && signer->keyid[1] == pripk->keyid[1])
+            *is_selfsig = 1;
+          else
+            *is_selfsig = 0;
+        }
+    }
+  else
+    {
+      /* Get the signer.  If possible, avoid a look up.  */
+      if (sig->keyid[0] == pripk->keyid[0]
+          && sig->keyid[1] == pripk->keyid[1])
+        /* Issued by the primary key.  */
+        {
+          signer = pripk;
+          if (is_selfsig)
+            *is_selfsig = 1;
+        }
+      else
+        {
+          kbnode_t ctx = NULL;
+          kbnode_t n;
+
+          /* See if one of the subkeys was the signer (although this
+             is extremely unlikely).  */
+          while ((n = walk_kbnode (kb, &ctx, 0)))
+            {
+              PKT_public_key *subk;
+
+              if (n->pkt->pkttype != PKT_PUBLIC_SUBKEY)
+                continue;
+
+              subk = n->pkt->pkt.public_key;
+              if (sig->keyid[0] == subk->keyid[0]
+                  && sig->keyid[1] == subk->keyid[1])
+                /* Issued by a subkey.  */
+                {
+                  signer = subk;
+                  break;
+                }
+            }
+
+          if (! signer)
+            /* Signer by some other key.  */
+            {
+              if (is_selfsig)
+                *is_selfsig = 0;
+              if (ret_pk)
+                {
+                  signer = ret_pk;
+                  memset (signer, 0, sizeof (*signer));
+                  signer_alloced = 1;
+                }
+              else
+                {
+                  signer = xmalloc_clear (sizeof (*signer));
+                  signer_alloced = 2;
+                }
+
+              rc = get_pubkey (signer, sig->keyid);
+              if (rc)
+                {
+                  xfree (signer);
+                  signer = NULL;
+                  signer_alloced = 0;
+                  goto out;
+                }
+            }
+        }
+    }
+
+  /* We checked above that we supported this algo, so an error here is
+     a bug.  */
+  if (gcry_md_open (&md, sig->digest_algo, 0))
+    BUG ();
+
+  /* Hash the relevant data.  */
+
+  if (/* Direct key signature.  */
+      sig->sig_class == 0x1f
+      /* Primary key revocation.  */
+      || sig->sig_class == 0x20)
+    {
+      log_assert (packet->pkttype == PKT_PUBLIC_KEY);
+      hash_public_key (md, packet->pkt.public_key);
+      rc = check_signature_end_simple (signer, sig, md);
+    }
+  else if (/* Primary key binding (made by a subkey).  */
+      sig->sig_class == 0x19)
+    {
+      log_assert (packet->pkttype == PKT_PUBLIC_KEY);
+      hash_public_key (md, packet->pkt.public_key);
+      hash_public_key (md, signer);
+      rc = check_signature_end_simple (signer, sig, md);
+    }
+  else if (/* Subkey binding.  */
+           sig->sig_class == 0x18
+           /* Subkey revocation.  */
+           || sig->sig_class == 0x28)
+    {
+      log_assert (packet->pkttype == PKT_PUBLIC_SUBKEY);
+      hash_public_key (md, pripk);
+      hash_public_key (md, packet->pkt.public_key);
+      rc = check_signature_end_simple (signer, sig, md);
+    }
+  else if (/* Certification.  */
+           sig->sig_class == 0x10
+           || sig->sig_class == 0x11
+           || sig->sig_class == 0x12
+           || sig->sig_class == 0x13
+           /* Certification revocation.  */
+           || sig->sig_class == 0x30)
+    {
+      log_assert (packet->pkttype == PKT_USER_ID);
+      hash_public_key (md, pripk);
+      hash_uid_packet (packet->pkt.user_id, md, sig);
+      rc = check_signature_end_simple (signer, sig, md);
+    }
+  else
+    /* We should never get here.  (The first if above should have
+       already caught this error.)  */
+    BUG ();
+
+  gcry_md_close (md);
+
+ out:
+  if (! rc && ret_pk && (signer_alloced == -1 || ret_pk != signer))
+    copy_public_key (ret_pk, signer);
+  if (signer_alloced == 1)
+    /* We looked up SIGNER; it is not a pointer into KB.  */
+    {
+      release_public_key_parts (signer);
+      if (signer_alloced == 2)
+        /* We also allocated the memory.  */
+        xfree (signer);
+    }
+
+  return rc;
+}
+
 /* Check that a signature over a key (e.g., a key revocation, key
  * binding, user id certification, etc.) is valid.  If the function
  * detects a self-signature, it uses the public key from the specified
@@ -687,7 +961,6 @@ check_key_signature2 (kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
                       PKT_public_key *ret_pk, int *is_selfsig,
                       u32 *r_expiredate, int *r_expired )
 {
-  gcry_md_hd_t md;
   PKT_public_key *pk;
   PKT_signature *sig;
   int algo;
@@ -699,8 +972,8 @@ check_key_signature2 (kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
     *r_expiredate = 0;
   if (r_expired)
     *r_expired = 0;
-  assert (node->pkt->pkttype == PKT_SIGNATURE);
-  assert (root->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (node->pkt->pkttype == PKT_SIGNATURE);
+  log_assert (root->pkt->pkttype == PKT_PUBLIC_KEY);
 
   pk = root->pkt->pkt.public_key;
   sig = node->pkt->pkt.signature;
@@ -748,114 +1021,69 @@ check_key_signature2 (kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
         rc = check_revocation_keys (pk, sig);
       else
         {
-          if (gcry_md_open (&md, algo, 0))
-            BUG ();
-          hash_public_key (md, pk);
-          rc = check_signature_end (pk, sig, md, r_expired, NULL, ret_pk);
-          cache_sig_result (sig, rc);
-          gcry_md_close (md);
+          rc = check_signature_metadata_validity (pk, sig,
+                                                  r_expired, NULL);
+          if (! rc)
+            rc = check_signature_over_key_or_uid (pk, sig, root, root->pkt,
+                                                  is_selfsig, ret_pk);
         }
     }
-  else if (sig->sig_class == 0x28) /* subkey revocation */
+  else if (sig->sig_class == 0x28  /* subkey revocation */
+           || sig->sig_class == 0x18) /* key binding */
     {
       kbnode_t snode = find_prev_kbnode (root, node, PKT_PUBLIC_SUBKEY);
 
       if (snode)
         {
-          if (gcry_md_open (&md, algo, 0))
-            BUG ();
-          hash_public_key (md, pk);
-          hash_public_key (md, snode->pkt->pkt.public_key);
-          rc = check_signature_end (pk, sig, md, r_expired, NULL, ret_pk);
-          cache_sig_result (sig, rc);
-          gcry_md_close (md);
+          rc = check_signature_metadata_validity (pk, sig,
+                                                  r_expired, NULL);
+          if (! rc)
+            /* 0x28 must be a self-sig, but 0x18 needn't be.  */
+            rc = check_signature_over_key_or_uid (sig->sig_class == 0x18
+                                                  ? NULL : pk,
+                                                  sig, root, snode->pkt,
+                                                  is_selfsig, ret_pk);
        }
       else
         {
           if (opt.verbose)
-            log_info (_("key %s: no subkey for subkey"
-                        " revocation signature\n"), keystr_from_pk(pk));
+            {
+              if (sig->sig_class == 0x28)
+                log_info (_("key %s: no subkey for subkey"
+                            " revocation signature\n"), keystr_from_pk(pk));
+              else if (sig->sig_class == 0x18)
+                log_info(_("key %s: no subkey for subkey"
+                           " binding signature\n"), keystr_from_pk(pk));
+            }
           rc = GPG_ERR_SIG_CLASS;
         }
     }
-    else if (sig->sig_class == 0x18) /* key binding */
-      {
-       kbnode_t snode = find_prev_kbnode (root, node, PKT_PUBLIC_SUBKEY);
-
-       if (snode)
-          {
-           if (is_selfsig)
-              {
-                /* Does this make sense?  It should always be a
-                   selfsig.  Yes: We can't be sure about this and we
-                   need to be able to indicate that it is a selfsig.
-                   FIXME: The question is whether we should reject
-                   such a signature if it is not a selfsig.  */
-               u32 keyid[2];
-
-               keyid_from_pk (pk, keyid);
-               if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1])
-                  *is_selfsig = 1;
-              }
-           if (gcry_md_open (&md, algo, 0))
-              BUG ();
-           hash_public_key (md, pk);
-           hash_public_key (md, snode->pkt->pkt.public_key);
-           rc = check_signature_end (pk, sig, md, r_expired, NULL, ret_pk);
-            cache_sig_result ( sig, rc );
-           gcry_md_close (md);
-          }
-       else
-         {
-            if (opt.verbose)
-             log_info(_("key %s: no subkey for subkey"
-                        " binding signature\n"), keystr_from_pk(pk));
-           rc = GPG_ERR_SIG_CLASS;
-         }
-      }
     else if (sig->sig_class == 0x1f) /* direct key signature */
       {
-        if (gcry_md_open (&md, algo, 0 ))
-          BUG ();
-       hash_public_key( md, pk );
-       rc = check_signature_end (pk, sig, md, r_expired, NULL, ret_pk);
-        cache_sig_result (sig, rc);
-       gcry_md_close (md);
+        rc = check_signature_metadata_validity (pk, sig,
+                                                r_expired, NULL);
+        if (! rc)
+          rc = check_signature_over_key_or_uid (pk, sig, root, root->pkt,
+                                                is_selfsig, ret_pk);
       }
-    else /* all other classes */
+    else if (/* Certification.  */
+             sig->sig_class == 0x10
+             || sig->sig_class == 0x11
+             || sig->sig_class == 0x12
+             || sig->sig_class == 0x13
+             /* Certification revocation.  */
+             || sig->sig_class == 0x30)
       {
        kbnode_t unode = find_prev_kbnode (root, node, PKT_USER_ID);
 
        if (unode)
           {
-           u32 keyid[2];
-
-           keyid_from_pk (pk, keyid);
-           if (gcry_md_open (&md, algo, 0))
-              BUG ();
-           hash_public_key (md, pk);
-           hash_uid_node (unode, md, sig);
-           if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1])
-             { /* The primary key is the signing key.  */
-
-               if (is_selfsig)
-                 *is_selfsig = 1;
-               rc = check_signature_end (pk, sig, md, r_expired, NULL, ret_pk);
-             }
-           else if (check_pk)
-              { /* The caller specified a key.  Try that.  */
-
-                rc = check_signature_end (check_pk, sig, md,
-                                          r_expired, NULL, ret_pk);
-              }
-           else
-              { /* Look up the key.  */
-                rc = check_signature2 (sig, md, r_expiredate, r_expired,
-                                       NULL, ret_pk);
-              }
-
-            cache_sig_result  (sig, rc);
-           gcry_md_close (md);
+            rc = check_signature_metadata_validity (pk, sig, r_expired, NULL);
+            if (! rc)
+              /* If this is a self-sig, ignore check_pk.  */
+              rc = check_signature_over_key_or_uid
+                (keyid_cmp (pk_keyid (pk), sig->keyid) == 0 ? pk : check_pk,
+                 sig, root, unode->pkt, NULL, ret_pk);
           }
        else
          {
@@ -865,6 +1093,16 @@ check_key_signature2 (kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
            rc = GPG_ERR_SIG_CLASS;
          }
       }
+  else
+    {
+      log_info ("sig issued by %s with class %d (digest: %02x %02x)"
+                " is not valid over a user id or a key id, ignoring.\n",
+                keystr (sig->keyid), sig->sig_class,
+                sig->digest_start[0], sig->digest_start[1]);
+      rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
+    }
+
+  cache_sig_result  (sig, rc);
 
   return rc;
 }