Fixed an email/DN bug.
[gnupg.git] / kbx / keybox-blob.c
index 84ebc90..a45c421 100644 (file)
@@ -1,11 +1,11 @@
 /* keybox-blob.c - KBX Blob handling
- *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2000, 2001, 2002, 2003, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 
 /* The keybox data formats
 
 The KeyBox uses an augmented OpenPGP/X.509 key format.  This makes
-random access to a keyblock/Certificate easier and also gives the
+random access to a keyblock/certificate easier and also gives the
 opportunity to store additional information (e.g. the fingerprint)
 along with the key.  All integers are stored in network byte order,
 offsets are counted from the beginning of the Blob.
@@ -35,11 +34,13 @@ The first record of a plain KBX file has a special format:
  byte reserved
  byte reserved
  u32  magic 'KBXf'
- byte pgp_marginals  used for validity calculation of this file
- byte pgp_completes  ditto.
- byte pgp_cert_depth ditto.
+ u32  reserved
+ u32  file_created_at
+ u32  last_maintenance_run
+ u32  reserved
+ u32  reserved
 
-The OpenPGP and X.509 blob are verry similiar, things which are
+The OpenPGP and X.509 blob are very similiar, things which are
 X.509 specific are noted like [X.509: xxx]
 
  u32  length of this blob (including these 4 bytes)
@@ -47,18 +48,19 @@ X.509 specific are noted like [X.509: xxx]
  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 or X509 DER encoded certificate
- u32  ant its length
+ 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.
+        or 0 if not known which is the case only for X509.
    u16 special key flags
-        bit 0 =
+        bit 0 = qualified signature (not yet implemented}
    u16 reserved
  u16  size of serialnumber(may be zero) 
    n  u16 (see above) bytes of serial number
@@ -71,7 +73,7 @@ X.509 specific are noted like [X.509: xxx]
         bit 0 =
    byte validity
    byte reserved
-   [For X509, the first user ID is the ISsuer, the second the subject
+   [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)
@@ -81,8 +83,11 @@ X.509 specific are noted like [X.509: xxx]
        0x00000002 = bad signature
        0x10000000 = valid and expires at some date in 1978.
        0xffffffff = valid and does not expire
- u8    assigned ownertrust [X509: no used]
- u8    all_validity        [X509: no used]
+ u8    assigned ownertrust [X509: not used]
+ u8    all_validity 
+           OpenPGP:  see ../g10/trustdb/TRUST_* [not yet used]
+           X509: Bit 4 set := key has been revoked.  Note that this value
+                              matches TRUST_FLAG_REVOKED
  u16   reserved
  u32   recheck_after
  u32   Newest timestamp in the keyblock (useful for KS syncronsiation?)
@@ -98,7 +103,7 @@ X.509 specific are noted like [X.509: xxx]
 
  b16   MD5 checksum  (useful for KS syncronisation), we might also want to use
     a mac here.
- b4    resevered
+ b4    reserved
 
 */
 
@@ -109,6 +114,9 @@ X.509 specific are noted like [X.509: xxx]
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <time.h>
+
+#include "keybox-defs.h"
 #include <gcrypt.h>
 
 #ifdef KEYBOX_WITH_OPENPGP
@@ -118,8 +126,6 @@ X.509 specific are noted like [X.509: xxx]
 #include <ksba.h>
 #endif
 
-#include "keybox-defs.h"
-
 
 
 /* special values of the signature status */
@@ -171,9 +177,11 @@ struct fixup_list {
 struct keyboxblob {
   byte *blob;
   size_t bloblen;
+  off_t fileoffset;
   
   /* stuff used only by keybox_create_blob */
-  unsigned char *serial;
+  unsigned char *serialbuf;
+  const unsigned char *serial;
   size_t seriallen;
   int nkeys;
   struct keyboxblob_key *keys;
@@ -191,7 +199,7 @@ struct keyboxblob {
 
 
 \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
@@ -475,7 +483,7 @@ pgp_create_blob_keyblock (KEYBOXBLOB blob, KBNODE keyblock)
 
 /* Write the raw certificate out */
 static int
-x509_create_blob_cert (KEYBOXBLOB blob, KsbaCert cert)
+x509_create_blob_cert (KEYBOXBLOB blob, ksba_cert_t cert)
 {
   struct membuf *a = blob->buf;
   const unsigned char *image;
@@ -487,7 +495,7 @@ x509_create_blob_cert (KEYBOXBLOB blob, KsbaCert cert)
 
   image = ksba_cert_get_image (cert, &length);
   if (!image)
-    return KEYBOX_General_Error;
+    return gpg_error (GPG_ERR_GENERAL);
   put_membuf (a, image, length);
 
   add_fixup (blob, 12, a->len - kbstart);
@@ -529,7 +537,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;
@@ -537,7 +545,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 */
@@ -555,7 +563,7 @@ create_blob_header (KEYBOXBLOB blob, int blobtype)
 
   put16 (a, blob->seriallen); /*fixme: check that it fits into 16 bits*/
   if (blob->serial)
-    put_membuf (a, blob->serial+4, blob->seriallen);
+    put_membuf (a, blob->serial, blob->seriallen);
 
   put16 ( a, blob->nuids );
   put16 ( a, 4 + 4 + 2 + 1 + 1 );  /* size of uid info */
@@ -637,8 +645,8 @@ static int
 create_blob_finish (KEYBOXBLOB blob)
 {
   struct membuf *a = blob->buf;
-  byte *p;
-  char *pp;
+  unsigned char *p;
+  unsigned char *pp;
   int i;
   size_t n;
 
@@ -647,9 +655,10 @@ create_blob_finish (KEYBOXBLOB blob)
     put32 (a, 0);  /* Hmmm: why put32() ?? */
   
   /* get the memory area */
+  n = 0; /* (Just to avoid compiler warning.) */
   p = get_membuf (a, &n);
   if (!p)
-    return KEYBOX_Out_Of_Core;
+    return gpg_error (GPG_ERR_ENOMEM);
   assert (n >= 20);
 
   /* fixup the length */
@@ -657,7 +666,7 @@ create_blob_finish (KEYBOXBLOB blob)
 
   /* do the fixups */
   if (blob->fixup_out_of_core)
-    return KEYBOX_Out_Of_Core;
+    return gpg_error (GPG_ERR_ENOMEM);
 
   {
     struct fixup_list *fl;
@@ -676,7 +685,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;
@@ -688,7 +697,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;
@@ -696,8 +705,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 */
 
@@ -721,7 +730,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;
     }
 
@@ -737,7 +746,7 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock)
   
   init_membuf (&blob->bufbuf, 1024);
   blob->buf = &blob->bufbuf;
-  rc = create_blob_header (blob, BLOBTYPE_OPENPGP);
+  rc = create_blob_header (blob, BLOBTYPE_OPENPGP, as_ephemeral);
   if (rc)
     goto leave;
   rc = pgp_create_blob_keyblock (blob, keyblock);
@@ -768,32 +777,133 @@ _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.  Note hat we use this code also in ../sm/keylist.c.  */
+static char *
+x509_email_kludge (const char *name)
+{
+  const char *p, *string;
+  unsigned char *buf;
+  int n;
+
+  string = name;
+  for (;;)
+    {
+      p = strstr (string, "1.2.840.113549.1.9.1=#");
+      if (!p)
+        return NULL;
+      if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\'))
+        {
+          name = p + 22;
+          break;
+        }
+      string = p + 22;
+    }
+
+
+  /* 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.  */
+  for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+    ;
+  if (!n)
+    return NULL;
+  buf = xtrymalloc (n+3);
+  if (!buf)
+    return NULL; /* oops, out of core */
+  *buf = '<';
+  for (n=1, p=name; hexdigitp (p); p +=2, n++)
+    buf[n] = xtoi_2 (p);
+  buf[n++] = '>';
+  buf[n] = 0;
+  return (char*)buf;
+}
+
+
+
 /* Note: We should move calculation of the digest into libksba and
    remove that parameter */
 int
-_keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
-                          unsigned char *sha1_digest)
+_keybox_create_x509_blob (KEYBOXBLOB *r_blob, ksba_cert_t cert,
+                          unsigned char *sha1_digest, int as_ephemeral)
 {
-  int rc = 0;
+  int i, rc = 0;
   KEYBOXBLOB blob;
-  unsigned char *p;
+  unsigned char *sn;
+  char *p;
+  char **names = NULL;
+  size_t max_names;
 
   *r_blob = NULL;
   blob = xtrycalloc (1, sizeof *blob);
   if( !blob )
-    return KEYBOX_Out_Of_Core;
+    return gpg_error (gpg_err_code_from_errno (errno));
 
-  p = ksba_cert_get_serial (cert);
-  if (p)
+  sn = ksba_cert_get_serial (cert);
+  if (sn)
     {
-      size_t n = (p[0] << 24) | (p[1] << 16) | (p[2] <<8) | p[3];
-      blob->seriallen = n;
-      blob->serial = p;
+      size_t n, len;
+      n = gcry_sexp_canon_len (sn, 0, NULL, NULL);
+      if (n < 2)
+        {
+          xfree (sn);
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+      blob->serialbuf = sn;
+      sn++; n--; /* skip '(' */
+      for (len=0; n && *sn && *sn != ':' && digitp (sn); n--, sn++)
+        len = len*10 + atoi_1 (sn);
+      if (*sn != ':')
+        {
+          xfree (blob->serialbuf);
+          blob->serialbuf = NULL;
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+      sn++;
+      blob->serial = sn;
+      blob->seriallen = len;
     }
-      
 
   blob->nkeys = 1;
-  blob->nuids = 2; /* issuer and subject - fixme: count alternate names */
+
+  /* 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)
+        {
+          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 );
@@ -801,7 +911,7 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
   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;
     }
 
@@ -809,21 +919,17 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
   blob->keys[0].off_kid = 0; /* We don't have keyids */
   blob->keys[0].flags = 0;
 
-  /* issuer */
-  p = ksba_cert_get_issuer (cert);
-  blob->uids[0].name = p;
-  blob->uids[0].len = p? (strlen(p)+1):0;
-  blob->uids[0].flags = 0;
-  blob->uids[0].validity = 0;
-
-  /* subject */
-  p = ksba_cert_get_subject (cert);
-  blob->uids[1].name = p;
-  blob->uids[1].len = p? (strlen(p)+1):0;
-  blob->uids[1].flags = 0;
-  blob->uids[1].validity = 0;
-
-  /* fixme: add alternate names */
+  /* 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 */
@@ -832,7 +938,7 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
   init_membuf (&blob->bufbuf, 1024);
   blob->buf = &blob->bufbuf;
   /* write out what we already have */
-  rc = create_blob_header (blob, BLOBTYPE_X509);
+  rc = create_blob_header (blob, BLOBTYPE_X509, as_ephemeral);
   if (rc)
     goto leave;
   rc = x509_create_blob_cert (blob, cert);
@@ -849,6 +955,12 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
  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);
@@ -865,21 +977,24 @@ _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert,
 
 \f
 int
-_keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen)
+_keybox_new_blob (KEYBOXBLOB *r_blob,
+                  unsigned 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;
 }
 
+
 void
 _keybox_release_blob (KEYBOXBLOB blob)
 {
@@ -888,7 +1003,7 @@ _keybox_release_blob (KEYBOXBLOB blob)
     return;
   /* hmmm: release membuf here?*/
   xfree (blob->keys );
-  xfree (blob->serial);
+  xfree (blob->serialbuf);
   for (i=0; i < blob->nuids; i++)
     xfree (blob->uids[i].name);
   xfree (blob->uids );
@@ -899,9 +1014,32 @@ _keybox_release_blob (KEYBOXBLOB blob)
 
 
 
-const char *
+const unsigned 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;
+}
+
+
+
+void
+_keybox_update_header_blob (KEYBOXBLOB blob)
+{
+  if (blob->bloblen >= 32 && blob->blob[4] == BLOBTYPE_HEADER)
+    {
+      u32 val = make_timestamp ();
+
+      /* Update the last maintenance run times tamp. */
+      blob->blob[20]   = (val >> 24);
+      blob->blob[20+1] = (val >> 16);
+      blob->blob[20+2] = (val >>  8);
+      blob->blob[20+3] = (val      );
+    }
 }