gpg: Revamp reading and writing of ring trust packets.
authorWerner Koch <wk@gnupg.org>
Thu, 30 Mar 2017 07:07:02 +0000 (09:07 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 30 Mar 2017 07:07:02 +0000 (09:07 +0200)
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this.  Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META.  Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants.  They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto.  Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta.  Remove special treatment
of ring trust packets and initialization of the signature caches.
--

This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes.  Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.

Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".

The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately.  Thus only the parser sees
the ring trust packets.  Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet.  That
function writes a ring trust packet when the needed information is
available.

As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.

Signed-off-by: Werner Koch <wk@gnupg.org>
doc/DETAILS
g10/build-packet.c
g10/export.c
g10/free-packet.c
g10/import.c
g10/keydb.c
g10/keydb.h
g10/keyring.c
g10/mainproc.c
g10/packet.h
g10/parse-packet.c

index 83d9fea..1624315 100644 (file)
@@ -1311,6 +1311,43 @@ CREATE TABLE signatures (
   S2K Specifier with an offset of 1000.
 
 
+* Format of the OpenPGP TRUST packet
+
+  According to RFC4880 (5.10), the trust packet (aka ring trust) is
+  only used within keyrings and contains data that records the user's
+  specifications of which key holds trusted introducers.  The RFC also
+  states that the format of this packet is implementation defined and
+  SHOULD NOT be emitted to output streams or should be ignored on
+  import.  GnuPG uses this packet in several additional ways:
+
+  - 1 octet :: Trust-Value (only used by Subtype SIG)
+  - 1 octet :: Signature-Cache (only used by Subtype SIG; value must
+               be less than 128)
+  - 3 octets :: Fixed value: "gpg"
+  - 1 octet  :: Subtype
+               - 0 :: Signature cache (SIG)
+               - 1 :: Key source on the primary key (KEY)
+               - 2 :: Key source on a user id (UID)
+  - 1 octet :: Key Source; i.e. the origin of the key:
+               - 0 :: Unknown source.
+               - 1 :: Direct import from a file.
+               - 2 :: Public keyserver.
+               - 3 :: Preferred keysrver.
+               - 4 :: Web Key Directory.
+               - 5 :: Web Key Directory via sub-domain.
+               - 6 :: OpenPGP DANE.
+  - 4 octets :: Time of last update.  This is a a four-octet scalar
+                with the seconds since Epoch.
+  - 1 octet  :: Scalar with the length of the following field.
+  - N octets :: String with the URL of the source.  This may be a
+                zero-length string.
+
+  If the packets contains only two octets a Subtype of 0 is assumed;
+  this is the only format recognized by GnuPG versions < 2.1.18.
+  Trust-Value and Signature-Cache must be zero for all subtypes other
+  than SIG.
+
+
 * Keyserver helper message format
 
   *This information is obsolete*
index 512e55c..60e7d45 100644 (file)
@@ -33,6 +33,7 @@
 #include "options.h"
 #include "../common/host2net.h"
 
+static gpg_error_t do_ring_trust (iobuf_t out, PKT_ring_trust *rt);
 static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid );
 static int do_key (iobuf_t out, int ctb, PKT_public_key *pk);
 static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc );
@@ -76,14 +77,11 @@ ctb_pkttype (int ctb)
     return (ctb & ((1 << 6) - 1)) >> 2;
 }
 
-/****************
- * Build a packet and write it to INP
- * Returns: 0 := okay
- *        >0 := error
- * Note: Caller must free the packet
- */
+
+/* Build a packet and write it to the stream OUT.
+ * Returns: 0 on success or on an error code.  */
 int
-build_packet( IOBUF out, PACKET *pkt )
+build_packet (IOBUF out, PACKET *pkt)
 {
   int rc = 0;
   int new_ctb = 0;
@@ -170,7 +168,7 @@ build_packet( IOBUF out, PACKET *pkt )
       rc = do_onepass_sig (out, ctb, pkt->pkt.onepass_sig);
       break;
     case PKT_RING_TRUST:
-      /* Ignore it (keyring.c does write it directly)  */
+      /* Ignore it (only written by build_packet_and_meta)  */
       break;
     case PKT_MDC:
       /* We write it directly, so we should never see it here. */
@@ -183,6 +181,62 @@ build_packet( IOBUF out, PACKET *pkt )
 }
 
 
+/* Build a packet and write it to the stream OUT.  This variant also
+ * writes the meta data using ring tyrust packets.  Returns: 0 on
+ * success or on aerror code.  */
+gpg_error_t
+build_packet_and_meta (iobuf_t out, PACKET *pkt)
+{
+  gpg_error_t err;
+  PKT_ring_trust rt = {0};
+
+  err = build_packet (out, pkt);
+  if (err)
+    ;
+  else if (pkt->pkttype == PKT_SIGNATURE)
+    {
+      PKT_signature *sig = pkt->pkt.signature;
+
+      rt.subtype = RING_TRUST_SIG;
+      /* Note: trustval is not yet used.  */
+      if (sig->flags.checked)
+        {
+          rt.sigcache = 1;
+          if (sig->flags.valid)
+            rt.sigcache |= 2;
+        }
+      err = do_ring_trust (out, &rt);
+    }
+  else if (pkt->pkttype == PKT_USER_ID
+           || pkt->pkttype == PKT_ATTRIBUTE)
+    {
+      PKT_user_id *uid = pkt->pkt.user_id;
+
+      rt.subtype = RING_TRUST_UID;
+      rt.keysrc = uid->keysrc;
+      rt.keyupdate = uid->keyupdate;
+      rt.url = uid->updateurl;
+      err = do_ring_trust (out, &rt);
+      rt.url = NULL;
+    }
+  else if (pkt->pkttype == PKT_PUBLIC_KEY
+           || pkt->pkttype == PKT_SECRET_KEY)
+    {
+      PKT_public_key *pk = pkt->pkt.public_key;
+
+      rt.subtype = RING_TRUST_KEY;
+      rt.keysrc = pk->keysrc;
+      rt.keyupdate = pk->keyupdate;
+      rt.url = pk->updateurl;
+      err = do_ring_trust (out, &rt);
+      rt.url = NULL;
+
+    }
+
+  return err;
+}
+
+
 /*
  * Write the mpi A to OUT.
  */
@@ -320,6 +374,38 @@ write_fake_data (IOBUF out, gcry_mpi_t a)
 }
 
 
+/* Write a ring trust meta packet.  */
+static gpg_error_t
+do_ring_trust (iobuf_t out, PKT_ring_trust *rt)
+{
+  unsigned int namelen = 0;
+  unsigned int pktlen = 6;
+
+  if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID)
+    {
+      if (rt->url)
+        namelen = strlen (rt->url);
+      pktlen += 1 + 4 + 1 + namelen;
+    }
+
+  write_header (out, (0x80 | ((PKT_RING_TRUST & 15)<<2)), pktlen);
+  iobuf_put (out, rt->trustval);
+  iobuf_put (out, rt->sigcache);
+  iobuf_write (out, "gpg", 3);
+  iobuf_put (out, rt->subtype);
+  if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID)
+    {
+      iobuf_put (out, rt->keysrc);
+      write_32 (out, rt->keyupdate);
+      iobuf_put (out, namelen);
+      if (namelen)
+        iobuf_write (out, rt->url, namelen);
+    }
+
+  return 0;
+}
+
+
 /* Serialize the user id (RFC 4880, Section 5.11) or the user
  * attribute UID (Section 5.12) and write it to OUT.
  *
index 5b0c81d..e2adcc4 100644 (file)
@@ -1284,14 +1284,17 @@ write_keyblock_to_output (kbnode_t keyblock, int with_armor,
     {
       if (is_deleted_kbnode (node))
         continue;
-      if (node->pkt->pkttype == PKT_RING_TRUST && !(options & EXPORT_BACKUP))
-        continue;
+      if (node->pkt->pkttype == PKT_RING_TRUST)
+        continue; /* Skip - they should not be here anyway.  */
 
       if (!pk && (node->pkt->pkttype == PKT_PUBLIC_KEY
                   || node->pkt->pkttype == PKT_SECRET_KEY))
         pk = node->pkt->pkt.public_key;
 
-      err = build_packet (out_help? out_help : out, node->pkt);
+      if ((options & EXPORT_BACKUP))
+        err = build_packet_and_meta (out_help? out_help : out, node->pkt);
+      else
+        err = build_packet (out_help? out_help : out, node->pkt);
       if (err)
         {
           log_error ("build_packet(%d) failed: %s\n",
@@ -1555,9 +1558,8 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
       if (node->pkt->pkttype == PKT_COMMENT)
         continue;
 
-      /* Make sure that ring_trust packets are only exported in backup
-       * mode. */
-      if (node->pkt->pkttype == PKT_RING_TRUST && !(options & EXPORT_BACKUP))
+      /* Skip ring trust packets - they should not ne here anyway.  */
+      if (node->pkt->pkttype == PKT_RING_TRUST)
         continue;
 
       /* If exact is set, then we only export what was requested
@@ -1723,7 +1725,10 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
                     ski->iv[ski->ivlen] = xtoi_2 (s);
                 }
 
-              err = build_packet (out, node->pkt);
+              if ((options & EXPORT_BACKUP))
+                err = build_packet_and_meta (out, node->pkt);
+              else
+                err = build_packet (out, node->pkt);
               if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
                 {
                   stats->exported++;
@@ -1744,7 +1749,10 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
                 }
               else
                 {
-                  err = build_packet (out, node->pkt);
+                  if ((options & EXPORT_BACKUP))
+                    err = build_packet_and_meta (out, node->pkt);
+                  else
+                    err = build_packet (out, node->pkt);
                   if (node->pkt->pkttype == PKT_PUBLIC_KEY)
                     {
                       stats->exported++;
@@ -1775,7 +1783,10 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
         }
       else /* Not secret or common packets.  */
         {
-          err = build_packet (out, node->pkt);
+          if ((options & EXPORT_BACKUP))
+            err = build_packet_and_meta (out, node->pkt);
+          else
+            err = build_packet (out, node->pkt);
           if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
             {
               stats->exported++;
index 535a17f..c144246 100644 (file)
@@ -127,6 +127,11 @@ release_public_key_parts (PKT_public_key *pk)
       xfree (pk->serialno);
       pk->serialno = NULL;
     }
+  if (pk->updateurl)
+    {
+      xfree (pk->updateurl);
+      pk->updateurl = NULL;
+    }
 }
 
 
@@ -314,6 +319,7 @@ free_user_id (PKT_user_id *uid)
   free_attributes(uid);
   xfree (uid->prefs);
   xfree (uid->namehash);
+  xfree (uid->updateurl);
   xfree (uid->mbox);
   xfree (uid);
 }
index 3321a7e..d43b2a8 100644 (file)
@@ -97,8 +97,8 @@ static int import (ctrl_t ctrl,
                    IOBUF inp, const char* fname, struct import_stats_s *stats,
                   unsigned char **fpr, size_t *fpr_len, unsigned int options,
                   import_screener_t screener, void *screener_arg);
-static int read_block (IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root,
-                       int *r_v3keys);
+static int read_block (IOBUF a, int with_meta,
+                       PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys);
 static void revocation_present (ctrl_t ctrl, kbnode_t keyblock);
 static int import_one (ctrl_t ctrl,
                        kbnode_t keyblock,
@@ -333,7 +333,7 @@ read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock)
   }
 
   /* Read the first non-v3 keyblock.  */
-  while (!(err = read_block (inp, &pending_pkt, &keyblock, &v3keys)))
+  while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys)))
     {
       if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY)
         break;
@@ -563,7 +563,8 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
       release_armor_context (afx);
     }
 
-  while (!(rc = read_block (inp, &pending_pkt, &keyblock, &v3keys)))
+  while (!(rc = read_block (inp, !!(options & IMPORT_RESTORE),
+                            &pending_pkt, &keyblock, &v3keys)))
     {
       stats->v3keys += v3keys;
       if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY)
@@ -637,7 +638,7 @@ import_old_secring (ctrl_t ctrl, const char *fname)
 
   getkey_disable_caches();
   stats = import_new_stats_handle ();
-  while (!(err = read_block (inp, &pending_pkt, &keyblock, &v3keys)))
+  while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys)))
     {
       if (keyblock->pkt->pkttype == PKT_SECRET_KEY)
         err = import_secret_one (ctrl, keyblock, stats, 1, 0, 1,
@@ -752,14 +753,15 @@ valid_keyblock_packet (int pkttype)
 
 /****************
  * Read the next keyblock from stream A.
- * PENDING_PKT should be initialzed to NULL
- * and not changed by the caller.
+ * Meta data (ring trust packets) are only considered of WITH_META is set.
+ * PENDING_PKT should be initialzed to NULL and not changed by the caller.
  * Return: 0 = okay, -1 no more blocks or another errorcode.
  *         The int at at R_V3KEY counts the number of unsupported v3
  *         keyblocks.
  */
 static int
-read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys)
+read_block( IOBUF a, int with_meta,
+            PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys)
 {
   int rc;
   struct parse_packet_ctx_s parsectx;
@@ -781,6 +783,8 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys)
   pkt = xmalloc (sizeof *pkt);
   init_packet (pkt);
   init_parse_packet (&parsectx, a);
+  if (!with_meta)
+    parsectx.skip_meta = 1;
   in_v3key = 0;
   while ((rc=parse_packet (&parsectx, pkt)) != -1)
     {
index 1bbda35..67957f8 100644 (file)
@@ -1202,6 +1202,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
         case PKT_USER_ID:
         case PKT_ATTRIBUTE:
         case PKT_SIGNATURE:
+        case PKT_RING_TRUST:
           break; /* Allowed per RFC.  */
 
         default:
@@ -1458,14 +1459,13 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
         case PKT_SIGNATURE:
         case PKT_USER_ID:
         case PKT_ATTRIBUTE:
-          /* Note that we don't want the ring trust packets.  They are
-             not useful. */
+        case PKT_RING_TRUST:
           break;
         default:
           continue;
         }
 
-      err = build_packet (iobuf, node->pkt);
+      err = build_packet_and_meta (iobuf, node->pkt);
       if (err)
         {
           iobuf_close (iobuf);
index 6f57583..2de52d5 100644 (file)
@@ -119,6 +119,19 @@ union pref_hint
 };
 
 
+/* Constants to describe from where a key was fetched or updated.  */
+enum
+  {
+    KEYSRC_UNKNOWN = 0,
+    KEYSRC_FILE    = 1, /* Direct import from a file.  */
+    KEYSRC_KS      = 2, /* Public keyserver.    */
+    KEYSRC_PREF_KS = 3, /* Preferred keysrver.  */
+    KEYSRC_WKD     = 4, /* Web Key Directory.   */
+    KEYSRC_WKD_SD  = 5, /* Web Key Directory but from a sub domain.  */
+    KEYSRC_DANE    = 6  /* OpenPGP DANE.  */
+  };
+
+
 /*-- keydb.c --*/
 
 #define KEYDB_RESOURCE_FLAG_PRIMARY  2  /* The primary resource.  */
index 2210df9..e7ebbb3 100644 (file)
@@ -473,29 +473,6 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
         }
 
         in_cert = 1;
-        if (pkt->pkttype == PKT_RING_TRUST)
-          {
-            /*(this code is duplicated after the loop)*/
-            if ( lastnode
-                 && lastnode->pkt->pkttype == PKT_SIGNATURE
-                 && (pkt->pkt.ring_trust->sigcache & 1) ) {
-                /* This is a ring trust packet with a checked signature
-                 * status cache following directly a signature paket.
-                 * Set the cache status into that signature packet.  */
-                PKT_signature *sig = lastnode->pkt->pkt.signature;
-
-                sig->flags.checked = 1;
-                sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
-            }
-            /* Reset LASTNODE, so that we set the cache status only from
-             * the ring trust packet immediately following a signature. */
-            lastnode = NULL;
-           free_packet(pkt, &parsectx);
-           init_packet(pkt);
-           continue;
-          }
-
-
         node = lastnode = new_kbnode (pkt);
         if (!keyblock)
           keyblock = node;
@@ -531,16 +508,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
     if (rc || !ret_kb)
        release_kbnode (keyblock);
     else {
-        /*(duplicated from the loop body)*/
-        if ( pkt && pkt->pkttype == PKT_RING_TRUST
-             && lastnode
-             && lastnode->pkt->pkttype == PKT_SIGNATURE
-             && (pkt->pkt.ring_trust->sigcache & 1) ) {
-            PKT_signature *sig = lastnode->pkt->pkt.signature;
-            sig->flags.checked = 1;
-            sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
-        }
-       *ret_kb = keyblock;
+        *ret_kb = keyblock;
     }
     free_packet (pkt, &parsectx);
     deinit_parse_packet (&parsectx);
@@ -1420,36 +1388,12 @@ write_keyblock (IOBUF fp, KBNODE keyblock)
 
   while ( (node = walk_kbnode (keyblock, &kbctx, 0)) )
     {
-      if (node->pkt->pkttype == PKT_RING_TRUST)
-        continue; /* we write it later on our own */
-
-      if ( (rc = build_packet (fp, node->pkt) ))
+      if ( (rc = build_packet_and_meta (fp, node->pkt) ))
         {
           log_error ("build_packet(%d) failed: %s\n",
                      node->pkt->pkttype, gpg_strerror (rc) );
           return rc;
         }
-      if (node->pkt->pkttype == PKT_SIGNATURE)
-        { /* always write a signature cache packet */
-          PKT_signature *sig = node->pkt->pkt.signature;
-          unsigned int cacheval = 0;
-
-          if (sig->flags.checked)
-            {
-              cacheval |= 1;
-              if (sig->flags.valid)
-                cacheval |= 2;
-            }
-          iobuf_put (fp, 0xb0); /* old style packet 12, 1 byte len*/
-          iobuf_put (fp, 2);    /* 2 bytes */
-          iobuf_put (fp, 0);    /* unused */
-          if (iobuf_put (fp, cacheval))
-            {
-              rc = gpg_error_from_syserror ();
-              log_error ("writing sigcache packet failed\n");
-              return rc;
-            }
-        }
     }
   return 0;
 }
@@ -1640,6 +1584,7 @@ keyring_rebuild_cache (void *token,int noisy)
   return rc;
 }
 
+
 \f
 /****************
  * Perform insert/delete/update operation.
index 8581104..7b2c456 100644 (file)
@@ -1046,13 +1046,6 @@ list_node (CTX c, kbnode_t node)
       else if ((pk->flags.primary && opt.fingerprint) || opt.fingerprint > 1)
         print_fingerprint (NULL, pk, 0);
 
-      if (opt.with_colons)
-        {
-          if (node->next && node->next->pkt->pkttype == PKT_RING_TRUST)
-            es_printf ("rtv:1:%u:\n",
-                       node->next->pkt->pkt.ring_trust->trustval);
-        }
-
       if (pk->flags.primary)
         {
           int kl = opt.keyid_format == KF_NONE? 0 : keystrlen ();
@@ -1077,14 +1070,6 @@ list_node (CTX c, kbnode_t node)
                   if (opt.with_colons)
                     es_putc (':', es_stdout);
                   es_putc ('\n', es_stdout);
-                  if (opt.with_colons
-                      && node->next
-                      && node->next->pkt->pkttype == PKT_RING_TRUST)
-                    {
-                      es_printf ("rtv:2:%u:\n",
-                                 node->next->pkt->pkt.ring_trust?
-                                 node->next->pkt->pkt.ring_trust->trustval : 0);
-                    }
                }
               else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
                 {
index ad6f317..b23298a 100644 (file)
@@ -283,20 +283,25 @@ typedef struct
   u32 expiredate;       /* expires at this date or 0 if not at all */
   prefitem_t *prefs;    /* list of preferences (may be NULL)*/
   u32 created;          /* according to the self-signature */
+  u32 keyupdate;        /* From the ring trust packet.  */
+  char *updateurl;      /* NULL or the URL of the last update origin.  */
+  byte keysrc;          /* From the ring trust packet.  */
   byte selfsigversion;
   struct
   {
     unsigned int mdc:1;
     unsigned int ks_modify:1;
     unsigned int compacted:1;
-    unsigned int primary:2;       /* 2 if set via the primary flag, 1 if calculated */
+    unsigned int primary:2; /* 2 if set via the primary flag, 1 if calculated */
     unsigned int revoked:1;
     unsigned int expired:1;
   } flags;
+
   char *mbox;   /* NULL or the result of mailbox_from_userid.  */
+
   /* The text contained in the user id packet, which is normally the
-     name and email address of the key holder (See RFC 4880 5.11).
-     (Serialized.). For convenience an extra Nul is always appended.  */
+   * name and email address of the key holder (See RFC 4880 5.11).
+   * (Serialized.). For convenience an extra Nul is always appended.  */
   char name[1];
 } PKT_user_id;
 
@@ -402,6 +407,9 @@ typedef struct
   u32     trust_timestamp;
   byte    trust_depth;
   byte    trust_value;
+  byte    keysrc;         /* From the ring trust packet.  */
+  u32     keyupdate;      /* From the ring trust packet.  */
+  char    *updateurl;     /* NULL or the URL of the last update origin.  */
   const byte *trust_regexp;
   char    *serialno;      /* Malloced hex string or NULL if it is
                              likely not on a card.  See also
@@ -474,11 +482,28 @@ typedef struct {
     byte hash[20];
 } PKT_mdc;
 
+
+/* Subtypes for the ring trust packet.  */
+#define RING_TRUST_SIG 0  /* The classical signature cache.  */
+#define RING_TRUST_KEY 1  /* A KEYSRC on a primary key.      */
+#define RING_TRUST_UID 2  /* A KEYSRC on a user id.          */
+
+/* The local only ring trust packet which OpenPGP declares as
+ * implementation defined.  GnuPG uses this to cache signature
+ * verification status and since 2.1.18 also to convey information
+ * about the origin of a key.  Note that this packet is not part
+ * struct packet_struct becuase we use it only local in the packet
+ * parser and builder. */
 typedef struct {
-    unsigned int trustval;
-    unsigned int sigcache;
+  unsigned int trustval;
+  unsigned int sigcache;
+  unsigned char subtype; /* The subtype of this ring trust packet.   */
+  unsigned char keysrc;  /* The origin of the key (KEYSRC_*).        */
+  u32 keyupdate;         /* The wall time the key was last updated.  */
+  char *url;             /* NULL or the URL of the source.           */
 } PKT_ring_trust;
 
+
 /* A plaintext packet (see RFC 4880, 5.9).  */
 typedef struct {
   /* The length of data in BUF or 0 if unknown.  */
@@ -519,7 +544,6 @@ struct packet_struct {
        PKT_compressed  *compressed;    /* PKT_COMPRESSED */
        PKT_encrypted   *encrypted;     /* PKT_ENCRYPTED[_MDC] */
        PKT_mdc         *mdc;           /* PKT_MDC */
-       PKT_ring_trust  *ring_trust;    /* PKT_RING_TRUST */
        PKT_plaintext   *plaintext;     /* PKT_PLAINTEXT */
         PKT_gpg_control *gpg_control;   /* PKT_GPG_CONTROL */
     } pkt;
@@ -599,6 +623,7 @@ struct parse_packet_ctx_s
   iobuf_t inp;       /* The input stream with the packets.  */
   PACKET *last_pkt;  /* The last parsed packet.  */
   int free_last_pkt; /* Indicates that LAST_PKT must be freed.  */
+  int skip_meta;     /* Skip right trust packets.  */
 };
 typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
 
@@ -606,6 +631,7 @@ typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
     (a)->inp = (i);                 \
     (a)->last_pkt = NULL;           \
     (a)->free_last_pkt = 0;         \
+    (a)->skip_meta = 0;             \
   } while (0)
 
 #define deinit_parse_packet(a) do { \
@@ -786,7 +812,8 @@ PACKET *create_gpg_control ( ctrlpkttype_t type,
                              size_t datalen );
 
 /*-- build-packet.c --*/
-int build_packet( iobuf_t inp, PACKET *pkt );
+int build_packet (iobuf_t out, PACKET *pkt);
+gpg_error_t build_packet_and_meta (iobuf_t out, PACKET *pkt);
 gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a);
 gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a);
 u32 calc_packet_length( PACKET *pkt );
index ab273a5..df04fbc 100644 (file)
@@ -74,8 +74,8 @@ static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
                            PACKET * packet);
 static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen,
                          PACKET * packet);
-static void parse_trust (IOBUF inp, int pkttype, unsigned long pktlen,
-                        PACKET * packet);
+static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx,
+                                     unsigned long pktlen);
 static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
                            PACKET * packet, int new_ctb, int partial);
 static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
@@ -542,6 +542,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
   *skip = 0;
   inp = ctx->inp;
 
+ again:
   log_assert (!pkt->pkt.generic);
   if (retpos || list_mode)
     {
@@ -800,8 +801,11 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
       rc = parse_comment (inp, pkttype, pktlen, pkt);
       break;
     case PKT_RING_TRUST:
-      parse_trust (inp, pkttype, pktlen, pkt);
-      rc = 0;
+      {
+        rc = parse_ring_trust (ctx, pktlen);
+        if (!rc)
+          goto again; /* Directly read the next packet.  */
+      }
       break;
     case PKT_PLAINTEXT:
       rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial);
@@ -2873,42 +2877,164 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
 }
 
 
-static void
-parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt)
+/* Parse a ring trust packet RFC4880 (5.10).
+ *
+ * This parser is special in that the packet is not stored as a packet
+ * but its content is merged into the previous packet.  */
+static gpg_error_t
+parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen)
 {
+  gpg_error_t err;
+  iobuf_t inp = ctx->inp;
+  PKT_ring_trust rt = {0};
   int c;
+  int not_gpg = 0;
 
-  (void) pkttype;
+  if (!pktlen)
+    {
+      if (list_mode)
+       es_fprintf (listfp, ":trust packet: empty\n");
+      err = 0;
+      goto leave;
+    }
 
-  pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust);
+  c = iobuf_get_noeof (inp);
+  pktlen--;
+  rt.trustval = c;
   if (pktlen)
     {
-      c = iobuf_get_noeof (inp);
+      if (!c)
+        {
+          c = iobuf_get_noeof (inp);
+          /* We require that bit 7 of the sigcache is 0 (easier
+           * eof handling).  */
+          if (!(c & 0x80))
+            rt.sigcache = c;
+        }
+      else
+        iobuf_get_noeof (inp);  /* Dummy read.  */
       pktlen--;
-      pkt->pkt.ring_trust->trustval = c;
-      pkt->pkt.ring_trust->sigcache = 0;
-      if (!c && pktlen == 1)
-       {
-         c = iobuf_get_noeof (inp);
-         pktlen--;
-         /* We require that bit 7 of the sigcache is 0 (easier eof
-             handling).  */
-         if (!(c & 0x80))
-           pkt->pkt.ring_trust->sigcache = c;
-       }
-      if (list_mode)
-       es_fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
-                    pkt->pkt.ring_trust->trustval,
-                    pkt->pkt.ring_trust->sigcache);
     }
-  else
+
+  /* Next is the optional subtype.  */
+  if (pktlen > 3)
     {
-      pkt->pkt.ring_trust->trustval = 0;
-      pkt->pkt.ring_trust->sigcache = 0;
-      if (list_mode)
-       es_fprintf (listfp, ":trust packet: empty\n");
+      char tmp[4];
+      tmp[0] = iobuf_get_noeof (inp);
+      tmp[1] = iobuf_get_noeof (inp);
+      tmp[2] = iobuf_get_noeof (inp);
+      tmp[3] = iobuf_get_noeof (inp);
+      pktlen -= 4;
+      if (!memcmp (tmp, "gpg", 3))
+        rt.subtype = tmp[3];
+      else
+        not_gpg = 1;
     }
+  /* If it is a key or uid subtype read the remaining data.  */
+  if ((rt.subtype == RING_TRUST_KEY || rt.subtype == RING_TRUST_UID)
+      && pktlen >= 6 )
+    {
+      int i;
+      unsigned int namelen;
+
+      rt.keysrc = iobuf_get_noeof (inp);
+      pktlen--;
+      rt.keyupdate = read_32 (inp);
+      pktlen -= 4;
+      namelen = iobuf_get_noeof (inp);
+      pktlen--;
+      if (namelen && pktlen)
+        {
+          rt.url = xtrymalloc (namelen + 1);
+          if (rt.url)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          for (i = 0; pktlen && i < namelen; pktlen--, i++)
+            rt.url[i] = iobuf_get_noeof (inp);
+          rt.url[i] = 0;
+        }
+    }
+
+  if (list_mode)
+    {
+      if (rt.subtype == RING_TRUST_SIG)
+        es_fprintf (listfp, ":trust packet: sig flag=%02x sigcache=%02x\n",
+                    rt.trustval, rt.sigcache);
+      else if (rt.subtype == RING_TRUST_UID || rt.subtype == RING_TRUST_KEY)
+        {
+          unsigned char *p;
+
+          es_fprintf (listfp, ":trust packet: %s upd=%lu src=%d%s",
+                      (rt.subtype == RING_TRUST_UID? "uid" : "key"),
+                      (unsigned long)rt.keyupdate,
+                      rt.keysrc,
+                      (rt.url? " url=":""));
+          if (rt.url)
+            {
+              for (p = rt.url; *p; p++)
+                {
+                  if (*p >= ' ' && *p <= 'z')
+                    es_putc (*p, listfp);
+                  else
+                    es_fprintf (listfp, "\\x%02x", *p);
+                }
+            }
+          es_putc ('\n', listfp);
+        }
+      else if (not_gpg)
+        es_fprintf (listfp, ":trust packet: not created by gpg\n");
+      else
+        es_fprintf (listfp, ":trust packet: subtype=%02x\n",
+                    rt.subtype);
+    }
+
+  /* Now transfer the data to the respective packet.  Do not do this
+   * if SKIP_META is set.  */
+  if (!ctx->last_pkt || ctx->skip_meta)
+    ;
+  else if (rt.subtype == RING_TRUST_SIG
+           && ctx->last_pkt->pkttype == PKT_SIGNATURE)
+    {
+      PKT_signature *sig = ctx->last_pkt->pkt.signature;
+
+      if ((rt.sigcache & 1))
+        {
+          sig->flags.checked = 1;
+          sig->flags.valid = !!(rt.sigcache & 2);
+        }
+    }
+  else if (rt.subtype == RING_TRUST_UID
+           && (ctx->last_pkt->pkttype == PKT_USER_ID
+               || ctx->last_pkt->pkttype == PKT_ATTRIBUTE))
+    {
+      PKT_user_id *uid = ctx->last_pkt->pkt.user_id;
+
+      uid->keysrc = rt.keysrc;
+      uid->keyupdate = rt.keyupdate;
+      uid->updateurl = rt.url;
+      rt.url = NULL;
+    }
+  else if (rt.subtype == RING_TRUST_KEY
+           && (ctx->last_pkt->pkttype == PKT_PUBLIC_KEY
+               || ctx->last_pkt->pkttype == PKT_SECRET_KEY))
+    {
+      PKT_public_key *pk = ctx->last_pkt->pkt.public_key;
+
+      pk->keysrc = rt.keysrc;
+      pk->keyupdate = rt.keyupdate;
+      pk->updateurl = rt.url;
+      rt.url = NULL;
+    }
+
+  err = 0;
+
+ leave:
+  xfree (rt.url);
+  free_packet (NULL, ctx); /* This sets ctx->last_pkt to NULL.  */
   iobuf_skip_rest (inp, pktlen, 0);
+  return err;
 }