Some minor bug fixes, new test utilities and started support for other
[gnupg.git] / kbx / keybox-blob.c
index 7db6216..3d81532 100644 (file)
@@ -1,5 +1,5 @@
 /* keybox-blob.c - KBX Blob handling
- *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -39,25 +39,30 @@ The first record of a plain KBX file has a special format:
  byte pgp_completes  ditto.
  byte pgp_cert_depth ditto.
 
-The OpenPGP KBX Blob looks like this:
+The OpenPGP and X.509 blob are verry similiar, things which are
+X.509 specific are noted like [X.509: xxx]
 
  u32  length of this blob (including these 4 bytes)
- byte Blob type (2)
+ byte Blob type (2) [X509: 3]
  byte version number of this blob type (1)
  u16  Blob flags
        bit 0 = contains secret key material
+        bit 1 = ephemeral blob (e.g. used while quering external resources)
 
- u32  offset to the OpenPGP keyblock
- u32  length of the keyblock
- u16  number of keys (at least 1!)
+ u32  offset to the OpenPGP keyblock or X509 DER encoded certificate
+ u32  and its length
+ u16  number of keys (at least 1!) [X509: always 1]
  u16  size of additional key information
  n times:
    b20 The keys fingerprint
        (fingerprints are always 20 bytes, MD5 left padded with zeroes)
    u32 offset to the n-th key's keyID (a keyID is always 8 byte)
+        or 0 if not known which is the case opnly for X509.
    u16 special key flags
         bit 0 =
    u16 reserved
+ u16  size of serialnumber(may be zero) 
+   n  u16 (see above) bytes of serial number
  u16  number of user IDs
  u16  size of additional user ID information
  n times:
@@ -67,6 +72,8 @@ The OpenPGP KBX Blob looks like this:
         bit 0 =
    byte validity
    byte reserved
+   [For X509, the first user ID is the Issuer, the second the Subject
+   and the others are subjectAltNames]
  u16  number of signatures
  u16  size of signature information (4)
    u32 expiration time of signature with some special values:
@@ -75,8 +82,8 @@ The OpenPGP KBX Blob looks like this:
        0x00000002 = bad signature
        0x10000000 = valid and expires at some date in 1978.
        0xffffffff = valid and does not expire
- u8    assigned ownertrust
- u8    all_validity
+ u8    assigned ownertrust [X509: no used]
+ u8    all_validity        [X509: no used]
  u16   reserved
  u32   recheck_after
  u32   Newest timestamp in the keyblock (useful for KS syncronsiation?)
@@ -90,7 +97,9 @@ The OpenPGP KBX Blob looks like this:
 
     maybe we put a signature here later.
 
- b16   MD5 checksum  (useful for KS syncronisation)
+ b16   MD5 checksum  (useful for KS syncronisation), we might also want to use
+    a mac here.
+ b4    resevered
 
 */
 
@@ -101,9 +110,19 @@ The OpenPGP KBX Blob looks like this:
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
-#include <gcrypt.h>
+#include <time.h>
 
 #include "keybox-defs.h"
+#include <gcrypt.h>
+
+#ifdef KEYBOX_WITH_OPENPGP
+/* include stuff to parse the packets */
+#endif
+#ifdef KEYBOX_WITH_X509
+#include <ksba.h>
+#endif
+
+
 
 /* special values of the signature status */
 #define SF_NONE(a)  ( !(a) )
@@ -125,16 +144,17 @@ struct membuf {
 /*  #endif */
 
 struct keyboxblob_key {
-    char   fpr[20];
-    u32    off_kid;
-    ulong  off_kid_addr;
-    u16    flags;
+  char   fpr[20];
+  u32    off_kid;
+  ulong  off_kid_addr;
+  u16    flags;
 };
 struct keyboxblob_uid {
-    ulong  off_addr;
-    u32    len;
-    u16    flags;
-    byte   validity;
+  ulong  off_addr;
+  char   *name;     /* used only with x509 */
+  u32    len;
+  u16    flags;
+  byte   validity;
 };
 
 struct keyid_list {
@@ -153,8 +173,12 @@ struct fixup_list {
 struct keyboxblob {
   byte *blob;
   size_t bloblen;
+  off_t fileoffset;
   
   /* stuff used only by keybox_create_blob */
+  unsigned char *serialbuf;
+  const unsigned char *serial;
+  size_t seriallen;
   int nkeys;
   struct keyboxblob_key *keys;
   int nuids;
@@ -162,14 +186,16 @@ struct keyboxblob {
   int nsigs;
   u32  *sigs;
   struct fixup_list *fixups;
+  int fixup_out_of_core;
   
   struct keyid_list *temp_kids;
-  struct membuf *buf; /* temporary store for the blob */
+  struct membuf bufbuf; /* temporary store for the blob */
+  struct membuf *buf; 
 };
 
 
 \f
-/* A simple implemnation of a dynamic buffer.  Use init_membuf() to
+/* A simple implemention of a dynamic buffer.  Use init_membuf() to
    create a buffer, put_membuf to append bytes and get_membuf to
    release and return the buffer.  Allocation errors are detected but
    only returned at the final get_membuf(), this helps not to clutter
@@ -256,6 +282,28 @@ put32 (struct membuf *mb, u32 a )
 }
 
 \f
+/* Store a value in the fixup list */
+static void
+add_fixup (KEYBOXBLOB blob, u32 off, u32 val)
+{
+  struct fixup_list *fl;
+  
+  if (blob->fixup_out_of_core)
+    return;
+
+  fl = xtrycalloc(1, sizeof *fl);
+  if (!fl)
+    blob->fixup_out_of_core = 1;
+  else 
+    {
+      fl->off = off;
+      fl->val = val;
+      fl->next = blob->fixups;
+      blob->fixups = fl;
+    }
+}
+
+\f
 /*
  Some wrappers
 */
@@ -396,16 +444,7 @@ pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock)
   int n;
   u32 kbstart = a->len;
 
-  {
-    struct fixup_list *fl = xtrycalloc(1, sizeof *fl ); 
-    
-    if (!fl)
-      return KEYBOX_Out_Of_Core;
-    fl->off = 8;
-    fl->val = kbstart;
-    fl->next = blob->fixups;
-    blob->fixups = fl;
-  }
+  add_fixup (blob, kbstart);
 
   for (n = 0, node = keyblock; node; node = node->next)
     {
@@ -420,28 +459,16 @@ pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock)
           PKT_user_id *u = node->pkt->pkt.user_id;
           /* build_packet has set the offset of the name into u ;
            * now we can do the fixup */
-          struct fixup_list *fl = xcalloc(1, sizeof *fl ); /* fixme */
-          fl->off = blob->uids[n].off_addr;
-          fl->val = u->stored_at;
-          fl->next = blob->fixups;
-          blob->fixups = fl;
+          add_fixup (blob, blob->uids[n].off_addr, u->stored_at);
           n++;
        }
     }
   assert (n == blob->nuids);
 
-  {
-    struct fixup_list *fl = xcalloc(1, sizeof *fl ); /* fixme */
-
-    fl->off = 12;
-    fl->val = a->len - kbstart;
-    fl->next = blob->fixups;
-    blob->fixups = fl;
-  }
-
+  add_fixup (blob, a->len - kbstart);
   return 0;
 }
-
 #endif /*KEYBOX_WITH_OPENPGP*/
 
 \f
@@ -450,6 +477,27 @@ pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock)
    X.509 specific stuff
  */
 
+/* Write the raw certificate out */
+static int
+x509_create_blob_cert (KEYBOXBLOB blob, ksba_cert_t cert)
+{
+  struct membuf *a = blob->buf;
+  const unsigned char *image;
+  size_t length;
+  u32 kbstart = a->len;
+
+  /* Store our offset for later fixup */
+  add_fixup (blob, 8, kbstart);
+
+  image = ksba_cert_get_image (cert, &length);
+  if (!image)
+    return gpg_error (GPG_ERR_GENERAL);
+  put_membuf (a, image, length);
+
+  add_fixup (blob, 12, a->len - kbstart);
+  return 0;
+}
 #endif /*KEYBOX_WITH_X509*/
 
 /* Write a stored keyID out to the buffer */
@@ -485,7 +533,7 @@ release_kid_list (struct keyid_list *kl)
 
 
 static int
-create_blob_header (KEYBOXBLOB blob, int blobtype)
+create_blob_header (KEYBOXBLOB blob, int blobtype, int as_ephemeral)
 {
   struct membuf *a = blob->buf;
   int i;
@@ -493,7 +541,7 @@ create_blob_header (KEYBOXBLOB blob, int blobtype)
   put32 ( a, 0 ); /* blob length, needs fixup */
   put8 ( a, blobtype);  
   put8 ( a, 1 );  /* blob type version */
-  put16 ( a, 0 ); /* blob flags */
+  put16 ( a, as_ephemeral? 2:0 ); /* blob flags */
 
   put32 ( a, 0 ); /* offset to the raw data, needs fixup */
   put32 ( a, 0 ); /* length of the raw data, needs fixup */
@@ -509,6 +557,10 @@ create_blob_header (KEYBOXBLOB blob, int blobtype)
       put16 ( a, 0 ); /* reserved */
     }
 
+  put16 (a, blob->seriallen); /*fixme: check that it fits into 16 bits*/
+  if (blob->serial)
+    put_membuf (a, blob->serial, blob->seriallen);
+
   put16 ( a, blob->nuids );
   put16 ( a, 4 + 4 + 2 + 1 + 1 );  /* size of uid info */
   for (i=0; i < blob->nuids; i++)
@@ -537,31 +589,42 @@ create_blob_header (KEYBOXBLOB blob, int blobtype)
   put32 ( a, 0 );  /* size of reserved space */
   /* reserved space (which is currently of size 0) */
 
-  /* We need to store the keyids for all pgp v3 keys because those key
-     IDs are not part of the fingerprint.  While we are doing that, we
-     fixup all the keyID offsets */
-  for (i=0; i < blob->nkeys; i++ )
+  /* space where we write keyIDs and and other stuff so that the
+     pointers can actually point to somewhere */
+  if (blobtype == BLOBTYPE_PGP)
     {
-      struct fixup_list *fl = xtrycalloc(1, sizeof *fl ); 
-      
-      if (!fl)
-        return KEYBOX_Out_Of_Core;
-
-      fl->off = blob->keys[i].off_kid_addr;
-      fl->next = blob->fixups;
-      blob->fixups = fl;
-
-      if (blob->keys[i].off_kid) 
-        { /* this is a v3 one */
-          fl->val = a->len;
-          write_stored_kid (blob, blob->keys[i].off_kid);
-       }
-      else
-        { /* the better v4 key IDs - just store an offset 8 bytes back */
-          fl->val = blob->keys[i].off_kid_addr - 8;
-       }
+      /* We need to store the keyids for all pgp v3 keys because those key
+         IDs are not part of the fingerprint.  While we are doing that, we
+         fixup all the keyID offsets */
+      for (i=0; i < blob->nkeys; i++ )
+        {
+          if (blob->keys[i].off_kid) 
+            { /* this is a v3 one */
+              add_fixup (blob, blob->keys[i].off_kid_addr, a->len);
+              write_stored_kid (blob, blob->keys[i].off_kid);
+            }
+          else
+            { /* the better v4 key IDs - just store an offset 8 bytes back */
+              add_fixup (blob, blob->keys[i].off_kid_addr,
+                         blob->keys[i].off_kid_addr - 8); 
+            }
+        }
     }
   
+  if (blobtype == BLOBTYPE_X509)
+    {
+      /* We don't want to point to ASN.1 encoded UserIDs (DNs) but to
+         the utf-8 string represenation of them */
+      for (i=0; i < blob->nuids; i++ )
+        {
+          if (blob->uids[i].name) 
+            { /* this is a v3 one */
+              add_fixup (blob, blob->uids[i].off_addr, a->len);
+              put_membuf (blob->buf, blob->uids[i].name, blob->uids[i].len);
+            }
+        }
+    }
+
     return 0;
 }
 
@@ -585,25 +648,21 @@ create_blob_finish (KEYBOXBLOB blob)
 
   /* write a placeholder for the checksum */
   for (i = 0; i < 16; i++ )
-    put32 (a, 0);
+    put32 (a, 0);  /* Hmmm: why put32() ?? */
   
   /* get the memory area */
-  p = a->buf;
-  n = a->len;
+  p = get_membuf (a, &n);
+  if (!p)
+    return gpg_error (GPG_ERR_ENOMEM);
   assert (n >= 20);
 
   /* fixup the length */
-  {
-    struct fixup_list *fl = xtrycalloc(1, sizeof *fl);
-    if (!fl)
-      return KEYBOX_Out_Of_Core;
-    fl->off = 0;
-    fl->val = n;
-    fl->next = blob->fixups;
-    blob->fixups = fl;
-  }
+  add_fixup (blob, 0, n);
 
   /* do the fixups */
+  if (blob->fixup_out_of_core)
+    return gpg_error (GPG_ERR_ENOMEM);
+
   {
     struct fixup_list *fl;
     for (fl = blob->fixups; fl; fl = fl->next)
@@ -621,7 +680,7 @@ create_blob_finish (KEYBOXBLOB blob)
 
   pp = xtrymalloc (n);
   if ( !pp )
-    return KEYBOX_Out_Of_Core;
+    return gpg_error (gpg_err_code_from_errno (errno));
   memcpy (pp , p, n);
   blob->blob = pp;
   blob->bloblen = n;
@@ -633,7 +692,7 @@ create_blob_finish (KEYBOXBLOB blob)
 #ifdef KEYBOX_WITH_OPENPGP
 
 int
-_keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
+_keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock, int as_ephemeral)
 {
   int rc = 0;
   KBNODE node;
@@ -641,8 +700,8 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
 
   *r_blob = NULL;
   blob = xtrycalloc (1, sizeof *blob);
-  if( !blob )
-    return KEYBOX_Out_Of_Core;
+  if (!blob)
+    return gpg_error (gpg_err_code_from_errno (errno));
 
   /* fixme: Do some sanity checks on the keyblock */
 
@@ -666,7 +725,7 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
   blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
   if (!blob->keys || !blob->uids || !blob->sigs)
     {
-      rc = KEYBOX_Out_Of_Core;
+      rc = gpg_error (GPG_ERR_ENOMEM);
       goto leave;
     }
 
@@ -680,8 +739,9 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
   if (rc)
     goto leave;
   
-  init_membuf (blob->buf, 1024);
-  rc = create_blob_header (blob, BLOBTYPE_OPENPGP);
+  init_membuf (&blob->bufbuf, 1024);
+  blob->buf = &blob->bufbuf;
+  rc = create_blob_header (blob, BLOBTYPE_OPENPGP, as_ephemeral);
   if (rc)
     goto leave;
   rc = pgp_create_blob_keyblock (blob, keyblock);
@@ -711,20 +771,206 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
 }
 #endif /*KEYBOX_WITH_OPENPGP*/
 
+#ifdef KEYBOX_WITH_X509
+
+/* return an allocated string with the email address extracted from a
+   DN */
+static char *
+x509_email_kludge (const char *name)
+{
+  const unsigned char *p;
+  unsigned char *buf;
+  int n;
+
+  if (strncmp (name, "1.2.840.113549.1.9.1=#", 22))
+    return NULL;
+  /* This looks pretty much like an email address in the subject's DN
+     we use this to add an additional user ID entry.  This way,
+     openSSL generated keys get a nicer and usable listing */
+  name += 22;    
+  for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+    ;
+  if (*p != '#' || !n)
+    return NULL;
+  buf = xtrymalloc (n+3);
+  if (!buf)
+    return NULL; /* oops, out of core */
+  *buf = '<';
+  for (n=1, p=name; *p != '#'; p +=2, n++)
+    buf[n] = xtoi_2 (p);
+  buf[n++] = '>';
+  buf[n] = 0;
+  return buf;
+}
+
+
+
+/* Note: We should move calculation of the digest into libksba and
+   remove that parameter */
+int
+_keybox_create_x509_blob (KEYBOXBLOB *r_blob, ksba_cert_t cert,
+                          unsigned char *sha1_digest, int as_ephemeral)
+{
+  int i, rc = 0;
+  KEYBOXBLOB blob;
+  unsigned char *p;
+  unsigned char **names = NULL;
+  size_t max_names;
+
+  *r_blob = NULL;
+  blob = xtrycalloc (1, sizeof *blob);
+  if( !blob )
+    return gpg_error (gpg_err_code_from_errno (errno));
+
+  p = ksba_cert_get_serial (cert);
+  if (p)
+    {
+      size_t n, len;
+      n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+      if (n < 2)
+        {
+          xfree (p);
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+      blob->serialbuf = p;
+      p++; n--; /* skip '(' */
+      for (len=0; n && *p && *p != ':' && digitp (p); n--, p++)
+        len = len*10 + atoi_1 (p);
+      if (*p != ':')
+        {
+          xfree (blob->serialbuf);
+          blob->serialbuf = NULL;
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+      p++;
+      blob->serial = p;
+      blob->seriallen = len;
+    }
+
+  blob->nkeys = 1;
+
+  /* create list of names */
+  blob->nuids = 0;
+  max_names = 100;
+  names = xtrymalloc (max_names * sizeof *names);
+  if (!names)
+    {
+      rc = gpg_error (gpg_err_code_from_errno (errno));
+      goto leave;
+    }
+  p = ksba_cert_get_issuer (cert, 0);
+  if (!p)
+    {
+      rc =  gpg_error (GPG_ERR_MISSING_VALUE);
+      goto leave;
+    }
+  names[blob->nuids++] = p;
+  for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
+    {
+
+      if (blob->nuids >= max_names)
+        {
+          unsigned char **tmp;
+          
+          max_names += 100;
+          tmp = xtryrealloc (names, max_names * sizeof *names);
+          if (!tmp)
+            {
+              rc = gpg_error (gpg_err_code_from_errno (errno));
+              goto leave;
+            }
+        }
+      names[blob->nuids++] = p;
+      if (!i && (p=x509_email_kludge (p)))
+        names[blob->nuids++] = p; /* due to !i we don't need to check bounds*/
+    }
+  
+  /* space for signature information */
+  blob->nsigs = 1; 
+
+  blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys );
+  blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids );
+  blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
+  if (!blob->keys || !blob->uids || !blob->sigs)
+    {
+      rc = gpg_error (GPG_ERR_ENOMEM);
+      goto leave;
+    }
+
+  memcpy (blob->keys[0].fpr, sha1_digest, 20);
+  blob->keys[0].off_kid = 0; /* We don't have keyids */
+  blob->keys[0].flags = 0;
+
+  /* issuer and subject names */
+  for (i=0; i < blob->nuids; i++)
+    {
+      blob->uids[i].name = names[i];
+      blob->uids[i].len = strlen(names[i]);
+      names[i] = NULL;
+      blob->uids[i].flags = 0;
+      blob->uids[i].validity = 0;
+    }
+  xfree (names);
+  names = NULL;
+
+  /* signatures */
+  blob->sigs[0] = 0;   /* not yet checked */
+
+  /* Create a temporary buffer for further processing */
+  init_membuf (&blob->bufbuf, 1024);
+  blob->buf = &blob->bufbuf;
+  /* write out what we already have */
+  rc = create_blob_header (blob, BLOBTYPE_X509, as_ephemeral);
+  if (rc)
+    goto leave;
+  rc = x509_create_blob_cert (blob, cert);
+  if (rc)
+    goto leave;
+  rc = create_blob_trailer (blob);
+  if (rc)
+    goto leave;
+  rc = create_blob_finish ( blob );
+  if (rc)
+    goto leave;
+
+  
+ leave:
+  release_kid_list (blob->temp_kids);
+  blob->temp_kids = NULL;
+  if (blob && names)
+    {
+      for (i=0; i < blob->nuids; i++)
+        xfree (names[i]); 
+    }
+  xfree (names);
+  if (rc)
+    {
+      _keybox_release_blob (blob);
+      *r_blob = NULL;
+    }
+  else
+    {
+      *r_blob = blob;
+    }
+  return rc;
+}
+#endif /*KEYBOX_WITH_X509*/
+
 
 \f
 int
-_keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen)
+_keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen, off_t off)
 {
   KEYBOXBLOB blob;
   
   *r_blob = NULL;
   blob = xtrycalloc (1, sizeof *blob);
   if (!blob)
-    return KEYBOX_Out_Of_Core;
+    return gpg_error (gpg_err_code_from_errno (errno));
 
   blob->blob = image;
   blob->bloblen = imagelen;
+  blob->fileoffset = off;
   *r_blob = blob;
   return 0;
 }
@@ -732,11 +978,14 @@ _keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen)
 void
 _keybox_release_blob (KEYBOXBLOB blob)
 {
+  int i;
   if (!blob)
     return;
-/*    if (blob->buf) */
-/*      iobuf_cancel( blob->buf ); */
+  /* hmmm: release membuf here?*/
   xfree (blob->keys );
+  xfree (blob->serialbuf);
+  for (i=0; i < blob->nuids; i++)
+    xfree (blob->uids[i].name);
   xfree (blob->uids );
   xfree (blob->sigs );
   xfree (blob->blob );
@@ -748,8 +997,13 @@ _keybox_release_blob (KEYBOXBLOB blob)
 const char *
 _keybox_get_blob_image ( KEYBOXBLOB blob, size_t *n )
 {
-    *n = blob->bloblen;
-    return blob->blob;
+  *n = blob->bloblen;
+  return blob->blob;
 }
 
+off_t
+_keybox_get_blob_fileoffset (KEYBOXBLOB blob)
+{
+  return blob->fileoffset;
+}