mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
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 <wk@gnupg.org>
This commit is contained in:
parent
1f980d23af
commit
5ea6250cc5
13 changed files with 1237 additions and 34 deletions
292
kbx/backend-kbx.c
Normal file
292
kbx/backend-kbx.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue