diff options
Diffstat (limited to 'kbx')
-rw-r--r-- | kbx/ChangeLog | 119 | ||||
-rw-r--r-- | kbx/keybox-defs.h | 186 | ||||
-rw-r--r-- | kbx/keybox-dump.c | 346 | ||||
-rw-r--r-- | kbx/keybox-file.c | 102 | ||||
-rw-r--r-- | kbx/keybox-init.c | 127 | ||||
-rw-r--r-- | kbx/keybox-search.c | 813 | ||||
-rw-r--r-- | kbx/keybox-update.c | 437 | ||||
-rw-r--r-- | kbx/keybox.h | 101 |
8 files changed, 2231 insertions, 0 deletions
diff --git a/kbx/ChangeLog b/kbx/ChangeLog new file mode 100644 index 000000000..af6e6b016 --- /dev/null +++ b/kbx/ChangeLog @@ -0,0 +1,119 @@ +2003-06-03 Werner Koch <wk@gnupg.org> + + Changed all error codes in all files to the new libgpg-error scheme. + + * keybox-defs.h: Include gpg-error.h . + (KeyboxError): Removed. + * Makefile.am: Removed keybox-error.c stuff. + +2002-11-14 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_name) <compare all names>: Fixed + length compare; there is no 0 stored since nearly a year. + +2002-10-31 Neal H. Walfield <neal@g10code.de> + + * Makefile.am (AM_CPPFLAGS): Fix ytpo. + +2002-08-10 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_fpr_part): New. + (has_short_kid, has_long_kid): Implemented. + +2002-07-22 Werner Koch <wk@gnupg.org> + + * keybox-defs.h: New BLOBTYPTE_EMPTY. + * keybox-dump.c (_keybox_dump_blob): Handle new type. + * keybox-file.c (_keybox_read_blob): Skip over empty blobs. Store + the file offset. + * keybox-blob.c (_keybox_new_blob): Add new arg OFF. + (_keybox_get_blob_fileoffset): New. + * keybox-update.c (keybox_delete): Implemented. + +2002-06-19 Werner Koch <wk@gnupg.org> + + * keybox-init.c (keybox_set_ephemeral): New. + * keybox-blob.c (create_blob_header): Store ephemeral flag. + (_keybox_create_x509_blob): Pass epheermal flag on. + * keybox-update.c (keybox_insert_cert): Ditto. + * keybox-search.c (blob_get_blob_flags): New. + (keybox_search): Ignore ephemeral blobs when not in ephemeral mode. + + * keybox-dump.c (_keybox_dump_blob): Print blob flags as strings. + +2002-02-25 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_mail): Use case-insensitive compare + because mail addresses are in general case insensitive (well + RFC2822 allows for case sensitive mailbox parts, but this is in + general considired a Bad Thing). Add additional substr parameter + to allow for substring matches within the mail address. Change + all callers to pass this along. + (blob_cmp_name): Likewise but do the case-insensitive search only + in sub string mode. + (keybox_search): Implement MAILSUB and SUBSTR mode. + +2002-01-21 Werner Koch <wk@gnupg.org> + + * keybox-search.c (keybox_search): Allow KEYDB_SEARCH_MODE_FPR20. + +2002-01-15 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_fpr): New. + (has_fingerprint): Implemented; + +2001-12-20 Werner Koch <wk@gnupg.org> + + * keybox-blob.c (_keybox_create_x509_blob): Skip the leading + parenthesis of the serial number's S-exp. + (_keybox_create_x509_blob): And fixed length calculation. + (create_blob_header): Don't add an offset when writing the serial. + +2001-12-18 Werner Koch <wk@gnupg.org> + + * Makefile.am (AM_CPPFLAGS): Add flags for libksba + + * keybox-blob.c (_keybox_create_x509_blob): Use + gcry_sexp_canon_len to get the length of the serial number. + (_keybox_release_blob): Need to use a new serialbuf to free the memory. + +2001-12-17 Werner Koch <wk@gnupg.org> + + * keybox-search.c: Changed the way the serial number is + represented. + +2001-12-15 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_name): There is no terminating 0 stored + for the uid; fixed length compare. + +2001-12-14 Werner Koch <wk@gnupg.org> + + * keybox-blob.c (x509_email_kludge): New. + (_keybox_create_x509_blob): Insert an extra email address if the + subject's DN has an email part. + * keybox-defs.h: Added the xtoi_2 and digitp macros. + +2001-12-13 Werner Koch <wk@gnupg.org> + + * keybox-search.c (blob_cmp_name): Kludge to allow searching for + more than one name. + (has_subject_or_alt): New. + (blob_cmp_mail): New. + (has_mail): New. + (keybox_search): Implemented exact search and exact mail search. + + * kbx/keybox-blob.c (_keybox_create_x509_blob): Insert alternate + names. + + + Copyright 2001 g10 Code GmbH + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +
\ No newline at end of file diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h new file mode 100644 index 000000000..e4578d76b --- /dev/null +++ b/kbx/keybox-defs.h @@ -0,0 +1,186 @@ +/* keybox-defs.h - interal Keybox defintions + * 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 + */ + +#ifndef KEYBOX_DEFS_H +#define KEYBOX_DEFS_H 1 + +#include <sys/types.h> /* off_t */ +#include "keybox.h" + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_KEYBOX +#include <gpg-error.h> + + +#ifndef HAVE_BYTE_TYPEDEF +typedef unsigned char byte; /* fixme */ +#endif +#ifndef HAVE_U16_TYPEDEF +typedef unsigned short u16; /* fixme */ +#endif +#ifndef HAVE_U32_TYPEDEF +typedef unsigned int u32; /* fixme */ +#endif + +enum { + BLOBTYPE_EMPTY = 0, + BLOBTYPE_HEADER = 1, + BLOBTYPE_PGP = 2, + BLOBTYPE_X509 = 3 +}; + + +typedef struct keyboxblob *KEYBOXBLOB; + + +typedef struct keybox_name *KB_NAME; +typedef struct keybox_name const * CONST_KB_NAME; +struct keybox_name { + struct keybox_name *next; + int secret; + /*DOTLOCK lockhd;*/ + int is_locked; + int did_full_scan; + char fname[1]; +}; + + + +struct keybox_handle { + CONST_KB_NAME kb; + int secret; /* this is for a secret keybox */ + FILE *fp; + int eof; + int error; + int ephemeral; + struct { + KEYBOXBLOB blob; + off_t offset; + size_t pk_no; + size_t uid_no; + unsigned int n_packets; /*used for delete and update*/ + } found; + struct { + char *name; + char *pattern; + } word_match; +}; + + +/* 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, int as_ephemeral); +#endif /*KEYBOX_WITH_X509*/ + +int _keybox_new_blob (KEYBOXBLOB *r_blob, char *image, size_t imagelen, + off_t off); +void _keybox_release_blob (KEYBOXBLOB blob); +const char *_keybox_get_blob_image (KEYBOXBLOB blob, size_t *n); +off_t _keybox_get_blob_fileoffset (KEYBOXBLOB blob); + +/*-- 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); +int _keybox_dump_file (const char *filename, FILE *outfp); + + +/*-- keybox-util.c --*/ +void *_keybox_malloc (size_t n); +void *_keybox_calloc (size_t n, size_t m); +void *_keybox_realloc (void *p, size_t n); +void _keybox_free (void *p); + +#define xtrymalloc(a) _keybox_malloc ((a)) +#define xtrycalloc(a,b) _keybox_calloc ((a),(b)) +#define xtryrealloc(a,b) _keybox_realloc((a),(b)) +#define xfree(a) _keybox_free ((a)) + + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) +#ifndef STR + #define STR(v) #v +#endif +#define STR2(v) STR(v) + +/* + a couple of handy macros +*/ + +#define return_if_fail(expr) do { \ + if (!(expr)) { \ + fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return; \ + } } while (0) +#define return_null_if_fail(expr) do { \ + if (!(expr)) { \ + fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return NULL; \ + } } while (0) +#define return_val_if_fail(expr,val) do { \ + if (!(expr)) { \ + fprintf (stderr, "%s:%d: assertion `%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return (val); \ + } } while (0) +#define never_reached() do { \ + fprintf (stderr, "%s:%d: oops; should never get here\n", \ + __FILE__, __LINE__ ); \ + } while (0) + + +/* some macros to replace ctype ones and avoid locale problems */ +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +/* the atoi macros assume that the buffer has only valid digits */ +#define atoi_1(p) (*(p) - '0' ) +#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) +#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + + +#endif /*KEYBOX_DEFS_H*/ + + diff --git a/kbx/keybox-dump.c b/kbx/keybox-dump.c new file mode 100644 index 000000000..2177bedae --- /dev/null +++ b/kbx/keybox-dump.c @@ -0,0 +1,346 @@ +/* keybox-dump.c - Debug helpers + * Copyright (C) 2001, 2003 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 "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; +} + +static ulong +get16 (const byte *buffer) +{ + ulong a; + a = *buffer << 8; + a |= buffer[1]; + 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) +{ + fprintf (fp, "Version: %d\n", buffer[5]); + if ( memcmp (buffer+8, "KBXf", 4)) + fprintf (fp, "[Error: invalid magic number]\n"); + return 0; +} + + +/* Dump one block to FP */ +int +_keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) +{ + const byte *buffer; + size_t length; + int type; + ulong n, nkeys, keyinfolen; + ulong nuids, uidinfolen; + ulong nsigs, siginfolen; + ulong rawdata_off, rawdata_len; + ulong nserial; + const byte *p; + + buffer = _keybox_get_blob_image (blob, &length); + + if (length < 40) + { + fprintf (fp, "[blob too short]\n"); + return -1; + } + + n = get32( buffer ); + if (n > length) + fprintf (fp, "[blob larger than length - output truncated]\n"); + else + length = n; /* ignore the rest */ + + fprintf (fp, "Length: %lu\n", n ); + type = buffer[4]; + switch (type) + { + case BLOBTYPE_EMPTY: + fprintf (fp, "Type: Empty\n"); + return 0; + + case BLOBTYPE_HEADER: + fprintf (fp, "Type: Header\n"); + return dump_header_blob (buffer, length, fp); + case BLOBTYPE_PGP: + fprintf (fp, "Type: OpenPGP\n"); + break; + case BLOBTYPE_X509: + fprintf (fp, "Type: X.509\n"); + break; + default: + fprintf (fp, "Type: %d\n", type); + fprintf (fp, "[can't dump this blob type]\n"); + return 0; + } + fprintf (fp, "Version: %d\n", buffer[5]); + + n = get16 (buffer + 6); + fprintf( fp, "Blob-Flags: %04lX", n); + if (n) + { + int any = 0; + + fputs (" (", fp); + if ((n & 1)) + { + fputs ("secret", fp); + any++; + } + if ((n & 2)) + { + if (any) + putc (',', fp); + fputs ("ephemeral", fp); + any++; + } + putc (')', fp); + } + putc ('\n', fp); + + rawdata_off = get32 (buffer + 8); + rawdata_len = get32 (buffer + 12); + + fprintf( fp, "Data-Offset: %lu\n", rawdata_off ); + fprintf( fp, "Data-Length: %lu\n", rawdata_len ); + + nkeys = get16 (buffer + 16); + fprintf (fp, "Key-Count: %lu\n", nkeys ); + if (!nkeys) + fprintf (fp, "[Error: no keys]\n"); + if (nkeys > 1 && type == BLOBTYPE_X509) + fprintf (fp, "[Error: only one key allowed for X509]\n"); + + keyinfolen = get16 (buffer + 18 ); + fprintf (fp, "Key-Info-Length: %lu\n", keyinfolen); + /* fixme: check bounds */ + p = buffer + 20; + for (n=0; n < nkeys; n++, p += keyinfolen) + { + int i; + ulong kidoff, kflags; + + fprintf (fp, "Key-Fpr[%lu]: ", n ); + for (i=0; i < 20; i++ ) + fprintf (fp, "%02X", p[i]); + kidoff = get32 (p + 20); + 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-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); + fprintf (fp, "Uid-Info-Length: %lu\n", uidinfolen); + /* fixme: check bounds */ + p += 4; + for (n=0; n < nuids; n++, p += uidinfolen) + { + ulong uidoff, uidlen, uflags; + + uidoff = get32( p ); + uidlen = get32( p+4 ); + 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); + 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); + fprintf (fp, "Sig-Count: %lu\n", nsigs ); + siginfolen = get16 (p + 2); + fprintf (fp, "Sig-Info-Length: %lu\n", siginfolen ); + /* fixme: check bounds */ + p += 4; + for (n=0; n < nsigs; n++, p += siginfolen) + { + ulong sflags; + + sflags = get32 (p); + fprintf (fp, "Sig-Expire[%lu]: ", n ); + if (!sflags) + fputs ("[not checked]", fp); + else if (sflags == 1 ) + fputs ("[missing key]", fp); + else if (sflags == 2 ) + fputs ("[bad signature]", fp); + else if (sflags < 0x10000000) + fprintf (fp, "[bad flag %0lx]", sflags); + else if (sflags == 0xffffffff) + fputs ("0", fp ); + else + fputs ("a time"/*strtimestamp( sflags )*/, fp ); + putc ('\n', fp ); + } + + fprintf (fp, "Ownertrust: %d\n", p[0] ); + fprintf (fp, "All-Validity: %d\n", p[1] ); + p += 4; + n = get32 (p); p += 4; + fprintf (fp, "Recheck-After: %s\n", /*n? strtimestamp(n) :*/ "0" ); + n = get32 (p ); p += 4; + fprintf( fp, "Latest-Timestamp: %s\n", "0"/*strtimestamp(n)*/ ); + n = get32 (p ); p += 4; + fprintf (fp, "Created-At: %s\n", "0"/*strtimestamp(n)*/ ); + n = get32 (p ); p += 4; + fprintf (fp, "Reserved-Space: %lu\n", n ); + + /* check that the keyblock is at the correct offset and other bounds */ + /*fprintf (fp, "Blob-Checksum: [MD5-hash]\n");*/ + return 0; +} + + + +int +_keybox_dump_file (const char *filename, FILE *outfp) +{ + FILE *fp; + KEYBOXBLOB blob; + int rc; + unsigned long count = 0; + + if (!filename) + { + filename = "-"; + fp = stdin; + } + else + fp = fopen (filename, "rb"); + if (!fp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + fprintf (outfp, "can't open `%s': %s\n", filename, strerror(errno)); + return tmperr; + } + + while ( !(rc = _keybox_read_blob (&blob, fp)) ) + { + fprintf (outfp, "BEGIN-RECORD: %lu\n", count ); + _keybox_dump_blob (blob, outfp); + _keybox_release_blob (blob); + fprintf (outfp, "END-RECORD\n"); + count++; + } + if (rc == -1) + rc = 0; + if (rc) + fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc)); + + if (fp != stdin) + fclose (fp); + return rc; +} diff --git a/kbx/keybox-file.c b/kbx/keybox-file.c new file mode 100644 index 000000000..fc9321478 --- /dev/null +++ b/kbx/keybox-file.c @@ -0,0 +1,102 @@ +/* keybox-file.c - file oeprations + * Copyright (C) 2001, 2003 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 "keybox-defs.h" + +/* Read a block at the current postion and return it in r_blob. + r_blob may be NULL to simply skip the current block */ +int +_keybox_read_blob (KEYBOXBLOB *r_blob, FILE *fp) +{ + char *image; + size_t imagelen = 0; + int c1, c2, c3, c4, type; + int rc; + off_t off; + + again: + *r_blob = NULL; + off = ftello (fp); + if (off == (off_t)-1) + return gpg_error (gpg_err_code_from_errno (errno)); + + if ((c1 = getc (fp)) == EOF + || (c2 = getc (fp)) == EOF + || (c3 = getc (fp)) == EOF + || (c4 = getc (fp)) == EOF + || (type = getc (fp)) == EOF) + { + if ( c1 == EOF && !ferror (fp) ) + return -1; /* eof */ + return gpg_error (gpg_err_code_from_errno (errno)); + } + + imagelen = (c1 << 24) | (c2 << 16) | (c3 << 8 ) | c4; + if (imagelen > 500000) /* sanity check */ + return gpg_error (GPG_ERR_TOO_LARGE); + + if (imagelen < 5) + return gpg_error (GPG_ERR_TOO_SHORT); + + if (!type) + { + /* special treatment for empty blobs. */ + if (fseek (fp, imagelen-5, SEEK_CUR)) + return gpg_error (gpg_err_code_from_errno (errno)); + goto again; + } + + image = xtrymalloc (imagelen); + if (!image) + return gpg_error (gpg_err_code_from_errno (errno)); + + image[0] = c1; image[1] = c2; image[2] = c3; image[3] = c4; image[4] = type; + if (fread (image+5, imagelen-5, 1, fp) != 1) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + xfree (image); + return tmperr; + } + + rc = r_blob? _keybox_new_blob (r_blob, image, imagelen, off) : 0; + if (rc || !r_blob) + xfree (image); + return rc; +} + + +/* Write the block to the current file position */ +int +_keybox_write_blob (KEYBOXBLOB blob, FILE *fp) +{ + const char *image; + size_t length; + + image = _keybox_get_blob_image (blob, &length); + if (fwrite (image, length, 1, fp) != 1) + return gpg_error (gpg_err_code_from_errno (errno)); + return 0; +} diff --git a/kbx/keybox-init.c b/kbx/keybox-init.c new file mode 100644 index 000000000..e11c4f09c --- /dev/null +++ b/kbx/keybox-init.c @@ -0,0 +1,127 @@ +/* keybox-init.c - Initalization of the library + * 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 <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 */ +void * +keybox_register_file (const char *fname, int secret) +{ + KB_NAME kr; + + for (kr=kb_names; kr; kr = kr->next) + { + if ( !compare_filenames (kr->fname, fname) ) + return NULL; /* already registered */ + } + + 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 +keybox_is_writable (void *token) +{ + KB_NAME r = 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; +} + +int +keybox_set_ephemeral (KEYBOX_HANDLE hd, int yes) +{ + if (!hd) + return gpg_error (GPG_ERR_INV_HANDLE); + hd->ephemeral = yes; + return 0; +} + diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c new file mode 100644 index 000000000..231a32d42 --- /dev/null +++ b/kbx/keybox-search.c @@ -0,0 +1,813 @@ +/* keybox-search.c - Search operations + * Copyright (C) 2001, 2002, 2003 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 <assert.h> +#include <errno.h> + +#include "../jnlib/stringhelp.h" /* ascii_xxxx() */ +#include "keybox-defs.h" + +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + +struct sn_array_s { + int snlen; + unsigned char *sn; +}; + + + +static ulong +get32 (const byte *buffer) +{ + ulong a; + a = *buffer << 24; + a |= buffer[1] << 16; + a |= buffer[2] << 8; + a |= buffer[3]; + return a; +} + +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 unsigned int +blob_get_blob_flags (KEYBOXBLOB blob) +{ + const unsigned char *buffer; + size_t length; + + buffer = _keybox_get_blob_image (blob, &length); + if (length < 8) + return 0; /* oops */ + + return get16 (buffer + 6); +} + + +static int +blob_cmp_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) +{ + const unsigned char *buffer; + size_t length; + size_t pos, off; + size_t nkeys, keyinfolen; + 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); + off = pos + 2; + if (off+nserial > length) + return 0; /* out of bounds */ + + return nserial == snlen && !memcmp (buffer+off, sn, snlen); +} + + +static int +blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr) +{ + const unsigned char *buffer; + size_t length; + size_t pos, off; + size_t nkeys, keyinfolen; + int idx; + + 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; + if (pos + keyinfolen*nkeys > length) + return 0; /* out of bounds */ + + for (idx=0; idx < nkeys; idx++) + { + off = pos + idx*keyinfolen; + if (!memcmp (buffer + off, fpr, 20)) + return 1; /* found */ + } + return 0; /* not found */ +} + +static int +blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, + int fproff, int fprlen) +{ + const unsigned char *buffer; + size_t length; + size_t pos, off; + size_t nkeys, keyinfolen; + int idx; + + 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; + if (pos + keyinfolen*nkeys > length) + return 0; /* out of bounds */ + + for (idx=0; idx < nkeys; idx++) + { + off = pos + idx*keyinfolen; + if (!memcmp (buffer + off + fproff, fpr, fprlen)) + return 1; /* found */ + } + return 0; /* not found */ +} + + +static int +blob_cmp_name (KEYBOXBLOB blob, int idx, + const char *name, size_t namelen, int substr) +{ + 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 < 0) + { /* compare all names starting with that (negated) index */ + idx = -idx; + + for ( ;idx < nuids; idx++) + { + size_t mypos = pos; + + mypos += idx*uidinfolen; + off = get32 (buffer+mypos); + len = get32 (buffer+mypos+4); + if (off+len > length) + return 0; /* error: better stop here out of bounds */ + if (len < 1) + continue; /* empty name */ + if (substr) + { + if (ascii_memcasemem (buffer+off, len, name, namelen)) + return 1; /* found */ + } + else + { + if (len == namelen && !memcmp (buffer+off, name, len)) + return 1; /* found */ + } + } + return 0; /* not found */ + } + else + { + 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 < 1) + return 0; /* empty name */ + + if (substr) + { + return !!ascii_memcasemem (buffer+off, len, name, namelen); + } + else + { + return len == namelen && !memcmp (buffer+off, name, len); + } + } +} + + +/* compare all email addresses of the subject. With SUBSTR given as + True a substring search is done in the mail address */ +static int +blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr) +{ + const unsigned char *buffer; + size_t length; + size_t pos, off, len; + size_t nkeys, keyinfolen; + size_t nuids, uidinfolen; + size_t nserial; + int idx; + + /* fixme: this code is common to blob_cmp_mail */ + 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 (namelen < 1) + return 0; + + for (idx=1 ;idx < nuids; idx++) + { + size_t mypos = pos; + + mypos += idx*uidinfolen; + off = get32 (buffer+mypos); + len = get32 (buffer+mypos+4); + if (off+len > length) + return 0; /* error: better stop here out of bounds */ + if (len < 2 || buffer[off] != '<') + continue; /* empty name or trailing 0 not stored */ + len--; /* one back */ + if ( len < 3 || buffer[off+len] != '>') + continue; /* not a proper email address */ + len--; + if (substr) + { + if (ascii_memcasemem (buffer+off+1, len, name, namelen)) + return 1; /* found */ + } + else + { + if (len == namelen && !ascii_memcasecmp (buffer+off+1, name, len)) + return 1; /* found */ + } + } + return 0; /* not found */ +} + + + + +/* + The has_foo functions are used as helpers for search +*/ +static int +has_short_kid (KEYBOXBLOB blob, const unsigned char *kid) +{ + return blob_cmp_fpr_part (blob, kid+4, 16, 4); +} + +static int +has_long_kid (KEYBOXBLOB blob, const unsigned char *kid) +{ + return blob_cmp_fpr_part (blob, kid, 12, 8); +} + +static int +has_fingerprint (KEYBOXBLOB blob, const unsigned char *fpr) +{ + return blob_cmp_fpr (blob, fpr); +} + + +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, 0); +} + +static int +has_issuer_sn (KEYBOXBLOB blob, const char *name, + const unsigned char *sn, int snlen) +{ + 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, snlen) + && blob_cmp_name (blob, 0 /* issuer */, name, namelen, 0)); +} + +static int +has_sn (KEYBOXBLOB blob, const unsigned char *sn, int snlen) +{ + return_val_if_fail (sn, 0); + + if (blob_get_type (blob) != BLOBTYPE_X509) + return 0; + return blob_cmp_sn (blob, sn, snlen); +} + +static int +has_subject (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, 1 /* subject */, name, namelen, 0); +} + +static int +has_subject_or_alt (KEYBOXBLOB blob, const char *name, int substr) +{ + 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, -1 /* all subject names*/, name, + namelen, substr); +} + + +static int +has_mail (KEYBOXBLOB blob, const char *name, int substr) +{ + size_t namelen; + + return_val_if_fail (name, 0); + + if (blob_get_type (blob) != BLOBTYPE_X509) + return 0; + + namelen = strlen (name); + if (namelen && name[namelen-1] == '>') + namelen--; + return blob_cmp_mail (blob, name, namelen, substr); +} + + +static void +release_sn_array (struct sn_array_s *array, size_t size) +{ + size_t n; + + for (n=0; n < size; n++) + xfree (array[n].sn); + xfree (array); +} + + +/* + + The search API + +*/ + +int +keybox_search_reset (KEYBOX_HANDLE hd) +{ + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + + if (hd->found.blob) + { + _keybox_release_blob (hd->found.blob); + hd->found.blob = NULL; + } + + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; + } + hd->error = 0; + hd->eof = 0; + return 0; +} + + +/* Note: When in ephemeral mode the search function does visit all + blobs but in standard mode, blobs flagged as ephemeral are ignored. */ +int +keybox_search (KEYBOX_HANDLE hd, KEYBOX_SEARCH_DESC *desc, size_t ndesc) +{ + int rc; + size_t n; + int need_words, any_skip; + KEYBOXBLOB blob = NULL; + struct sn_array_s *sn_array = NULL; + + if (!hd) + return gpg_error (GPG_ERR_INV_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 (desc[n].snlen == -1 && !sn_array) + { + sn_array = xtrycalloc (ndesc, sizeof *sn_array); + if (!sn_array) + return (hd->error = gpg_error (gpg_err_code_from_errno (errno))); + } + } + + if (!hd->fp) + { + hd->fp = fopen (hd->kb->fname, "rb"); + if (!hd->fp) + { + hd->error = gpg_error (gpg_err_code_from_errno (errno)); + xfree (sn_array); + return hd->error; + } + } + + /* kludge: we need to convert an SN given as hexstring to it's + binary representation - in some cases we are not able to store it + in the search descriptor, because due to its usgae it is not + possible to free allocated memory */ + if (sn_array) + { + const unsigned char *s; + int i, odd; + size_t snlen; + + for (n=0; n < ndesc; n++) + { + if (!desc[n].sn) + ; + else if (desc[n].snlen == -1) + { + unsigned char *sn; + + s = desc[n].sn; + for (i=0; *s && *s != '/'; s++, i++) + ; + odd = (i & 1); + snlen = (i+1)/2; + sn_array[n].sn = xtrymalloc (snlen); + if (!sn_array[n].sn) + { + hd->error = gpg_error (gpg_err_code_from_errno (errno)); + release_sn_array (sn_array, n); + return hd->error; + } + sn_array[n].snlen = snlen; + sn = sn_array[n].sn; + s = desc[n].sn; + if (odd) + { + *sn++ = xtoi_1 (s); + s++; + } + for (; *s && *s != '/'; s += 2) + *sn++ = xtoi_2 (s); + } + else + { + const unsigned char *sn; + + sn = desc[n].sn; + snlen = desc[n].snlen; + sn_array[n].sn = xtrymalloc (snlen); + if (!sn_array[n].sn) + { + hd->error = gpg_error (gpg_err_code_from_errno (errno)); + release_sn_array (sn_array, n); + return hd->error; + } + sn_array[n].snlen = snlen; + memcpy (sn_array[n].sn, sn, snlen); + } + } + } + + + for (;;) + { + unsigned int blobflags; + + _keybox_release_blob (blob); blob = NULL; + rc = _keybox_read_blob (&blob, hd->fp); + if (rc) + break; + + blobflags = blob_get_blob_flags (blob); + if (!hd->ephemeral && (blobflags & 2)) + continue; /* not in ephemeral mode but blob is flagged ephemeral */ + + for (n=0; n < ndesc; n++) + { + switch (desc[n].mode) + { + case KEYDB_SEARCH_MODE_NONE: + never_reached (); + break; + case KEYDB_SEARCH_MODE_EXACT: + if (has_subject_or_alt (blob, desc[n].u.name, 0)) + goto found; + break; + case KEYDB_SEARCH_MODE_MAIL: + if (has_mail (blob, desc[n].u.name, 0)) + goto found; + break; + case KEYDB_SEARCH_MODE_MAILSUB: + if (has_mail (blob, desc[n].u.name, 1)) + goto found; + break; + case KEYDB_SEARCH_MODE_SUBSTR: + if (has_subject_or_alt (blob, desc[n].u.name, 1)) + goto found; + break; + 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, + sn_array? sn_array[n].sn : desc[n].sn, + sn_array? sn_array[n].snlen : desc[n].snlen)) + goto found; + break; + case KEYDB_SEARCH_MODE_SN: + if (has_sn (blob, sn_array? sn_array[n].sn : desc[n].sn, + sn_array? sn_array[n].snlen : desc[n].snlen)) + goto found; + break; + case KEYDB_SEARCH_MODE_SUBJECT: + if (has_subject (blob, desc[n].u.name)) + goto found; + break; + case KEYDB_SEARCH_MODE_SHORT_KID: + if (has_short_kid (blob, desc[n].u.kid)) + 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: + case KEYDB_SEARCH_MODE_FPR20: + 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 = gpg_error (GPG_ERR_INV_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; + } + + if (sn_array) + release_sn_array (sn_array, ndesc); + + 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_get_cert (KEYBOX_HANDLE hd, KsbaCert *r_cert) +{ + const unsigned char *buffer; + size_t length; + size_t cert_off, cert_len; + KsbaReader reader = NULL; + KsbaCert cert = NULL; + int rc; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + if (!hd->found.blob) + return gpg_error (GPG_ERR_NOTHING_FOUND); + + if (blob_get_type (hd->found.blob) != BLOBTYPE_X509) + return gpg_error (GPG_ERR_WRONG_BLOB_TYPE); + + buffer = _keybox_get_blob_image (hd->found.blob, &length); + if (length < 40) + return gpg_error (GPG_ERR_TOO_SHORT); + cert_off = get32 (buffer+8); + cert_len = get32 (buffer+12); + if (cert_off+cert_len > length) + return gpg_error (GPG_ERR_TOO_SHORT); + + reader = ksba_reader_new (); + if (!reader) + return gpg_error (GPG_ERR_ENOMEM); + 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 gpg_error (GPG_ERR_GENERAL); + } + + cert = ksba_cert_new (); + if (!cert) + { + ksba_reader_release (reader); + return gpg_error (GPG_ERR_ENOMEM); + } + + 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 gpg_error (GPG_ERR_GENERAL); + } + + *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..52ad258b0 --- /dev/null +++ b/kbx/keybox-update.c @@ -0,0 +1,437 @@ +/* keybox-update.c - keybox update operations + * Copyright (C) 2001, 2003 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 gpg_error (gpg_err_code_from_errno (errno)); + strcpy (bakfname, template); + strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak"); + + tmpfname = xtrymalloc (strlen (template) + 1); + if (!tmpfname) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + xfree (bakfname); + return tmperr; + } + 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 gpg_error (gpg_err_code_from_errno (errno)); + strcpy (stpcpy (bakfname, template), EXTSEP_S "bak"); + + tmpfname = xtrymalloc ( strlen (template) + 5); + if (!tmpfname) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + xfree (bakfname); + return tmperr; + } + strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp"); + } +# else /* Posix file names */ + bakfname = xtrymalloc (strlen (template) + 2); + if (!bakfname) + return gpg_error (gpg_err_code_from_errno (errno)); + strcpy (stpcpy (bakfname,template),"~"); + + tmpfname = xtrymalloc ( strlen (template) + 5); + if (!tmpfname) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + xfree (bakfname); + return tmperr; + } + strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp"); +# endif /* Posix filename */ + + *r_fp = fopen (tmpfname, "wb"); + if (!*r_fp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + xfree (tmpfname); + xfree (bakfname); + return tmperr; + } + + *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 gpg_error (gpg_err_code_from_errno (errno)); + } + } + + /* then rename the file */ +#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__) + remove (fname); +#endif + if (rename (tmpfname, fname) ) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + 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 gpg_error (gpg_err_code_from_errno (errno)); + + 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 gpg_error (gpg_err_code_from_errno (errno)); + } + + rc = _keybox_write_blob (blob, newfp); + if (rc) + { + return rc; + } + if ( fclose (newfp) ) + { + return gpg_error (gpg_err_code_from_errno (errno)); + } + +/* 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 = gpg_error (gpg_err_code_from_errno (errno)); + 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 = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + } + if (ferror (fp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + 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 = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + } + if (ferror (fp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + 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 = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + } + if (ferror (fp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + goto leave; + } + } + + /* close both files */ + if (fclose(fp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + fclose (newfp); + goto leave; + } + if (fclose(newfp)) + { + rc = gpg_error (gpg_err_code_from_errno (errno)); + 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 gpg_error (GPG_ERR_INV_HANDLE); + if (!hd->kb) + return gpg_error (GPG_ERR_INV_HANDLE); + fname = hd->kb->fname; + if (!fname) + return gpg_error (GPG_ERR_INV_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, hd->ephemeral); + 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) +{ + off_t off; + const char *fname; + FILE *fp; + int rc; + + if (!hd) + return gpg_error (GPG_ERR_INV_VALUE); + if (!hd->found.blob) + return gpg_error (GPG_ERR_NOTHING_FOUND); + if (!hd->kb) + return gpg_error (GPG_ERR_INV_HANDLE); + fname = hd->kb->fname; + if (!fname) + return gpg_error (GPG_ERR_INV_HANDLE); + + off = _keybox_get_blob_fileoffset (hd->found.blob); + if (off == (off_t)-1) + return gpg_error (GPG_ERR_GENERAL); + off += 4; + + if (hd->fp) + { + fclose (hd->fp); + hd->fp = NULL; + } + + fp = fopen (hd->kb->fname, "r+b"); + if (!fp) + return gpg_error (gpg_err_code_from_errno (errno)); + + if (fseeko (fp, off, SEEK_SET)) + rc = gpg_error (gpg_err_code_from_errno (errno)); + else if (putc (0, fp) == EOF) + rc = gpg_error (gpg_err_code_from_errno (errno)); + else + rc = 0; + + if (fclose (fp)) + { + if (!rc) + rc = gpg_error (gpg_err_code_from_errno (errno)); + } + + return rc; +} + + diff --git a/kbx/keybox.h b/kbx/keybox.h new file mode 100644 index 000000000..5fe5516d4 --- /dev/null +++ b/kbx/keybox.h @@ -0,0 +1,101 @@ +/* keybox.h - Keybox operations + * Copyright (C) 2001, 2003 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 + */ + +#ifndef KEYBOX_H +#define KEYBOX_H 1 +#ifdef __cplusplus +extern "C" { +#if 0 + } +#endif +#endif + +#include "keybox-search-desc.h" + +#define KEYBOX_WITH_OPENPGP 1 +#define KEYBOX_WITH_X509 1 + + +#ifdef KEYBOX_WITH_OPENPGP +# undef KEYBOX_WITH_OPENPGP +/*#include <lib-to-handle-gpg-data-structs.h>*/ +#endif + +#ifdef KEYBOX_WITH_X509 +# include <ksba.h> +#endif + + + +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_set_ephemeral (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_locate_writable (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 *); +#endif + + +/*-- keybox-util.c --*/ +void keybox_set_malloc_hooks ( void *(*new_alloc_func)(size_t n), + void *(*new_realloc_func)(void *p, size_t n), + void (*new_free_func)(void*) ); + + +#ifdef __cplusplus +} +#endif +#endif /*KEYBOX_H*/ |