aboutsummaryrefslogtreecommitdiffstats
path: root/kbx/backend-kbx.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2019-08-06 14:07:33 +0000
committerWerner Koch <[email protected]>2019-08-06 14:07:33 +0000
commit5ea6250cc5761612d17ca4fb34eed096f26e2826 (patch)
tree062877d21a2f53ff01918a2a73ed02fcf80fded1 /kbx/backend-kbx.c
parentkbx: Allow writing using a estream. (diff)
downloadgnupg-5ea6250cc5761612d17ca4fb34eed096f26e2826.tar.gz
gnupg-5ea6250cc5761612d17ca4fb34eed096f26e2826.zip
kbx: Add framework for the SEARCH command
* kbx/backend-kbx.c: New. * kbx/backend-support.c: New. * kbx/backend.h: New. * kbx/frontend.c: New. * kbx/frontend.h: New. * kbx/kbxserver.c: Implement SEARCH and NEXT command. * kbx/keybox-search-desc.h (enum pubkey_types): New. * kbx/keybox-search.c (keybox_get_data): New. * kbx/keyboxd.c (main): Add a standard resource. Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'kbx/backend-kbx.c')
-rw-r--r--kbx/backend-kbx.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c
new file mode 100644
index 000000000..7f9ef358b
--- /dev/null
+++ b/kbx/backend-kbx.c
@@ -0,0 +1,292 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0+
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#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; /* Id of this 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 0;
+}
+
+
+/* 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);
+}
+
+
+/* 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. */
+ for (part = request->part; part; part = part->next)
+ if (part->backend_id == backend_hd->backend_id)
+ break;
+ if (!part)
+ {
+ part = xtrycalloc (1, sizeof *part);
+ if (!part)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ part->backend_id = backend_hd->backend_id;
+ part->kbx_hd = keybox_new_openpgp (backend_hd->token, 0);
+ if (!part->kbx_hd)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (part);
+ goto leave;
+ }
+ part->next = request->part;
+ request->part = part;
+ }
+
+ 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;
+
+ err = keybox_get_data (part->kbx_hd, &buffer, &buflen, &pubkey_type);
+ if (err)
+ goto leave;
+ /* Note: be_return_pubkey always takes ownership of BUFFER. */
+ err = be_return_pubkey (ctrl, buffer, buflen, pubkey_type);
+ }
+
+ leave:
+ return err;
+}