summaryrefslogtreecommitdiffstats
path: root/kbx
diff options
context:
space:
mode:
Diffstat (limited to 'kbx')
-rw-r--r--kbx/ChangeLog119
-rw-r--r--kbx/keybox-defs.h186
-rw-r--r--kbx/keybox-dump.c346
-rw-r--r--kbx/keybox-file.c102
-rw-r--r--kbx/keybox-init.c127
-rw-r--r--kbx/keybox-search.c813
-rw-r--r--kbx/keybox-update.c437
-rw-r--r--kbx/keybox.h101
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*/