diff options
author | Werner Koch <wk@gnupg.org> | 2001-11-13 13:50:14 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2001-11-13 13:50:14 +0100 |
commit | 90d060c1997c6c0b9f26c9088020d62f91d450da (patch) | |
tree | b2e7720d625699a49729b36286fb66a774c5faed | |
parent | A Makefile is a pretty useful thing (diff) | |
download | gnupg2-90d060c1997c6c0b9f26c9088020d62f91d450da.tar.xz gnupg2-90d060c1997c6c0b9f26c9088020d62f91d450da.zip |
We have reached a state where we are able to import certs and
check the certification path.
-rw-r--r-- | kbx/Makefile.am | 8 | ||||
-rw-r--r-- | kbx/kbxutil.c | 3 | ||||
-rw-r--r-- | kbx/keybox-blob.c | 314 | ||||
-rw-r--r-- | kbx/keybox-defs.h | 34 | ||||
-rw-r--r-- | kbx/keybox-dump.c | 94 | ||||
-rw-r--r-- | kbx/keybox-errors.c | 6 | ||||
-rw-r--r-- | kbx/keybox-file.c | 58 | ||||
-rw-r--r-- | kbx/keybox-init.c | 103 | ||||
-rw-r--r-- | kbx/keybox-search-desc.h | 69 | ||||
-rw-r--r-- | kbx/keybox-search.c | 511 | ||||
-rw-r--r-- | kbx/keybox-update.c | 390 | ||||
-rw-r--r-- | kbx/keybox.h | 44 | ||||
-rw-r--r-- | sm/Makefile.am | 7 | ||||
-rw-r--r-- | sm/certchain.c | 131 | ||||
-rw-r--r-- | sm/certcheck.c | 156 | ||||
-rw-r--r-- | sm/certdump.c | 131 | ||||
-rw-r--r-- | sm/certpath.c | 131 | ||||
-rw-r--r-- | sm/fingerprint.c | 104 | ||||
-rw-r--r-- | sm/gpgsm.c | 15 | ||||
-rw-r--r-- | sm/gpgsm.h | 35 | ||||
-rw-r--r-- | sm/import.c | 200 | ||||
-rw-r--r-- | sm/keydb.c | 557 | ||||
-rw-r--r-- | sm/keydb.h | 46 | ||||
-rw-r--r-- | sm/misc.c | 54 | ||||
-rw-r--r-- | sm/util.h | 8 |
25 files changed, 2482 insertions, 727 deletions
diff --git a/kbx/Makefile.am b/kbx/Makefile.am index 81885f3ca..4c783fc1e 100644 --- a/kbx/Makefile.am +++ b/kbx/Makefile.am @@ -27,13 +27,14 @@ noinst_LIBRARIES = libkeybox.a bin_PROGRAMS = kbxutil common_sources = \ - keybox.h \ - keybox-defs.h \ + keybox.h keybox-defs.h keybox-search-desc.h \ keybox-util.c \ keybox-errors.c \ keybox-init.c \ keybox-blob.c \ keybox-file.c \ + keybox-search.c \ + keybox-update.c \ keybox-dump.c @@ -41,7 +42,8 @@ libkeybox_a_SOURCES = $(common_sources) kbxutil_SOURCES = kbxutil.c $(common_sources) kbxutil_LDADD = ../jnlib/libjnlib.a \ - ../../libgcrypt/src/.libs/libgcrypt.so.1 + ../../libksba/src/.libs/libksba.a \ + ../../libgcrypt/src/.libs/libgcrypt.so.1 keybox-errors.c : keybox.h mkerrors $(srcdir)/mkerrors < $(srcdir)/keybox.h > keybox-errors.c diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index 8c3dee97a..e8b015ba6 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -234,8 +234,9 @@ main( int argc, char **argv ) /*create_dotlock(NULL); register locking cleanup */ i18n_init(); - /* We need to use the gcry malloc function becuase jnlib does use them */ + /* We need to use the gcry malloc function because jnlib does use them */ keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); pargs.argc = &argc; diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 7db62165a..84ebc904c 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -39,25 +39,29 @@ 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 - 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 ant 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 +71,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 +81,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 +96,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 */ @@ -103,8 +111,17 @@ The OpenPGP KBX Blob looks like this: #include <assert.h> #include <gcrypt.h> +#ifdef KEYBOX_WITH_OPENPGP +/* include stuff to parse the packets */ +#endif +#ifdef KEYBOX_WITH_X509 +#include <ksba.h> +#endif + #include "keybox-defs.h" + + /* special values of the signature status */ #define SF_NONE(a) ( !(a) ) #define SF_NOKEY(a) ((a) & (1<<0)) @@ -125,16 +142,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 { @@ -155,6 +173,8 @@ struct keyboxblob { size_t bloblen; /* stuff used only by keybox_create_blob */ + unsigned char *serial; + size_t seriallen; int nkeys; struct keyboxblob_key *keys; int nuids; @@ -162,9 +182,11 @@ 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; }; @@ -256,6 +278,28 @@ put32 (struct membuf *mb, u32 a ) } +/* 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; + } +} + + /* Some wrappers */ @@ -396,16 +440,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 +455,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*/ @@ -450,6 +473,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, KsbaCert 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 KEYBOX_General_Error; + 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 */ @@ -509,6 +553,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+4, 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 +585,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 +644,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 KEYBOX_Out_Of_Core; 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 KEYBOX_Out_Of_Core; + { struct fixup_list *fl; for (fl = blob->fixups; fl; fl = fl->next) @@ -680,7 +735,8 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock) if (rc) goto leave; - init_membuf (blob->buf, 1024); + init_membuf (&blob->bufbuf, 1024); + blob->buf = &blob->bufbuf; rc = create_blob_header (blob, BLOBTYPE_OPENPGP); if (rc) goto leave; @@ -711,6 +767,101 @@ _keybox_create_pgp_blob (KEYBOXBLOB *r_blob, KBNODE keyblock) } #endif /*KEYBOX_WITH_OPENPGP*/ +#ifdef KEYBOX_WITH_X509 +/* 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) +{ + int rc = 0; + KEYBOXBLOB blob; + unsigned char *p; + + *r_blob = NULL; + blob = xtrycalloc (1, sizeof *blob); + if( !blob ) + return KEYBOX_Out_Of_Core; + + p = ksba_cert_get_serial (cert); + if (p) + { + size_t n = (p[0] << 24) | (p[1] << 16) | (p[2] <<8) | p[3]; + blob->seriallen = n; + blob->serial = p; + } + + + blob->nkeys = 1; + blob->nuids = 2; /* issuer and subject - fixme: count alternate names */ + 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 = KEYBOX_Out_Of_Core; + 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 */ + 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 */ + + /* 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); + 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 (rc) + { + _keybox_release_blob (blob); + *r_blob = NULL; + } + else + { + *r_blob = blob; + } + return rc; +} +#endif /*KEYBOX_WITH_X509*/ + int @@ -732,11 +883,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->serial); + for (i=0; i < blob->nuids; i++) + xfree (blob->uids[i].name); xfree (blob->uids ); xfree (blob->sigs ); xfree (blob->blob ); @@ -751,5 +905,3 @@ _keybox_get_blob_image ( KEYBOXBLOB blob, size_t *n ) *n = blob->bloblen; return blob->blob; } - - diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h index 2adbdd22f..308c1fff3 100644 --- a/kbx/keybox-defs.h +++ b/kbx/keybox-defs.h @@ -41,6 +41,8 @@ enum { }; +typedef struct keyboxblob *KEYBOXBLOB; + typedef struct keybox_name *KB_NAME; typedef struct keybox_name const * CONST_KB_NAME; @@ -56,16 +58,13 @@ struct keybox_name { struct keybox_handle { - CONST_KB_NAME resource; + CONST_KB_NAME kb; int secret; /* this is for a secret keybox */ + FILE *fp; + int eof; + int error; struct { - CONST_KB_NAME kb; - /*IOBUF iobuf;*/ - int eof; - int error; - } current; - struct { - CONST_KB_NAME kb; + KEYBOXBLOB blob; off_t offset; size_t pk_no; size_t uid_no; @@ -78,17 +77,32 @@ struct keybox_handle { }; -typedef struct keyboxblob *KEYBOXBLOB; - +/* Don't know whether this is needed: */ +/* static struct { */ +/* const char *homedir; */ +/* int dry_run; */ +/* int quiet; */ +/* int verbose; */ +/* int preserve_permissions; */ +/* } keybox_opt; */ /*-- keybox-blob.c --*/ +#ifdef KEYBOX_WITH_OPENPGP + /* fixme */ +#endif /*KEYBOX_WITH_OPENPGP*/ +#ifdef KEYBOX_WITH_X509 +int _keybox_create_x509_blob (KEYBOXBLOB *r_blob, KsbaCert cert, + unsigned char *sha1_digest); +#endif /*KEYBOX_WITH_X509*/ + int _keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen); void _keybox_release_blob (KEYBOXBLOB blob); const char *_keybox_get_blob_image (KEYBOXBLOB blob, size_t *n); /*-- keybox-file.c --*/ int _keybox_read_blob (KEYBOXBLOB *r_blob, FILE *fp); +int _keybox_write_blob (KEYBOXBLOB blob, FILE *fp); /*-- keybox-dump.c --*/ int _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp); diff --git a/kbx/keybox-dump.c b/kbx/keybox-dump.c index 6022900f2..d84ae7349 100644 --- a/kbx/keybox-dump.c +++ b/kbx/keybox-dump.c @@ -46,6 +46,35 @@ get16 (const byte *buffer) return a; } +void +print_string (FILE *fp, const byte *p, size_t n, int delim) +{ + for ( ; n; n--, p++ ) + { + if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim) + { + putc('\\', fp); + if( *p == '\n' ) + putc('n', fp); + else if( *p == '\r' ) + putc('r', fp); + else if( *p == '\f' ) + putc('f', fp); + else if( *p == '\v' ) + putc('v', fp); + else if( *p == '\b' ) + putc('b', fp); + else if( !*p ) + putc('0', fp); + else + fprintf(fp, "x%02x", *p ); + } + else + putc(*p, fp); + } +} + + static int dump_header_blob (const byte *buffer, size_t length, FILE *fp) { @@ -67,6 +96,7 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) ulong nuids, uidinfolen; ulong nsigs, siginfolen; ulong rawdata_off, rawdata_len; + ulong nserial; const byte *p; buffer = _keybox_get_blob_image (blob, &length); @@ -128,19 +158,33 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) int i; ulong kidoff, kflags; - fprintf (fp, "Key-%lu-Fpr: ", n ); + fprintf (fp, "Key-Fpr[%lu]: ", n ); for (i=0; i < 20; i++ ) fprintf (fp, "%02X", p[i]); kidoff = get32 (p + 20); - fprintf (fp, "\nKey-%lu-Kid-Off: %lu\n", n, kidoff ); - fprintf (fp, "Key-%lu-Kid: ", n ); + fprintf (fp, "\nKey-Kid-Off[%lu]: %lu\n", n, kidoff ); + fprintf (fp, "Key-Kid[%lu]: ", n ); /* fixme: check bounds */ for (i=0; i < 8; i++ ) fprintf (fp, "%02X", buffer[kidoff+i] ); kflags = get16 (p + 24 ); - fprintf( fp, "\nKey-%lu-Flags: %04lX\n", n, kflags); + fprintf( fp, "\nKey-Flags[%lu]: %04lX\n", n, kflags); } + /* serial number */ + fputs ("Serial-No: ", fp); + nserial = get16 (p); + p += 2; + if (!nserial) + fputs ("none", fp); + else + { + for (; nserial; nserial--, p++) + fprintf (fp, "%02X", *p); + } + putc ('\n', fp); + + /* user IDs */ nuids = get16 (p); fprintf (fp, "Uid-Count: %lu\n", nuids ); uidinfolen = get16 (p + 2); @@ -153,14 +197,42 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) uidoff = get32( p ); uidlen = get32( p+4 ); - fprintf (fp, "Uid-%lu-Off: %lu\n", n, uidoff ); - fprintf (fp, "Uid-%lu-Len: %lu\n", n, uidlen ); - fprintf (fp, "Uid-%lu: \"", n ); - /*print_string (fp, buffer+uidoff, uidlen, '\"');*/ + if (type == BLOBTYPE_X509 && !n) + { + fprintf (fp, "Issuer-Off: %lu\n", uidoff ); + fprintf (fp, "Issuer-Len: %lu\n", uidlen ); + fprintf (fp, "Issuer: \""); + } + else if (type == BLOBTYPE_X509 && n == 1) + { + fprintf (fp, "Subject-Off: %lu\n", uidoff ); + fprintf (fp, "Subject-Len: %lu\n", uidlen ); + fprintf (fp, "Subject: \""); + } + else + { + fprintf (fp, "Uid-Off[%lu]: %lu\n", n, uidoff ); + fprintf (fp, "Uid-Len[%lu]: %lu\n", n, uidlen ); + fprintf (fp, "Uid[%lu]: \"", n ); + } + print_string (fp, buffer+uidoff, uidlen, '\"'); fputs ("\"\n", fp); uflags = get16 (p + 8); - fprintf (fp, "Uid-%lu-Flags: %04lX\n", n, uflags ); - fprintf (fp, "Uid-%lu-Validity: %d\n", n, p[10] ); + if (type == BLOBTYPE_X509 && !n) + { + fprintf (fp, "Issuer-Flags: %04lX\n", uflags ); + fprintf (fp, "Issuer-Validity: %d\n", p[10] ); + } + else if (type == BLOBTYPE_X509 && n == 1) + { + fprintf (fp, "Subject-Flags: %04lX\n", uflags ); + fprintf (fp, "Subject-Validity: %d\n", p[10] ); + } + else + { + fprintf (fp, "Uid-Flags[%lu]: %04lX\n", n, uflags ); + fprintf (fp, "Uid-Validity[%lu]: %d\n", n, p[10] ); + } } nsigs = get16 (p); @@ -174,7 +246,7 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) ulong sflags; sflags = get32 (p); - fprintf (fp, "Sig-%lu-Expire: ", n ); + fprintf (fp, "Sig-Expire[%lu]: ", n ); if (!sflags) fputs ("[not checked]", fp); else if (sflags == 1 ) diff --git a/kbx/keybox-errors.c b/kbx/keybox-errors.c index bd03cdff3..e75373c0b 100644 --- a/kbx/keybox-errors.c +++ b/kbx/keybox-errors.c @@ -32,6 +32,12 @@ keybox_strerror (KeyboxError err) case KEYBOX_File_Error: s="file error"; break; case KEYBOX_Blob_Too_Short: s="blob too short"; break; case KEYBOX_Blob_Too_Large: s="blob too large"; break; + case KEYBOX_Invalid_Handle: s="invalid handle"; break; + case KEYBOX_File_Create_Error: s="file create error"; break; + case KEYBOX_File_Open_Error: s="file open error"; break; + case KEYBOX_File_Close_Error: s="file close error"; break; + case KEYBOX_Nothing_Found: s="nothing found"; break; + case KEYBOX_Wrong_Blob_Type: s="wrong blob type"; break; default: sprintf (buf, "ec=%d", err ); s=buf; break; } diff --git a/kbx/keybox-file.c b/kbx/keybox-file.c index 66bca504f..715d3fbe2 100644 --- a/kbx/keybox-file.c +++ b/kbx/keybox-file.c @@ -25,7 +25,7 @@ #include "keybox-defs.h" - +/* Read a block at the current postion ant return it in r_blocb. r_blob may be NULL sto simply skip the current block */ int _keybox_read_blob (KEYBOXBLOB *r_blob, FILE *fp) { @@ -58,56 +58,28 @@ _keybox_read_blob (KEYBOXBLOB *r_blob, FILE *fp) image[0] = c1; image[1] = c2; image[2] = c3; image[3] = c4; if (fread (image+4, imagelen-4, 1, fp) != 1) { + xfree (image); return KEYBOX_Read_Error; } - rc = _keybox_new_blob (r_blob, image, imagelen); - if (rc) - xfree (image); + rc = r_blob? _keybox_new_blob (r_blob, image, imagelen) : 0; + if (rc || !r_blob) + xfree (image); return rc; } - - - -void -export_as_kbxfile(void) +/* Write the block to the current file position */ +int +_keybox_write_blob (KEYBOXBLOB blob, FILE *fp) { -#if 0 - KBPOS kbpos; - KBNODE keyblock = NULL; - int rc=0; + const char *image; + size_t length; - rc = enum_keyblocks_begin( &kbpos, 0 ); - if( rc ) { - if( rc != -1 ) - log_error("enum_keyblocks(open) failed: %s\n", gpg_errstr(rc) ); - goto leave; - } - - while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) { - KBXBLOB blob; - const char *p; - size_t n; - - merge_keys_and_selfsig( keyblock ); - rc = kbx_create_blob ( &blob, keyblock ); - if( rc ) { - log_error("kbx_create_blob failed: %s\n", gpg_errstr(rc) ); - goto leave; - } - p = kbx_get_blob_image ( blob, &n ); - fwrite( p, n, 1, stdout ); - kbx_release_blob ( blob ); + image = _keybox_get_blob_image (blob, &length); + if (fwrite (image, length, 1, fp) != 1) + { + return KEYBOX_Write_Error; } - - if( rc && rc != -1 ) - log_error("enum_keyblocks(read) failed: %s\n", gpg_errstr(rc)); - - leave: - enum_keyblocks_end( kbpos ); - release_kbnode( keyblock ); -#endif + return 0; } - diff --git a/kbx/keybox-init.c b/kbx/keybox-init.c index a4649d18c..1a4a587b9 100644 --- a/kbx/keybox-init.c +++ b/kbx/keybox-init.c @@ -23,44 +23,47 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <assert.h> #include "keybox-defs.h" +#define compare_filenames strcmp + +static KB_NAME kb_names; + + /* - * Register a filename for plain keybox files. Returns a pointer to - * be used to create a handles etc or NULL to indicate that it has - * already been registered */ + Register a filename for plain keybox files. Returns a pointer to be + used to create a handles etc or NULL to indicate that it has already + been registered */ void * keybox_register_file (const char *fname, int secret) { - return NULL; -#if 0 - KB_NAME kr; + KB_NAME kr; - if (active_handles) - BUG (); /* We don't allow that */ - - for (kr=kb_names; kr; kr = kr->next) { - if ( !compare_filenames (kr->fname, fname) ) - return NULL; /* already registered */ + for (kr=kb_names; kr; kr = kr->next) + { + if ( !compare_filenames (kr->fname, fname) ) + return NULL; /* already registered */ } - kr = m_alloc (sizeof *kr + strlen (fname)); - strcpy (kr->fname, fname); - kr->secret = !!secret; - kr->lockhd = NULL; - kr->is_locked = 0; - kr->did_full_scan = 0; - /* keep a list of all issued pointers */ - kr->next = kb_names; - kb_names = kr; - - /* create the offset table the first time a function here is used */ - if (!kb_offtbl) - kb_offtbl = new_offset_hash_table (); - - return kr; -#endif + kr = xtrymalloc (sizeof *kr + strlen (fname)); + if (!kr) + return NULL; + strcpy (kr->fname, fname); + kr->secret = !!secret; + /* kr->lockhd = NULL;*/ + kr->is_locked = 0; + kr->did_full_scan = 0; + /* keep a list of all issued pointers */ + kr->next = kb_names; + kb_names = kr; + + /* create the offset table the first time a function here is used */ +/* if (!kb_offtbl) */ +/* kb_offtbl = new_offset_hash_table (); */ + + return kr; } int @@ -70,4 +73,48 @@ keybox_is_writable (void *token) return r? !access (r->fname, W_OK) : 0; } + + +/* Create a new handle for the resource associated with TOKEN. SECRET + is just a cross-check. + + The returned handle must be released using keybox_release (). */ +KEYBOX_HANDLE +keybox_new (void *token, int secret) +{ + KEYBOX_HANDLE hd; + KB_NAME resource = token; + + assert (resource && !resource->secret == !secret); + hd = xtrycalloc (1, sizeof *hd); + if (hd) + { + hd->kb = resource; + hd->secret = !!secret; + } + return hd; +} + +void +keybox_release (KEYBOX_HANDLE hd) +{ + if (!hd) + return; + _keybox_release_blob (hd->found.blob); + xfree (hd->word_match.name); + xfree (hd->word_match.pattern); + xfree (hd); +} + + +const char * +keybox_get_resource_name (KEYBOX_HANDLE hd) +{ + if (!hd || !hd->kb) + return NULL; + return hd->kb->fname; +} + + + diff --git a/kbx/keybox-search-desc.h b/kbx/keybox-search-desc.h new file mode 100644 index 000000000..22bcba69b --- /dev/null +++ b/kbx/keybox-search-desc.h @@ -0,0 +1,69 @@ +/* keybox-search-desc.h - Keybox serch description + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +/* + This file is a temporary kludge until we can come up with solution + to share this description between keybox and the application + specific keydb +*/ + +#ifndef KEYBOX_SEARCH_DESC_H +#define KEYBOX_SEARCH_DESC_H 1 + +typedef enum { + KEYDB_SEARCH_MODE_NONE, + KEYDB_SEARCH_MODE_EXACT, + KEYDB_SEARCH_MODE_SUBSTR, + KEYDB_SEARCH_MODE_MAIL, + KEYDB_SEARCH_MODE_MAILSUB, + KEYDB_SEARCH_MODE_MAILEND, + KEYDB_SEARCH_MODE_WORDS, + KEYDB_SEARCH_MODE_SHORT_KID, + KEYDB_SEARCH_MODE_LONG_KID, + KEYDB_SEARCH_MODE_FPR16, + KEYDB_SEARCH_MODE_FPR20, + KEYDB_SEARCH_MODE_FPR, + KEYDB_SEARCH_MODE_ISSUER, + KEYDB_SEARCH_MODE_ISSUER_SN, + KEYDB_SEARCH_MODE_FIRST, + KEYDB_SEARCH_MODE_NEXT +} KeydbSearchMode; + +struct keydb_search_desc { + KeydbSearchMode mode; + int (*skipfnc)(void *,void*); /* used to be: void*, u32* */ + void *skipfncvalue; + const unsigned char *sn; /* used only with _MODE_ISSUER_SN */ + union { + const char *name; + char fpr[24]; + /*fixme: u32 kid[2];*/ + } u; +}; + + +struct keydb_search_desc; +typedef struct keydb_search_desc KEYDB_SEARCH_DESC; + +typedef struct keydb_search_desc KEYBOX_SEARCH_DESC; + + + +#endif /*KEYBOX_SEARCH_DESC_H*/ diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index d2c61ff21..3468a8ecf 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -25,134 +25,417 @@ #include "keybox-defs.h" +static ulong +get32 (const byte *buffer) +{ + ulong a; + a = *buffer << 24; + a |= buffer[1] << 16; + a |= buffer[2] << 8; + a |= buffer[3]; + return a; +} -/**************** - * Check whether the given fingerprint (20 bytes) is in the - * given keyblob. fpr is always 20 bytes. - * Return: 0 = found - * -1 = not found - other = error (fixme: do not always reurn gpgerr_general) - */ -int -keybox_blob_has_fpr ( KEYBOXBLOB blob, const byte *fpr ) +static ulong +get16 (const byte *buffer) +{ + ulong a; + a = *buffer << 8; + a |= buffer[1]; + return a; +} + + + +static int +blob_get_type (KEYBOXBLOB blob) +{ + const unsigned char *buffer; + size_t length; + + buffer = _keybox_get_blob_image (blob, &length); + if (length < 40) + return -1; /* blob too short */ + + return buffer[4]; +} + + +static int +blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn) +{ + size_t snlen; + const unsigned char *buffer; + size_t length; + size_t pos, off; + size_t nkeys, keyinfolen; + size_t nserial; + + snlen = (sn[0] << 24) | (sn[1] << 16) | (sn[2] << 8) | sn[3]; + sn += 4; + + buffer = _keybox_get_blob_image (blob, &length); + if (length < 40) + return 0; /* blob too short */ + + /*keys*/ + nkeys = get16 (buffer + 16); + keyinfolen = get16 (buffer + 18 ); + if (keyinfolen < 28) + return 0; /* invalid blob */ + pos = 20 + keyinfolen*nkeys; + if (pos+2 > length) + return 0; /* out of bounds */ + + /*serial*/ + nserial = get16 (buffer+pos); + off = pos + 2; + if (off+nserial > length) + return 0; /* out of bounds */ + + return nserial == snlen && !memcmp (buffer+off, sn, snlen); +} + +static int +blob_cmp_name (KEYBOXBLOB blob, int idx, const char *name, size_t namelen) { - ulong n, nkeys, keyinfolen; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - nkeys = get16( buffer + 16 ); - keyinfolen = get16( buffer + 18 ); - p = buffer + 20; - for(n=0; n < nkeys; n++, p += keyinfolen ) { - if ( p+20 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - if (!memcmp ( p, fpr, 20 ) ) - return 0; /* found */ + const unsigned char *buffer; + size_t length; + size_t pos, off, len; + size_t nkeys, keyinfolen; + size_t nuids, uidinfolen; + size_t nserial; + + buffer = _keybox_get_blob_image (blob, &length); + if (length < 40) + return 0; /* blob too short */ + + /*keys*/ + nkeys = get16 (buffer + 16); + keyinfolen = get16 (buffer + 18 ); + if (keyinfolen < 28) + return 0; /* invalid blob */ + pos = 20 + keyinfolen*nkeys; + if (pos+2 > length) + return 0; /* out of bounds */ + + /*serial*/ + nserial = get16 (buffer+pos); + pos += 2 + nserial; + if (pos+4 > length) + return 0; /* out of bounds */ + + /* user ids*/ + nuids = get16 (buffer + pos); pos += 2; + uidinfolen = get16 (buffer + pos); pos += 2; + if (uidinfolen < 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */) + return 0; /* invalid blob */ + if (pos + uidinfolen*nuids > length) + return 0; /* out of bounds */ + + if (idx > nuids) + return 0; /* no user ID with that idx */ + pos += idx*uidinfolen; + off = get32 (buffer+pos); + len = get32 (buffer+pos+4); + if (off+len > length) + return 0; /* out of bounds */ + if (len < 2) + return 0; /* empty name or 0 not stored */ + len--; + + return len == namelen && !memcmp (buffer+off, name, len); +} + + + + +/* + The has_foo functions are used as helpers for search +*/ +#if 0 +static int +has_short_kid (KEYBOXBLOB blob, u32 kid) +{ + return 0; +} + +static int +has_long_kid (KEYBOXBLOB blob, u32 *kid) +{ + return 0; +} +#endif + +static int +has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr) +{ + return 0; +} + + +static int +has_issuer (KEYBOXBLOB blob, const char *name) +{ + size_t namelen; + + return_val_if_fail (name, 0); + + if (blob_get_type (blob) != BLOBTYPE_X509) + return 0; + + namelen = strlen (name); + return blob_cmp_name (blob, 0 /* issuer */, name, namelen); +} + +static int +has_issuer_sn (KEYBOXBLOB blob, const char *name, const unsigned char *sn) +{ + size_t namelen; + + return_val_if_fail (name, 0); + return_val_if_fail (sn, 0); + + if (blob_get_type (blob) != BLOBTYPE_X509) + return 0; + + namelen = strlen (name); + + return (blob_cmp_sn (blob, sn) + && blob_cmp_name (blob, 0 /* issuer */, name, namelen)); +} + + + +/* + + The search API + +*/ + +int +keybox_search_reset (KEYBOX_HANDLE hd) +{ + if (!hd) + return KEYBOX_Invalid_Value; + + if (hd->found.blob) + { + _keybox_release_blob (hd->found.blob); + hd->found.blob = NULL; + } + + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; } - return -1; + hd->error = 0; + hd->eof = 0; + return 0; } -/**************** - * Check whether the given keyID (20 bytes) is in the - * given keyblob. - * Return: 0 = found - * -1 = not found - other = error (fixme: do not always return gpgerr_general) - */ -int -keybox_blob_has_kid ( KEYBOXBLOB blob, const byte *keyidbuf, size_t keyidlen ) +int +keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc) { - ulong n, nkeys, keyinfolen, off; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - nkeys = get16( buffer + 16 ); - keyinfolen = get16( buffer + 18 ); - p = buffer + 20; - for(n=0; n < nkeys; n++, p += keyinfolen ) { - if ( p+24 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - off = get32 ( p + 20 ); - if (keyidlen < 8 ) /* actually keyidlen may either be 4 or 8 */ - off +=4; - if ( off+keyidlen > buflen ) - return GPGERR_GENERAL; /* offset out of bounds */ - if ( !memcmp ( buffer+off, keyidbuf, keyidlen ) ) - return 0; /* found */ + int rc; + size_t n; + int need_words, any_skip; + KEYBOXBLOB blob = NULL; + + if (!hd) + return KEYBOX_Invalid_Value; + + /* clear last found result */ + if (hd->found.blob) + { + _keybox_release_blob (hd->found.blob); + hd->found.blob = NULL; + } + + if (hd->error) + return hd->error; /* still in error state */ + if (hd->eof) + return -1; /* still EOF */ + + /* figure out what information we need */ + need_words = any_skip = 0; + for (n=0; n < ndesc; n++) + { + switch (desc[n].mode) + { + case KEYDB_SEARCH_MODE_WORDS: + need_words = 1; + break; + case KEYDB_SEARCH_MODE_FIRST: + /* always restart the search in this mode */ + keybox_search_reset (hd); + break; + default: + break; + } + if (desc[n].skipfnc) + any_skip = 1; + } + + if (!hd->fp) + { + hd->fp = fopen (hd->kb->fname, "rb"); + if (!hd->fp) + return (hd->error = KEYBOX_File_Open_Error); + } + + + for (;;) + { + _keybox_release_blob (blob); blob = NULL; + rc = _keybox_read_blob (&blob, hd->fp); + if (rc) + break; + + for (n=0; n < ndesc; n++) + { + switch (desc[n].mode) + { + case KEYDB_SEARCH_MODE_NONE: + never_reached (); + break; + case KEYDB_SEARCH_MODE_EXACT: + case KEYDB_SEARCH_MODE_SUBSTR: + case KEYDB_SEARCH_MODE_MAIL: + case KEYDB_SEARCH_MODE_MAILSUB: + case KEYDB_SEARCH_MODE_MAILEND: + case KEYDB_SEARCH_MODE_WORDS: + never_reached (); /* not yet implemented */ + break; + case KEYDB_SEARCH_MODE_ISSUER: + if (has_issuer (blob, desc[n].u.name)) + goto found; + break; + case KEYDB_SEARCH_MODE_ISSUER_SN: + if (has_issuer_sn (blob, desc[n].u.name, desc[n].sn)) + goto found; + break; + case KEYDB_SEARCH_MODE_SHORT_KID: +/* if (has_short_kid (blob, desc[n].u.kid[1])) */ +/* goto found; */ + break; + case KEYDB_SEARCH_MODE_LONG_KID: +/* if (has_long_kid (blob, desc[n].u.kid)) */ +/* goto found; */ + break; + case KEYDB_SEARCH_MODE_FPR: + if (has_fingerprint (blob, desc[n].u.fpr)) + goto found; + break; + case KEYDB_SEARCH_MODE_FIRST: + goto found; + break; + case KEYDB_SEARCH_MODE_NEXT: + goto found; + break; + default: + rc = KEYBOX_Invalid_Value; + goto found; + } + } + continue; + found: + for (n=any_skip?0:ndesc; n < ndesc; n++) + { +/* if (desc[n].skipfnc */ +/* && desc[n].skipfnc (desc[n].skipfncvalue, aki)) */ +/* break; */ + } + if (n == ndesc) + break; /* got it */ + } + + if (!rc) + { + hd->found.blob = blob; + } + else if (rc == -1) + { + _keybox_release_blob (blob); + hd->eof = 1; + } + else + { + _keybox_release_blob (blob); + hd->error = rc; } - return -1; + + return rc; } + +/* + Functions to return a certificate or a keyblock. To be used after + a successful search operation. +*/ +#ifdef KEYBOX_WITH_X509 +/* + Return the last found cert. Caller must free it. + */ int -keybox_blob_has_uid ( KEYBOXBLOB blob, - int (*cmp)(const byte *, size_t, void *), void *opaque ) +keybox_get_cert (KEYBOX_HANDLE hd, KsbaCert *r_cert) { - ulong n, nuids, uidinfolen, off, len; - const byte *p, *pend; - byte *buffer = blob->blob; - size_t buflen = blob->bloblen; - - if ( buflen < 40 ) - return GPGERR_GENERAL; /* blob too short */ - n = get32( buffer ); - if ( n > buflen ) - return GPGERR_GENERAL; /* blob larger than announced length */ - buflen = n; /* ignore trailing stuff */ - pend = buffer + n - 1; - - if ( buffer[4] != 2 ) - return GPGERR_GENERAL; /* invalid blob type */ - if ( buffer[5] != 1 ) - return GPGERR_GENERAL; /* invalid blob format version */ - - p = buffer + 20 + get16( buffer + 16 ) * get16( buffer + 18 ); - if ( p+4 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - - nuids = get16( p ); p+= 2; - uidinfolen = get16( p ); p+=2; - for(n=0; n < nuids; n++, p += uidinfolen ) { - if ( p+8 > pend ) - return GPGERR_GENERAL; /* blob shorter than required */ - off = get32 ( p ); - len = get32 ( p + 4 ); - if ( off+len > buflen ) - return GPGERR_GENERAL; /* offset out of bounds */ - if ( (*cmp) ( buffer+off, len, opaque ) ) - return 0; /* found */ + const unsigned char *buffer; + size_t length; + size_t cert_off, cert_len; + KsbaReader reader = NULL; + KsbaCert cert = NULL; + int rc; + + if (!hd) + return KEYBOX_Invalid_Value; + if (!hd->found.blob) + return KEYBOX_Nothing_Found; + + if (blob_get_type (hd->found.blob) != BLOBTYPE_X509) + return KEYBOX_Wrong_Blob_Type; + + buffer = _keybox_get_blob_image (hd->found.blob, &length); + if (length < 40) + return KEYBOX_Blob_Too_Short; + cert_off = get32 (buffer+8); + cert_len = get32 (buffer+12); + if (cert_off+cert_len > length) + return KEYBOX_Blob_Too_Short; + + reader = ksba_reader_new (); + if (!reader) + return KEYBOX_Out_Of_Core; + rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len); + if (rc) + { + ksba_reader_release (reader); + /* fixme: need to map the error codes */ + return KEYBOX_General_Error; } - return -1; -} + cert = ksba_cert_new (); + if (!cert) + { + ksba_reader_release (reader); + return KEYBOX_Out_Of_Core; + } + rc = ksba_cert_read_der (cert, reader); + if (rc) + { + ksba_cert_release (cert); + ksba_reader_release (reader); + /* fixme: need to map the error codes */ + return KEYBOX_General_Error; + } + + *r_cert = cert; + ksba_reader_release (reader); + return 0; +} +#endif /*KEYBOX_WITH_X509*/ diff --git a/kbx/keybox-update.c b/kbx/keybox-update.c new file mode 100644 index 000000000..d49c3d027 --- /dev/null +++ b/kbx/keybox-update.c @@ -0,0 +1,390 @@ +/* keybox-update.c - keybox update operations + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "keybox-defs.h" + +#define EXTSEP_S "." + + +static int +create_tmp_file (const char *template, + char **r_bakfname, char **r_tmpfname, FILE **r_fp) +{ + char *bakfname, *tmpfname; + + *r_bakfname = NULL; + *r_tmpfname = NULL; + +# ifdef USE_ONLY_8DOT3 + /* Here is another Windoze bug?: + * you cant rename("pubring.kbx.tmp", "pubring.kbx"); + * but rename("pubring.kbx.tmp", "pubring.aaa"); + * works. So we replace .kbx by .bak or .tmp + */ + if (strlen (template) > 4 + && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") ) + { + bakfname = xtrymalloc (strlen (template) + 1); + if (!bakfname) + return KEYBOX_Out_Of_Core; + strcpy (bakfname, template); + strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak"); + + tmpfname = xtrymalloc (strlen (template) + 1); + if (!tmpfname) + { + xfree (bakfname); + return KEYBOX_Out_Of_Core; + } + strcpy (tmpfname,template); + strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp"); + } + else + { /* file does not end with kbx; hmmm */ + bakfname = xtrymalloc ( strlen (template) + 5); + if (!bakfname) + return KEYBOX_Out_Of_Core; + strcpy (stpcpy (bakfname, template), EXTSEP_S "bak"); + + tmpfname = xtrymalloc ( strlen (template) + 5); + if (!tmpfname) + { + xfree (bakfname); + return KEYBOX_Out_Of_Core; + } + strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp"); + } +# else /* Posix file names */ + bakfname = xtrymalloc (strlen (template) + 2); + if (!bakfname) + return KEYBOX_Out_Of_Core; + strcpy (stpcpy (bakfname,template),"~"); + + tmpfname = xtrymalloc ( strlen (template) + 5); + if (!tmpfname) + { + xfree (bakfname); + return KEYBOX_Out_Of_Core; + } + strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp"); +# endif /* Posix filename */ + + *r_fp = fopen (tmpfname, "wb"); + if (!*r_fp) + { + xfree (tmpfname); + xfree (bakfname); + return KEYBOX_File_Create_Error; + } + + *r_bakfname = bakfname; + *r_tmpfname = tmpfname; + return 0; +} + + +static int +rename_tmp_file (const char *bakfname, const char *tmpfname, + const char *fname, int secret ) +{ + int rc=0; + + /* restrict the permissions for secret keyboxs */ +#ifndef HAVE_DOSISH_SYSTEM +/* if (secret && !opt.preserve_permissions) */ +/* { */ +/* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */ +/* { */ +/* log_debug ("chmod of `%s' failed: %s\n", */ +/* tmpfname, strerror(errno) ); */ +/* return KEYBOX_Write_File; */ +/* } */ +/* } */ +#endif + + /* fixme: invalidate close caches (not used with stdio)*/ +/* iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */ +/* iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */ +/* iobuf_ioctl (NULL, 2, 0, (char*)fname ); */ + + /* first make a backup file except for secret keyboxs */ + if (!secret) + { +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove (bakfname); +#endif + if (rename (fname, bakfname) ) + { + return KEYBOX_File_Error; + } + } + + /* then rename the file */ +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove (fname); +#endif + if (rename (tmpfname, fname) ) + { + rc = KEYBOX_File_Error; + if (secret) + { +/* log_info ("WARNING: 2 files with confidential" */ +/* " information exists.\n"); */ +/* log_info ("%s is the unchanged one\n", fname ); */ +/* log_info ("%s is the new one\n", tmpfname ); */ +/* log_info ("Please fix this possible security flaw\n"); */ + } + return rc; + } + + return 0; +} + + + +/* Perform insert/delete/update operation. + mode 1 = insert + 2 = delete + 3 = update +*/ +static int +blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, + int secret, off_t start_offset, unsigned int n_packets ) +{ + FILE *fp, *newfp; + int rc=0; + char *bakfname = NULL; + char *tmpfname = NULL; + char buffer[4096]; + int nread, nbytes; + + /* Open the source file. Because we do a rename, we have to check the + permissions of the file */ + if (access (fname, W_OK)) + return KEYBOX_Write_Error; + + fp = fopen (fname, "rb"); + if (mode == 1 && !fp && errno == ENOENT) + { /* insert mode but file does not exist: create a new keybox file */ + newfp = fopen (fname, "wb"); + if (!newfp ) + { + return KEYBOX_File_Create_Error; + } + + rc = _keybox_write_blob (blob, newfp); + if (rc) + { + return rc; + } + if ( fclose (newfp) ) + { + return KEYBOX_File_Create_Error; + } + +/* if (chmod( fname, S_IRUSR | S_IWUSR )) */ +/* { */ +/* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */ +/* return KEYBOX_File_Error; */ +/* } */ + return 0; /* ready */ + } + + if (!fp) + { + rc = KEYBOX_File_Open_Error; + goto leave; + } + + /* create the new file */ + rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); + if (rc) + { + fclose(fp); + goto leave; + } + + /* prepare for insert */ + if (mode == 1) + { + /* copy everything to the new file */ + while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) + { + if (fwrite (buffer, nread, 1, newfp) != 1) + { + rc = KEYBOX_Write_Error; + goto leave; + } + } + if (ferror (fp)) + { + rc = KEYBOX_Read_Error; + goto leave; + } + } + + /* prepare for delete or update */ + if ( mode == 2 || mode == 3 ) + { + off_t current = 0; + + /* copy first part to the new file */ + while ( current < start_offset ) + { + nbytes = DIM(buffer); + if (current + nbytes > start_offset) + nbytes = start_offset - current; + nread = fread (buffer, 1, nbytes, fp); + if (!fread) + break; + current += nread; + + if (fwrite (buffer, nread, 1, newfp) != 1) + { + rc = KEYBOX_Write_Error; + goto leave; + } + } + if (ferror (fp)) + { + rc = KEYBOX_Read_Error; + goto leave; + } + + /* skip this blob */ + rc = _keybox_read_blob (NULL, fp); + if (rc) + return rc; + } + + /* Do an insert or update */ + if ( mode == 1 || mode == 3 ) + { + rc = _keybox_write_blob (blob, newfp); + if (rc) + return rc; + } + + /* copy the rest of the packet for an delete or update */ + if (mode == 2 || mode == 3) + { + while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 ) + { + if (fwrite (buffer, nread, 1, newfp) != 1) + { + rc = KEYBOX_Write_Error; + goto leave; + } + } + if (ferror (fp)) + { + rc = KEYBOX_Read_Error; + goto leave; + } + } + + /* close both files */ + if (fclose(fp)) + { + rc = KEYBOX_File_Close_Error; + fclose (newfp); + goto leave; + } + if (fclose(newfp)) + { + rc = KEYBOX_File_Close_Error; + goto leave; + } + + rc = rename_tmp_file (bakfname, tmpfname, fname, secret); + + leave: + xfree(bakfname); + xfree(tmpfname); + return rc; +} + + + + +#ifdef KEYBOX_WITH_X509 +int +keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, + unsigned char *sha1_digest) +{ + int rc; + const char *fname; + KEYBOXBLOB blob; + + if (!hd) + return KEYBOX_Invalid_Handle; + if (!hd->kb) + return KEYBOX_Invalid_Handle; + fname = hd->kb->fname; + if (!fname) + return KEYBOX_Invalid_Handle; + + /* close this one otherwise we will mess up the position for a next + search. Fixme: it would be better to adjust the position after + the write opertions. */ + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; + } + + rc = _keybox_create_x509_blob (&blob, cert, sha1_digest); + if (!rc) + { + rc = blob_filecopy (1, fname, blob, hd->secret, 0, 0 ); + _keybox_release_blob (blob); + /* if (!rc && !hd->secret && kb_offtbl) */ + /* { */ + /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */ + /* } */ + } + return rc; +} + +int +keybox_update_cert (KEYBOX_HANDLE hd, KsbaCert cert, + unsigned char *sha1_digest) +{ + return -1; +} + + +#endif /*KEYBOX_WITH_X509*/ + + +int +keybox_delete (KEYBOX_HANDLE hd) +{ + return -1; +} + + diff --git a/kbx/keybox.h b/kbx/keybox.h index 90088236a..645f1e715 100644 --- a/kbx/keybox.h +++ b/kbx/keybox.h @@ -27,6 +27,8 @@ extern "C" { #endif #endif +#include "keybox-search-desc.h" + #define KEYBOX_WITH_OPENPGP 1 #define KEYBOX_WITH_X509 1 @@ -51,8 +53,13 @@ typedef enum { KEYBOX_Write_Error = 6, KEYBOX_File_Error = 7, KEYBOX_Blob_Too_Short = 8, - KEYBOX_Blob_Too_Large = 9 - + KEYBOX_Blob_Too_Large = 9, + KEYBOX_Invalid_Handle = 10, + KEYBOX_File_Create_Error = 11, + KEYBOX_File_Open_Error = 12, + KEYBOX_File_Close_Error = 13, + KEYBOX_Nothing_Found = 14, + KEYBOX_Wrong_Blob_Type = 15, } KeyboxError; @@ -60,24 +67,41 @@ typedef enum { typedef struct keybox_handle *KEYBOX_HANDLE; - /*-- keybox-init.c --*/ void *keybox_register_file (const char *fname, int secret); int keybox_is_writable (void *token); - -/*-- --*/ - KEYBOX_HANDLE keybox_new (void *token, int secret); void keybox_release (KEYBOX_HANDLE hd); const char *keybox_get_resource_name (KEYBOX_HANDLE hd); -int keybox_lock (KEYBOX_HANDLE hd, int yes); + + +/*-- keybox-search.c --*/ +#ifdef KEYBOX_WITH_X509 +int keybox_get_cert (KEYBOX_HANDLE hd, KsbaCert *ret_cert); +#endif /*KEYBOX_WITH_X509*/ + +int keybox_search_reset (KEYBOX_HANDLE hd); +int keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc); + + +/*-- keybox-update.c --*/ +#ifdef KEYBOX_WITH_X509 +int keybox_insert_cert (KEYBOX_HANDLE hd, KsbaCert cert, + unsigned char *sha1_digest); +int keybox_update_cert (KEYBOX_HANDLE hd, KsbaCert cert, + unsigned char *sha1_digest); +#endif /*KEYBOX_WITH_X509*/ + +int keybox_delete (KEYBOX_HANDLE hd); + + +/*-- --*/ + #if 0 +int keybox_lock (KEYBOX_HANDLE hd, int yes); int keybox_get_keyblock (KEYBOX_HANDLE hd, KBNODE *ret_kb); -int keybox_update_keyblock (KEYBOX_HANDLE hd, KBNODE kb); -int keybox_insert_keyblock (KEYBOX_HANDLE hd, KBNODE kb); int keybox_locate_writable (KEYBOX_HANDLE hd); -int keybox_delete_keyblock (KEYBOX_HANDLE hd); int keybox_search_reset (KEYBOX_HANDLE hd); int keybox_search (KEYBOX_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); int keybox_rebuild_cache (void *); diff --git a/sm/Makefile.am b/sm/Makefile.am index ec2c1b247..ab34f10b6 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -28,10 +28,15 @@ BUILT_SOURCES = errors.c gpgsm_SOURCES = \ gpgsm.c gpgsm.h \ util.h misc.c errors.c \ + keydb.c keydb.h \ server.c \ + fingerprint.c \ + certdump.c \ + certcheck.c \ + certpath.c \ import.c -gpgsm_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \ +gpgsm_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a ../kbx/libkeybox.a \ ../../libksba/src/.libs/libksba.a \ ../../libgcrypt/src/.libs/libgcrypt.so.1 diff --git a/sm/certchain.c b/sm/certchain.c new file mode 100644 index 000000000..aef1612cc --- /dev/null +++ b/sm/certchain.c @@ -0,0 +1,131 @@ +/* certpath.c - path validation + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include <gcrypt.h> +#include <ksba.h> + +#include "gpgsm.h" +#include "keydb.h" +#include "i18n.h" + +int +gpgsm_validate_path (KsbaCert cert) +{ + int rc = 0, depth = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert subject_cert = NULL, issuer_cert = NULL; + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = GPGSM_General_Error; + goto leave; + } + + log_debug ("validate path for certificate:\n"); + gpgsm_dump_cert (cert); + + subject_cert = cert; + + for (;;) + { + xfree (issuer); + xfree (subject); + issuer = ksba_cert_get_issuer (subject_cert); + subject = ksba_cert_get_subject (subject_cert); + + if (!issuer) + { + if (DBG_X509) + log_debug ("ERROR: issuer missing\n"); + rc = GPGSM_Bad_Certificate; + goto leave; + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) + { + log_debug ("selfsigned certificate has a BAD signatures\n"); + rc = depth? GPGSM_Bad_Certificate_Path : GPGSM_Bad_Certificate; + goto leave; + } + log_debug ("selfsigned certificate is good\n"); + break; /* okay, a self-signed certicate is an end-point */ + } + + depth++; + /* fixme: check against a maximum path length */ + + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = keydb_search_issuer (kh, issuer); + if (rc) + { + log_debug ("failed to find issuer's certificate: rc=%d\n", rc); + rc = GPGSM_Missing_Certificate; + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_debug ("failed to get cert: rc=%d\n", rc); + rc = GPGSM_General_Error; + goto leave; + } + + log_debug ("got issuer's certificate:\n"); + gpgsm_dump_cert (issuer_cert); + + if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) + { + log_debug ("certificate has a BAD signatures\n"); + rc = GPGSM_Bad_Certificate_Path; + goto leave; + } + log_debug ("certificate is good\n"); + + keydb_search_reset (kh); + subject_cert = issuer_cert; + issuer_cert = NULL; + } + + leave: + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + if (subject_cert != cert) + ksba_cert_release (subject_cert); + return rc; +} + diff --git a/sm/certcheck.c b/sm/certcheck.c new file mode 100644 index 000000000..a86aa2d9e --- /dev/null +++ b/sm/certcheck.c @@ -0,0 +1,156 @@ +/* certcheck.c - check one certificate + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include <gcrypt.h> +#include <ksba.h> + +#include "gpgsm.h" +#include "keydb.h" +#include "i18n.h" + +static int +do_encode_md (GCRY_MD_HD md, int algo, size_t len, unsigned nbits, + const byte *asn, size_t asnlen, GCRY_MPI *r_val) +{ + int nframe = (nbits+7) / 8; + byte *frame; + int i, n; + + if ( len + asnlen + 4 > nframe ) + { + log_error ("can't encode a %d bit MD into a %d bits frame\n", + (int)(len*8), (int)nbits); + return GPGSM_Internal_Error; + } + + /* We encode the MD in this way: + * + * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = xtrymalloc (nframe); + if (!frame) + return GPGSM_Out_Of_Core; + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - len - asnlen -3 ; + assert ( i > 1 ); + memset ( frame+n, 0xff, i ); n += i; + frame[n++] = 0; + memcpy ( frame+n, asn, asnlen ); n += asnlen; + memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len; + assert ( n == nframe ); + gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, &nframe); + xfree (frame); + return 0; +} + + +/* + Check the signature on CERT using the ISSUER-CERT. This function + does only test the cryptographic signature and nothing else. It is + assumed that the ISSUER_CERT is valid. */ +int +gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert) +{ + /* OID for MD5 as defined in PKCS#1 (rfc2313) */ + static byte asn[18] = /* Object ID is 1.2.840.113549.2.5 (md5) */ + { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 + }; + + GCRY_MD_HD md; + int rc, algo; + GCRY_MPI frame; + char *p; + GCRY_SEXP s_sig, s_hash, s_pkey; + + algo = ksba_cert_get_digest_algo (cert); + md = gcry_md_open (algo, 0); + if (!md) + { + log_error ("md_open failed: %s\n", gcry_strerror (-1)); + return GPGSM_General_Error; + } + + rc = ksba_cert_hash (cert, 1, HASH_FNC, md); + if (rc) + { + log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc)); + gcry_md_close (md); + return map_ksba_err (rc); + } + gcry_md_final (md); + + p = ksba_cert_get_sig_val (cert); /* fixme: check p*/ + if (DBG_X509) + log_debug ("signature: %s\n", p); + + rc = gcry_sexp_sscan ( &s_sig, NULL, p, strlen(p)); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc)); + return map_gcry_err (rc); + } + /*gcry_sexp_dump (s_sig);*/ + + + /* FIXME: need to map the algo to the ASN OID - we assume a fixed + one for now */ + rc = do_encode_md (md, algo, 16, 2048, asn, DIM(asn), &frame); + if (rc) + { + /* fixme: clean up some things */ + return rc; + } + /* put hash into the S-Exp s_hash */ + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + /*fputs ("hash:\n", stderr); gcry_sexp_dump (s_hash);*/ + + p = ksba_cert_get_public_key (issuer_cert); + if (DBG_X509) + log_debug ("issuer public key: %s\n", p); + + rc = gcry_sexp_sscan ( &s_pkey, NULL, p, strlen(p)); + if (rc) + { + log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc)); + return map_gcry_err (rc); + } + /*gcry_sexp_dump (s_pkey);*/ + + rc = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (DBG_CRYPTO) + log_debug ("gcry_pk_verify: %s\n", gcry_strerror (rc)); + return map_gcry_err (rc); +} + diff --git a/sm/certdump.c b/sm/certdump.c new file mode 100644 index 000000000..5dfce2a02 --- /dev/null +++ b/sm/certdump.c @@ -0,0 +1,131 @@ +/* certdump.c - Dump a certificate for debugging + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include <gcrypt.h> +#include <ksba.h> + +#include "gpgsm.h" +#include "keydb.h" + +static void +print_integer (unsigned char *p) +{ + unsigned long len; + + if (!p) + fputs ("none", stdout); + else + { + len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + for (p+=4; len; len--, p++) + printf ("%02X", *p); + } +} + +static void +print_time (time_t t) +{ + + if (!t) + fputs ("none", stdout); + else if ( t == (time_t)(-1) ) + fputs ("error", stdout); + else + { + struct tm *tp; + + tp = gmtime (&t); + printf ("%04d-%02d-%02d %02d:%02d:%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + assert (!tp->tm_isdst); + } +} + +static void +print_dn (char *p) +{ + + if (!p) + fputs ("error", stdout); + else + printf ("`%s'", p); +} + + +void +gpgsm_dump_cert (KsbaCert cert) +{ + unsigned char *p; + char *dn; + time_t t; + + if (!cert) + { + fputs ("[no certificate]\n", stdout); + return; + } + + p = ksba_cert_get_serial (cert); + fputs ("serial: ", stdout); + print_integer (p); + ksba_free (p); + putchar ('\n'); + + t = ksba_cert_get_validity (cert, 0); + fputs ("notBefore: ", stdout); + print_time (t); + putchar ('\n'); + t = ksba_cert_get_validity (cert, 1); + fputs ("notAfter: ", stdout); + print_time (t); + putchar ('\n'); + + dn = ksba_cert_get_issuer (cert); + fputs ("issuer: ", stdout); + print_dn (dn); + ksba_free (dn); + putchar ('\n'); + + dn = ksba_cert_get_subject (cert); + fputs ("subject: ", stdout); + print_dn (dn); + ksba_free (dn); + putchar ('\n'); + + printf ("hash algo: %d\n", ksba_cert_get_digest_algo (cert)); + + p = gpgsm_get_fingerprint_string (cert, 0); + printf ("SHA1 Fingerprint=%s\n", p); + xfree (p); +} + + + + diff --git a/sm/certpath.c b/sm/certpath.c new file mode 100644 index 000000000..aef1612cc --- /dev/null +++ b/sm/certpath.c @@ -0,0 +1,131 @@ +/* certpath.c - path validation + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include <gcrypt.h> +#include <ksba.h> + +#include "gpgsm.h" +#include "keydb.h" +#include "i18n.h" + +int +gpgsm_validate_path (KsbaCert cert) +{ + int rc = 0, depth = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert subject_cert = NULL, issuer_cert = NULL; + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = GPGSM_General_Error; + goto leave; + } + + log_debug ("validate path for certificate:\n"); + gpgsm_dump_cert (cert); + + subject_cert = cert; + + for (;;) + { + xfree (issuer); + xfree (subject); + issuer = ksba_cert_get_issuer (subject_cert); + subject = ksba_cert_get_subject (subject_cert); + + if (!issuer) + { + if (DBG_X509) + log_debug ("ERROR: issuer missing\n"); + rc = GPGSM_Bad_Certificate; + goto leave; + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) + { + log_debug ("selfsigned certificate has a BAD signatures\n"); + rc = depth? GPGSM_Bad_Certificate_Path : GPGSM_Bad_Certificate; + goto leave; + } + log_debug ("selfsigned certificate is good\n"); + break; /* okay, a self-signed certicate is an end-point */ + } + + depth++; + /* fixme: check against a maximum path length */ + + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = keydb_search_issuer (kh, issuer); + if (rc) + { + log_debug ("failed to find issuer's certificate: rc=%d\n", rc); + rc = GPGSM_Missing_Certificate; + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_debug ("failed to get cert: rc=%d\n", rc); + rc = GPGSM_General_Error; + goto leave; + } + + log_debug ("got issuer's certificate:\n"); + gpgsm_dump_cert (issuer_cert); + + if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) + { + log_debug ("certificate has a BAD signatures\n"); + rc = GPGSM_Bad_Certificate_Path; + goto leave; + } + log_debug ("certificate is good\n"); + + keydb_search_reset (kh); + subject_cert = issuer_cert; + issuer_cert = NULL; + } + + leave: + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + if (subject_cert != cert) + ksba_cert_release (subject_cert); + return rc; +} + diff --git a/sm/fingerprint.c b/sm/fingerprint.c new file mode 100644 index 000000000..9453a6eab --- /dev/null +++ b/sm/fingerprint.c @@ -0,0 +1,104 @@ +/* fingerprint.c - Get the fingerprint + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include <gcrypt.h> +#include <ksba.h> + +#include "gpgsm.h" + +/* Return the fingerprint of the certificate (we can't put this into + libksba becuase we need libgcrypt support). The caller must + provide an array of sufficient length or NULL so that the function + allocates the array. If r_len is not NULL, the length of the + digest is return, well, this can also be done by using + gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used. + + If there is a problem , the function does never return NULL but a + digest of all 0xff. + */ +char * +gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len) +{ + GCRY_MD_HD md; + int rc, len; + + if (!algo) + algo = GCRY_MD_SHA1; + + len = gcry_md_get_algo_dlen (algo); + assert (len); + if (!array) + array = xmalloc (len); + + if (r_len) + *r_len = len; + + md = gcry_md_open (algo, 0); + if (!md) + { + log_error ("md_open failed: %s\n", gcry_strerror (-1)); + memset (array, 0xff, len); /* better return an invalid fpr than NULL */ + return array; + } + + rc = ksba_cert_hash (cert, 0, HASH_FNC, md); + if (rc) + { + log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc)); + gcry_md_close (md); + memset (array, 0xff, len); /* better return an invalid fpr than NULL */ + return array; + } + gcry_md_final (md); + memcpy (array, gcry_md_read(md, algo), len ); + return array; +} + + +/* Return an allocated buffer with the formatted fungerprint */ +char * +gpgsm_get_fingerprint_string (KsbaCert cert, int algo) +{ + unsigned char digest[MAX_DIGEST_LEN]; + char *buf; + int len, i; + + if (!algo) + algo = GCRY_MD_SHA1; + + len = gcry_md_get_algo_dlen (algo); + assert (len <= MAX_DIGEST_LEN ); + gpgsm_get_fingerprint (cert, algo, digest, NULL); + buf = xmalloc (len*3+1); + *buf = 0; + for (i=0; i < len; i++ ) + sprintf (buf+strlen(buf), i? ":%02X":"%02X", digest[i]); + return buf; +} + diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 3ca58d3c8..39db2433e 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -29,7 +29,9 @@ #include <gcrypt.h> #include "gpgsm.h" #include "../assuan/assuan.h" /* malloc hooks */ +#include "../kbx/keybox.h" /* malloc hooks */ #include "i18n.h" +#include "keydb.h" enum cmd_and_opt_values { aNull = 0, @@ -441,7 +443,7 @@ set_debug(void) { if (opt.debug & DBG_MPI_VALUE) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); - if (opt.debug & DBG_CIPHER_VALUE ) + if (opt.debug & DBG_CRYPTO_VALUE ) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); } @@ -562,8 +564,9 @@ main ( int argc, char **argv) Now we are now working under our real uid */ + ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); - /* ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );*/ + keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); if (default_config ) configname = make_filename (opt.homedir, "gpgsm.conf", NULL); @@ -831,12 +834,10 @@ main ( int argc, char **argv) if (!cmd && opt.fingerprint && !with_fpr) set_cmd (&cmd, aListKeys); -#if 0 /* fixme */ - if (!nrings && default_keyring) /* add default ring */ - add_keyblock_resource ("pubcerts.gpg", 0, 0); + if (!nrings && default_keyring) /* add default keybox */ + keydb_add_resource ("pubcerts.kbx", 0, 0); for (sl = nrings; sl; sl = sl->next) - add_keyblock_resource( sl->d, 0, 0 ); -#endif + keydb_add_resource (sl->d, 0, 0); FREE_STRLIST(nrings); fname = argc? *argv : NULL; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 1c66e1032..a4361dc1c 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -21,6 +21,7 @@ #ifndef GPGSM_H #define GPGSM_H +#include <ksba.h> #include "util.h" /* Error numbers */ @@ -31,10 +32,15 @@ enum { GPGSM_Out_Of_Core = 2, GPGSM_Invalid_Value = 3, GPGSM_IO_Error = 4, - + GPGSM_Resource_Limit = 5, + GPGSM_Internal_Error = 6, + GPGSM_Bad_Certificate = 7, + GPGSM_Bad_Certificate_Path = 8, + GPGSM_Missing_Certificate = 9, }; +#define MAX_DIGEST_LEN 24 /* A large struct name "opt" to keep global flags */ struct { @@ -77,14 +83,14 @@ struct { #define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ -#define DBG_CIPHER_VALUE 4 /* debug cipher handling */ +#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) -#define DBG_CIPHER (opt.debug & DBG_CIPHER_VALUE) +#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) @@ -95,8 +101,31 @@ void gpgsm_exit (int rc); /*-- server.c --*/ void gpgsm_server (void); +/*-- fingerprint --*/ +char *gpgsm_get_fingerprint (KsbaCert cert, int algo, char *array, int *r_len); +char *gpgsm_get_fingerprint_string (KsbaCert cert, int algo); + +/*-- certdump.c --*/ +void gpgsm_dump_cert (KsbaCert cert); + +/*-- certcheck.c --*/ +int gpgsm_check_cert_sig (KsbaCert issuer_cert, KsbaCert cert); + + +/*-- certpath.c --*/ +int gpgsm_validate_path (KsbaCert cert); + + + /*-- import.c --*/ int gpgsm_import (int in_fd); + + + +/*-- errors.c (built) --*/ +const char *gpgsm_strerror (int err); + + #endif /*GPGSM_H*/ diff --git a/sm/import.c b/sm/import.c index 7fde82327..8dc36c1ad 100644 --- a/sm/import.c +++ b/sm/import.c @@ -31,6 +31,8 @@ #include <ksba.h> #include "gpgsm.h" +#include "keydb.h" +#include "i18n.h" struct reader_cb_parm_s { FILE *fp; @@ -68,199 +70,31 @@ reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) static void -print_integer (unsigned char *p) +store_cert (KsbaCert cert) { - unsigned long len; - - if (!p) - fputs ("none", stdout); - else - { - len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - for (p+=4; len; len--, p++) - printf ("%02X", *p); - } -} - -static void -print_time (time_t t) -{ - - if (!t) - fputs ("none", stdout); - else if ( t == (time_t)(-1) ) - fputs ("error", stdout); - else - { - struct tm *tp; - - tp = gmtime (&t); - printf ("%04d-%02d-%02d %02d:%02d:%02d", - 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, - tp->tm_hour, tp->tm_min, tp->tm_sec); - assert (!tp->tm_isdst); - } -} - -static void -print_dn (char *p) -{ - - if (!p) - fputs ("error", stdout); - else - printf ("`%s'", p); -} - -static void -print_cert (KsbaCert cert) -{ - unsigned char *p; - char *dn; - time_t t; - - p = ksba_cert_get_serial (cert); - fputs ("serial: ", stdout); - print_integer (p); - ksba_free (p); - putchar ('\n'); - - t = ksba_cert_get_validity (cert, 0); - fputs ("notBefore: ", stdout); - print_time (t); - putchar ('\n'); - t = ksba_cert_get_validity (cert, 1); - fputs ("notAfter: ", stdout); - print_time (t); - putchar ('\n'); - - dn = ksba_cert_get_issuer (cert); - fputs ("issuer: ", stdout); - print_dn (dn); - ksba_free (dn); - putchar ('\n'); - - dn = ksba_cert_get_subject (cert); - fputs ("subject: ", stdout); - print_dn (dn); - ksba_free (dn); - putchar ('\n'); - - printf ("hash algo: %d\n", ksba_cert_get_digest_algo (cert)); -} - - - -static MPI -do_encode_md (GCRY_MD_HD md, int algo, size_t len, unsigned nbits, - const byte *asn, size_t asnlen) -{ - int nframe = (nbits+7) / 8; - byte *frame; - int i,n; - MPI a; - - if( len + asnlen + 4 > nframe ) - log_bug("can't encode a %d bit MD into a %d bits frame\n", - (int)(len*8), (int)nbits); - - /* We encode the MD in this way: - * - * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) - * - * PAD consists of FF bytes. - */ - frame = xmalloc (nframe); - n = 0; - frame[n++] = 0; - frame[n++] = 1; /* block type */ - i = nframe - len - asnlen -3 ; - assert( i > 1 ); - memset( frame+n, 0xff, i ); n += i; - frame[n++] = 0; - memcpy( frame+n, asn, asnlen ); n += asnlen; - memcpy( frame+n, gcry_md_read(md, algo), len ); n += len; - assert( n == nframe ); - gcry_mpi_scan ( &a, GCRYMPI_FMT_USG, frame, &nframe); - xfree(frame); - return a; -} - - - - -static void -check_selfsigned_cert (KsbaCert cert) -{ - /* OID for MD5 as defined in PKCS#1 (rfc2313) */ - static byte asn[18] = /* Object ID is 1.2.840.113549.2.5 (md5) */ - { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 - }; - - GCRY_MD_HD md; - int rc, algo; - GCRY_MPI frame; - char *p; - GCRY_SEXP s_sig, s_hash, s_pkey; - - algo = ksba_cert_get_digest_algo (cert); - md = gcry_md_open (algo, 0); - if (!md) - { - log_error ("md_open failed: %s\n", gcry_strerror (-1)); - return; - } + KEYDB_HANDLE kh; + int rc; - gcry_md_start_debug (md, "cert"); - rc = ksba_cert_hash (cert, gcry_md_write, md); - if (rc) + kh = keydb_new (0); + if (!kh) { - log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc)); - gcry_md_close (md); + log_error (_("failed to allocated keyDB handle\n")); return; } - gcry_md_final (md); - - p = ksba_cert_get_sig_val (cert); - printf ("signature: %s\n", p); - - rc = gcry_sexp_sscan ( &s_sig, NULL, p, strlen(p)); + rc = keydb_locate_writable (kh, 0); if (rc) - { - log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc)); - return; - } - /*gcry_sexp_dump (s_sig);*/ - - - /* FIXME: need to map the algo to the ASN OID - we assume a fixed - one for now */ - frame = do_encode_md (md, algo, 16, 2048, asn, DIM(asn)); + log_error (_("error finding writable keyDB: %s\n"), gpgsm_strerror (rc)); - /* put hash into the S-Exp s_hash */ - if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) - BUG (); - /*fputs ("hash:\n", stderr); gcry_sexp_dump (s_hash);*/ - _gcry_log_mpidump ("hash", frame); - - p = ksba_cert_get_public_key (cert); - printf ("public key: %s\n", p); - - rc = gcry_sexp_sscan ( &s_pkey, NULL, p, strlen(p)); + rc = keydb_insert_cert (kh, cert); if (rc) { - log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc)); - return; + log_error (_("error storing certificate: %s\n"), gpgsm_strerror (rc)); } - /*gcry_sexp_dump (s_pkey);*/ - - rc = gcry_pk_verify (s_sig, s_hash, s_pkey); - log_error ("gcry_pk_verify: %s\n", gcry_strerror (rc)); - + keydb_release (kh); } + int gpgsm_import (int in_fd) @@ -311,9 +145,8 @@ gpgsm_import (int in_fd) goto leave; } - print_cert (cert); - check_selfsigned_cert (cert); - + if ( !gpgsm_validate_path (cert) ) + store_cert (cert); leave: ksba_cert_release (cert); @@ -323,3 +156,4 @@ gpgsm_import (int in_fd) return rc; } + diff --git a/sm/keydb.c b/sm/keydb.c index b12ba1df3..ef839edfb 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -28,19 +28,12 @@ #include <sys/stat.h> #include <unistd.h> -#include "util.h" -#include "options.h" -#include "keybox.h" +#include "gpgsm.h" +#include "../kbx/keybox.h" #include "keydb.h" #include "i18n.h" -static struct { - const char *homedir; - int dry_run; - int quiet; - int verbose; - int preserve_permissions; -} keydbopt; +#define DIRSEP_C '/' static int active_handles; @@ -87,25 +80,24 @@ keydb_add_resource (const char *url, int force, int secret) { static int any_secret, any_public; const char *resname = url; - IOBUF iobuf = NULL; char *filename = NULL; - int rc = 0; + int rc = 0; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; - const char *created_fname = NULL; +/* const char *created_fname = NULL; */ /* Do we have an URL? * gnupg-ring:filename := this is a plain keybox * filename := See what is is, but create as plain keybox. */ if (strlen (resname) > 11) { - if (!strncmp( resname, "gnupg-ring:", 11) ) { + if (!strncmp( resname, "gnupg-kbx:", 10) ) { rt = KEYDB_RESOURCE_TYPE_KEYBOX; resname += 11; } #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL `%s'\n", url ); - rc = G10ERR_GENERAL; + rc = GPGSM_General_Error; goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ @@ -115,59 +107,62 @@ keydb_add_resource (const char *url, int force, int secret) if (strchr(resname, DIRSEP_C) ) filename = make_filename (resname, NULL); else - filename = make_filename (keydbopt.homedir, resname, NULL); + filename = make_filename (opt.homedir, resname, NULL); } else - filename = m_strdup (resname); + filename = xstrdup (resname); if (!force) force = secret? !any_secret : !any_public; /* see whether we can determine the filetype */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { - FILE *fp = fopen( filename, "rb" ); + FILE *fp2 = fopen( filename, "rb" ); - if (fp) { + if (fp2) { u32 magic; - if (fread( &magic, 4, 1, fp) == 1 ) { + /* FIXME: check for the keybox magic */ + if (fread( &magic, 4, 1, fp2) == 1 ) + { if (magic == 0x13579ace || magic == 0xce9a5713) - ; /* GDBM magic - no more support */ + ; /* GDBM magic - no more support */ else - rt = KEYDB_RESOURCE_TYPE_KEYBOX; - } + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + } else /* maybe empty: assume ring */ - rt = KEYDB_RESOURCE_TYPE_KEYBOX; - fclose( fp ); + rt = KEYDB_RESOURCE_TYPE_KEYBOX; + fclose (fp2); } else /* no file yet: create ring */ - rt = KEYDB_RESOURCE_TYPE_KEYBOX; + rt = KEYDB_RESOURCE_TYPE_KEYBOX; } switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource `%s'\n", url ); - rc = G10ERR_GENERAL; + rc = GPGSM_General_Error; goto leave; case KEYDB_RESOURCE_TYPE_KEYBOX: - iobuf = iobuf_open (filename); +#if 0 + fp = fopen (filename); if (!iobuf && !force) { rc = G10ERR_OPEN_FILE; goto leave; } - if (!iobuf) { + if (!fp) { char *last_slash_in_filename; last_slash_in_filename = strrchr (filename, DIRSEP_C); *last_slash_in_filename = 0; if (access(filename, F_OK)) { - /* on the first time we try to create the default homedir and - * in this case the process will be terminated, so that on the - * next invocation it can read the options file in on startup - */ + /* on the first time we try to create the default + homedir and in this case the process will be + terminated, so that on the next invocation it can + read the options file in on startup */ try_make_homedir (filename); rc = G10ERR_OPEN_FILE; *last_slash_in_filename = DIRSEP_C; @@ -185,7 +180,7 @@ keydb_add_resource (const char *url, int force, int secret) } else { #ifndef HAVE_DOSISH_SYSTEM - if (secret && !keydbopt.preserve_permissionws) { + if (secret && !opt.preserve_permissionws) { if (chmod (filename, S_IRUSR | S_IWUSR) ) { log_error (_("changing permission of " " `%s' failed: %s\n"), @@ -195,7 +190,7 @@ keydb_add_resource (const char *url, int force, int secret) } } #endif - if (!keydbopt.quiet) + if (!opt.quiet) log_info (_("keybox `%s' created\n"), filename); created_fname = filename; } @@ -204,12 +199,13 @@ keydb_add_resource (const char *url, int force, int secret) iobuf = NULL; if (created_fname) /* must invalidate that ugly cache */ iobuf_ioctl (NULL, 2, 0, (char*)created_fname); +#endif { - void *token = keybox_register_filename (filename, secret); + void *token = keybox_register_file (filename, secret); if (!token) ; /* already registered - ignore it */ else if (used_resources >= MAX_KEYDB_RESOURCES) - rc = G10ERR_RESOURCE_LIMIT; + rc = GPGSM_Resource_Limit; else { all_resources[used_resources].type = rt; @@ -223,7 +219,7 @@ keydb_add_resource (const char *url, int force, int secret) default: log_error ("resource type of `%s' not supported\n", url); - rc = G10ERR_GENERAL; + rc = GPGSM_General_Error; goto leave; } @@ -231,12 +227,12 @@ keydb_add_resource (const char *url, int force, int secret) leave: if (rc) - log_error ("keyblock resource `%s': %s\n", filename, g10_errstr(rc)); + log_error ("keyblock resource `%s': %s\n", filename, gpgsm_strerror(rc)); else if (secret) any_secret = 1; else any_public = 1; - m_free (filename); + xfree (filename); return rc; } @@ -249,7 +245,7 @@ keydb_new (int secret) KEYDB_HANDLE hd; int i, j; - hd = m_alloc_clear (sizeof *hd); + hd = xcalloc (1, sizeof *hd); hd->found = -1; assert (used_resources <= MAX_KEYDB_RESOURCES); @@ -267,7 +263,7 @@ keydb_new (int secret) hd->active[j].secret = all_resources[i].secret; hd->active[j].u.kr = keybox_new (all_resources[i].token, secret); if (!hd->active[j].u.kr) { - m_free (hd); + xfree (hd); return NULL; /* fixme: release all previously allocated handles*/ } j++; @@ -283,95 +279,101 @@ keydb_new (int secret) void keydb_release (KEYDB_HANDLE hd) { - int i; - - if (!hd) - return; - assert (active_handles > 0); - active_handles--; + int i; + + if (!hd) + return; + assert (active_handles > 0); + active_handles--; - unlock_all (hd); - for (i=0; i < hd->used; i++) { - switch (hd->active[i].type) { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_release (hd->active[i].u.kr); - break; + unlock_all (hd); + for (i=0; i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + keybox_release (hd->active[i].u.kr); + break; } } - m_free (hd); + xfree (hd); } -/* - * Return the name of the current resource. This is function first - * looks for the last found found, then for the current search - * position, and last returns the first available resource. The - * returned string is only valid as long as the handle exists. This - * function does only return NULL if no handle is specified, in all - * other error cases an empty string is returned. - */ +/* Return the name of the current resource. This is function first + looks for the last found found, then for the current search + position, and last returns the first available resource. The + returned string is only valid as long as the handle exists. This + function does only return NULL if no handle is specified, in all + other error cases an empty string is returned. */ const char * keydb_get_resource_name (KEYDB_HANDLE hd) { - int idx; - const char *s = NULL; - - if (!hd) - return NULL; + int idx; + const char *s = NULL; + + if (!hd) + return NULL; - if ( hd->found >= 0 && hd->found < hd->used) - idx = hd->found; - else if ( hd->current >= 0 && hd->current < hd->used) - idx = hd->current; - else - idx = 0; + if ( hd->found >= 0 && hd->found < hd->used) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < hd->used) + idx = hd->current; + else + idx = 0; - switch (hd->active[idx].type) { - case KEYDB_RESOURCE_TYPE_NONE: - s = NULL; - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - s = keybox_get_resource_name (hd->active[idx].u.kr); - break; + switch (hd->active[idx].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + s = NULL; + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + s = keybox_get_resource_name (hd->active[idx].u.kr); + break; } - - return s? s: ""; + + return s? s: ""; } - + static int lock_all (KEYDB_HANDLE hd) { - int i, rc = 0; - - for (i=0; !rc && i < hd->used; i++) { - switch (hd->active[i].type) { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_lock (hd->active[i].u.kr, 1); - break; + int i, rc = 0; + + for (i=0; !rc && i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + /* FIXME rc = keybox_lock (hd->active[i].u.kr, 1);*/ + break; } } - if (rc) { + if (rc) + { /* revert the already set locks */ - for (i--; i >= 0; i--) { - switch (hd->active[i].type) { + for (i--; i >= 0; i--) + { + switch (hd->active[i].type) + { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_lock (hd->active[i].u.kr, 0); + /* Fixme: keybox_lock (hd->active[i].u.kr, 0);*/ break; - } - } - } + } + } + } else - hd->locked = 1; + hd->locked = 1; return rc; } @@ -379,24 +381,27 @@ lock_all (KEYDB_HANDLE hd) static void unlock_all (KEYDB_HANDLE hd) { - int i; - - if (!hd->locked) - return; - - for (i=hd->used-1; i >= 0; i--) { - switch (hd->active[i].type) { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_lock (hd->active[i].u.kr, 0); - break; + int i; + + if (!hd->locked) + return; + + for (i=hd->used-1; i >= 0; i--) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + /* fixme: keybox_lock (hd->active[i].u.kr, 0);*/ + break; } } - hd->locked = 0; + hd->locked = 0; } - + +#if 0 /* * Return the last found keybox. Caller must free it. * The returned keyblock has the kbode flag bit 0 set for the node with @@ -440,7 +445,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb) if ( hd->found < 0 || hd->found >= hd->used) return -1; /* nothing found */ - if( keydbopt.dry_run ) + if( opt.dry_run ) return 0; rc = lock_all (hd); @@ -473,7 +478,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) if (!hd) return G10ERR_INV_ARG; - if( keydbopt.dry_run ) + if( opt.dry_run ) return 0; if ( hd->found >= 0 && hd->found < hd->used) @@ -500,41 +505,157 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb) return rc; } +#endif /*disabled code*/ + + + +/* + Return the last found keybox. Caller must free it. The returned + keyblock has the kbode flag bit 0 set for the node with the public + key used to locate the keyblock or flag bit 1 set for the user ID + node. */ +int +keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert) +{ + int rc = 0; + + if (!hd) + return GPGSM_Invalid_Value; + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = GPGSM_General_Error; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert); + break; + } + + return rc; +} /* - * The current keyblock will be deleted. + * Insert a new Certificate into one of the resources. */ int -keydb_delete_keyblock (KEYDB_HANDLE hd) +keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert) { - int rc = -1; + int rc = -1; + int idx; + char digest[20]; + + if (!hd) + return GPGSM_Invalid_Value; - if (!hd) - return G10ERR_INV_ARG; + if (opt.dry_run) + return 0; + + if ( hd->found >= 0 && hd->found < hd->used) + idx = hd->found; + else if ( hd->current >= 0 && hd->current < hd->used) + idx = hd->current; + else + return GPGSM_General_Error; + + rc = lock_all (hd); + if (rc) + return rc; - if ( hd->found < 0 || hd->found >= hd->used) - return -1; /* nothing found */ + gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ - if( keydbopt.dry_run ) - return 0; + switch (hd->active[idx].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = GPGSM_General_Error; + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); + break; + } + + unlock_all (hd); + return rc; +} - rc = lock_all (hd); - if (rc) - return rc; - switch (hd->active[hd->found].type) { - case KEYDB_RESOURCE_TYPE_NONE: - rc = G10ERR_GENERAL; /* oops */ - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_delete_keyblock (hd->active[hd->found].u.kr); - break; + +/* update the current keyblock with KB */ +int +keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert) +{ + int rc = 0; + char digest[20]; + + if (!hd) + return GPGSM_Invalid_Value; + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + if (opt.dry_run) + return 0; + + rc = lock_all (hd); + if (rc) + return rc; + + gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = GPGSM_General_Error; /* oops */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); + break; } - unlock_all (hd); + unlock_all (hd); + return rc; +} + + +/* + * The current keyblock or cert will be deleted. + */ +int +keydb_delete (KEYDB_HANDLE hd) +{ + int rc = -1; + + if (!hd) + return GPGSM_Invalid_Value; + + if ( hd->found < 0 || hd->found >= hd->used) + return -1; /* nothing found */ + + if( opt.dry_run ) + return 0; + + rc = lock_all (hd); + if (rc) return rc; + + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + rc = GPGSM_General_Error; + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_delete (hd->active[hd->found].u.kr); + break; + } + + unlock_all (hd); + return rc; } + /* * Locate the default writable key resource, so that the next @@ -547,12 +668,12 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) int rc; if (!hd) - return G10ERR_INV_ARG; + return GPGSM_Invalid_Value; rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; - + for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { switch (hd->active[hd->current].type) @@ -576,7 +697,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) void keydb_rebuild_caches (void) { - int i, rc; + int i; for (i=0; i < used_resources; i++) { @@ -587,10 +708,10 @@ keydb_rebuild_caches (void) case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_rebuild_cache (all_resources[i].token); - if (rc) - log_error (_("failed to rebuild keybox cache: %s\n"), - g10_errstr (rc)); +/* rc = keybox_rebuild_cache (all_resources[i].token); */ +/* if (rc) */ +/* log_error (_("failed to rebuild keybox cache: %s\n"), */ +/* g10_errstr (rc)); */ break; } } @@ -604,27 +725,29 @@ keydb_rebuild_caches (void) int keydb_search_reset (KEYDB_HANDLE hd) { - int i, rc = 0; - - if (!hd) - return G10ERR_INV_ARG; + int i, rc = 0; + + if (!hd) + return GPGSM_Invalid_Value; - hd->current = 0; - hd->found = -1; - /* and reset all resources */ - for (i=0; !rc && i < hd->used; i++) { - switch (hd->active[i].type) { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_search_reset (hd->active[i].u.kr); - break; + hd->current = 0; + hd->found = -1; + /* and reset all resources */ + for (i=0; !rc && i < hd->used; i++) + { + switch (hd->active[i].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_search_reset (hd->active[i].u.kr); + break; } } - return rc; + return rc; /* fixme: we need to map error codes or share them with + all modules*/ } - /* * Search through all keydb resources, starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. @@ -632,71 +755,101 @@ keydb_search_reset (KEYDB_HANDLE hd) int keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) { - int rc = -1; - - if (!hd) - return G10ERR_INV_ARG; + int rc = -1; + + if (!hd) + return GPGSM_Invalid_Value; - while (rc == -1 && hd->current >= 0 && hd->current < hd->used) { - switch (hd->active[hd->current].type) { - case KEYDB_RESOURCE_TYPE_NONE: - BUG(); /* we should never see it here */ - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc); - break; + while (rc == -1 && hd->current >= 0 && hd->current < hd->used) + { + switch (hd->active[hd->current].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + BUG(); /* we should never see it here */ + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc); + break; } - if (rc == -1) /* EOF -> switch to next resource */ - hd->current++; - else if (!rc) - hd->found = hd->current; + if (rc == -1) /* EOF -> switch to next resource */ + hd->current++; + else if (!rc) + hd->found = hd->current; } - - return rc; + + return rc; } int keydb_search_first (KEYDB_HANDLE hd) { - KEYDB_SEARCH_DESC desc; - - memset (&desc, 0, sizeof desc); - desc.mode = KEYDB_SEARCH_MODE_FIRST; - return keydb_search (hd, &desc, 1); + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FIRST; + return keydb_search (hd, &desc, 1); } int keydb_search_next (KEYDB_HANDLE hd) { - KEYDB_SEARCH_DESC desc; - - memset (&desc, 0, sizeof desc); - desc.mode = KEYDB_SEARCH_MODE_NEXT; - return keydb_search (hd, &desc, 1); + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_NEXT; + return keydb_search (hd, &desc, 1); } int keydb_search_kid (KEYDB_HANDLE hd, u32 *kid) { - KEYDB_SEARCH_DESC desc; - - memset (&desc, 0, sizeof desc); - desc.mode = KEYDB_SEARCH_MODE_LONG_KID; - desc.u.kid[0] = kid[0]; - desc.u.kid[1] = kid[1]; - return keydb_search (hd, &desc, 1); + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_LONG_KID; +/* desc.u.kid[0] = kid[0]; */ +/* desc.u.kid[1] = kid[1]; */ + return keydb_search (hd, &desc, 1); } int keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr) { - KEYDB_SEARCH_DESC desc; + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_FPR; + memcpy (desc.u.fpr, fpr, 20); + return keydb_search (hd, &desc, 1); +} - memset (&desc, 0, sizeof desc); - desc.mode = KEYDB_SEARCH_MODE_FPR; - memcpy (desc.u.fpr, fpr, MAX_FINGERPRINT_LEN); - return keydb_search (hd, &desc, 1); +int +keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer) +{ + KEYDB_SEARCH_DESC desc; + int rc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_ISSUER; + desc.u.name = issuer; + rc = keydb_search (hd, &desc, 1); + return rc; +} + +int +keydb_search_issuer_sn (KEYDB_HANDLE hd, + const char *issuer, const unsigned char *serial) +{ + KEYDB_SEARCH_DESC desc; + int rc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN; + desc.sn = serial; + desc.u.name = issuer; + rc = keydb_search (hd, &desc, 1); + return rc; } diff --git a/sm/keydb.h b/sm/keydb.h index f67f7362e..71f457fef 100644 --- a/sm/keydb.h +++ b/sm/keydb.h @@ -21,53 +21,43 @@ #ifndef GNUPG_KEYDB_H #define GNUPG_KEYDB_H +#include <ksba.h> + +#include "../kbx/keybox-search-desc.h" + typedef struct keydb_handle *KEYDB_HANDLE; -typedef enum { - KEYDB_SEARCH_MODE_NONE, - KEYDB_SEARCH_MODE_EXACT, - KEYDB_SEARCH_MODE_SUBSTR, - KEYDB_SEARCH_MODE_MAIL, - KEYDB_SEARCH_MODE_MAILSUB, - KEYDB_SEARCH_MODE_MAILEND, - KEYDB_SEARCH_MODE_WORDS, - KEYDB_SEARCH_MODE_SHORT_KID, - KEYDB_SEARCH_MODE_LONG_KID, - KEYDB_SEARCH_MODE_FPR16, - KEYDB_SEARCH_MODE_FPR20, - KEYDB_SEARCH_MODE_FPR, - KEYDB_SEARCH_MODE_FIRST, - KEYDB_SEARCH_MODE_NEXT -} KeydbSearchMode; - -struct keydb_search_desc { - KeydbSearchMode mode; - int (*skipfnc)(void *,u32*); - void *skipfncvalue; - union { - const char *name; - char fpr[MAX_FINGERPRINT_LEN]; - u32 kid[2]; - } u; -}; /*-- keydb.c --*/ int keydb_add_resource (const char *url, int force, int secret); KEYDB_HANDLE keydb_new (int secret); void keydb_release (KEYDB_HANDLE hd); const char *keydb_get_resource_name (KEYDB_HANDLE hd); + +#if 0 /* pgp stuff */ int keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb); int keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb); int keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb); -int keydb_delete_keyblock (KEYDB_HANDLE hd); +#endif + +int keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert); +int keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert); +int keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert); + +int keydb_delete (KEYDB_HANDLE hd); + int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved); void keydb_rebuild_caches (void); + int keydb_search_reset (KEYDB_HANDLE hd); int keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); int keydb_search_first (KEYDB_HANDLE hd); int keydb_search_next (KEYDB_HANDLE hd); int keydb_search_kid (KEYDB_HANDLE hd, u32 *kid); int keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr); +int keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer); +int keydb_search_issuer_sn (KEYDB_HANDLE hd, + const char *issuer, const unsigned char *serial); #endif /*GNUPG_KEYDB_H*/ @@ -29,13 +29,63 @@ #include <ksba.h> #include "util.h" +#include "gpgsm.h" /* Note: we might want to wrap this in a macro to get our hands on - the line and file wehre the error occired */ + the line and file where the error occired */ int map_ksba_err (int err) { - return -1; + switch (err) + { + case -1: + case 0: + break; + + default: + err = GPGSM_General_Error; + break; + } + return err; } + +int +map_gcry_err (int err) +{ + switch (err) + { + case -1: + case 0: + break; + + default: + err = GPGSM_General_Error; + break; + } + return err; +} + +int +map_kbx_err (int err) +{ + switch (err) + { + case -1: + case 0: + break; + + default: + err = GPGSM_General_Error; + break; + } + return err; +} + + + + + + + @@ -23,6 +23,10 @@ #include <gcrypt.h> /* we need this for the memory function protos */ +/* to pass the fucntion to libksba we need to cast it */ +#define HASH_FNC ((void (*)(void *, const byte*,size_t))gcry_md_write) + + #include "../jnlib/logging.h" #include "../jnlib/argparse.h" #include "../jnlib/stringhelp.h" @@ -46,5 +50,9 @@ /*-- misc.c --*/ int map_ksba_err (int err); +int map_gcry_err (int err); +int map_kbx_err (int err); #endif /*UTIL_H*/ + + |