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>
This commit is contained in:
Werner Koch 2019-10-01 20:09:42 +02:00
parent 61765136cf
commit c7293a4d12
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
8 changed files with 324 additions and 20 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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 },

View File

@ -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;