mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-18 14:17:03 +01:00
agent: separate out daemon handling infrastructure for reuse
* agent/call-scd.c: Factor re-usable code out to ... * agent/call-daemon.c: new. Store infos in an array to allow for other backend daemons. * agent/Makefile.am (gpg_agent_SOURCES): Add new file. * agent/agent.h: Include assuan.h. (enum daemon_type): New. (opt): Replace scdaemon_program by daemon_program array. Replace scd_local by a array d_local. Change users accordingly. -- The model I'm using for a TPM daemon is the current scdaemon. That includes start and stop handlers plus liveness checks and an assuan socket generator. To avoid massive code duplication (and save me a lot of effort), I've elected to strip this code out of call-scd.c into a generic framework which can then be reused as is by the TPM handling daemon. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Co-authored-by: Werner Koch <wk@gnupg.org> Modified original patch for 2.2 heavily to fit the new framework used in master (gnupg 2.3) Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
2d8f060679
commit
f541e1d95a
@ -54,6 +54,7 @@ gpg_agent_SOURCES = \
|
||||
divert-scd.c \
|
||||
cvt-openpgp.c cvt-openpgp.h \
|
||||
call-scd.c \
|
||||
call-daemon.c \
|
||||
learncard.c
|
||||
|
||||
common_libs = $(libcommon)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define map_assuan_err(a) \
|
||||
map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a))
|
||||
#include <errno.h>
|
||||
#include <assuan.h>
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include "../common/util.h"
|
||||
@ -53,6 +54,13 @@
|
||||
this shouldn't be a problem in practice. */
|
||||
#define MAX_PASSPHRASE_LEN 255
|
||||
|
||||
/* The daemons we support. When you add a new daemon, add to
|
||||
both the daemon_type and the daemon_modules array in call-daemon.c */
|
||||
enum daemon_type
|
||||
{
|
||||
DAEMON_SCD,
|
||||
DAEMON_MAX_TYPE
|
||||
};
|
||||
|
||||
/* A large struct name "opt" to keep global flags */
|
||||
EXTERN_UNLESS_MAIN_MODULE
|
||||
@ -79,10 +87,10 @@ struct
|
||||
/* Filename of the program to start as pinentry. */
|
||||
const char *pinentry_program;
|
||||
|
||||
/* Filename of the program to handle smartcard tasks. */
|
||||
const char *scdaemon_program;
|
||||
/* Filename of the program to handle daemon tasks. */
|
||||
const char *daemon_program[DAEMON_MAX_TYPE];
|
||||
|
||||
int disable_scdaemon; /* Never use the SCdaemon. */
|
||||
int disable_daemon[DAEMON_MAX_TYPE]; /* Never use the daemon. */
|
||||
|
||||
int no_grab; /* Don't let the pinentry grab the keyboard */
|
||||
|
||||
@ -207,7 +215,7 @@ struct ssh_control_file_s;
|
||||
typedef struct ssh_control_file_s *ssh_control_file_t;
|
||||
|
||||
/* Forward reference for local definitions in call-scd.c. */
|
||||
struct scd_local_s;
|
||||
struct daemon_local_s;
|
||||
|
||||
/* Collection of data per session (aka connection). */
|
||||
struct server_control_s
|
||||
@ -227,8 +235,8 @@ struct server_control_s
|
||||
/* Private data of the server (command.c). */
|
||||
struct server_local_s *server_local;
|
||||
|
||||
/* Private data of the SCdaemon (call-scd.c). */
|
||||
struct scd_local_s *scd_local;
|
||||
/* Private data of the daemon (call-XXX.c). */
|
||||
struct daemon_local_s *d_local[DAEMON_MAX_TYPE];
|
||||
|
||||
/* Environment settings for the connection. */
|
||||
session_env_t session_env;
|
||||
@ -386,7 +394,7 @@ const char *get_agent_socket_name (void);
|
||||
const char *get_agent_ssh_socket_name (void);
|
||||
int get_agent_active_connection_count (void);
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
void *get_agent_scd_notify_event (void);
|
||||
void *get_agent_daemon_notify_event (void);
|
||||
#endif
|
||||
void agent_sighup_action (void);
|
||||
int map_pk_openpgp_to_gcry (int openpgp_algo);
|
||||
@ -583,12 +591,18 @@ gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno,
|
||||
const char *keyref,
|
||||
const char *keydata, size_t keydatalen);
|
||||
|
||||
/*-- call-daemon.c --*/
|
||||
gpg_error_t daemon_start (enum daemon_type type, ctrl_t ctrl);
|
||||
assuan_context_t daemon_type_ctx (enum daemon_type type, ctrl_t ctrl);
|
||||
gpg_error_t daemon_unlock (enum daemon_type type, ctrl_t ctrl, gpg_error_t rc);
|
||||
void initialize_module_daemon (void);
|
||||
void agent_daemon_dump_state (void);
|
||||
int agent_daemon_check_running (enum daemon_type type);
|
||||
void agent_daemon_check_aliveness (void);
|
||||
void agent_reset_daemon (ctrl_t ctrl);
|
||||
void agent_kill_daemon (enum daemon_type type);
|
||||
|
||||
/*-- call-scd.c --*/
|
||||
void initialize_module_call_scd (void);
|
||||
void agent_scd_dump_state (void);
|
||||
int agent_scd_check_running (void);
|
||||
int agent_reset_scd (ctrl_t ctrl);
|
||||
int agent_card_learn (ctrl_t ctrl,
|
||||
void (*kpinfo_cb)(void*, const char *),
|
||||
void *kpinfo_cb_arg,
|
||||
@ -631,10 +645,10 @@ int agent_card_scd (ctrl_t ctrl, const char *cmdline,
|
||||
int (*getpin_cb)(void *, const char *,
|
||||
const char *, char*, size_t),
|
||||
void *getpin_cb_arg, void *assuan_context);
|
||||
|
||||
void agent_card_free_keyinfo (struct card_key_info_s *l);
|
||||
gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip,
|
||||
int cap, struct card_key_info_s **result);
|
||||
void agent_card_killscd (void);
|
||||
|
||||
|
||||
/*-- learncard.c --*/
|
||||
|
688
agent/call-daemon.c
Normal file
688
agent/call-daemon.c
Normal file
@ -0,0 +1,688 @@
|
||||
/* call-daemon - Common code for the call-XXX.c modules
|
||||
* Copyright (C) 2001, 2002, 2005, 2007, 2010,
|
||||
* 2011 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2013 Werner Koch
|
||||
*
|
||||
* 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-or-later
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include <npth.h>
|
||||
|
||||
#include "agent.h"
|
||||
#include <assuan.h>
|
||||
#include "../common/strlist.h"
|
||||
|
||||
/* Daemon type to module mapping. Make sure that they are added in the
|
||||
* same order as given by the daemon_type enum. */
|
||||
static const int daemon_modules[DAEMON_MAX_TYPE] =
|
||||
{
|
||||
GNUPG_MODULE_NAME_SCDAEMON
|
||||
};
|
||||
|
||||
/* Definition of module local data of the CTRL structure. */
|
||||
struct daemon_local_s
|
||||
{
|
||||
/* We keep a list of all allocated context with an anchor at
|
||||
DAEMON_LOCAL_LIST (see below). */
|
||||
struct daemon_local_s *next_local;
|
||||
|
||||
/* Link back to the global structure. */
|
||||
struct daemon_global_s *g;
|
||||
|
||||
assuan_context_t ctx; /* NULL or session context for the daemon
|
||||
used with this connection. */
|
||||
unsigned int in_use: 1; /* CTX is in use. */
|
||||
unsigned int invalid:1; /* CTX is invalid, should be released. */
|
||||
};
|
||||
|
||||
|
||||
/* Primary holder of all the started daemons */
|
||||
struct daemon_global_s
|
||||
{
|
||||
/* To keep track of all active daemon contexts, we keep a linked list
|
||||
anchored at this variable. */
|
||||
struct daemon_local_s *local_list;
|
||||
/* A malloced string with the name of the socket to be used for
|
||||
additional connections. May be NULL if not provided by
|
||||
daemon. */
|
||||
char *socket_name;
|
||||
|
||||
/* The context of the primary connection. This is also used as a flag
|
||||
to indicate whether the daemon has been started. */
|
||||
assuan_context_t primary_ctx;
|
||||
|
||||
/* To allow reuse of the primary connection, the following flag is set
|
||||
to true if the primary context has been reset and is not in use by
|
||||
any connection. */
|
||||
int primary_ctx_reusable;
|
||||
};
|
||||
|
||||
static struct daemon_global_s daemon_global[DAEMON_MAX_TYPE];
|
||||
|
||||
|
||||
/* A Mutex used inside the start_daemon function. */
|
||||
static npth_mutex_t start_daemon_lock;
|
||||
|
||||
|
||||
/* Communication object for wait_child_thread. */
|
||||
struct wait_child_thread_parm_s
|
||||
{
|
||||
enum daemon_type type;
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE pid;
|
||||
#else
|
||||
pid_t pid;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/* Thread to wait for daemon termination and cleanup of resources. */
|
||||
static void *
|
||||
wait_child_thread (void *arg)
|
||||
{
|
||||
int err;
|
||||
struct wait_child_thread_parm_s *parm = arg;
|
||||
enum daemon_type type = parm->type;
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE pid = parm->pid;
|
||||
#else
|
||||
pid_t pid = parm->pid;
|
||||
int wstatus;
|
||||
#endif
|
||||
const char *name = gnupg_module_name (daemon_modules[type]);
|
||||
struct daemon_global_s *g = &daemon_global[type];
|
||||
struct daemon_local_s *sl;
|
||||
|
||||
xfree (parm); /* We have copied all data to the stack. */
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
npth_unprotect ();
|
||||
WaitForSingleObject ((HANDLE)pid, INFINITE);
|
||||
npth_protect ();
|
||||
log_info ("daemon %s finished\n", name);
|
||||
#else /* !HAVE_W32_SYSTEM*/
|
||||
|
||||
again:
|
||||
npth_unprotect ();
|
||||
err = waitpid (pid, &wstatus, 0);
|
||||
npth_protect ();
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
log_error ("waitpid for %s failed: %s\n", name, strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WIFEXITED (wstatus))
|
||||
log_info ("daemon %s finished (status %d)\n",
|
||||
name, WEXITSTATUS (wstatus));
|
||||
else if (WIFSIGNALED (wstatus))
|
||||
log_info ("daemon %s killed by signal %d\n", name, WTERMSIG (wstatus));
|
||||
else
|
||||
{
|
||||
if (WIFSTOPPED (wstatus))
|
||||
log_info ("daemon %s stopped by signal %d\n",
|
||||
name, WSTOPSIG (wstatus));
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
#endif /*!HAVE_W32_SYSTEM*/
|
||||
|
||||
agent_flush_cache (1); /* Flush the PIN cache. */
|
||||
|
||||
err = npth_mutex_lock (&start_daemon_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to acquire the start_daemon lock: %s\n",
|
||||
strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
assuan_set_flag (g->primary_ctx, ASSUAN_NO_WAITPID, 1);
|
||||
|
||||
for (sl = g->local_list; sl; sl = sl->next_local)
|
||||
{
|
||||
sl->invalid = 1;
|
||||
if (!sl->in_use && sl->ctx)
|
||||
{
|
||||
assuan_release (sl->ctx);
|
||||
sl->ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
g->primary_ctx = NULL;
|
||||
g->primary_ctx_reusable = 0;
|
||||
|
||||
xfree (g->socket_name);
|
||||
g->socket_name = NULL;
|
||||
|
||||
err = npth_mutex_unlock (&start_daemon_lock);
|
||||
if (err)
|
||||
log_error ("failed to release the start_daemon lock"
|
||||
" after waitpid for %s: %s\n", name, strerror (err));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function shall be called after having accessed the daemon. It
|
||||
* is currently not very useful but gives an opportunity to keep track
|
||||
* of connections currently calling daemon. Note that the "lock"
|
||||
* operation is done by the daemon_start function which must be called
|
||||
* and error checked before any daemon operation. CTRL is the usual
|
||||
* connection context and RC the error code to be passed through the
|
||||
* function. */
|
||||
gpg_error_t
|
||||
daemon_unlock (enum daemon_type type, ctrl_t ctrl, gpg_error_t rc)
|
||||
{
|
||||
gpg_error_t err;
|
||||
|
||||
if (ctrl->d_local[type]->in_use == 0)
|
||||
{
|
||||
log_error ("%s: CTX for type %d is not in use\n", __func__, (int)type);
|
||||
if (!rc)
|
||||
rc = gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
err = npth_mutex_lock (&start_daemon_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to acquire the start_daemon lock: %s\n",
|
||||
strerror (err));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
ctrl->d_local[type]->in_use = 0;
|
||||
if (ctrl->d_local[type]->invalid)
|
||||
{
|
||||
assuan_release (ctrl->d_local[type]->ctx);
|
||||
ctrl->d_local[type]->ctx = NULL;
|
||||
ctrl->d_local[type]->invalid = 0;
|
||||
}
|
||||
err = npth_mutex_unlock (&start_daemon_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to release the start_daemon lock: %s\n",
|
||||
strerror (err));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* To make sure we leave no secrets in our image after forking of the
|
||||
daemon, we use this callback. */
|
||||
static void
|
||||
atfork_cb (void *opaque, int where)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
if (!where)
|
||||
gcry_control (GCRYCTL_TERM_SECMEM);
|
||||
}
|
||||
|
||||
|
||||
/* Fork off the daemon if this has not already been done. Lock the
|
||||
* daemon and make sure that a proper context has been setup in CTRL.
|
||||
* This function might also lock the daemon, which means that the
|
||||
* caller must call unlock_daemon after this function has returned
|
||||
* success and the actual Assuan transaction been done. */
|
||||
gpg_error_t
|
||||
daemon_start (enum daemon_type type, ctrl_t ctrl)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const char *pgmname;
|
||||
assuan_context_t ctx = NULL;
|
||||
const char *argv[5];
|
||||
assuan_fd_t no_close_list[3];
|
||||
int i;
|
||||
int rc;
|
||||
char *abs_homedir = NULL;
|
||||
struct daemon_global_s *g = &daemon_global[type];
|
||||
const char *name = gnupg_module_name (daemon_modules[type]);
|
||||
|
||||
log_assert (type < DAEMON_MAX_TYPE);
|
||||
/* if this fails, you forgot to add your new type to daemon_modules */
|
||||
log_assert (DAEMON_MAX_TYPE == DIM (daemon_modules));
|
||||
|
||||
if (opt.disable_daemon[type])
|
||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||
|
||||
if (ctrl->d_local[type] && ctrl->d_local[type]->ctx)
|
||||
{
|
||||
ctrl->d_local[type]->in_use = 1;
|
||||
return 0; /* Okay, the context is fine. */
|
||||
}
|
||||
|
||||
if (ctrl->d_local[type] && ctrl->d_local[type]->in_use)
|
||||
{
|
||||
log_error ("%s: CTX of type %d is in use\n", __func__, type);
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/* We need to serialize the access to scd_local_list and primary_scd_ctx. */
|
||||
rc = npth_mutex_lock (&start_daemon_lock);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to acquire the start_daemon lock: %s\n",
|
||||
strerror (rc));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/* If this is the first call for this session, setup the local data
|
||||
structure. */
|
||||
if (!ctrl->d_local[type])
|
||||
{
|
||||
ctrl->d_local[type] = xtrycalloc (1, sizeof *ctrl->d_local[type]);
|
||||
if (!ctrl->d_local[type])
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
rc = npth_mutex_unlock (&start_daemon_lock);
|
||||
if (rc)
|
||||
log_error ("failed to release the start_daemon lock: %s\n",
|
||||
strerror (rc));
|
||||
return err;
|
||||
}
|
||||
ctrl->d_local[type]->g = g;
|
||||
ctrl->d_local[type]->next_local = g->local_list;
|
||||
g->local_list = ctrl->d_local[type]; /* FIXME: CHECK the G thing */
|
||||
}
|
||||
|
||||
ctrl->d_local[type]->in_use = 1;
|
||||
|
||||
/* Check whether the pipe server has already been started and in
|
||||
this case either reuse a lingering pipe connection or establish a
|
||||
new socket based one. */
|
||||
if (g->primary_ctx && g->primary_ctx_reusable)
|
||||
{
|
||||
ctx = g->primary_ctx;
|
||||
g->primary_ctx_reusable = 0;
|
||||
if (opt.verbose)
|
||||
log_info ("new connection to %s daemon established (reusing)\n",
|
||||
name);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = assuan_new (&ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
|
||||
err = rc;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (g->socket_name)
|
||||
{
|
||||
rc = assuan_socket_connect (ctx, g->socket_name, 0, 0);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to socket '%s': %s\n",
|
||||
g->socket_name, gpg_strerror (rc));
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("new connection to %s daemon established\n",
|
||||
name);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (g->primary_ctx)
|
||||
{
|
||||
log_info ("%s daemon is running but won't accept further connections\n",
|
||||
name);
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Nope, it has not been started. Fire it up now. */
|
||||
if (opt.verbose)
|
||||
log_info ("no running %s daemon - starting it\n", name);
|
||||
|
||||
agent_flush_cache (1); /* Make sure the PIN cache is flushed. */
|
||||
|
||||
if (fflush (NULL))
|
||||
{
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
err = gpg_error_from_syserror ();
|
||||
#endif
|
||||
log_error ("error flushing pending output: %s\n", strerror (errno));
|
||||
/* At least Windows XP fails here with EBADF. According to docs
|
||||
and Wine an fflush(NULL) is the same as _flushall. However
|
||||
the Wime implementation does not flush stdin,stdout and stderr
|
||||
- see above. Lets try to ignore the error. */
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
goto leave;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!opt.daemon_program[type] || !*opt.daemon_program[type])
|
||||
opt.daemon_program[type] = gnupg_module_name (daemon_modules[type]);
|
||||
|
||||
if ( !(pgmname = strrchr (opt.daemon_program[type], '/')))
|
||||
pgmname = opt.daemon_program[type];
|
||||
else
|
||||
pgmname++;
|
||||
|
||||
argv[0] = pgmname;
|
||||
argv[1] = "--multi-server";
|
||||
if (gnupg_default_homedir_p ())
|
||||
argv[2] = NULL;
|
||||
else
|
||||
{
|
||||
abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
|
||||
if (!abs_homedir)
|
||||
{
|
||||
log_error ("error building filename: %s\n",
|
||||
gpg_strerror (gpg_error_from_syserror ()));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
argv[2] = "--homedir";
|
||||
argv[3] = abs_homedir;
|
||||
argv[4] = NULL;
|
||||
}
|
||||
|
||||
i=0;
|
||||
if (!opt.running_detached)
|
||||
{
|
||||
if (log_get_fd () != -1)
|
||||
no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
|
||||
no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
|
||||
}
|
||||
no_close_list[i] = ASSUAN_INVALID_FD;
|
||||
|
||||
/* Connect to the daemon and perform initial handshaking. Use
|
||||
detached flag so that under Windows DAEMON does not show up a
|
||||
new window. */
|
||||
rc = assuan_pipe_connect (ctx, opt.daemon_program[type], argv,
|
||||
no_close_list, atfork_cb, NULL,
|
||||
ASSUAN_PIPE_CONNECT_DETACHED);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to the daemon %s: %s\n",
|
||||
name, gpg_strerror (rc));
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("first connection to daemon %s established\n", name);
|
||||
|
||||
|
||||
/* Get the name of the additional socket opened by daemon. */
|
||||
{
|
||||
membuf_t data;
|
||||
unsigned char *databuf;
|
||||
size_t datalen;
|
||||
|
||||
xfree (g->socket_name);
|
||||
g->socket_name = NULL;
|
||||
init_membuf (&data, 256);
|
||||
assuan_transact (ctx, "GETINFO socket_name",
|
||||
put_membuf_cb, &data, NULL, NULL, NULL, NULL);
|
||||
|
||||
databuf = get_membuf (&data, &datalen);
|
||||
if (databuf && datalen)
|
||||
{
|
||||
g->socket_name = xtrymalloc (datalen + 1);
|
||||
if (!g->socket_name)
|
||||
log_error ("warning: can't store socket name: %s\n",
|
||||
strerror (errno));
|
||||
else
|
||||
{
|
||||
memcpy (g->socket_name, databuf, datalen);
|
||||
g->socket_name[datalen] = 0;
|
||||
if (DBG_IPC)
|
||||
log_debug ("additional connections at '%s'\n", g->socket_name);
|
||||
}
|
||||
}
|
||||
xfree (databuf);
|
||||
}
|
||||
|
||||
/* Tell the daemon we want him to send us an event signal. We
|
||||
don't support this for W32CE. */
|
||||
#ifndef HAVE_W32CE_SYSTEM
|
||||
if (opt.sigusr2_enabled)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
snprintf (buf, sizeof buf, "OPTION event-signal=%lx",
|
||||
(unsigned long)get_agent_daemon_notify_event ());
|
||||
#else
|
||||
snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2);
|
||||
#endif
|
||||
assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
#endif /*HAVE_W32CE_SYSTEM*/
|
||||
|
||||
g->primary_ctx = ctx;
|
||||
g->primary_ctx_reusable = 0;
|
||||
|
||||
{
|
||||
npth_t thread;
|
||||
npth_attr_t tattr;
|
||||
struct wait_child_thread_parm_s *wctp;
|
||||
|
||||
wctp = xtrymalloc (sizeof *wctp);
|
||||
if (!wctp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error preparing wait_child_thread: %s\n", strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
wctp->type = type;
|
||||
wctp->pid = assuan_get_pid (g->primary_ctx);
|
||||
err = npth_attr_init (&tattr);
|
||||
if (!err)
|
||||
{
|
||||
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
||||
err = npth_create (&thread, &tattr, wait_child_thread, wctp);
|
||||
if (err)
|
||||
log_error ("error spawning wait_child_thread: %s\n", strerror (err));
|
||||
npth_attr_destroy (&tattr);
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
rc = npth_mutex_unlock (&start_daemon_lock);
|
||||
if (rc)
|
||||
log_error ("failed to release the start_daemon lock: %s\n", strerror (rc));
|
||||
|
||||
xfree (abs_homedir);
|
||||
if (err)
|
||||
{
|
||||
daemon_unlock (type, ctrl, err);
|
||||
if (ctx)
|
||||
assuan_release (ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl->d_local[type]->ctx = ctx;
|
||||
ctrl->d_local[type]->invalid = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* This function must be called once to initialize this module. This
|
||||
has to be done before a second thread is spawned. We can't do the
|
||||
static initialization because NPth emulation code might not be able
|
||||
to do a static init; in particular, it is not possible for W32. */
|
||||
void
|
||||
initialize_module_daemon (void)
|
||||
{
|
||||
static int initialized;
|
||||
int err;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
err = npth_mutex_init (&start_daemon_lock, NULL);
|
||||
if (err)
|
||||
log_fatal ("error initializing mutex: %s\n", strerror (err));
|
||||
initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function may be called to print information pertaining to the
|
||||
current state of this module to the log. */
|
||||
void
|
||||
agent_daemon_dump_state (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DAEMON_MAX_TYPE; i++) {
|
||||
struct daemon_global_s *g = &daemon_global[i];
|
||||
|
||||
log_info ("%s: name %s primary_ctx=%p pid=%ld reusable=%d\n", __func__,
|
||||
gnupg_module_name (daemon_modules[i]),
|
||||
g->primary_ctx,
|
||||
(long)assuan_get_pid (g->primary_ctx),
|
||||
g->primary_ctx_reusable);
|
||||
if (g->socket_name)
|
||||
log_info ("%s: socket='%s'\n", __func__, g->socket_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the daemon is active. This is a fast check without
|
||||
* any locking and might give a wrong result if another thread is
|
||||
* about to start the daemon or the daemon is about to be stopped. */
|
||||
int
|
||||
agent_daemon_check_running (enum daemon_type type)
|
||||
{
|
||||
return !!daemon_global[type].primary_ctx;
|
||||
}
|
||||
|
||||
|
||||
/* Send a kill command to the daemon of TYPE */
|
||||
void
|
||||
agent_kill_daemon (enum daemon_type type)
|
||||
{
|
||||
struct daemon_global_s *g = &daemon_global[type];
|
||||
|
||||
if (g->primary_ctx == NULL)
|
||||
return;
|
||||
/* FIXME: This assumes SCdaemon; we should add a new command
|
||||
* (e.g. SHUTDOWN) so that there is no need to have a daemon
|
||||
* specific command. */
|
||||
assuan_transact (g->primary_ctx, "KILLSCD",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
agent_flush_cache (1); /* 1 := Flush the PIN cache. */
|
||||
}
|
||||
|
||||
|
||||
/* Reset the daemons if they have been used. Actually it is not a
|
||||
reset but a cleanup of resources used by the current connection. */
|
||||
void
|
||||
agent_reset_daemon (ctrl_t ctrl)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
rc = npth_mutex_lock (&start_daemon_lock);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to acquire the start_daemon lock: %s\n",
|
||||
strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < DAEMON_MAX_TYPE; i++)
|
||||
if (ctrl->d_local[i])
|
||||
{
|
||||
struct daemon_global_s *g = ctrl->d_local[i]->g;
|
||||
|
||||
if (ctrl->d_local[i]->ctx)
|
||||
{
|
||||
/* For the primary connection we send a reset and keep
|
||||
* that connection open for reuse. */
|
||||
if (ctrl->d_local[i]->ctx == g->primary_ctx)
|
||||
{
|
||||
/* Send a RESTART to the daemon. This is required for the
|
||||
primary connection as a kind of virtual EOF; we don't
|
||||
have another way to tell it that the next command
|
||||
should be viewed as if a new connection has been
|
||||
made. For the non-primary connections this is not
|
||||
needed as we simply close the socket. We don't check
|
||||
for an error here because the RESTART may fail for
|
||||
example if the daemon has already been terminated.
|
||||
Anyway, we need to set the reusable flag to make sure
|
||||
that the aliveness check can clean it up. */
|
||||
assuan_transact (g->primary_ctx, "RESTART",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
g->primary_ctx_reusable = 1;
|
||||
}
|
||||
else /* Secondary connections. */
|
||||
assuan_release (ctrl->d_local[i]->ctx);
|
||||
ctrl->d_local[i]->ctx = NULL;
|
||||
}
|
||||
|
||||
/* Remove the local context from our list and release it. */
|
||||
if (!g->local_list)
|
||||
BUG ();
|
||||
else if (g->local_list == ctrl->d_local[i])
|
||||
g->local_list = ctrl->d_local[i]->next_local;
|
||||
else
|
||||
{
|
||||
struct daemon_local_s *sl;
|
||||
|
||||
for (sl=g->local_list; sl->next_local; sl = sl->next_local)
|
||||
if (sl->next_local == ctrl->d_local[i])
|
||||
break;
|
||||
if (!sl->next_local)
|
||||
BUG ();
|
||||
sl->next_local = ctrl->d_local[i]->next_local;
|
||||
}
|
||||
xfree (ctrl->d_local[i]);
|
||||
ctrl->d_local[i] = NULL;
|
||||
}
|
||||
|
||||
|
||||
rc = npth_mutex_unlock (&start_daemon_lock);
|
||||
if (rc)
|
||||
log_error ("failed to release the start_daemon lock: %s\n", strerror (rc));
|
||||
}
|
||||
|
||||
|
||||
assuan_context_t
|
||||
daemon_type_ctx (enum daemon_type type, ctrl_t ctrl)
|
||||
{
|
||||
return ctrl->d_local[type]->ctx;
|
||||
}
|
581
agent/call-scd.c
581
agent/call-scd.c
@ -17,6 +17,7 @@
|
||||
*
|
||||
* 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-or-later
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
@ -88,544 +89,26 @@ struct inq_needpin_parm_s
|
||||
};
|
||||
|
||||
|
||||
/* To keep track of all active SCD contexts, we keep a linked list
|
||||
anchored at this variable. */
|
||||
static struct scd_local_s *scd_local_list;
|
||||
|
||||
/* A Mutex used inside the start_scd function. */
|
||||
static npth_mutex_t start_scd_lock;
|
||||
|
||||
/* A malloced string with the name of the socket to be used for
|
||||
additional connections. May be NULL if not provided by
|
||||
SCdaemon. */
|
||||
static char *socket_name;
|
||||
|
||||
/* The context of the primary connection. This is also used as a flag
|
||||
to indicate whether the scdaemon has been started. */
|
||||
static assuan_context_t primary_scd_ctx;
|
||||
|
||||
/* To allow reuse of the primary connection, the following flag is set
|
||||
to true if the primary context has been reset and is not in use by
|
||||
any connection. */
|
||||
static int primary_scd_ctx_reusable;
|
||||
|
||||
|
||||
|
||||
/* Local prototypes. */
|
||||
|
||||
|
||||
|
||||
|
||||
/* This function must be called once to initialize this module. This
|
||||
has to be done before a second thread is spawned. We can't do the
|
||||
static initialization because NPth emulation code might not be able
|
||||
to do a static init; in particular, it is not possible for W32. */
|
||||
void
|
||||
initialize_module_call_scd (void)
|
||||
{
|
||||
static int initialized;
|
||||
int err;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
err = npth_mutex_init (&start_scd_lock, NULL);
|
||||
if (err)
|
||||
log_fatal ("error initializing mutex: %s\n", strerror (err));
|
||||
initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function may be called to print information pertaining to the
|
||||
current state of this module to the log. */
|
||||
void
|
||||
agent_scd_dump_state (void)
|
||||
{
|
||||
log_info ("agent_scd_dump_state: primary_scd_ctx=%p pid=%ld reusable=%d\n",
|
||||
primary_scd_ctx,
|
||||
(long)assuan_get_pid (primary_scd_ctx),
|
||||
primary_scd_ctx_reusable);
|
||||
if (socket_name)
|
||||
log_info ("agent_scd_dump_state: socket='%s'\n", socket_name);
|
||||
}
|
||||
|
||||
|
||||
/* The unlock_scd function shall be called after having accessed the
|
||||
SCD. It is currently not very useful but gives an opportunity to
|
||||
keep track of connections currently calling SCD. Note that the
|
||||
"lock" operation is done by the start_scd() function which must be
|
||||
called and error checked before any SCD operation. CTRL is the
|
||||
usual connection context and RC the error code to be passed trhough
|
||||
the function. */
|
||||
static int
|
||||
unlock_scd (ctrl_t ctrl, int rc)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (ctrl->scd_local->in_use == 0)
|
||||
{
|
||||
log_error ("unlock_scd: CTX is not in use\n");
|
||||
if (!rc)
|
||||
rc = gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
err = npth_mutex_lock (&start_scd_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to acquire the start_scd lock: %s\n", strerror (err));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
ctrl->scd_local->in_use = 0;
|
||||
if (ctrl->scd_local->invalid)
|
||||
{
|
||||
assuan_release (ctrl->scd_local->ctx);
|
||||
ctrl->scd_local->ctx = NULL;
|
||||
ctrl->scd_local->invalid = 0;
|
||||
}
|
||||
err = npth_mutex_unlock (&start_scd_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to release the start_scd lock: %s\n", strerror (err));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* To make sure we leave no secrets in our image after forking of the
|
||||
scdaemon, we use this callback. */
|
||||
static void
|
||||
atfork_cb (void *opaque, int where)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
if (!where)
|
||||
gcry_control (GCRYCTL_TERM_SECMEM);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
wait_child_thread (void *arg)
|
||||
{
|
||||
int err;
|
||||
struct scd_local_s *sl;
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
HANDLE pid = (HANDLE)arg;
|
||||
|
||||
npth_unprotect ();
|
||||
WaitForSingleObject ((HANDLE)pid, INFINITE);
|
||||
npth_protect ();
|
||||
log_info ("scdaemon finished\n");
|
||||
#else
|
||||
int wstatus;
|
||||
pid_t pid = (pid_t)(uintptr_t)arg;
|
||||
|
||||
again:
|
||||
npth_unprotect ();
|
||||
err = waitpid (pid, &wstatus, 0);
|
||||
npth_protect ();
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
log_error ("waitpid failed: %s\n", strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (WIFEXITED (wstatus))
|
||||
log_info ("scdaemon finished (status %d)\n", WEXITSTATUS (wstatus));
|
||||
else if (WIFSIGNALED (wstatus))
|
||||
log_info ("scdaemon killed by signal %d\n", WTERMSIG (wstatus));
|
||||
else
|
||||
{
|
||||
if (WIFSTOPPED (wstatus))
|
||||
log_info ("scdaemon stopped by signal %d\n", WSTOPSIG (wstatus));
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
agent_flush_cache (1); /* Flush the PIN cache. */
|
||||
|
||||
err = npth_mutex_lock (&start_scd_lock);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to acquire the start_scd lock: %s\n",
|
||||
strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1);
|
||||
|
||||
for (sl = scd_local_list; sl; sl = sl->next_local)
|
||||
{
|
||||
sl->invalid = 1;
|
||||
if (!sl->in_use && sl->ctx)
|
||||
{
|
||||
assuan_release (sl->ctx);
|
||||
sl->ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
primary_scd_ctx = NULL;
|
||||
primary_scd_ctx_reusable = 0;
|
||||
|
||||
xfree (socket_name);
|
||||
socket_name = NULL;
|
||||
|
||||
err = npth_mutex_unlock (&start_scd_lock);
|
||||
if (err)
|
||||
log_error ("failed to release the start_scd lock after waitpid: %s\n",
|
||||
strerror (err));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Fork off the SCdaemon if this has not already been done. Lock the
|
||||
daemon and make sure that a proper context has been setup in CTRL.
|
||||
This function might also lock the daemon, which means that the
|
||||
caller must call unlock_scd after this function has returned
|
||||
success and the actual Assuan transaction been done. */
|
||||
static int
|
||||
start_scd (ctrl_t ctrl)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const char *pgmname;
|
||||
assuan_context_t ctx = NULL;
|
||||
const char *argv[5];
|
||||
assuan_fd_t no_close_list[3];
|
||||
int i;
|
||||
int rc;
|
||||
char *abs_homedir = NULL;
|
||||
|
||||
if (opt.disable_scdaemon)
|
||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||
|
||||
if (ctrl->scd_local && ctrl->scd_local->ctx)
|
||||
{
|
||||
ctrl->scd_local->in_use = 1;
|
||||
return 0; /* Okay, the context is fine. */
|
||||
}
|
||||
|
||||
if (ctrl->scd_local && ctrl->scd_local->in_use)
|
||||
{
|
||||
log_error ("start_scd: CTX is in use\n");
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/* We need to serialize the access to scd_local_list and primary_scd_ctx. */
|
||||
rc = npth_mutex_lock (&start_scd_lock);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to acquire the start_scd lock: %s\n",
|
||||
strerror (rc));
|
||||
return gpg_error (GPG_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
/* If this is the first call for this session, setup the local data
|
||||
structure. */
|
||||
if (!ctrl->scd_local)
|
||||
{
|
||||
ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local);
|
||||
if (!ctrl->scd_local)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
rc = npth_mutex_unlock (&start_scd_lock);
|
||||
if (rc)
|
||||
log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
|
||||
return err;
|
||||
}
|
||||
ctrl->scd_local->next_local = scd_local_list;
|
||||
scd_local_list = ctrl->scd_local;
|
||||
}
|
||||
|
||||
ctrl->scd_local->in_use = 1;
|
||||
|
||||
/* Check whether the pipe server has already been started and in
|
||||
this case either reuse a lingering pipe connection or establish a
|
||||
new socket based one. */
|
||||
if (primary_scd_ctx && primary_scd_ctx_reusable)
|
||||
{
|
||||
ctx = primary_scd_ctx;
|
||||
primary_scd_ctx_reusable = 0;
|
||||
if (opt.verbose)
|
||||
log_info ("new connection to SCdaemon established (reusing)\n");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = assuan_new (&ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
|
||||
err = rc;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (socket_name)
|
||||
{
|
||||
rc = assuan_socket_connect (ctx, socket_name, 0, 0);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to socket '%s': %s\n",
|
||||
socket_name, gpg_strerror (rc));
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("new connection to SCdaemon established\n");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (primary_scd_ctx)
|
||||
{
|
||||
log_info ("SCdaemon is running but won't accept further connections\n");
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Nope, it has not been started. Fire it up now. */
|
||||
if (opt.verbose)
|
||||
log_info ("no running SCdaemon - starting it\n");
|
||||
|
||||
agent_flush_cache (1); /* Make sure the PIN cache is flushed. */
|
||||
|
||||
if (fflush (NULL))
|
||||
{
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
err = gpg_error_from_syserror ();
|
||||
#endif
|
||||
log_error ("error flushing pending output: %s\n", strerror (errno));
|
||||
/* At least Windows XP fails here with EBADF. According to docs
|
||||
and Wine an fflush(NULL) is the same as _flushall. However
|
||||
the Wime implementation does not flush stdin,stdout and stderr
|
||||
- see above. Lets try to ignore the error. */
|
||||
#ifndef HAVE_W32_SYSTEM
|
||||
goto leave;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!opt.scdaemon_program || !*opt.scdaemon_program)
|
||||
opt.scdaemon_program = gnupg_module_name (GNUPG_MODULE_NAME_SCDAEMON);
|
||||
if ( !(pgmname = strrchr (opt.scdaemon_program, '/')))
|
||||
pgmname = opt.scdaemon_program;
|
||||
else
|
||||
pgmname++;
|
||||
|
||||
argv[0] = pgmname;
|
||||
argv[1] = "--multi-server";
|
||||
if (gnupg_default_homedir_p ())
|
||||
argv[2] = NULL;
|
||||
else
|
||||
{
|
||||
abs_homedir = make_absfilename_try (gnupg_homedir (), NULL);
|
||||
if (!abs_homedir)
|
||||
{
|
||||
log_error ("error building filename: %s\n",
|
||||
gpg_strerror (gpg_error_from_syserror ()));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
argv[2] = "--homedir";
|
||||
argv[3] = abs_homedir;
|
||||
argv[4] = NULL;
|
||||
}
|
||||
|
||||
i=0;
|
||||
if (!opt.running_detached)
|
||||
{
|
||||
if (log_get_fd () != -1)
|
||||
no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
|
||||
no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
|
||||
}
|
||||
no_close_list[i] = ASSUAN_INVALID_FD;
|
||||
|
||||
/* Connect to the scdaemon and perform initial handshaking. Use
|
||||
detached flag so that under Windows SCDAEMON does not show up a
|
||||
new window. */
|
||||
rc = assuan_pipe_connect (ctx, opt.scdaemon_program, argv,
|
||||
no_close_list, atfork_cb, NULL,
|
||||
ASSUAN_PIPE_CONNECT_DETACHED);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't connect to the SCdaemon: %s\n",
|
||||
gpg_strerror (rc));
|
||||
err = gpg_error (GPG_ERR_NO_SCDAEMON);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_debug ("first connection to SCdaemon established\n");
|
||||
|
||||
|
||||
/* Get the name of the additional socket opened by scdaemon. */
|
||||
{
|
||||
membuf_t data;
|
||||
unsigned char *databuf;
|
||||
size_t datalen;
|
||||
|
||||
xfree (socket_name);
|
||||
socket_name = NULL;
|
||||
init_membuf (&data, 256);
|
||||
assuan_transact (ctx, "GETINFO socket_name",
|
||||
put_membuf_cb, &data, NULL, NULL, NULL, NULL);
|
||||
|
||||
databuf = get_membuf (&data, &datalen);
|
||||
if (databuf && datalen)
|
||||
{
|
||||
socket_name = xtrymalloc (datalen + 1);
|
||||
if (!socket_name)
|
||||
log_error ("warning: can't store socket name: %s\n",
|
||||
strerror (errno));
|
||||
else
|
||||
{
|
||||
memcpy (socket_name, databuf, datalen);
|
||||
socket_name[datalen] = 0;
|
||||
if (DBG_IPC)
|
||||
log_debug ("additional connections at '%s'\n", socket_name);
|
||||
}
|
||||
}
|
||||
xfree (databuf);
|
||||
}
|
||||
|
||||
/* Tell the scdaemon we want him to send us an event signal. We
|
||||
don't support this for W32CE. */
|
||||
#ifndef HAVE_W32CE_SYSTEM
|
||||
if (opt.sigusr2_enabled)
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
snprintf (buf, sizeof buf, "OPTION event-signal=%p",
|
||||
get_agent_scd_notify_event ());
|
||||
#else
|
||||
snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2);
|
||||
#endif
|
||||
assuan_transact (ctx, buf, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
#endif /*HAVE_W32CE_SYSTEM*/
|
||||
|
||||
primary_scd_ctx = ctx;
|
||||
primary_scd_ctx_reusable = 0;
|
||||
|
||||
{
|
||||
npth_t thread;
|
||||
npth_attr_t tattr;
|
||||
pid_t pid;
|
||||
|
||||
pid = assuan_get_pid (primary_scd_ctx);
|
||||
err = npth_attr_init (&tattr);
|
||||
if (!err)
|
||||
{
|
||||
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
||||
err = npth_create (&thread, &tattr, wait_child_thread,
|
||||
(void *)(uintptr_t)pid);
|
||||
if (err)
|
||||
log_error ("error spawning wait_child_thread: %s\n", strerror (err));
|
||||
npth_attr_destroy (&tattr);
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
rc = npth_mutex_unlock (&start_scd_lock);
|
||||
if (rc)
|
||||
log_error ("failed to release the start_scd lock: %s\n", strerror (rc));
|
||||
|
||||
xfree (abs_homedir);
|
||||
if (err)
|
||||
{
|
||||
unlock_scd (ctrl, err);
|
||||
if (ctx)
|
||||
assuan_release (ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl->scd_local->invalid = 0;
|
||||
ctrl->scd_local->ctx = ctx;
|
||||
}
|
||||
return err;
|
||||
return daemon_start (DAEMON_SCD, ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the SCdaemon is active. This is a fast check without
|
||||
any locking and might give a wrong result if another thread is about
|
||||
to start the daemon or the daemon is about to be stopped.. */
|
||||
int
|
||||
agent_scd_check_running (void)
|
||||
static gpg_error_t
|
||||
unlock_scd (ctrl_t ctrl, gpg_error_t err)
|
||||
{
|
||||
return !!primary_scd_ctx;
|
||||
return daemon_unlock (DAEMON_SCD, ctrl, err);
|
||||
}
|
||||
|
||||
|
||||
/* Reset the SCD if it has been used. Actually it is not a reset but
|
||||
a cleanup of resources used by the current connection. */
|
||||
int
|
||||
agent_reset_scd (ctrl_t ctrl)
|
||||
static assuan_context_t
|
||||
daemon_ctx (ctrl_t ctrl)
|
||||
{
|
||||
int err = npth_mutex_lock (&start_scd_lock);
|
||||
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to acquire the start_scd lock: %s\n",
|
||||
strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctrl->scd_local)
|
||||
{
|
||||
if (ctrl->scd_local->ctx)
|
||||
{
|
||||
/* We send a reset and keep that connection for reuse. */
|
||||
if (ctrl->scd_local->ctx == primary_scd_ctx)
|
||||
{
|
||||
/* Send a RESTART to the SCD. This is required for the
|
||||
primary connection as a kind of virtual EOF; we don't
|
||||
have another way to tell it that the next command
|
||||
should be viewed as if a new connection has been
|
||||
made. For the non-primary connections this is not
|
||||
needed as we simply close the socket. We don't check
|
||||
for an error here because the RESTART may fail for
|
||||
example if the scdaemon has already been terminated.
|
||||
Anyway, we need to set the reusable flag to make sure
|
||||
that the aliveness check can clean it up. */
|
||||
assuan_transact (primary_scd_ctx, "RESTART",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
primary_scd_ctx_reusable = 1;
|
||||
}
|
||||
else
|
||||
assuan_release (ctrl->scd_local->ctx);
|
||||
ctrl->scd_local->ctx = NULL;
|
||||
}
|
||||
|
||||
/* Remove the local context from our list and release it. */
|
||||
if (!scd_local_list)
|
||||
BUG ();
|
||||
else if (scd_local_list == ctrl->scd_local)
|
||||
scd_local_list = ctrl->scd_local->next_local;
|
||||
else
|
||||
{
|
||||
struct scd_local_s *sl;
|
||||
|
||||
for (sl=scd_local_list; sl->next_local; sl = sl->next_local)
|
||||
if (sl->next_local == ctrl->scd_local)
|
||||
break;
|
||||
if (!sl->next_local)
|
||||
BUG ();
|
||||
sl->next_local = ctrl->scd_local->next_local;
|
||||
}
|
||||
xfree (ctrl->scd_local);
|
||||
ctrl->scd_local = NULL;
|
||||
}
|
||||
|
||||
err = npth_mutex_unlock (&start_scd_lock);
|
||||
if (err)
|
||||
log_error ("failed to release the start_scd lock: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return daemon_type_ctx (DAEMON_SCD, ctrl);
|
||||
}
|
||||
|
||||
|
||||
@ -796,7 +279,7 @@ agent_card_learn (ctrl_t ctrl,
|
||||
parm.certinfo_cb_arg = certinfo_cb_arg;
|
||||
parm.sinfo_cb = sinfo_cb;
|
||||
parm.sinfo_cb_arg = sinfo_cb_arg;
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, "LEARN --force",
|
||||
rc = assuan_transact (daemon_ctx (ctrl), "LEARN --force",
|
||||
NULL, NULL, NULL, NULL,
|
||||
learn_status_cb, &parm);
|
||||
if (rc)
|
||||
@ -862,7 +345,7 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char *demand)
|
||||
else
|
||||
snprintf (line, DIM(line), "SERIALNO --demand=%s", demand);
|
||||
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
NULL, NULL, NULL, NULL,
|
||||
get_serialno_cb, &serialno);
|
||||
if (rc)
|
||||
@ -1011,13 +494,13 @@ agent_card_pksign (ctrl_t ctrl,
|
||||
|
||||
bin2hex (indata, indatalen, stpcpy (line, "SETDATA "));
|
||||
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
NULL, NULL, NULL, NULL, pincache_put_cb, NULL);
|
||||
if (rc)
|
||||
return unlock_scd (ctrl, rc);
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
inqparm.ctx = ctrl->scd_local->ctx;
|
||||
inqparm.ctx = daemon_ctx (ctrl);
|
||||
inqparm.getpin_cb = getpin_cb;
|
||||
inqparm.getpin_cb_arg = getpin_cb_arg;
|
||||
inqparm.getpin_cb_desc = desc_text;
|
||||
@ -1030,7 +513,7 @@ agent_card_pksign (ctrl_t ctrl,
|
||||
else
|
||||
snprintf (line, sizeof line, "PKSIGN %s %s",
|
||||
hash_algo_option (mdalgo), keyid);
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
put_membuf_cb, &data,
|
||||
inq_needpin, &inqparm,
|
||||
pincache_put_cb, NULL);
|
||||
@ -1108,14 +591,14 @@ agent_card_pkdecrypt (ctrl_t ctrl,
|
||||
sprintf (p, "%02X", indata[len]);
|
||||
p += 2;
|
||||
}
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return unlock_scd (ctrl, rc);
|
||||
}
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
inqparm.ctx = ctrl->scd_local->ctx;
|
||||
inqparm.ctx = daemon_ctx (ctrl);
|
||||
inqparm.getpin_cb = getpin_cb;
|
||||
inqparm.getpin_cb_arg = getpin_cb_arg;
|
||||
inqparm.getpin_cb_desc = desc_text;
|
||||
@ -1123,7 +606,7 @@ agent_card_pkdecrypt (ctrl_t ctrl,
|
||||
inqparm.keydata = NULL;
|
||||
inqparm.keydatalen = 0;
|
||||
snprintf (line, DIM(line), "PKDECRYPT %s", keyid);
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
put_membuf_cb, &data,
|
||||
inq_needpin, &inqparm,
|
||||
padding_info_cb, r_padding);
|
||||
@ -1159,7 +642,7 @@ agent_card_readcert (ctrl_t ctrl,
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
snprintf (line, DIM(line), "READCERT %s", id);
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
put_membuf_cb, &data,
|
||||
NULL, NULL,
|
||||
pincache_put_cb, NULL);
|
||||
@ -1251,7 +734,7 @@ agent_card_readkey (ctrl_t ctrl, const char *id,
|
||||
init_membuf (&data, 1024);
|
||||
snprintf (line, DIM(line), "READKEY%s -- %s",
|
||||
r_keyref? " --info":"", id);
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
rc = assuan_transact (daemon_ctx (ctrl), line,
|
||||
put_membuf_cb, &data,
|
||||
NULL, NULL,
|
||||
readkey_status_cb, &parm);
|
||||
@ -1318,7 +801,7 @@ agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
|
||||
return err;
|
||||
|
||||
snprintf (line, DIM(line), "WRITEKEY %s%s", force ? "--force " : "", keyref);
|
||||
parms.ctx = ctrl->scd_local->ctx;
|
||||
parms.ctx = daemon_ctx (ctrl);
|
||||
parms.getpin_cb = getpin_cb;
|
||||
parms.getpin_cb_arg = getpin_cb_arg;
|
||||
parms.getpin_cb_desc= NULL;
|
||||
@ -1326,7 +809,7 @@ agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
|
||||
parms.keydata = keydata;
|
||||
parms.keydatalen = keydatalen;
|
||||
|
||||
err = assuan_transact (ctrl->scd_local->ctx, line, NULL, NULL,
|
||||
err = assuan_transact (daemon_ctx (ctrl), line, NULL, NULL,
|
||||
inq_writekey_parms, &parms,
|
||||
pincache_put_cb, NULL);
|
||||
return unlock_scd (ctrl, err);
|
||||
@ -1406,7 +889,7 @@ agent_card_getattr (ctrl_t ctrl, const char *name, char **result,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
err = assuan_transact (daemon_ctx (ctrl), line,
|
||||
NULL, NULL, NULL, NULL,
|
||||
card_getattr_cb, &parm);
|
||||
if (!err && parm.error)
|
||||
@ -1577,7 +1060,7 @@ agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, int cap,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = assuan_transact (ctrl->scd_local->ctx, line,
|
||||
err = assuan_transact (daemon_ctx (ctrl), line,
|
||||
NULL, NULL, NULL, NULL,
|
||||
card_keyinfo_cb, &parm);
|
||||
if (!err && parm.error)
|
||||
@ -1656,7 +1139,7 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
inqparm.ctx = ctrl->scd_local->ctx;
|
||||
inqparm.ctx = daemon_ctx (ctrl);
|
||||
inqparm.getpin_cb = getpin_cb;
|
||||
inqparm.getpin_cb_arg = getpin_cb_arg;
|
||||
inqparm.getpin_cb_desc = NULL;
|
||||
@ -1664,14 +1147,14 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline,
|
||||
inqparm.keydata = NULL;
|
||||
inqparm.keydatalen = 0;
|
||||
|
||||
saveflag = assuan_get_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS);
|
||||
assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, 1);
|
||||
rc = assuan_transact (ctrl->scd_local->ctx, cmdline,
|
||||
saveflag = assuan_get_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS);
|
||||
assuan_set_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS, 1);
|
||||
rc = assuan_transact (daemon_ctx (ctrl), cmdline,
|
||||
pass_data_thru, assuan_context,
|
||||
inq_needpin, &inqparm,
|
||||
pass_status_thru, assuan_context);
|
||||
|
||||
assuan_set_flag (ctrl->scd_local->ctx, ASSUAN_CONVEY_COMMENTS, saveflag);
|
||||
assuan_set_flag (daemon_ctx (ctrl), ASSUAN_CONVEY_COMMENTS, saveflag);
|
||||
if (rc)
|
||||
{
|
||||
return unlock_scd (ctrl, rc);
|
||||
@ -1679,13 +1162,3 @@ agent_card_scd (ctrl_t ctrl, const char *cmdline,
|
||||
|
||||
return unlock_scd (ctrl, 0);
|
||||
}
|
||||
|
||||
void
|
||||
agent_card_killscd (void)
|
||||
{
|
||||
if (primary_scd_ctx == NULL)
|
||||
return;
|
||||
assuan_transact (primary_scd_ctx, "KILLSCD",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
agent_flush_cache (1); /* Flush the PIN cache. */
|
||||
}
|
||||
|
@ -2476,7 +2476,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
|
||||
reader - this should be allowed even without being listed in
|
||||
sshcontrol. */
|
||||
|
||||
if (!opt.disable_scdaemon)
|
||||
if (!opt.disable_daemon[DAEMON_SCD])
|
||||
{
|
||||
char *serialno;
|
||||
struct card_key_info_s *keyinfo_list;
|
||||
@ -3610,8 +3610,8 @@ start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
|
||||
es_ungetc (c, stream_sock);
|
||||
}
|
||||
|
||||
/* Reset the SCD in case it has been used. */
|
||||
agent_reset_scd (ctrl);
|
||||
/* Reset the daemon in case it has been used. */
|
||||
agent_reset_daemon (ctrl);
|
||||
|
||||
|
||||
out:
|
||||
@ -3758,8 +3758,8 @@ serve_mmapped_ssh_request (ctrl_t ctrl,
|
||||
valid_response = 1;
|
||||
}
|
||||
|
||||
/* Reset the SCD in case it has been used. */
|
||||
agent_reset_scd (ctrl);
|
||||
/* Reset the daemon in case it has been used. */
|
||||
agent_reset_daemon (ctrl);
|
||||
|
||||
return valid_response? 0 : -1;
|
||||
}
|
||||
|
@ -3257,7 +3257,7 @@ cmd_getinfo (assuan_context_t ctx, char *line)
|
||||
}
|
||||
else if (!strcmp (line, "scd_running"))
|
||||
{
|
||||
rc = agent_scd_check_running ()? 0 : gpg_error (GPG_ERR_FALSE);
|
||||
rc = agent_daemon_check_running (DAEMON_SCD)? 0:gpg_error (GPG_ERR_FALSE);
|
||||
}
|
||||
else if (!strcmp (line, "std_env_names"))
|
||||
{
|
||||
@ -3747,7 +3747,7 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
|
||||
clear_nonce_cache (ctrl);
|
||||
|
||||
/* Reset the SCD if needed. */
|
||||
agent_reset_scd (ctrl);
|
||||
agent_reset_daemon (ctrl);
|
||||
|
||||
/* Reset the pinentry (in case of popup messages). */
|
||||
agent_reset_query (ctrl);
|
||||
|
@ -845,7 +845,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
|
||||
xfree (opt.pinentry_invisible_char);
|
||||
opt.pinentry_invisible_char = NULL;
|
||||
opt.pinentry_timeout = 0;
|
||||
opt.scdaemon_program = NULL;
|
||||
memset (opt.daemon_program, 0, sizeof opt.daemon_program);
|
||||
opt.def_cache_ttl = DEFAULT_CACHE_TTL;
|
||||
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
|
||||
opt.max_cache_ttl = MAX_CACHE_TTL;
|
||||
@ -862,7 +862,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
|
||||
opt.allow_external_cache = 1;
|
||||
opt.allow_loopback_pinentry = 1;
|
||||
opt.allow_emacs_pinentry = 0;
|
||||
opt.disable_scdaemon = 0;
|
||||
memset (opt.disable_daemon, 0, sizeof opt.disable_daemon);
|
||||
disable_check_own_socket = 0;
|
||||
/* Note: When changing the next line, change also gpgconf_list. */
|
||||
opt.ssh_fingerprint_digest = GCRY_MD_MD5;
|
||||
@ -905,8 +905,8 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
|
||||
opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break;
|
||||
break;
|
||||
case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break;
|
||||
case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
|
||||
case oDisableScdaemon: opt.disable_scdaemon = 1; break;
|
||||
case oScdaemonProgram: opt.daemon_program[DAEMON_SCD] = pargs->r.ret_str; break;
|
||||
case oDisableScdaemon: opt.disable_daemon[DAEMON_SCD] = 1; break;
|
||||
case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
|
||||
|
||||
case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
|
||||
@ -1020,7 +1020,7 @@ initialize_modules (void)
|
||||
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
|
||||
initialize_module_cache ();
|
||||
initialize_module_call_pinentry ();
|
||||
initialize_module_call_scd ();
|
||||
initialize_module_daemon ();
|
||||
initialize_module_trustlist ();
|
||||
}
|
||||
|
||||
@ -2064,7 +2064,7 @@ get_agent_active_connection_count (void)
|
||||
event. */
|
||||
#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
|
||||
void *
|
||||
get_agent_scd_notify_event (void)
|
||||
get_agent_daemon_notify_event (void)
|
||||
{
|
||||
static HANDLE the_event = INVALID_HANDLE_VALUE;
|
||||
|
||||
@ -2403,8 +2403,8 @@ agent_sighup_action (void)
|
||||
"pinentry-basic" fallback was in use. */
|
||||
gnupg_module_name_flush_some ();
|
||||
|
||||
if (opt.disable_scdaemon)
|
||||
agent_card_killscd ();
|
||||
if (opt.disable_daemon[DAEMON_SCD])
|
||||
agent_kill_daemon (DAEMON_SCD);
|
||||
}
|
||||
|
||||
|
||||
@ -2438,7 +2438,7 @@ handle_signal (int signo)
|
||||
logging system. */
|
||||
/* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
|
||||
agent_query_dump_state ();
|
||||
agent_scd_dump_state ();
|
||||
agent_daemon_dump_state ();
|
||||
break;
|
||||
|
||||
case SIGUSR2:
|
||||
@ -2841,7 +2841,7 @@ handle_connections (gnupg_fd_t listen_fd,
|
||||
sigs = 0;
|
||||
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
|
||||
# else
|
||||
events[0] = get_agent_scd_notify_event ();
|
||||
events[0] = get_agent_daemon_notify_event ();
|
||||
events[1] = INVALID_HANDLE_VALUE;
|
||||
# endif
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user