diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c
index 0b36c5b78..d8dafe0e5 100644
--- a/kbx/backend-kbx.c
+++ b/kbx/backend-kbx.c
@@ -1,388 +1,439 @@
/* backend-kbx.c - Keybox format backend for keyboxd
* Copyright (C) 2019 g10 Code GmbH
*
* 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 3 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, see .
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
#include
#include
#include
#include
#include "keyboxd.h"
#include "../common/i18n.h"
#include "backend.h"
#include "keybox.h"
/* Our definition of the backend handle. */
struct backend_handle_s
{
enum database_types db_type; /* Always DB_TYPE_KBX. */
unsigned int backend_id; /* Always the id of the backend. */
void *token; /* Used to create a new KEYBOX_HANDLE. */
char filename[1];
};
/* Check that the file FILENAME is a valid keybox file which can be
* used here. Common return codes:
*
* 0 := Valid keybox file
* GPG_ERR_ENOENT := No such file
* GPG_ERR_NO_OBJ := File exists with size zero.
* GPG_ERR_INV_OBJ:= File exists but is not a keybox file.
*/
static gpg_error_t
check_kbx_file_magic (const char *filename)
{
gpg_error_t err;
u32 magic;
unsigned char verbuf[4];
estream_t fp;
fp = es_fopen (filename, "rb");
if (!fp)
return gpg_error_from_syserror ();
err = gpg_error (GPG_ERR_INV_OBJ);
if (es_fread (&magic, 4, 1, fp) == 1 )
{
if (es_fread (&verbuf, 4, 1, fp) == 1
&& verbuf[0] == 1
&& es_fread (&magic, 4, 1, fp) == 1
&& !memcmp (&magic, "KBXf", 4))
{
err = 0;
}
}
else /* Maybe empty: Let's create it. */
err = gpg_error (GPG_ERR_NO_OBJ);
es_fclose (fp);
return err;
}
/* Create new keybox file. This can also be used if the keybox
* already exists but has a length of zero. Do not use it in any
* other cases. */
static gpg_error_t
create_keybox (const char *filename)
{
gpg_error_t err;
dotlock_t lockhd = NULL;
estream_t fp;
/* To avoid races with other temporary instances of keyboxd trying
* to create or update the keybox, we do the next stuff in a locked
* state. */
lockhd = dotlock_create (filename, 0);
if (!lockhd)
{
err = gpg_error_from_syserror ();
/* A reason for this to fail is that the directory is not
* writable. However, this whole locking stuff does not make
* sense if this is the case. An empty non-writable directory
* with no keybox is not really useful at all. */
if (opt.verbose)
log_info ("can't allocate lock for '%s': %s\n",
filename, gpg_strerror (err));
return err;
}
if ( dotlock_take (lockhd, -1) )
{
err = gpg_error_from_syserror ();
/* This is something bad. Probably a stale lockfile. */
log_info ("can't lock '%s': %s\n", filename, gpg_strerror (err));
goto leave;
}
/* Make sure that at least one record is in a new keybox file, so
* that the detection magic will work the next time it is used.
* We always set the OpenPGP blobs maybe availabale flag. */
fp = es_fopen (filename, "w+b,mode=-rw-------");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("error creating keybox '%s': %s\n"),
filename, gpg_strerror (err));
goto leave;
}
err = _keybox_write_header_blob (NULL, fp, 1);
es_fclose (fp);
if (err)
{
log_error (_("error creating keybox '%s': %s\n"),
filename, gpg_strerror (err));
goto leave;
}
if (!opt.quiet)
log_info (_("keybox '%s' created\n"), filename);
err = 0;
leave:
if (lockhd)
{
dotlock_release (lockhd);
dotlock_destroy (lockhd);
}
return err;
}
/* Install a new resource and return a handle for that backend. */
gpg_error_t
be_kbx_add_resource (ctrl_t ctrl, backend_handle_t *r_hd,
const char *filename, int readonly)
{
gpg_error_t err;
backend_handle_t hd;
void *token;
(void)ctrl;
*r_hd = NULL;
hd = xtrycalloc (1, sizeof *hd + strlen (filename));
if (!hd)
return gpg_error_from_syserror ();
hd->db_type = DB_TYPE_KBX;
strcpy (hd->filename, filename);
err = check_kbx_file_magic (filename);
switch (gpg_err_code (err))
{
case 0:
break;
case GPG_ERR_ENOENT:
case GPG_ERR_NO_OBJ:
if (readonly)
{
err = gpg_error (GPG_ERR_ENOENT);
goto leave;
}
err = create_keybox (filename);
if (err)
goto leave;
break;
default:
goto leave;
}
err = keybox_register_file (filename, 0, &token);
if (err)
goto leave;
hd->backend_id = be_new_backend_id ();
hd->token = token;
*r_hd = hd;
hd = NULL;
leave:
xfree (hd);
return err;
}
/* Release the backend handle HD and all its resources. HD is not
* valid after a call to this function. */
void
be_kbx_release_resource (ctrl_t ctrl, backend_handle_t hd)
{
(void)ctrl;
if (!hd)
return;
hd->db_type = DB_TYPE_NONE;
xfree (hd);
}
void
be_kbx_release_kbx_hd (KEYBOX_HANDLE kbx_hd)
{
keybox_release (kbx_hd);
}
/* Helper for be_find_request_part to initialize a kbx request part. */
gpg_error_t
be_kbx_init_request_part (backend_handle_t backend_hd, db_request_part_t part)
{
part->kbx_hd = keybox_new_openpgp (backend_hd->token, 0);
if (!part->kbx_hd)
return gpg_error_from_syserror ();
return 0;
}
/* Search for the keys described by (DESC,NDESC) and return them to
* the caller. BACKEND_HD is the handle for this backend and REQUEST
* is the current database request object. */
gpg_error_t
be_kbx_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc)
{
gpg_error_t err;
db_request_part_t part;
size_t descindex;
unsigned long skipped_long_blobs;
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 (!desc)
err = keybox_search_reset (part->kbx_hd);
else
err = keybox_search (part->kbx_hd, desc, ndesc, KEYBOX_BLOBTYPE_PGP,
&descindex, &skipped_long_blobs);
if (err == -1)
err = gpg_error (GPG_ERR_EOF);
if (desc && !err)
{
/* Successful search operation. */
void *buffer;
size_t buflen;
enum pubkey_types pubkey_type;
unsigned char blobid[20];
err = keybox_get_data (part->kbx_hd, &buffer, &buflen, &pubkey_type);
if (err)
goto leave;
gcry_md_hash_buffer (GCRY_MD_SHA1, blobid, buffer, buflen);
err = be_return_pubkey (ctrl, buffer, buflen, pubkey_type, blobid);
if (!err)
be_cache_pubkey (ctrl, blobid, buffer, buflen, pubkey_type);
xfree (buffer);
}
leave:
return err;
}
/* 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, const unsigned char *ubid,
const unsigned char *fpr, unsigned int fprlen)
{
gpg_error_t err;
db_request_part_t part;
size_t descindex;
unsigned long skipped_long_blobs;
KEYDB_SEARCH_DESC desc;
(void)ctrl;
log_assert (backend_hd && backend_hd->db_type == DB_TYPE_KBX);
log_assert (request);
memset (&desc, 0, sizeof desc);
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);
if (err)
goto leave;
err = keybox_search_reset (part->kbx_hd);
if (!err)
err = keybox_search (part->kbx_hd, &desc, 1, 0,
&descindex, &skipped_long_blobs);
if (err == -1)
err = gpg_error (GPG_ERR_EOF);
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;
}
+
+
+/* Update (BLOB,BLOBLEN) in the keybox. BACKEND_HD is the handle for
+ * this backend and REQUEST is the current database request object. */
+gpg_error_t
+be_kbx_update (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;
+
+ /* FIXME: We make use of the fact that we know that the caller
+ * already did a keybox search. This needs to be made more
+ * explicit. */
+ if (pktype == PUBKEY_TYPE_OPGP)
+ {
+ err = keybox_update_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_update_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.h b/kbx/backend.h
index 1581ae582..e97855246 100644
--- a/kbx/backend.h
+++ b/kbx/backend.h
@@ -1,147 +1,150 @@
/* backend.h - Definitions for keyboxd backends
* Copyright (C) 2019 g10 Code GmbH
*
* 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 3 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, see .
*/
#ifndef KBX_BACKEND_H
#define KBX_BACKEND_H
#include "keybox-search-desc.h"
/* Forward declaration of the keybox handle type. */
struct keybox_handle;
typedef struct keybox_handle *KEYBOX_HANDLE;
/* The types of the backends. */
enum database_types
{
DB_TYPE_NONE, /* No database at all (unitialized etc.). */
DB_TYPE_CACHE, /* The cache backend (backend-cache.c). */
DB_TYPE_KBX /* Keybox type database (backend-kbx.c). */
};
/* Declaration of the backend handle. Each backend uses its own
* hidden handle structure with the only common thing being that the
* first field is the database_type to help with debugging. */
struct backend_handle_s;
typedef struct backend_handle_s *backend_handle_t;
/* Object to store backend specific database information per database
* handle. */
struct db_request_part_s
{
struct db_request_part_s *next;
/* Id of the backend instance this object pertains to. */
unsigned int backend_id;
/* The handle used for a KBX backend or NULL. */
KEYBOX_HANDLE kbx_hd;
/* For the CACHE backend the indices into the bloblist for each
* index type. */
struct {
unsigned int fpr;
unsigned int kid;
unsigned int grip;
unsigned int ubid;
} cache_seqno;
};
typedef struct db_request_part_s *db_request_part_t;
/* A database request handle. This keeps per session search
* information as well as a list of per-backend infos. */
struct db_request_s
{
unsigned int any_search:1; /* Any search has been done. */
unsigned int any_found:1; /* Any object has been found. */
unsigned int last_cached_valid:1; /* see below */
unsigned int last_cached_final:1; /* see below */
unsigned int last_cached_fprlen:8;/* see below */
db_request_part_t part;
/* Counter to track the next to be searched database index. */
unsigned int next_dbidx;
/* The last UBID found in the cache and the corresponding keyid and,
* if found via fpr, the fingerprint. For the LAST_CACHE_FPRLEN see
* above. The entry here is only valid if LAST_CACHE_VALID is set;
* if LAST_CACHE_FINAL is also set, this indicates that no further
* database searches are required. */
unsigned char last_cached_ubid[20];
u32 last_cached_kid_h;
u32 last_cached_kid_l;
unsigned char last_cached_fpr[32];
};
/*-- backend-support.c --*/
const char *strdbtype (enum database_types t);
unsigned int be_new_backend_id (void);
void be_generic_release_backend (ctrl_t ctrl, backend_handle_t hd);
void be_release_request (db_request_t req);
gpg_error_t be_find_request_part (backend_handle_t backend_hd,
db_request_t request,
db_request_part_t *r_part);
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 --*/
gpg_error_t be_cache_add_resource (ctrl_t ctrl, backend_handle_t *r_hd);
void be_cache_release_resource (ctrl_t ctrl, backend_handle_t hd);
gpg_error_t be_cache_search (ctrl_t ctrl, backend_handle_t backend_hd,
db_request_t request,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc);
void be_cache_mark_final (ctrl_t ctrl, db_request_t request);
void be_cache_pubkey (ctrl_t ctrl, const unsigned char *ubid,
const void *blob, unsigned int bloblen,
enum pubkey_types pubkey_type);
void be_cache_not_found (ctrl_t ctrl, enum pubkey_types pubkey_type,
KEYDB_SEARCH_DESC *desc, unsigned int ndesc);
/*-- backend-kbx.c --*/
gpg_error_t be_kbx_add_resource (ctrl_t ctrl, backend_handle_t *r_hd,
const char *filename, int readonly);
void be_kbx_release_resource (ctrl_t ctrl, backend_handle_t hd);
void be_kbx_release_kbx_hd (KEYBOX_HANDLE kbx_hd);
gpg_error_t be_kbx_init_request_part (backend_handle_t backend_hd,
db_request_part_t part);
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, 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);
+gpg_error_t be_kbx_update (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 8ad4fed3c..5bf18809e 100644
--- a/kbx/frontend.c
+++ b/kbx/frontend.c
@@ -1,465 +1,466 @@
/* frontend.c - Database fronend code for keyboxd
* Copyright (C) 2019 g10 Code GmbH
*
* 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 3 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, see .
* SPDX-License-Identifier: GPL-3.0+
*/
#include
#include
#include
#include
#include
#include "keyboxd.h"
#include
#include "../common/i18n.h"
#include "../common/userids.h"
#include "backend.h"
#include "frontend.h"
/* An object to describe a single database. */
struct db_desc_s
{
enum database_types db_type;
backend_handle_t backend_handle;
};
typedef struct db_desc_s *db_desc_t;
/* The table of databases and the size of that table. */
static db_desc_t databases;
static unsigned int no_of_databases;
/* Take a lock for reading the databases. */
static void
take_read_lock (ctrl_t ctrl)
{
/* FIXME */
(void)ctrl;
}
/* Take a lock for reading and writing the databases. */
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
* taken in which case this is a nop. */
static void
release_lock (ctrl_t ctrl)
{
/* FIXME */
(void)ctrl;
}
/* Add a new resource to the database. Depending on the FILENAME
* suffix we decide which one to use. This function must be called at
* daemon startup because it employs no locking. If FILENAME has no
* directory separator, the file is expected or created below
* "$GNUPGHOME/public-keys-v1.d/". In READONLY mode the file must
* exists; otherwise it is created. */
gpg_error_t
kbxd_add_resource (ctrl_t ctrl, const char *filename_arg, int readonly)
{
gpg_error_t err;
char *filename;
enum database_types db_type = 0;
backend_handle_t handle = NULL;
unsigned int n, dbidx;
/* Do tilde expansion etc. */
if (!strcmp (filename_arg, "[cache]"))
{
filename = xstrdup (filename_arg);
db_type = DB_TYPE_CACHE;
}
else if (strchr (filename_arg, DIRSEP_C)
#ifdef HAVE_W32_SYSTEM
|| strchr (filename_arg, '/') /* Windows also accepts a slash. */
#endif
)
filename = make_filename (filename_arg, NULL);
else
filename = make_filename (gnupg_homedir (), GNUPG_PUBLIC_KEYS_DIR,
filename_arg, NULL);
/* If this is the first call to the function and the request is not
* for the cache backend, add the cache backend so that it will
* always be the first to be queried. */
if (!no_of_databases && !db_type)
{
err = kbxd_add_resource (ctrl, "[cache]", 0);
if (err)
goto leave;
}
n = strlen (filename);
if (db_type)
; /* We already know it. */
else if (n > 4 && !strcmp (filename + n - 4, ".kbx"))
db_type = DB_TYPE_KBX;
else
{
log_error (_("can't use file '%s': %s\n"), filename, _("unknown suffix"));
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
goto leave;
}
err = gpg_error (GPG_ERR_BUG);
switch (db_type)
{
case DB_TYPE_NONE: /* NOTREACHED */
break;
case DB_TYPE_CACHE:
err = be_cache_add_resource (ctrl, &handle);
break;
case DB_TYPE_KBX:
err = be_kbx_add_resource (ctrl, &handle, filename, readonly);
break;
}
if (err)
goto leave;
/* All good, create an entry in the table. */
for (dbidx = 0; dbidx < no_of_databases; dbidx++)
if (!databases[dbidx].db_type)
break;
if (dbidx == no_of_databases)
{
/* No table yet or table is full. */
if (!databases)
{
/* Create first set of databases. Note that the initial
* type for all entries is DB_TYPE_NONE. */
dbidx = 4;
databases = xtrycalloc (dbidx, sizeof *databases);
if (!databases)
{
err = gpg_error_from_syserror ();
goto leave;
}
no_of_databases = dbidx;
dbidx = 0; /* Put into first slot. */
}
else
{
db_desc_t newdb;
dbidx = no_of_databases + (no_of_databases == 4? 12 : 16);
newdb = xtrycalloc (dbidx, sizeof *databases);
if (!databases)
{
err = gpg_error_from_syserror ();
goto leave;
}
for (n=0; n < no_of_databases; n++)
newdb[n] = databases[n];
xfree (databases);
databases = newdb;
n = no_of_databases;
no_of_databases = dbidx;
dbidx = n; /* Put into first new slot. */
}
}
databases[dbidx].db_type = db_type;
databases[dbidx].backend_handle = handle;
handle = NULL;
leave:
if (err)
{
log_error ("error adding resource '%s': %s\n",
filename, gpg_strerror (err));
be_generic_release_backend (ctrl, handle);
}
xfree (filename);
return err;
}
/* Release all per session objects. */
void
kbxd_release_session_info (ctrl_t ctrl)
{
if (!ctrl)
return;
be_release_request (ctrl->opgp_req);
ctrl->opgp_req = NULL;
be_release_request (ctrl->x509_req);
ctrl->x509_req = NULL;
}
/* Search for the keys described by (DESC,NDESC) and return them to
* the caller. If RESET is set, the search state is first reset. */
gpg_error_t
kbxd_search (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, unsigned int ndesc,
int reset)
{
gpg_error_t err;
int i;
unsigned int dbidx;
db_desc_t db;
db_request_t request;
int start_at_ubid = 0;
if (DBG_CLOCK)
log_clock ("%s: enter", __func__);
if (DBG_LOOKUP)
{
log_debug ("%s: %u search descriptions:\n", __func__, ndesc);
for (i = 0; i < ndesc; i ++)
{
/* char *t = keydb_search_desc_dump (&desc[i]); */
/* log_debug ("%s %d: %s\n", __func__, i, t); */
/* xfree (t); */
}
}
take_read_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;
/* If requested do a reset. Using the reset flag is faster than
* letting the caller do a separate call for an intial reset. */
if (!desc || reset)
{
for (dbidx=0; dbidx < no_of_databases; dbidx++)
{
db = databases + dbidx;
if (!db->db_type)
continue; /* Empty slot. */
switch (db->db_type)
{
case DB_TYPE_NONE: /* NOTREACHED */
break;
case DB_TYPE_CACHE:
err = 0; /* Nothing to do. */
break;
case DB_TYPE_KBX:
err = be_kbx_search (ctrl, db->backend_handle, request, NULL, 0);
break;
}
if (err)
{
log_error ("error during the %ssearch reset: %s\n",
reset? "initial ":"", gpg_strerror (err));
goto leave;
}
}
request->any_search = 0;
request->any_found = 0;
request->next_dbidx = 0;
if (!desc) /* Reset only mode */
{
err = 0;
goto leave;
}
}
/* Move to the next non-empty slot. */
next_db:
for (dbidx=request->next_dbidx; (dbidx < no_of_databases
&& !databases[dbidx].db_type); dbidx++)
;
request->next_dbidx = dbidx;
if (!(dbidx < no_of_databases))
{
/* All databases have been searched. Put the non-found mark
* into the cache for all descriptors.
* FIXME: We need to see which pubkey type we need to insert. */
be_cache_not_found (ctrl, PUBKEY_TYPE_UNKNOWN, desc, ndesc);
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
db = databases + dbidx;
/* Divert to the backend for the actual search. */
switch (db->db_type)
{
case DB_TYPE_NONE:
/* NOTREACHED */
err = gpg_error (GPG_ERR_INTERNAL);
break;
case DB_TYPE_CACHE:
err = be_cache_search (ctrl, db->backend_handle, request,
desc, ndesc);
/* Expected error codes from the cache lookup are:
* 0 - found and returned via the cache
* GPG_ERR_NOT_FOUND - marked in the cache as not available
* GPG_ERR_EOF - cache miss. */
break;
case DB_TYPE_KBX:
if (start_at_ubid)
{
/* We need to set the startpoint for the search. */
err = be_kbx_seek (ctrl, db->backend_handle, request,
request->last_cached_ubid, NULL, 0);
if (err)
{
log_debug ("%s: seeking %s to an UBID failed: %s\n",
__func__, strdbtype (db->db_type), gpg_strerror (err));
break;
}
}
err = be_kbx_search (ctrl, db->backend_handle, request,
desc, ndesc);
if (start_at_ubid && gpg_err_code (err) == GPG_ERR_EOF)
be_cache_mark_final (ctrl, request);
break;
}
if (DBG_LOOKUP)
log_debug ("%s: searched %s (db %u of %u) => %s\n",
__func__, strdbtype (db->db_type), dbidx, no_of_databases,
gpg_strerror (err));
request->any_search = 1;
start_at_ubid = 0;
if (!err)
{
request->any_found = 1;
}
else if (gpg_err_code (err) == GPG_ERR_EOF)
{
if (db->db_type == DB_TYPE_CACHE && request->last_cached_valid)
{
if (request->last_cached_final)
goto leave;
start_at_ubid = 1;
}
request->next_dbidx++;
goto next_db;
}
leave:
release_lock (ctrl);
if (DBG_CLOCK)
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);
+ err = be_kbx_update (ctrl, db->backend_handle, request,
+ pktype, blob, bloblen);
}
leave:
release_lock (ctrl);
if (DBG_CLOCK)
log_clock ("%s: leave", __func__);
return err;
}