diff options
author | Werner Koch <[email protected]> | 2019-08-06 14:07:33 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2019-08-06 14:07:33 +0000 |
commit | 5ea6250cc5761612d17ca4fb34eed096f26e2826 (patch) | |
tree | 062877d21a2f53ff01918a2a73ed02fcf80fded1 /kbx/backend-kbx.c | |
parent | kbx: Allow writing using a estream. (diff) | |
download | gnupg-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.c | 292 |
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; +} |