summaryrefslogtreecommitdiffstats
path: root/kbx
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2019-10-01 20:09:42 +0200
committerWerner Koch <wk@gnupg.org>2019-10-01 20:09:42 +0200
commitc7293a4d125c4675c86ecdee0f2f3186fc4bdaf7 (patch)
treebbbc1122eda0eda3703efd60129570f13d52fd09 /kbx
parentcommon: New function hex2fixedbuf. (diff)
downloadgnupg2-c7293a4d125c4675c86ecdee0f2f3186fc4bdaf7.tar.xz
gnupg2-c7293a4d125c4675c86ecdee0f2f3186fc4bdaf7.zip
kbx: Add first version of STORE command to keyboxd.
* kbx/Makefile.am (keyboxd_CFLAGS): -DKEYBOX_WITH_X509. (keyboxd_LDADD): Add libksba. * kbx/kbxserver.c (cmd_store): New. * kbx/frontend.c (kbxd_store): New. * kbx/backend-support.c (is_x509_blob): New. (be_fingerprint_from_blob): New. * kbx/backend-kbx.c (be_kbx_seek): Add args FPR and FPRLEN. (be_kbx_insert): New. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'kbx')
-rw-r--r--kbx/Makefile.am6
-rw-r--r--kbx/backend-kbx.c74
-rw-r--r--kbx/backend-support.c107
-rw-r--r--kbx/backend.h9
-rw-r--r--kbx/frontend.c94
-rw-r--r--kbx/frontend.h2
-rw-r--r--kbx/kbxserver.c50
-rw-r--r--kbx/keybox-openpgp.c2
8 files changed, 324 insertions, 20 deletions
diff --git a/kbx/Makefile.am b/kbx/Makefile.am
index 42c3c4be8..9f8f7953d 100644
--- a/kbx/Makefile.am
+++ b/kbx/Makefile.am
@@ -81,10 +81,10 @@ keyboxd_SOURCES = \
backend-kbx.c \
$(common_sources)
-keyboxd_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \
- $(INCICONV)
+keyboxd_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1 \
+ $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(INCICONV)
keyboxd_LDADD = $(commonpth_libs) \
- $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(KSBA_LIBS) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
$(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \
$(resource_objs)
keyboxd_LDFLAGS = $(extra_bin_ldflags)
diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c
index 438d300b0..0b36c5b78 100644
--- a/kbx/backend-kbx.c
+++ b/kbx/backend-kbx.c
@@ -288,13 +288,15 @@ be_kbx_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request,
}
-/* Seek in the keybox to the given UBID. BACKEND_HD is the handle for
- * this backend and REQUEST is the current database request object.
- * This does a dummy read so that the next search operation starts
- * right after that UBID. */
+/* Seek in the keybox to the given UBID (if UBID is not NULL) or to
+ * the primary fingerprint specified by (FPR,FPRLEN). BACKEND_HD is
+ * the handle for this backend and REQUEST is the current database
+ * request object. This does a dummy read so that the next search
+ * operation starts right after that UBID. */
gpg_error_t
be_kbx_seek (ctrl_t ctrl, backend_handle_t backend_hd,
- db_request_t request, unsigned char *ubid)
+ db_request_t request, const unsigned char *ubid,
+ const unsigned char *fpr, unsigned int fprlen)
{
gpg_error_t err;
db_request_part_t part;
@@ -308,8 +310,19 @@ be_kbx_seek (ctrl_t ctrl, backend_handle_t backend_hd,
log_assert (request);
memset (&desc, 0, sizeof desc);
- desc.mode = KEYDB_SEARCH_MODE_UBID;
- memcpy (desc.u.ubid, ubid, 20);
+ if (ubid)
+ {
+ desc.mode = KEYDB_SEARCH_MODE_FPR;
+ memcpy (desc.u.ubid, ubid, 20);
+ }
+ else
+ {
+ if (fprlen > sizeof desc.u.fpr)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+ desc.mode = KEYDB_SEARCH_MODE_FPR;
+ memcpy (desc.u.fpr, fpr, fprlen);
+ desc.fprlen = fprlen;
+ }
/* Find the specific request part or allocate it. */
err = be_find_request_part (backend_hd, request, &part);
@@ -326,3 +339,50 @@ be_kbx_seek (ctrl_t ctrl, backend_handle_t backend_hd,
leave:
return err;
}
+
+
+/* Insert (BLOB,BLOBLEN) into the keybox. BACKEND_HD is the handle
+ * for this backend and REQUEST is the current database request
+ * object. */
+gpg_error_t
+be_kbx_insert (ctrl_t ctrl, backend_handle_t backend_hd,
+ db_request_t request, enum pubkey_types pktype,
+ const void *blob, size_t bloblen)
+{
+ gpg_error_t err;
+ db_request_part_t part;
+ ksba_cert_t cert = NULL;
+
+ (void)ctrl;
+
+ log_assert (backend_hd && backend_hd->db_type == DB_TYPE_KBX);
+ log_assert (request);
+
+ /* Find the specific request part or allocate it. */
+ err = be_find_request_part (backend_hd, request, &part);
+ if (err)
+ goto leave;
+
+ if (pktype == PUBKEY_TYPE_OPGP)
+ err = keybox_insert_keyblock (part->kbx_hd, blob, bloblen);
+ else if (pktype == PUBKEY_TYPE_X509)
+ {
+ unsigned char sha1[20];
+
+ err = ksba_cert_new (&cert);
+ if (err)
+ goto leave;
+ err = ksba_cert_init_from_mem (cert, blob, bloblen);
+ if (err)
+ goto leave;
+ gcry_md_hash_buffer (GCRY_MD_SHA1, sha1, blob, bloblen);
+
+ err = keybox_insert_cert (part->kbx_hd, cert, sha1);
+ }
+ else
+ err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
+
+ leave:
+ ksba_cert_release (cert);
+ return err;
+}
diff --git a/kbx/backend-support.c b/kbx/backend-support.c
index 62551cafa..f1a97996f 100644
--- a/kbx/backend-support.c
+++ b/kbx/backend-support.c
@@ -27,8 +27,9 @@
#include "keyboxd.h"
#include "../common/i18n.h"
#include "../common/asshelp.h"
+#include "../common/tlv.h"
#include "backend.h"
-#include "keybox.h"
+#include "keybox-defs.h"
/* Common definition part of all backend handle. All definitions of
@@ -169,3 +170,107 @@ be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen,
leave:
return err;
}
+
+
+
+/* Return true if (BLOB/BLOBLEN) seems to be an X509 certificate. */
+static int
+is_x509_blob (const unsigned char *blob, size_t bloblen)
+{
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, cons, ndef;
+
+ /* An X.509 certificate can be identified by this DER encoding:
+ *
+ * 30 82 05 B8 30 82 04 A0 A0 03 02 01 02 02 07 15 46 A0 BF 30 07 39
+ * ----------- +++++++++++ ----- ++++++++ --------------------------
+ * SEQUENCE SEQUENCE [0] INTEGER INTEGER
+ * (tbs) (version) (s/n)
+ *
+ */
+
+ p = blob;
+ n = bloblen;
+ if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen))
+ return 0; /* Not a proper BER object. */
+ if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
+ return 0; /* Does not start with a sequence. */
+
+ if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen))
+ return 0; /* Not a proper BER object. */
+ if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons))
+ return 0; /* No TBS sequence. */
+ if (n < 7 || objlen < 7)
+ return 0; /* Too short: [0], version and min. s/n required. */
+
+ if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen))
+ return 0; /* Not a proper BER object. */
+ if (!(class == CLASS_CONTEXT && tag == 0 && cons))
+ return 0; /* No context tag. */
+
+ if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen))
+ return 0; /* Not a proper BER object. */
+
+ if (!(class == CLASS_UNIVERSAL && tag == TAG_INTEGER
+ && !cons && objlen == 1 && n && (*p == 1 || *p == 2)))
+ return 0; /* Unknown X.509 version. */
+ p++; /* Skip version number. */
+ n--;
+
+ if (parse_ber_header (&p, &n, &class, &tag, &cons, &ndef, &objlen, &hdrlen))
+ return 0; /* Not a proper BER object. */
+ if (!(class == CLASS_UNIVERSAL && tag == TAG_INTEGER && !cons))
+ return 0; /* No s/n. */
+
+ return 1; /* Looks like an X.509 certificate. */
+}
+
+
+/* Return the public key type and the (primary) fingerprint for
+ * (BLOB,BLOBLEN). R_FPR must point to a buffer of at least 32 bytes,
+ * it received the fi gerprint on success with the length of that
+ * fingerprint stored at R_FPRLEN. R_PKTYPE receives the public key
+ * type. */
+gpg_error_t
+be_fingerprint_from_blob (const void *blob, size_t bloblen,
+ enum pubkey_types *r_pktype,
+ char *r_fpr, unsigned int *r_fprlen)
+{
+ gpg_error_t err;
+
+ if (is_x509_blob (blob, bloblen))
+ {
+ /* Although libksba has a dedicated function to compute the
+ * fingerprint we compute it here directly because we know that
+ * we have the entire certificate here (we checked the start of
+ * the blob and assume that the length is also okay). */
+ *r_pktype = PUBKEY_TYPE_X509;
+ gcry_md_hash_buffer (GCRY_MD_SHA1, r_fpr, blob, bloblen);
+ *r_fprlen = 20;
+
+ err = 0;
+ }
+ else
+ {
+ struct _keybox_openpgp_info info;
+
+ err = _keybox_parse_openpgp (blob, bloblen, NULL, &info);
+ if (err)
+ {
+ log_info ("error parsing OpenPGP blob: %s\n", gpg_strerror (err));
+ err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
+ }
+ else
+ {
+ *r_pktype = PUBKEY_TYPE_OPGP;
+ log_assert (info.primary.fprlen <= 32);
+ memcpy (r_fpr, info.primary.fpr, info.primary.fprlen);
+ *r_fprlen = info.primary.fprlen;
+
+ _keybox_destroy_openpgp_info (&info);
+ }
+ }
+
+ return err;
+}
diff --git a/kbx/backend.h b/kbx/backend.h
index 675ec213d..1581ae582 100644
--- a/kbx/backend.h
+++ b/kbx/backend.h
@@ -106,6 +106,9 @@ gpg_error_t be_find_request_part (backend_handle_t backend_hd,
gpg_error_t be_return_pubkey (ctrl_t ctrl, const void *buffer, size_t buflen,
enum pubkey_types pubkey_type,
const unsigned char *ubid);
+gpg_error_t be_fingerprint_from_blob (const void *blob, size_t bloblen,
+ enum pubkey_types *r_pktype,
+ char *r_fpr, unsigned int *r_fprlen);
/*-- backend-cache.c --*/
@@ -134,7 +137,11 @@ gpg_error_t be_kbx_search (ctrl_t ctrl, backend_handle_t hd,
db_request_t request,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc);
gpg_error_t be_kbx_seek (ctrl_t ctrl, backend_handle_t backend_hd,
- db_request_t request, unsigned char *ubid);
+ db_request_t request, const unsigned char *ubid,
+ const unsigned char *fpr, unsigned int fprlen);
+gpg_error_t be_kbx_insert (ctrl_t ctrl, backend_handle_t backend_hd,
+ db_request_t request, enum pubkey_types pktype,
+ const void *blob, size_t bloblen);
#endif /*KBX_BACKEND_H*/
diff --git a/kbx/frontend.c b/kbx/frontend.c
index 6e0cbcb11..8ad4fed3c 100644
--- a/kbx/frontend.c
+++ b/kbx/frontend.c
@@ -58,12 +58,12 @@ take_read_lock (ctrl_t ctrl)
/* Take a lock for reading and writing the databases. */
-/* static void */
-/* take_read_write_lock (ctrl_t ctrl) */
-/* { */
-/* /\* FIXME *\/ */
-/* (void)ctrl; */
-/* } */
+static void
+take_read_write_lock (ctrl_t ctrl)
+{
+ /* FIXME */
+ (void)ctrl;
+}
/* Release a lock. It is valid to call this even if no lock has been
@@ -339,7 +339,7 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc,
{
/* We need to set the startpoint for the search. */
err = be_kbx_seek (ctrl, db->backend_handle, request,
- request->last_cached_ubid);
+ request->last_cached_ubid, NULL, 0);
if (err)
{
log_debug ("%s: seeking %s to an UBID failed: %s\n",
@@ -383,3 +383,83 @@ kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc,
log_clock ("%s: leave (%s)", __func__, err? "not found" : "found");
return err;
}
+
+
+
+/* Store; that is insert or update the key (BLOB,BLOBLEN). If
+ * ONLY_UPDATE is set the key must exist. */
+gpg_error_t
+kbxd_store (ctrl_t ctrl, const void *blob, size_t bloblen, int only_update)
+{
+ gpg_error_t err;
+ db_request_t request;
+ unsigned int dbidx;
+ db_desc_t db;
+ char fpr[32];
+ unsigned int fprlen;
+ enum pubkey_types pktype;
+ int insert = 0;
+
+ if (DBG_CLOCK)
+ log_clock ("%s: enter", __func__);
+
+ take_read_write_lock (ctrl);
+
+ /* Allocate a handle object if none exists for this context. */
+ if (!ctrl->opgp_req)
+ {
+ ctrl->opgp_req = xtrycalloc (1, sizeof *ctrl->opgp_req);
+ if (!ctrl->opgp_req)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+ request = ctrl->opgp_req;
+
+ /* Check whether to insert or update. */
+ err = be_fingerprint_from_blob (blob, bloblen, &pktype, fpr, &fprlen);
+ if (err)
+ goto leave;
+
+ /* FIXME: We force the use of the KBX backend. */
+ for (dbidx=0; dbidx < no_of_databases; dbidx++)
+ if (databases[dbidx].db_type == DB_TYPE_KBX)
+ break;
+ if (!(dbidx < no_of_databases))
+ {
+ err = gpg_error (GPG_ERR_NOT_INITIALIZED);
+ goto leave;
+ }
+ db = databases + dbidx;
+
+ err = be_kbx_seek (ctrl, db->backend_handle, request, NULL, fpr, fprlen);
+ if (!err)
+ ; /* Found - need to update. */
+ else if (gpg_err_code (err) == GPG_ERR_EOF)
+ insert = 1; /* Not found - need to insert. */
+ else
+ {
+ log_debug ("%s: searching fingerprint failed: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ if (insert)
+ {
+ err = be_kbx_insert (ctrl, db->backend_handle, request,
+ pktype, blob, bloblen);
+ }
+ else if (only_update)
+ err = gpg_error (GPG_ERR_DUP_KEY);
+ else /* Update. */
+ {
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+
+ leave:
+ release_lock (ctrl);
+ if (DBG_CLOCK)
+ log_clock ("%s: leave", __func__);
+ return err;
+}
diff --git a/kbx/frontend.h b/kbx/frontend.h
index 55d041fb0..7c86514d0 100644
--- a/kbx/frontend.h
+++ b/kbx/frontend.h
@@ -31,6 +31,8 @@ void kbxd_release_session_info (ctrl_t ctrl);
gpg_error_t kbxd_search (ctrl_t ctrl,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc,
int reset);
+gpg_error_t kbxd_store (ctrl_t ctrl, const void *blob, size_t bloblen,
+ int only_update);
#endif /*KBX_FRONTEND_H*/
diff --git a/kbx/kbxserver.c b/kbx/kbxserver.c
index 929ee6116..0da937f39 100644
--- a/kbx/kbxserver.c
+++ b/kbx/kbxserver.c
@@ -465,6 +465,55 @@ cmd_next (assuan_context_t ctx, char *line)
}
+static const char hlp_store[] =
+ "STORE [--update]\n"
+ "\n"
+ "Insert a key into the database. Whether to insert or update\n"
+ "the key is decided by looking at the primary key's fingerprint.\n"
+ "With option --update the key must already exist. The actual key\n"
+ "material is requested by this function using\n"
+ " INQUIRE BLOB";
+static gpg_error_t
+cmd_store (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int opt_update;
+ gpg_error_t err;
+ unsigned char *value = NULL;
+ size_t valuelen;
+
+ opt_update = has_option (line, "--update");
+ line = skip_options (line);
+ if (*line)
+ {
+ err = set_error (GPG_ERR_INV_ARG, "no args expected");
+ goto leave;
+ }
+
+ /* Ask for the key material. */
+ err = assuan_inquire (ctx, "BLOB", &value, &valuelen, 0);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ if (!valuelen) /* No data received. */
+ {
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+
+ err = kbxd_store (ctrl, value, valuelen, opt_update);
+
+
+ leave:
+ xfree (value);
+ return leave_cmd (ctx, err);
+}
+
+
+
static const char hlp_getinfo[] =
"GETINFO <what>\n"
@@ -584,6 +633,7 @@ register_commands (assuan_context_t ctx)
} table[] = {
{ "SEARCH", cmd_search, hlp_search },
{ "NEXT", cmd_next, hlp_next },
+ { "STORE", cmd_store, hlp_store },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "OUTPUT", NULL, hlp_output },
{ "KILLKEYBOXD",cmd_killkeyboxd,hlp_killkeyboxd },
diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c
index 7a35475ca..0835909e6 100644
--- a/kbx/keybox-openpgp.c
+++ b/kbx/keybox-openpgp.c
@@ -667,7 +667,7 @@ _keybox_destroy_openpgp_info (keybox_openpgp_info_t info)
struct _keybox_openpgp_key_info *k, *k2;
struct _keybox_openpgp_uid_info *u, *u2;
- assert (!info->primary.next);
+ log_assert (!info->primary.next);
for (k=info->subkeys.next; k; k = k2)
{
k2 = k->next;