mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-11 21:48:50 +01:00
280e9c9cfa
* kbx/backend.h (enum database_types): Add DB_TYPE_CACHE. (struct db_request_part_s): Add seqno fields. (struct db_request_s): Add infos for the cache backend. * kbx/backend-support.c (struct backend_handle_s): Add 'backend_id'. (strdbtype): Support DB_TYPE_CACHE. (be_generic_release_backend): Ditto. (be_find_request_part): New. (be_return_pubkey): New arg UBID and chnage status name. * kbx/backend-cache.c: New. * kbx/backend-kbx.c (be_kbx_init_request_part): New. (be_kbx_search): Factor some code out to a support function. (be_kbx_seek): New. * kbx/frontend.c (kbxd_add_resource): Support DB_TYPE_CACHE. (kbxd_search): Support the NEXR operation with the cache. * kbx/keybox-search-desc.h (KEYDB_SEARCH_MODE_UBID): New. (struct keydb_search_desc): Add field u.ubid. * kbx/keybox-search.c (has_ubid): New. (keybox_search): Support the UBID search. -- This adds a caching backend to the keyboxd. This tries to accommodate for duplicate use of fingerprints and thus be correct in case a fingerprint is used in several keys. It also turned out that we need to have a unique identifier (UBID) to identify a keyblock or X.509 certificate. In particular with an OpenPGP keyblob we can't easily use the primary fingerprint as an identifier because that fingerprint may also be used as subkey in another key. Thus using a hash of the entire keyblock is a better identifier to be used to address a keyblock for restarting a search or for identifying the keyblock to be updated. Note that this new UBID is not a permanent identifier because it changes with all keyblock update; it should be viewed as a handle to the keyblock or X509 cert.
776 lines
21 KiB
C
776 lines
21 KiB
C
/* kbxserver.c - Handle Assuan commands send to the 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 <assert.h>
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
|
||
#include "keyboxd.h"
|
||
#include <assuan.h>
|
||
#include "../common/i18n.h"
|
||
#include "../common/server-help.h"
|
||
#include "../common/userids.h"
|
||
#include "../common/asshelp.h"
|
||
#include "../common/host2net.h"
|
||
#include "frontend.h"
|
||
|
||
|
||
|
||
#define PARM_ERROR(t) assuan_set_error (ctx, \
|
||
gpg_error (GPG_ERR_ASS_PARAMETER), (t))
|
||
#define set_error(e,t) (ctx ? assuan_set_error (ctx, gpg_error (e), (t)) \
|
||
/**/: gpg_error (e))
|
||
|
||
|
||
|
||
/* Control structure per connection. */
|
||
struct server_local_s
|
||
{
|
||
/* Data used to associate an Assuan context with local server data */
|
||
assuan_context_t assuan_ctx;
|
||
|
||
/* The session id (a counter). */
|
||
unsigned int session_id;
|
||
|
||
/* If this flag is set to true this process will be terminated after
|
||
* the end of this session. */
|
||
int stopme;
|
||
|
||
/* If the first both flags are set the assuan logging of data lines
|
||
* is suppressed. The count variable is used to show the number of
|
||
* non-logged bytes. */
|
||
size_t inhibit_data_logging_count;
|
||
unsigned int inhibit_data_logging : 1;
|
||
unsigned int inhibit_data_logging_now : 1;
|
||
|
||
/* This flag is set if the last search command was called with --more. */
|
||
unsigned int search_expecting_more : 1;
|
||
|
||
/* This flag is set if the last search command was successful. */
|
||
unsigned int search_any_found : 1;
|
||
|
||
/* The first is the current search description as parsed by the
|
||
* cmd_search. If more than one pattern is required, cmd_search
|
||
* also allocates and sets multi_search_desc and
|
||
* multi_search_desc_len. If a search description has ever been
|
||
* allocated the allocated size is stored at
|
||
* multi_search_desc_size. */
|
||
KEYBOX_SEARCH_DESC search_desc;
|
||
KEYBOX_SEARCH_DESC *multi_search_desc;
|
||
unsigned int multi_search_desc_size;
|
||
unsigned int multi_search_desc_len;
|
||
|
||
/* If not NULL write output to this stream instead of using D lines. */
|
||
estream_t outstream;
|
||
};
|
||
|
||
|
||
|
||
|
||
/* Return the assuan contxt from the local server info in CTRL. */
|
||
static assuan_context_t
|
||
get_assuan_ctx_from_ctrl (ctrl_t ctrl)
|
||
{
|
||
if (!ctrl || !ctrl->server_local)
|
||
return NULL;
|
||
return ctrl->server_local->assuan_ctx;
|
||
}
|
||
|
||
|
||
/* If OUTPUT has been used prepare the output FD for use. This needs
|
||
* to be called by all functions which will in any way use
|
||
* kbxd_write_data_line later. Whether the output goes to the output
|
||
* stream is decided by this function. */
|
||
static gpg_error_t
|
||
prepare_outstream (ctrl_t ctrl)
|
||
{
|
||
int fd;
|
||
|
||
log_assert (ctrl && ctrl->server_local);
|
||
|
||
if (ctrl->server_local->outstream)
|
||
return 0; /* Already enabled. */
|
||
|
||
fd = translate_sys2libc_fd
|
||
(assuan_get_output_fd (get_assuan_ctx_from_ctrl (ctrl)), 1);
|
||
if (fd == -1)
|
||
return 0; /* No Output command active. */
|
||
|
||
ctrl->server_local->outstream = es_fdopen_nc (fd, "w");
|
||
if (!ctrl->server_local->outstream)
|
||
return gpg_err_code_from_syserror ();
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* The usual writen function; here with diagnostic output. */
|
||
static gpg_error_t
|
||
kbxd_writen (estream_t fp, const void *buffer, size_t length)
|
||
{
|
||
gpg_error_t err;
|
||
size_t nwritten;
|
||
|
||
if (es_write (fp, buffer, length, &nwritten))
|
||
{
|
||
err = gpg_error_from_syserror ();
|
||
log_error ("error writing OUTPUT: %s\n", gpg_strerror (err));
|
||
}
|
||
else if (length != nwritten)
|
||
{
|
||
err = gpg_error (GPG_ERR_EIO);
|
||
log_error ("error writing OUTPUT: %s\n", "short write");
|
||
}
|
||
else
|
||
err = 0;
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
/* A wrapper around assuan_send_data which makes debugging the output
|
||
* in verbose mode easier. It also takes CTRL as argument. */
|
||
gpg_error_t
|
||
kbxd_write_data_line (ctrl_t ctrl, const void *buffer_arg, size_t size)
|
||
{
|
||
const char *buffer = buffer_arg;
|
||
assuan_context_t ctx = get_assuan_ctx_from_ctrl (ctrl);
|
||
gpg_error_t err;
|
||
|
||
if (!ctx) /* Oops - no assuan context. */
|
||
return gpg_error (GPG_ERR_NOT_PROCESSED);
|
||
|
||
/* Write toa file descriptor if enabled. */
|
||
if (ctrl && ctrl->server_local && ctrl->server_local->outstream)
|
||
{
|
||
unsigned char lenbuf[4];
|
||
|
||
ulongtobuf (lenbuf, size);
|
||
err = kbxd_writen (ctrl->server_local->outstream, lenbuf, 4);
|
||
if (!err)
|
||
err = kbxd_writen (ctrl->server_local->outstream, buffer, size);
|
||
if (!err && es_fflush (ctrl->server_local->outstream))
|
||
{
|
||
err = gpg_error_from_syserror ();
|
||
log_error ("error writing OUTPUT: %s\n", gpg_strerror (err));
|
||
}
|
||
|
||
goto leave;
|
||
}
|
||
|
||
/* If we do not want logging, enable it here. */
|
||
if (ctrl && ctrl->server_local && ctrl->server_local->inhibit_data_logging)
|
||
ctrl->server_local->inhibit_data_logging_now = 1;
|
||
|
||
if (0 && opt.verbose && buffer && size)
|
||
{
|
||
/* Ease reading of output by limiting the line length. */
|
||
size_t n, nbytes;
|
||
|
||
nbytes = size;
|
||
do
|
||
{
|
||
n = nbytes > 64? 64 : nbytes;
|
||
err = assuan_send_data (ctx, buffer, n);
|
||
if (err)
|
||
{
|
||
gpg_err_set_errno (EIO);
|
||
goto leave;
|
||
}
|
||
buffer += n;
|
||
nbytes -= n;
|
||
if (nbytes && (err=assuan_send_data (ctx, NULL, 0))) /* Flush line. */
|
||
{
|
||
gpg_err_set_errno (EIO);
|
||
goto leave;
|
||
}
|
||
}
|
||
while (nbytes);
|
||
}
|
||
else
|
||
{
|
||
err = assuan_send_data (ctx, buffer, size);
|
||
if (err)
|
||
{
|
||
gpg_err_set_errno (EIO); /* For use by data_line_cookie_write. */
|
||
goto leave;
|
||
}
|
||
}
|
||
|
||
leave:
|
||
if (ctrl && ctrl->server_local && ctrl->server_local->inhibit_data_logging)
|
||
{
|
||
ctrl->server_local->inhibit_data_logging_count += size;
|
||
ctrl->server_local->inhibit_data_logging_now = 0;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
/* Helper to print a message while leaving a command. */
|
||
static gpg_error_t
|
||
leave_cmd (assuan_context_t ctx, gpg_error_t err)
|
||
{
|
||
if (err && opt.verbose)
|
||
{
|
||
const char *name = assuan_get_command_name (ctx);
|
||
if (!name)
|
||
name = "?";
|
||
if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
|
||
log_error ("command '%s' failed: %s\n", name,
|
||
gpg_strerror (err));
|
||
else
|
||
log_error ("command '%s' failed: %s <%s>\n", name,
|
||
gpg_strerror (err), gpg_strsource (err));
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
/* Handle OPTION commands. */
|
||
static gpg_error_t
|
||
option_handler (assuan_context_t ctx, const char *key, const char *value)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
gpg_error_t err = 0;
|
||
|
||
if (!strcmp (key, "lc-messages"))
|
||
{
|
||
if (ctrl->lc_messages)
|
||
xfree (ctrl->lc_messages);
|
||
ctrl->lc_messages = xtrystrdup (value);
|
||
if (!ctrl->lc_messages)
|
||
return out_of_core ();
|
||
}
|
||
else
|
||
err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
static const char hlp_search[] =
|
||
"SEARCH [--no-data] [[--more] PATTERN]\n"
|
||
"\n"
|
||
"Search for the keys identified by PATTERN. With --more more\n"
|
||
"patterns to be used for the search are expected with the next\n"
|
||
"command. With --no-data only the search status is returned but\n"
|
||
"not the actual data. See also \"NEXT\".";
|
||
static gpg_error_t
|
||
cmd_search (assuan_context_t ctx, char *line)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
int opt_more, opt_no_data;
|
||
gpg_error_t err;
|
||
unsigned int n, k;
|
||
|
||
opt_no_data = has_option (line, "--no-data");
|
||
opt_more = has_option (line, "--more");
|
||
line = skip_options (line);
|
||
|
||
ctrl->server_local->search_any_found = 0;
|
||
|
||
if (!*line)
|
||
{
|
||
if (opt_more)
|
||
{
|
||
err = set_error (GPG_ERR_INV_ARG, "--more but no pattern");
|
||
goto leave;
|
||
}
|
||
else if (!*line && ctrl->server_local->search_expecting_more)
|
||
{
|
||
/* It would be too surprising to first set a pattern but
|
||
* finally add no pattern to search the entire DB. */
|
||
err = set_error (GPG_ERR_INV_ARG, "--more pending but no pattern");
|
||
goto leave;
|
||
}
|
||
else /* No pattern - return the first item. */
|
||
{
|
||
memset (&ctrl->server_local->search_desc, 0,
|
||
sizeof ctrl->server_local->search_desc);
|
||
ctrl->server_local->search_desc.mode = KEYDB_SEARCH_MODE_FIRST;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
err = classify_user_id (line, &ctrl->server_local->search_desc, 0);
|
||
if (err)
|
||
goto leave;
|
||
}
|
||
|
||
if (opt_more || ctrl->server_local->search_expecting_more)
|
||
{
|
||
/* More pattern are expected - store the current one and return
|
||
* success. */
|
||
if (!ctrl->server_local->multi_search_desc_size)
|
||
{
|
||
n = 10;
|
||
ctrl->server_local->multi_search_desc
|
||
= xtrycalloc (n, sizeof *ctrl->server_local->multi_search_desc);
|
||
if (!ctrl->server_local->multi_search_desc)
|
||
{
|
||
err = gpg_error_from_syserror ();
|
||
goto leave;
|
||
}
|
||
ctrl->server_local->multi_search_desc_size = n;
|
||
}
|
||
|
||
if (ctrl->server_local->multi_search_desc_len
|
||
== ctrl->server_local->multi_search_desc_size)
|
||
{
|
||
KEYBOX_SEARCH_DESC *desc;
|
||
n = ctrl->server_local->multi_search_desc_size + 10;
|
||
desc = xtrycalloc (n, sizeof *desc);
|
||
if (!desc)
|
||
{
|
||
err = gpg_error_from_syserror ();
|
||
goto leave;
|
||
}
|
||
for (k=0; k < ctrl->server_local->multi_search_desc_size; k++)
|
||
desc[k] = ctrl->server_local->multi_search_desc[k];
|
||
xfree (ctrl->server_local->multi_search_desc);
|
||
ctrl->server_local->multi_search_desc = desc;
|
||
ctrl->server_local->multi_search_desc_size = n;
|
||
}
|
||
/* Actually store. */
|
||
ctrl->server_local->multi_search_desc
|
||
[ctrl->server_local->multi_search_desc_len++]
|
||
= ctrl->server_local->search_desc;
|
||
|
||
if (opt_more)
|
||
{
|
||
/* We need to be called aagain with more pattern. */
|
||
ctrl->server_local->search_expecting_more = 1;
|
||
goto leave;
|
||
}
|
||
ctrl->server_local->search_expecting_more = 0;
|
||
/* Continue with the actual search. */
|
||
}
|
||
else
|
||
ctrl->server_local->multi_search_desc_len = 0;
|
||
|
||
ctrl->server_local->inhibit_data_logging = 1;
|
||
ctrl->server_local->inhibit_data_logging_now = 0;
|
||
ctrl->server_local->inhibit_data_logging_count = 0;
|
||
ctrl->no_data_return = opt_no_data;
|
||
err = prepare_outstream (ctrl);
|
||
if (err)
|
||
;
|
||
else if (ctrl->server_local->multi_search_desc_len)
|
||
err = kbxd_search (ctrl, ctrl->server_local->multi_search_desc,
|
||
ctrl->server_local->multi_search_desc_len, 1);
|
||
else
|
||
err = kbxd_search (ctrl, &ctrl->server_local->search_desc, 1, 1);
|
||
if (err)
|
||
goto leave;
|
||
|
||
/* Set a flag for use by NEXT. */
|
||
ctrl->server_local->search_any_found = 1;
|
||
|
||
leave:
|
||
if (err)
|
||
ctrl->server_local->multi_search_desc_len = 0;
|
||
ctrl->no_data_return = 0;
|
||
ctrl->server_local->inhibit_data_logging = 0;
|
||
return leave_cmd (ctx, err);
|
||
}
|
||
|
||
|
||
static const char hlp_next[] =
|
||
"NEXT [--no-data]\n"
|
||
"\n"
|
||
"Get the next search result from a previus search.";
|
||
static gpg_error_t
|
||
cmd_next (assuan_context_t ctx, char *line)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
int opt_no_data;
|
||
gpg_error_t err;
|
||
|
||
opt_no_data = has_option (line, "--no-data");
|
||
line = skip_options (line);
|
||
|
||
if (*line)
|
||
{
|
||
err = set_error (GPG_ERR_INV_ARG, "no args expected");
|
||
goto leave;
|
||
}
|
||
|
||
if (!ctrl->server_local->search_any_found)
|
||
{
|
||
err = set_error (GPG_ERR_NOTHING_FOUND, "no previous SEARCH");
|
||
goto leave;
|
||
}
|
||
|
||
ctrl->server_local->inhibit_data_logging = 1;
|
||
ctrl->server_local->inhibit_data_logging_now = 0;
|
||
ctrl->server_local->inhibit_data_logging_count = 0;
|
||
ctrl->no_data_return = opt_no_data;
|
||
err = prepare_outstream (ctrl);
|
||
if (err)
|
||
;
|
||
else if (ctrl->server_local->multi_search_desc_len)
|
||
{
|
||
/* The next condition should never be tru but we better handle
|
||
* the first/next transition anyway. */
|
||
if (ctrl->server_local->multi_search_desc[0].mode
|
||
== KEYDB_SEARCH_MODE_FIRST)
|
||
ctrl->server_local->multi_search_desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
|
||
|
||
err = kbxd_search (ctrl, ctrl->server_local->multi_search_desc,
|
||
ctrl->server_local->multi_search_desc_len, 0);
|
||
}
|
||
else
|
||
{
|
||
/* We need to do the transition from first to next here. */
|
||
if (ctrl->server_local->search_desc.mode == KEYDB_SEARCH_MODE_FIRST)
|
||
ctrl->server_local->search_desc.mode = KEYDB_SEARCH_MODE_NEXT;
|
||
|
||
err = kbxd_search (ctrl, &ctrl->server_local->search_desc, 1, 0);
|
||
}
|
||
if (err)
|
||
goto leave;
|
||
|
||
leave:
|
||
ctrl->no_data_return = 0;
|
||
ctrl->server_local->inhibit_data_logging = 0;
|
||
return leave_cmd (ctx, err);
|
||
}
|
||
|
||
|
||
|
||
static const char hlp_getinfo[] =
|
||
"GETINFO <what>\n"
|
||
"\n"
|
||
"Multi purpose command to return certain information. \n"
|
||
"Supported values of WHAT are:\n"
|
||
"\n"
|
||
"version - Return the version of the program.\n"
|
||
"pid - Return the process id of the server.\n"
|
||
"socket_name - Return the name of the socket.\n"
|
||
"session_id - Return the current session_id.\n"
|
||
"getenv NAME - Return value of envvar NAME\n";
|
||
static gpg_error_t
|
||
cmd_getinfo (assuan_context_t ctx, char *line)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
gpg_error_t err;
|
||
char numbuf[50];
|
||
|
||
if (!strcmp (line, "version"))
|
||
{
|
||
const char *s = VERSION;
|
||
err = assuan_send_data (ctx, s, strlen (s));
|
||
}
|
||
else if (!strcmp (line, "pid"))
|
||
{
|
||
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
|
||
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
|
||
}
|
||
else if (!strcmp (line, "socket_name"))
|
||
{
|
||
const char *s = get_kbxd_socket_name ();
|
||
if (!s)
|
||
s = "[none]";
|
||
err = assuan_send_data (ctx, s, strlen (s));
|
||
}
|
||
else if (!strcmp (line, "session_id"))
|
||
{
|
||
snprintf (numbuf, sizeof numbuf, "%u", ctrl->server_local->session_id);
|
||
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
|
||
}
|
||
else if (!strncmp (line, "getenv", 6)
|
||
&& (line[6] == ' ' || line[6] == '\t' || !line[6]))
|
||
{
|
||
line += 6;
|
||
while (*line == ' ' || *line == '\t')
|
||
line++;
|
||
if (!*line)
|
||
err = gpg_error (GPG_ERR_MISSING_VALUE);
|
||
else
|
||
{
|
||
const char *s = getenv (line);
|
||
if (!s)
|
||
err = set_error (GPG_ERR_NOT_FOUND, "No such envvar");
|
||
else
|
||
err = assuan_send_data (ctx, s, strlen (s));
|
||
}
|
||
}
|
||
else
|
||
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
|
||
|
||
return leave_cmd (ctx, err);
|
||
}
|
||
|
||
|
||
|
||
static const char hlp_killkeyboxd[] =
|
||
"KILLKEYBOXD\n"
|
||
"\n"
|
||
"This command allows a user - given sufficient permissions -\n"
|
||
"to kill this keyboxd process.\n";
|
||
static gpg_error_t
|
||
cmd_killkeyboxd (assuan_context_t ctx, char *line)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
|
||
(void)line;
|
||
|
||
ctrl->server_local->stopme = 1;
|
||
assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
|
||
return gpg_error (GPG_ERR_EOF);
|
||
}
|
||
|
||
|
||
static const char hlp_reloadkeyboxd[] =
|
||
"RELOADKEYBOXD\n"
|
||
"\n"
|
||
"This command is an alternative to SIGHUP\n"
|
||
"to reload the configuration.";
|
||
static gpg_error_t
|
||
cmd_reloadkeyboxd (assuan_context_t ctx, char *line)
|
||
{
|
||
(void)ctx;
|
||
(void)line;
|
||
|
||
kbxd_sighup_action ();
|
||
return 0;
|
||
}
|
||
|
||
|
||
static const char hlp_output[] =
|
||
"OUTPUT FD[=<n>]\n"
|
||
"\n"
|
||
"Set the file descriptor to write the output data to N. If N is not\n"
|
||
"given and the operating system supports file descriptor passing, the\n"
|
||
"file descriptor currently in flight will be used.";
|
||
|
||
|
||
/* Tell the assuan library about our commands. */
|
||
static int
|
||
register_commands (assuan_context_t ctx)
|
||
{
|
||
static struct {
|
||
const char *name;
|
||
assuan_handler_t handler;
|
||
const char * const help;
|
||
} table[] = {
|
||
{ "SEARCH", cmd_search, hlp_search },
|
||
{ "NEXT", cmd_next, hlp_next },
|
||
{ "GETINFO", cmd_getinfo, hlp_getinfo },
|
||
{ "OUTPUT", NULL, hlp_output },
|
||
{ "KILLKEYBOXD",cmd_killkeyboxd,hlp_killkeyboxd },
|
||
{ "RELOADKEYBOXD",cmd_reloadkeyboxd,hlp_reloadkeyboxd },
|
||
{ NULL, NULL }
|
||
};
|
||
int i, j, rc;
|
||
|
||
for (i=j=0; table[i].name; i++)
|
||
{
|
||
rc = assuan_register_command (ctx, table[i].name, table[i].handler,
|
||
table[i].help);
|
||
if (rc)
|
||
return rc;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Note that we do not reset the list of configured keyservers. */
|
||
static gpg_error_t
|
||
reset_notify (assuan_context_t ctx, char *line)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
|
||
(void)line;
|
||
(void)ctrl;
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* This function is called by our assuan log handler to test whether a
|
||
* log message shall really be printed. The function must return
|
||
* false to inhibit the logging of MSG. CAT gives the requested log
|
||
* category. MSG might be NULL. */
|
||
int
|
||
kbxd_assuan_log_monitor (assuan_context_t ctx, unsigned int cat,
|
||
const char *msg)
|
||
{
|
||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||
|
||
(void)cat;
|
||
(void)msg;
|
||
|
||
if (!ctrl || !ctrl->server_local)
|
||
return 1; /* Can't decide - allow logging. */
|
||
|
||
if (!ctrl->server_local->inhibit_data_logging)
|
||
return 1; /* Not requested - allow logging. */
|
||
|
||
/* Disallow logging if *_now is true. */
|
||
return !ctrl->server_local->inhibit_data_logging_now;
|
||
}
|
||
|
||
|
||
/* Startup the server and run the main command loop. With FD = -1,
|
||
* use stdin/stdout. SESSION_ID is either 0 or a unique number
|
||
* identifying a session. */
|
||
void
|
||
kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id)
|
||
{
|
||
static const char hello[] = "Keyboxd " VERSION " at your service";
|
||
static char *hello_line;
|
||
int rc;
|
||
assuan_context_t ctx;
|
||
|
||
ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
|
||
if (!ctrl->server_local)
|
||
{
|
||
log_error (_("can't allocate control structure: %s\n"),
|
||
gpg_strerror (gpg_error_from_syserror ()));
|
||
xfree (ctrl);
|
||
return;
|
||
}
|
||
|
||
rc = assuan_new (&ctx);
|
||
if (rc)
|
||
{
|
||
log_error (_("failed to allocate assuan context: %s\n"),
|
||
gpg_strerror (rc));
|
||
kbxd_exit (2);
|
||
}
|
||
|
||
if (fd == GNUPG_INVALID_FD)
|
||
{
|
||
assuan_fd_t filedes[2];
|
||
|
||
filedes[0] = assuan_fdopen (0);
|
||
filedes[1] = assuan_fdopen (1);
|
||
rc = assuan_init_pipe_server (ctx, filedes);
|
||
}
|
||
else
|
||
{
|
||
rc = assuan_init_socket_server (ctx, fd,
|
||
(ASSUAN_SOCKET_SERVER_ACCEPTED
|
||
|ASSUAN_SOCKET_SERVER_FDPASSING));
|
||
}
|
||
|
||
if (rc)
|
||
{
|
||
assuan_release (ctx);
|
||
log_error (_("failed to initialize the server: %s\n"),
|
||
gpg_strerror (rc));
|
||
kbxd_exit (2);
|
||
}
|
||
|
||
rc = register_commands (ctx);
|
||
if (rc)
|
||
{
|
||
log_error (_("failed to the register commands with Assuan: %s\n"),
|
||
gpg_strerror(rc));
|
||
kbxd_exit (2);
|
||
}
|
||
|
||
|
||
if (!hello_line)
|
||
{
|
||
hello_line = xtryasprintf
|
||
("Home: %s\n"
|
||
"Config: %s\n"
|
||
"%s",
|
||
gnupg_homedir (),
|
||
/*opt.config_filename? opt.config_filename :*/ "[none]",
|
||
hello);
|
||
}
|
||
|
||
ctrl->server_local->assuan_ctx = ctx;
|
||
assuan_set_pointer (ctx, ctrl);
|
||
|
||
assuan_set_hello_line (ctx, hello_line);
|
||
assuan_register_option_handler (ctx, option_handler);
|
||
assuan_register_reset_notify (ctx, reset_notify);
|
||
|
||
ctrl->server_local->session_id = session_id;
|
||
|
||
/* The next call enable the use of status_printf. */
|
||
set_assuan_context_func (get_assuan_ctx_from_ctrl);
|
||
|
||
for (;;)
|
||
{
|
||
rc = assuan_accept (ctx);
|
||
if (rc == -1)
|
||
break;
|
||
if (rc)
|
||
{
|
||
log_info (_("Assuan accept problem: %s\n"), gpg_strerror (rc));
|
||
break;
|
||
}
|
||
|
||
#ifndef HAVE_W32_SYSTEM
|
||
if (opt.verbose)
|
||
{
|
||
assuan_peercred_t peercred;
|
||
|
||
if (!assuan_get_peercred (ctx, &peercred))
|
||
log_info ("connection from process %ld (%ld:%ld)\n",
|
||
(long)peercred->pid, (long)peercred->uid,
|
||
(long)peercred->gid);
|
||
}
|
||
#endif
|
||
|
||
rc = assuan_process (ctx);
|
||
if (rc)
|
||
{
|
||
log_info (_("Assuan processing failed: %s\n"), gpg_strerror (rc));
|
||
continue;
|
||
}
|
||
}
|
||
|
||
assuan_close_output_fd (ctx);
|
||
|
||
set_assuan_context_func (NULL);
|
||
ctrl->server_local->assuan_ctx = NULL;
|
||
assuan_release (ctx);
|
||
|
||
if (ctrl->server_local->stopme)
|
||
kbxd_exit (0);
|
||
|
||
if (ctrl->refcount)
|
||
log_error ("oops: connection control structure still referenced (%d)\n",
|
||
ctrl->refcount);
|
||
else
|
||
{
|
||
xfree (ctrl->server_local->multi_search_desc);
|
||
xfree (ctrl->server_local);
|
||
ctrl->server_local = NULL;
|
||
}
|
||
}
|