diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 8d6ecfc8e..b8a5a3e07 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -1,2784 +1,2776 @@
/* gpg-agent.c - The GnuPG Agent
* Copyright (C) 2000-2007, 2009-2010 Free Software Foundation, Inc.
* Copyright (C) 2000-2016 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
# ifndef WINVER
# define WINVER 0x0500 /* Same as in common/sysutils.c */
# endif
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
#endif /*!HAVE_W32_SYSTEM*/
#include
#ifdef HAVE_SIGNAL_H
# include
#endif
#include
#define GNUPG_COMMON_NEED_AFLOCAL
#include "agent.h"
#include /* Malloc hooks and socket wrappers. */
#include "i18n.h"
#include "sysutils.h"
#include "gc-opt-flags.h"
#include "exechelp.h"
#include "asshelp.h"
#include "../common/init.h"
enum cmd_and_opt_values
{ aNull = 0,
oCsh = 'c',
oQuiet = 'q',
oSh = 's',
oVerbose = 'v',
oNoVerbose = 500,
aGPGConfList,
aGPGConfTest,
aUseStandardSocketP,
oOptions,
oDebug,
oDebugAll,
oDebugLevel,
oDebugWait,
oDebugQuickRandom,
oDebugPinentry,
oNoGreeting,
oNoOptions,
oHomedir,
oNoDetach,
oNoGrab,
oLogFile,
oServer,
oDaemon,
oBatch,
oPinentryProgram,
oPinentryTouchFile,
oPinentryInvisibleChar,
oPinentryTimeout,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oScdaemonProgram,
oDefCacheTTL,
oDefCacheTTLSSH,
oMaxCacheTTL,
oMaxCacheTTLSSH,
oEnforcePassphraseConstraints,
oMinPassphraseLen,
oMinPassphraseNonalpha,
oCheckPassphrasePattern,
oMaxPassphraseDays,
oEnablePassphraseHistory,
oUseStandardSocket,
oNoUseStandardSocket,
oExtraSocket,
oBrowserSocket,
oFakedSystemTime,
oIgnoreCacheForSigning,
oAllowMarkTrusted,
oNoAllowMarkTrusted,
oAllowPresetPassphrase,
oAllowLoopbackPinentry,
oNoAllowLoopbackPinentry,
oNoAllowExternalCache,
oAllowEmacsPinentry,
oKeepTTY,
oKeepDISPLAY,
oSSHSupport,
oPuttySupport,
oDisableScdaemon,
oDisableCheckOwnSocket,
oWriteEnvFile
};
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
ARGPARSE_c (aUseStandardSocketP, "use-standard-socket-p", "@"),
ARGPARSE_group (301, N_("@Options:\n ")),
ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
ARGPARSE_s_n (oServer, "server", N_("run in server mode (foreground)")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")),
ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")),
ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
ARGPARSE_s_i (oDebugWait," debug-wait", "@"),
ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"),
ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"),
ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_n (oNoGrab, "no-grab", N_("do not grab keyboard and mouse")),
ARGPARSE_s_s (oLogFile, "log-file", N_("use a log file for the server")),
ARGPARSE_s_s (oPinentryProgram, "pinentry-program",
/* */ N_("|PGM|use PGM as the PIN-Entry program")),
ARGPARSE_s_s (oPinentryTouchFile, "pinentry-touch-file", "@"),
ARGPARSE_s_s (oPinentryInvisibleChar, "pinentry-invisible-char", "@"),
ARGPARSE_s_u (oPinentryTimeout, "pinentry-timeout", "@"),
ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
/* */ N_("|PGM|use PGM as the SCdaemon program") ),
ARGPARSE_s_n (oDisableScdaemon, "disable-scdaemon",
/* */ N_("do not use the SCdaemon") ),
ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
ARGPARSE_s_s (oExtraSocket, "extra-socket",
/* */ N_("|NAME|accept some commands via NAME")),
ARGPARSE_s_s (oBrowserSocket, "browser-socket", "@"),
ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oDisplay, "display", "@"),
ARGPARSE_s_s (oTTYname, "ttyname", "@"),
ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
ARGPARSE_s_s (oLCmessages, "lc-messages", "@"),
ARGPARSE_s_s (oXauthority, "xauthority", "@"),
ARGPARSE_s_n (oKeepTTY, "keep-tty",
/* */ N_("ignore requests to change the TTY")),
ARGPARSE_s_n (oKeepDISPLAY, "keep-display",
/* */ N_("ignore requests to change the X display")),
ARGPARSE_s_u (oDefCacheTTL, "default-cache-ttl",
N_("|N|expire cached PINs after N seconds")),
ARGPARSE_s_u (oDefCacheTTLSSH, "default-cache-ttl-ssh", "@" ),
ARGPARSE_s_u (oMaxCacheTTL, "max-cache-ttl", "@" ),
ARGPARSE_s_u (oMaxCacheTTLSSH, "max-cache-ttl-ssh", "@" ),
ARGPARSE_s_n (oEnforcePassphraseConstraints, "enforce-passphrase-constraints",
/* */ "@"),
ARGPARSE_s_u (oMinPassphraseLen, "min-passphrase-len", "@"),
ARGPARSE_s_u (oMinPassphraseNonalpha, "min-passphrase-nonalpha", "@"),
ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern", "@"),
ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days", "@"),
ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", "@"),
ARGPARSE_s_n (oIgnoreCacheForSigning, "ignore-cache-for-signing",
/* */ N_("do not use the PIN cache when signing")),
ARGPARSE_s_n (oNoAllowExternalCache, "no-allow-external-cache",
/* */ N_("disallow the use of an external password cache")),
ARGPARSE_s_n (oNoAllowMarkTrusted, "no-allow-mark-trusted",
/* */ N_("disallow clients to mark keys as \"trusted\"")),
ARGPARSE_s_n (oAllowMarkTrusted, "allow-mark-trusted", "@"),
ARGPARSE_s_n (oAllowPresetPassphrase, "allow-preset-passphrase",
/* */ N_("allow presetting passphrase")),
ARGPARSE_s_n (oNoAllowLoopbackPinentry, "no-allow-loopback-pinentry",
N_("disallow caller to override the pinentry")),
ARGPARSE_s_n (oAllowLoopbackPinentry, "allow-loopback-pinentry", "@"),
ARGPARSE_s_n (oAllowEmacsPinentry, "allow-emacs-pinentry",
/* */ N_("allow passphrase to be prompted through Emacs")),
ARGPARSE_s_n (oSSHSupport, "enable-ssh-support", N_("enable ssh support")),
ARGPARSE_s_n (oPuttySupport, "enable-putty-support",
#ifdef HAVE_W32_SYSTEM
/* */ N_("enable putty support")
#else
/* */ "@"
#endif
),
/* Dummy options for backward compatibility. */
ARGPARSE_o_s (oWriteEnvFile, "write-env-file", "@"),
ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"),
{0} /* End of list */
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_COMMAND_VALUE, "command" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
{ 77, NULL } /* 77 := Do not exit on "help" or "?". */
};
#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */
#define DEFAULT_CACHE_TTL_SSH (30*60) /* 30 minutes */
#define MAX_CACHE_TTL (120*60) /* 2 hours */
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
#define MIN_PASSPHRASE_LEN (8)
#define MIN_PASSPHRASE_NONALPHA (1)
#define MAX_PASSPHRASE_DAYS (0)
/* The timer tick used for housekeeping stuff. For Windows we use a
longer period as the SetWaitableTimer seems to signal earlier than
the 2 seconds. CHECK_OWN_SOCKET_INTERVAL defines how often we
check our own socket in standard socket mode. If that value is 0
we don't check at all. All values are in seconds. */
#if defined(HAVE_W32CE_SYSTEM)
# define TIMERTICK_INTERVAL (60)
# define CHECK_OWN_SOCKET_INTERVAL (0) /* Never */
#elif defined(HAVE_W32_SYSTEM)
# define TIMERTICK_INTERVAL (4)
# define CHECK_OWN_SOCKET_INTERVAL (60)
#else
# define TIMERTICK_INTERVAL (2)
# define CHECK_OWN_SOCKET_INTERVAL (60)
#endif
/* Flag indicating that the ssh-agent subsystem has been enabled. */
static int ssh_support;
#ifdef HAVE_W32_SYSTEM
/* Flag indicating that support for Putty has been enabled. */
static int putty_support;
/* A magic value used with WM_COPYDATA. */
#define PUTTY_IPC_MAGIC 0x804e50ba
/* To avoid surprises we limit the size of the mapped IPC file to this
value. Putty currently (0.62) uses 8k, thus 16k should be enough
for the foreseeable future. */
#define PUTTY_IPC_MAXLEN 16384
#endif /*HAVE_W32_SYSTEM*/
/* The list of open file descriptors at startup. Note that this list
has been allocated using the standard malloc. */
static int *startup_fd_list;
/* The signal mask at startup and a flag telling whether it is valid. */
#ifdef HAVE_SIGPROCMASK
static sigset_t startup_signal_mask;
static int startup_signal_mask_valid;
#endif
/* Flag to indicate that a shutdown was requested. */
static int shutdown_pending;
/* Counter for the currently running own socket checks. */
static int check_own_socket_running;
/* Flags to indicate that check_own_socket shall not be called. */
static int disable_check_own_socket;
/* It is possible that we are currently running under setuid permissions */
static int maybe_setuid = 1;
/* Name of the communication socket used for native gpg-agent
requests. The second variable is either NULL or a malloced string
with the real socket name in case it has been redirected. */
static char *socket_name;
static char *redir_socket_name;
/* Name of the optional extra socket used for native gpg-agent requests. */
static char *socket_name_extra;
static char *redir_socket_name_extra;
/* Name of the optional browser socket used for native gpg-agent requests. */
static char *socket_name_browser;
static char *redir_socket_name_browser;
/* Name of the communication socket used for ssh-agent-emulation. */
static char *socket_name_ssh;
static char *redir_socket_name_ssh;
/* We need to keep track of the server's nonces (these are dummies for
POSIX systems). */
static assuan_sock_nonce_t socket_nonce;
static assuan_sock_nonce_t socket_nonce_extra;
static assuan_sock_nonce_t socket_nonce_browser;
static assuan_sock_nonce_t socket_nonce_ssh;
/* Default values for options passed to the pinentry. */
static char *default_display;
static char *default_ttyname;
static char *default_ttytype;
static char *default_lc_ctype;
static char *default_lc_messages;
static char *default_xauthority;
/* Name of a config file, which will be reread on a HUP if it is not NULL. */
static char *config_filename;
/* Helper to implement --debug-level */
static const char *debug_level;
/* Keep track of the current log file so that we can avoid updating
the log file after a SIGHUP if it didn't changed. Malloced. */
static char *current_logfile;
/* The handle_tick() function may test whether a parent is still
running. We record the PID of the parent here or -1 if it should be
watched. */
static pid_t parent_pid = (pid_t)(-1);
/* Number of active connections. */
static int active_connections;
/* This object is used to dispatch progress messages from Libgcrypt to
* the right thread. Given that we won't have at max a few dozen
* connections at the same time using a linked list is the easiest way
* to handle this. */
struct progress_dispatch_s
{
struct progress_dispatch_s *next;
/* The control object of the connection. If this is NULL no
* connection is associated with this item and it is free for reuse
* by new connections. */
ctrl_t ctrl;
/* The thread id of (npth_self) of the connection. */
npth_t tid;
/* The callback set by the connection. This is similar to the
* Libgcrypt callback but with the control object passed as the
* first argument. */
void (*cb)(ctrl_t ctrl,
const char *what, int printchar,
int current, int total);
};
struct progress_dispatch_s *progress_dispatch_list;
/*
Local prototypes.
*/
static char *create_socket_name (char *standard_name, int with_homedir);
static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin,
char **r_redir_name,
assuan_sock_nonce_t *nonce);
static void create_directories (void);
static void agent_libgcrypt_progress_cb (void *data, const char *what,
int printchar,
int current, int total);
static void agent_init_default_ctrl (ctrl_t ctrl);
static void agent_deinit_default_ctrl (ctrl_t ctrl);
static void handle_connections (gnupg_fd_t listen_fd,
gnupg_fd_t listen_fd_extra,
gnupg_fd_t listen_fd_browser,
gnupg_fd_t listen_fd_ssh);
static void check_own_socket (void);
static int check_for_running_agent (int silent);
/* Pth wrapper function definitions. */
ASSUAN_SYSTEM_NPTH_IMPL;
/*
Functions.
*/
/* Allocate a string describing a library version by calling a GETFNC.
This function is expected to be called only once. GETFNC is
expected to have a semantic like gcry_check_version (). */
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
if (maybe_setuid)
{
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
maybe_setuid = 0;
}
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
/* Return strings describing this program. The case values are
described in common/argparse.c:strusage. The values here override
the default values given by strusage. */
static const char *
my_strusage (int level)
{
static char *ver_gcry;
const char *p;
switch (level)
{
case 11: p = "@GPG_AGENT@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
/* TRANSLATORS: @EMAIL@ will get replaced by the actual bug
reporting address. This is so that we can change the
reporting address without breaking the translations. */
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
case 1:
case 40: p = _("Usage: @GPG_AGENT@ [options] (-h for help)");
break;
case 41: p = _("Syntax: @GPG_AGENT@ [options] [command [args]]\n"
"Secret key management for @GNUPG@\n");
break;
default: p = NULL;
}
return p;
}
/* Setup the debugging. With the global variable DEBUG_LEVEL set to NULL
only the active debug flags are propagated to the subsystems. With
DEBUG_LEVEL set, a specific set of debug flags is set; thus overriding
all flags already set. Note that we don't fail here, because it is
important to keep gpg-agent running even after re-reading the
options due to a SIGHUP. */
static void
set_debug (void)
{
int numok = (debug_level && digitp (debug_level));
int numlvl = numok? atoi (debug_level) : 0;
if (!debug_level)
;
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
|DBG_CACHE_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), debug_level);
opt.debug = 0; /* Reset debugging, so that prior debug
statements won't have an undesired effect. */
}
if (opt.debug && !opt.verbose)
opt.verbose = 1;
if (opt.debug && opt.quiet)
opt.quiet = 0;
if (opt.debug & DBG_MPI_VALUE)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
}
/* Helper for cleanup to remove one socket with NAME. REDIR_NAME is
the corresponding real name if the socket has been redirected. */
static void
remove_socket (char *name, char *redir_name)
{
if (name && *name)
{
if (redir_name)
name = redir_name;
gnupg_remove (name);
*name = 0;
}
}
/* Cleanup code for this program. This is either called has an atexit
handler or directly. */
static void
cleanup (void)
{
static int done;
if (done)
return;
done = 1;
deinitialize_module_cache ();
remove_socket (socket_name, redir_socket_name);
if (opt.extra_socket > 1)
remove_socket (socket_name_extra, redir_socket_name_extra);
if (opt.browser_socket > 1)
remove_socket (socket_name_browser, redir_socket_name_browser);
remove_socket (socket_name_ssh, redir_socket_name_ssh);
}
/* Handle options which are allowed to be reset after program start.
Return true when the current option in PARGS could be handled and
false if not. As a special feature, passing a value of NULL for
PARGS, resets the options to the default. REREAD should be set
true if it is not the initial option parsing. */
static int
parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
{
if (!pargs)
{ /* reset mode */
opt.quiet = 0;
opt.verbose = 0;
opt.debug = 0;
opt.no_grab = 0;
opt.debug_pinentry = 0;
opt.pinentry_program = NULL;
opt.pinentry_touch_file = NULL;
xfree (opt.pinentry_invisible_char);
opt.pinentry_invisible_char = NULL;
opt.pinentry_timeout = 0;
opt.scdaemon_program = NULL;
opt.def_cache_ttl = DEFAULT_CACHE_TTL;
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
opt.max_cache_ttl = MAX_CACHE_TTL;
opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
opt.enforce_passphrase_constraints = 0;
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
opt.check_passphrase_pattern = NULL;
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
opt.enable_passhrase_history = 0;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 1;
opt.allow_external_cache = 1;
opt.allow_loopback_pinentry = 1;
opt.allow_emacs_pinentry = 0;
opt.disable_scdaemon = 0;
disable_check_own_socket = 0;
return 1;
}
switch (pargs->r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oDebug:
parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs->r.ret_str; break;
case oDebugPinentry: opt.debug_pinentry = 1; break;
case oLogFile:
if (!reread)
return 0; /* not handeld */
if (!current_logfile || !pargs->r.ret_str
|| strcmp (current_logfile, pargs->r.ret_str))
{
log_set_file (pargs->r.ret_str);
xfree (current_logfile);
current_logfile = xtrystrdup (pargs->r.ret_str);
}
break;
case oNoGrab: opt.no_grab = 1; break;
case oPinentryProgram: opt.pinentry_program = pargs->r.ret_str; break;
case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
case oPinentryInvisibleChar:
xfree (opt.pinentry_invisible_char);
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 oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break;
case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break;
case oEnforcePassphraseConstraints:
opt.enforce_passphrase_constraints=1;
break;
case oMinPassphraseLen: opt.min_passphrase_len = pargs->r.ret_ulong; break;
case oMinPassphraseNonalpha:
opt.min_passphrase_nonalpha = pargs->r.ret_ulong;
break;
case oCheckPassphrasePattern:
opt.check_passphrase_pattern = pargs->r.ret_str;
break;
case oMaxPassphraseDays:
opt.max_passphrase_days = pargs->r.ret_ulong;
break;
case oEnablePassphraseHistory:
opt.enable_passhrase_history = 1;
break;
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
case oNoAllowMarkTrusted: opt.allow_mark_trusted = 0; break;
case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
case oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break;
case oNoAllowLoopbackPinentry: opt.allow_loopback_pinentry = 0; break;
case oNoAllowExternalCache: opt.allow_external_cache = 0;
break;
case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
break;
default:
return 0; /* not handled */
}
return 1; /* handled */
}
/* Fixup some options after all have been processed. */
static void
finalize_rereadable_options (void)
{
}
/* The main entry point. */
int
main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int orig_argc;
char **orig_argv;
FILE *configfp = NULL;
char *configname = NULL;
const char *shell;
unsigned configlineno;
int parse_debug = 0;
int default_config =1;
int pipe_server = 0;
int is_daemon = 0;
int nodetach = 0;
int csh_style = 0;
char *logfile = NULL;
int debug_wait = 0;
int gpgconf_list = 0;
gpg_error_t err;
struct assuan_malloc_hooks malloc_hooks;
early_system_init ();
/* Before we do anything else we save the list of currently open
file descriptors and the signal mask. This info is required to
do the exec call properly. */
startup_fd_list = get_all_open_fds ();
#ifdef HAVE_SIGPROCMASK
if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask))
startup_signal_mask_valid = 1;
#endif /*HAVE_SIGPROCMASK*/
/* Set program name etc. */
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* Please note that we may running SUID(ROOT), so be very CAREFUL
when adding any stuff between here and the call to INIT_SECMEM()
somewhere after the option parsing */
log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
npth_init ();
- /* Check that the libraries are suitable. Do it here because
- the option parsing may need services of the library. */
- if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
- {
- log_fatal( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
- NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
- }
-
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
assuan_sock_init ();
setup_libassuan_logging (&opt.debug);
setup_libgcrypt_logging ();
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL);
disable_core_dumps ();
/* Set default options. */
parse_rereadable_options (NULL, 0); /* Reset them to default values. */
shell = getenv ("SHELL");
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
csh_style = 1;
/* Record some of the original environment strings. */
{
const char *s;
int idx;
static const char *names[] =
{ "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL };
err = 0;
opt.startup_env = session_env_new ();
if (!opt.startup_env)
err = gpg_error_from_syserror ();
for (idx=0; !err && names[idx]; idx++)
{
s = getenv (names[idx]);
if (s)
err = session_env_setenv (opt.startup_env, names[idx], s);
}
if (!err)
{
s = gnupg_ttyname (0);
if (s)
err = session_env_setenv (opt.startup_env, "GPG_TTY", s);
}
if (err)
log_fatal ("error recording startup environment: %s\n",
gpg_strerror (err));
/* Fixme: Better use the locale function here. */
opt.startup_lc_ctype = getenv ("LC_CTYPE");
if (opt.startup_lc_ctype)
opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype);
opt.startup_lc_messages = getenv ("LC_MESSAGES");
if (opt.startup_lc_messages)
opt.startup_lc_messages = xstrdup (opt.startup_lc_messages);
}
/* Check whether we have a config file on the commandline */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
while (arg_parse( &pargs, opts))
{
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
parse_debug++;
else if (pargs.r_opt == oOptions)
{ /* yes there is one, so we do not try the default one, but
read the option file when it is encountered at the
commandline */
default_config = 0;
}
else if (pargs.r_opt == oNoOptions)
default_config = 0; /* --no-options */
else if (pargs.r_opt == oHomedir)
gnupg_set_homedir (pargs.r.ret_str);
else if (pargs.r_opt == oDebugQuickRandom)
{
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
}
}
/* Initialize the secure memory. */
gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
maybe_setuid = 0;
/*
Now we are now working under our real uid
*/
if (default_config)
configname = make_filename (gnupg_homedir (),
GPG_AGENT_NAME EXTSEP_S "conf", NULL);
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
next_pass:
if (configname)
{
configlineno = 0;
configfp = fopen (configname, "r");
if (!configfp)
{
if (default_config)
{
if( parse_debug )
log_info (_("Note: no default option file '%s'\n"),
configname );
/* Save the default conf file name so that
reread_configuration is able to test whether the
config file has been created in the meantime. */
xfree (config_filename);
config_filename = configname;
configname = NULL;
}
else
{
log_error (_("option file '%s': %s\n"),
configname, strerror(errno) );
exit(2);
}
xfree (configname);
configname = NULL;
}
if (parse_debug && configname )
log_info (_("reading options from '%s'\n"), configname );
default_config = 0;
}
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
{
if (parse_rereadable_options (&pargs, 0))
continue; /* Already handled */
switch (pargs.r_opt)
{
case aGPGConfList: gpgconf_list = 1; break;
case aGPGConfTest: gpgconf_list = 2; break;
case aUseStandardSocketP: gpgconf_list = 3; break;
case oBatch: opt.batch=1; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break;
case oOptions:
/* config files may not be nested (silently ignore them) */
if (!configfp)
{
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoGreeting: /* Dummy option. */ break;
case oNoVerbose: opt.verbose = 0; break;
case oNoOptions: break; /* no-options */
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oNoDetach: nodetach = 1; break;
case oLogFile: logfile = pargs.r.ret_str; break;
case oCsh: csh_style = 1; break;
case oSh: csh_style = 0; break;
case oServer: pipe_server = 1; break;
case oDaemon: is_daemon = 1; break;
case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
case oTTYtype: default_ttytype = xstrdup (pargs.r.ret_str); break;
case oLCctype: default_lc_ctype = xstrdup (pargs.r.ret_str); break;
case oLCmessages: default_lc_messages = xstrdup (pargs.r.ret_str);
break;
case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
break;
case oUseStandardSocket:
case oNoUseStandardSocket:
obsolete_option (configname, configlineno, "use-standard-socket");
break;
case oFakedSystemTime:
{
time_t faked_time = isotime2epoch (pargs.r.ret_str);
if (faked_time == (time_t)(-1))
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
gnupg_set_time (faked_time, 0);
}
break;
case oKeepTTY: opt.keep_tty = 1; break;
case oKeepDISPLAY: opt.keep_display = 1; break;
case oSSHSupport:
ssh_support = 1;
break;
case oPuttySupport:
# ifdef HAVE_W32_SYSTEM
putty_support = 1;
# endif
break;
case oExtraSocket:
opt.extra_socket = 1; /* (1 = points into argv) */
socket_name_extra = pargs.r.ret_str;
break;
case oBrowserSocket:
opt.browser_socket = 1; /* (1 = points into argv) */
socket_name_browser = pargs.r.ret_str;
break;
case oDebugQuickRandom:
/* Only used by the first stage command line parser. */
break;
case oWriteEnvFile:
obsolete_option (configname, configlineno, "write-env-file");
break;
default : pargs.err = configfp? 1:2; break;
}
}
if (configfp)
{
fclose( configfp );
configfp = NULL;
/* Keep a copy of the name so that it can be read on SIGHUP. */
if (config_filename != configname)
{
xfree (config_filename);
config_filename = configname;
}
configname = NULL;
goto next_pass;
}
xfree (configname);
configname = NULL;
if (log_get_errorcount(0))
exit(2);
finalize_rereadable_options ();
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
#ifdef ENABLE_NLS
/* gpg-agent usually does not output any messages because it runs in
the background. For log files it is acceptable to have messages
always encoded in utf-8. We switch here to utf-8, so that
commands like --help still give native messages. It is far
easier to switch only once instead of for every message and it
actually helps when more then one thread is active (avoids an
extra copy step). */
bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
#endif
if (!pipe_server && !is_daemon && !gpgconf_list)
{
/* We have been called without any options and thus we merely
check whether an agent is already running. We do this right
here so that we don't clobber a logfile with this check but
print the status directly to stderr. */
opt.debug = 0;
set_debug ();
check_for_running_agent (0);
agent_exit (0);
}
set_debug ();
if (atexit (cleanup))
{
log_error ("atexit failed\n");
cleanup ();
exit (1);
}
initialize_module_cache ();
initialize_module_call_pinentry ();
initialize_module_call_scd ();
initialize_module_trustlist ();
/* Try to create missing directories. */
create_directories ();
if (debug_wait && pipe_server)
{
log_debug ("waiting for debugger - my pid is %u .....\n",
(unsigned int)getpid());
gnupg_sleep (debug_wait);
log_debug ("... okay\n");
}
if (gpgconf_list == 3)
{
/* We now use the standard socket always - return true for
backward compatibility. */
agent_exit (0);
}
else if (gpgconf_list == 2)
agent_exit (0);
else if (gpgconf_list)
{
char *filename;
char *filename_esc;
/* List options and default values in the GPG Conf format. */
filename = make_filename (gnupg_homedir (),
GPG_AGENT_NAME EXTSEP_S "conf", NULL);
filename_esc = percent_escape (filename, NULL);
es_printf ("%s-%s.conf:%lu:\"%s\n",
GPGCONF_NAME, GPG_AGENT_NAME,
GC_OPT_FLAG_DEFAULT, filename_esc);
xfree (filename);
xfree (filename_esc);
es_printf ("verbose:%lu:\n"
"quiet:%lu:\n"
"debug-level:%lu:\"none:\n"
"log-file:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
es_printf ("default-cache-ttl:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL );
es_printf ("default-cache-ttl-ssh:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL_SSH );
es_printf ("max-cache-ttl:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
es_printf ("max-cache-ttl-ssh:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
es_printf ("enforce-passphrase-constraints:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("min-passphrase-len:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
es_printf ("min-passphrase-nonalpha:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
MIN_PASSPHRASE_NONALPHA);
es_printf ("check-passphrase-pattern:%lu:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
es_printf ("max-passphrase-days:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
MAX_PASSPHRASE_DAYS);
es_printf ("enable-passphrase-history:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("ignore-cache-for-signing:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-allow-external-cache:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("no-allow-mark-trusted:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("disable-scdaemon:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
#ifdef HAVE_W32_SYSTEM
es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
#endif
es_printf ("no-allow-loopback-pinentry:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("allow-emacs-pinentry:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
es_printf ("pinentry-timeout:%lu:0:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
agent_exit (0);
}
/* Now start with logging to a file if this is desired. */
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
| GPGRT_LOG_WITH_TIME
| GPGRT_LOG_WITH_PID));
current_logfile = xstrdup (logfile);
}
/* Make sure that we have a default ttyname. */
if (!default_ttyname && gnupg_ttyname (1))
default_ttyname = xstrdup (gnupg_ttyname (1));
if (!default_ttytype && getenv ("TERM"))
default_ttytype = xstrdup (getenv ("TERM"));
if (pipe_server)
{
/* This is the simple pipe based server */
ctrl_t ctrl;
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
agent_exit (1);
}
ctrl->session_env = session_env_new ();
if (!ctrl->session_env)
{
log_error ("error allocating session environment block: %s\n",
strerror (errno) );
xfree (ctrl);
agent_exit (1);
}
agent_init_default_ctrl (ctrl);
start_command_handler (ctrl, GNUPG_INVALID_FD, GNUPG_INVALID_FD);
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
}
else if (!is_daemon)
; /* NOTREACHED */
else
{ /* Regular server mode */
gnupg_fd_t fd;
gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
#ifndef HAVE_W32_SYSTEM
pid_t pid;
#endif
/* Remove the DISPLAY variable so that a pinentry does not
default to a specific display. There is still a default
display when gpg-agent was started using --display or a
client requested this using an OPTION command. Note, that we
don't do this when running in reverse daemon mode (i.e. when
exec the program given as arguments). */
#ifndef HAVE_W32_SYSTEM
if (!opt.keep_display && !argc)
gnupg_unsetenv ("DISPLAY");
#endif
/* Remove the INSIDE_EMACS variable so that a pinentry does not
always try to interact with Emacs. The variable is set when
a client requested this using an OPTION command. */
gnupg_unsetenv ("INSIDE_EMACS");
/* Create the sockets. */
socket_name = create_socket_name (GPG_AGENT_SOCK_NAME, 1);
fd = create_server_socket (socket_name, 1, 0,
&redir_socket_name, &socket_nonce);
if (opt.extra_socket)
{
socket_name_extra = create_socket_name (socket_name_extra, 0);
opt.extra_socket = 2; /* Indicate that it has been malloced. */
fd_extra = create_server_socket (socket_name_extra, 0, 0,
&redir_socket_name_extra,
&socket_nonce_extra);
}
if (opt.browser_socket)
{
socket_name_browser = create_socket_name (socket_name_browser, 0);
opt.browser_socket = 2; /* Indicate that it has been malloced. */
fd_browser = create_server_socket (socket_name_browser, 0, 0,
&redir_socket_name_browser,
&socket_nonce_browser);
}
if (ssh_support)
{
socket_name_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME, 1);
fd_ssh = create_server_socket (socket_name_ssh, 0, 1,
&redir_socket_name_ssh,
&socket_nonce_ssh);
}
/* If we are going to exec a program in the parent, we record
the PID, so that the child may check whether the program is
still alive. */
if (argc)
parent_pid = getpid ();
fflush (NULL);
#ifdef HAVE_W32_SYSTEM
(void)csh_style;
(void)nodetach;
#else /*!HAVE_W32_SYSTEM*/
pid = fork ();
if (pid == (pid_t)-1)
{
log_fatal ("fork failed: %s\n", strerror (errno) );
exit (1);
}
else if (pid)
{ /* We are the parent */
char *infostr_ssh_sock, *infostr_ssh_valid;
/* Close the socket FD. */
close (fd);
/* The signal mask might not be correct right now and thus
we restore it. That is not strictly necessary but some
programs falsely assume a cleared signal mask. */
#ifdef HAVE_SIGPROCMASK
if (startup_signal_mask_valid)
{
if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
log_error ("error restoring signal mask: %s\n",
strerror (errno));
}
else
log_info ("no saved signal mask\n");
#endif /*HAVE_SIGPROCMASK*/
/* Create the SSH info string if enabled. */
if (ssh_support)
{
if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
socket_name_ssh) < 0)
{
log_error ("out of core\n");
kill (pid, SIGTERM);
exit (1);
}
if (asprintf (&infostr_ssh_valid, "gnupg_SSH_AUTH_SOCK_by=%lu",
(unsigned long)getpid()) < 0)
{
log_error ("out of core\n");
kill (pid, SIGTERM);
exit (1);
}
}
*socket_name = 0; /* Don't let cleanup() remove the socket -
the child should do this from now on */
if (opt.extra_socket)
*socket_name_extra = 0;
if (opt.browser_socket)
*socket_name_browser = 0;
if (ssh_support)
*socket_name_ssh = 0;
if (argc)
{ /* Run the program given on the commandline. */
if (ssh_support && (putenv (infostr_ssh_sock)
|| putenv (infostr_ssh_valid)))
{
log_error ("failed to set environment: %s\n",
strerror (errno) );
kill (pid, SIGTERM );
exit (1);
}
/* Close all the file descriptors except the standard
ones and those open at startup. We explicitly don't
close 0,1,2 in case something went wrong collecting
them at startup. */
close_all_fds (3, startup_fd_list);
/* Run the command. */
execvp (argv[0], argv);
log_error ("failed to run the command: %s\n", strerror (errno));
kill (pid, SIGTERM);
exit (1);
}
else
{
/* Print the environment string, so that the caller can use
shell's eval to set it */
if (csh_style)
{
if (ssh_support)
{
*strchr (infostr_ssh_sock, '=') = ' ';
es_printf ("setenv %s;\n", infostr_ssh_sock);
}
}
else
{
if (ssh_support)
{
es_printf ("%s; export SSH_AUTH_SOCK;\n",
infostr_ssh_sock);
}
}
if (ssh_support)
{
xfree (infostr_ssh_sock);
xfree (infostr_ssh_valid);
}
exit (0);
}
/*NOTREACHED*/
} /* End parent */
/*
This is the child
*/
/* Detach from tty and put process into a new session */
if (!nodetach )
{
int i;
unsigned int oldflags;
/* Close stdin, stdout and stderr unless it is the log stream */
for (i=0; i <= 2; i++)
{
if (!log_test_fd (i) && i != fd )
{
if ( ! close (i)
&& open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
{
log_error ("failed to open '%s': %s\n",
"/dev/null", strerror (errno));
cleanup ();
exit (1);
}
}
}
if (setsid() == -1)
{
log_error ("setsid() failed: %s\n", strerror(errno) );
cleanup ();
exit (1);
}
log_get_prefix (&oldflags);
log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
opt.running_detached = 1;
}
if (chdir("/"))
{
log_error ("chdir to / failed: %s\n", strerror (errno));
exit (1);
}
{
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
sigaction (SIGPIPE, &sa, NULL);
}
#endif /*!HAVE_W32_SYSTEM*/
log_info ("%s %s started\n", strusage(11), strusage(13) );
handle_connections (fd, fd_extra, fd_browser, fd_ssh);
assuan_sock_close (fd);
}
return 0;
}
/* Exit entry point. This function should be called instead of a
plain exit. */
void
agent_exit (int rc)
{
/*FIXME: update_random_seed_file();*/
/* We run our cleanup handler because that may close cipher contexts
stored in secure memory and thus this needs to be done before we
explicitly terminate secure memory. */
cleanup ();
#if 1
/* at this time a bit annoying */
if (opt.debug & DBG_MEMSTAT_VALUE)
{
gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
}
if (opt.debug)
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
#endif
gcry_control (GCRYCTL_TERM_SECMEM );
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
exit (rc);
}
/* This is our callback function for gcrypt progress messages. It is
set once at startup and dispatches progress messages to the
corresponding threads of the agent. */
static void
agent_libgcrypt_progress_cb (void *data, const char *what, int printchar,
int current, int total)
{
struct progress_dispatch_s *dispatch;
npth_t mytid = npth_self ();
(void)data;
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (dispatch && dispatch->cb)
dispatch->cb (dispatch->ctrl, what, printchar, current, total);
}
/* If a progress dispatcher callback has been associated with the
* current connection unregister it. */
static void
unregister_progress_cb (void)
{
struct progress_dispatch_s *dispatch;
npth_t mytid = npth_self ();
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (dispatch)
{
dispatch->ctrl = NULL;
dispatch->cb = NULL;
}
}
/* Setup a progress callback CB for the current connection. Using a
* CB of NULL disables the callback. */
void
agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what,
int printchar, int current, int total),
ctrl_t ctrl)
{
struct progress_dispatch_s *dispatch, *firstfree;
npth_t mytid = npth_self ();
firstfree = NULL;
for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next)
{
if (dispatch->ctrl && dispatch->tid == mytid)
break;
if (!dispatch->ctrl && !firstfree)
firstfree = dispatch;
}
if (!dispatch) /* None allocated: Reuse or allocate a new one. */
{
if (firstfree)
{
dispatch = firstfree;
}
else if ((dispatch = xtrycalloc (1, sizeof *dispatch)))
{
dispatch->next = progress_dispatch_list;
progress_dispatch_list = dispatch;
}
else
{
log_error ("error allocating new progress dispatcher slot: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
return;
}
dispatch->ctrl = ctrl;
dispatch->tid = mytid;
}
dispatch->cb = cb;
}
/* Each thread has its own local variables conveyed by a control
structure usually identified by an argument named CTRL. This
function is called immediately after allocating the control
structure. Its purpose is to setup the default values for that
structure. Note that some values may have already been set. */
static void
agent_init_default_ctrl (ctrl_t ctrl)
{
assert (ctrl->session_env);
/* Note we ignore malloc errors because we can't do much about it
and the request will fail anyway shortly after this
initialization. */
session_env_setenv (ctrl->session_env, "DISPLAY", default_display);
session_env_setenv (ctrl->session_env, "GPG_TTY", default_ttyname);
session_env_setenv (ctrl->session_env, "TERM", default_ttytype);
session_env_setenv (ctrl->session_env, "XAUTHORITY", default_xauthority);
session_env_setenv (ctrl->session_env, "PINENTRY_USER_DATA", NULL);
if (ctrl->lc_ctype)
xfree (ctrl->lc_ctype);
ctrl->lc_ctype = default_lc_ctype? xtrystrdup (default_lc_ctype) : NULL;
if (ctrl->lc_messages)
xfree (ctrl->lc_messages);
ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages)
/**/ : NULL;
ctrl->cache_ttl_opt_preset = CACHE_TTL_OPT_PRESET;
}
/* Release all resources allocated by default in the control
structure. This is the counterpart to agent_init_default_ctrl. */
static void
agent_deinit_default_ctrl (ctrl_t ctrl)
{
unregister_progress_cb ();
session_env_release (ctrl->session_env);
if (ctrl->lc_ctype)
xfree (ctrl->lc_ctype);
if (ctrl->lc_messages)
xfree (ctrl->lc_messages);
}
/* Because the ssh protocol does not send us information about the
current TTY setting, we use this function to use those from startup
or those explicitly set. This is also used for the restricted mode
where we ignore requests to change the environment. */
gpg_error_t
agent_copy_startup_env (ctrl_t ctrl)
{
static const char *names[] =
{"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL};
gpg_error_t err = 0;
int idx;
const char *value;
for (idx=0; !err && names[idx]; idx++)
if ((value = session_env_getenv (opt.startup_env, names[idx])))
err = session_env_setenv (ctrl->session_env, names[idx], value);
if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype)
if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype)))
err = gpg_error_from_syserror ();
if (!err && !ctrl->lc_messages && opt.startup_lc_messages)
if (!(ctrl->lc_messages = xtrystrdup (opt.startup_lc_messages)))
err = gpg_error_from_syserror ();
if (err)
log_error ("error setting default session environment: %s\n",
gpg_strerror (err));
return err;
}
/* Reread parts of the configuration. Note, that this function is
obviously not thread-safe and should only be called from the PTH
signal handler.
Fixme: Due to the way the argument parsing works, we create a
memory leak here for all string type arguments. There is currently
no clean way to tell whether the memory for the argument has been
allocated or points into the process' original arguments. Unless
we have a mechanism to tell this, we need to live on with this. */
static void
reread_configuration (void)
{
ARGPARSE_ARGS pargs;
FILE *fp;
unsigned int configlineno = 0;
int dummy;
if (!config_filename)
return; /* No config file. */
fp = fopen (config_filename, "r");
if (!fp)
{
log_info (_("option file '%s': %s\n"),
config_filename, strerror(errno) );
return;
}
parse_rereadable_options (NULL, 1); /* Start from the default values. */
memset (&pargs, 0, sizeof pargs);
dummy = 0;
pargs.argc = &dummy;
pargs.flags = 1; /* do not remove the args */
while (optfile_parse (fp, config_filename, &configlineno, &pargs, opts) )
{
if (pargs.r_opt < -1)
pargs.err = 1; /* Print a warning. */
else /* Try to parse this option - ignore unchangeable ones. */
parse_rereadable_options (&pargs, 1);
}
fclose (fp);
finalize_rereadable_options ();
set_debug ();
}
/* Return the file name of the socket we are using for native
requests. */
const char *
get_agent_socket_name (void)
{
const char *s = socket_name;
return (s && *s)? s : NULL;
}
/* Return the file name of the socket we are using for SSH
requests. */
const char *
get_agent_ssh_socket_name (void)
{
const char *s = socket_name_ssh;
return (s && *s)? s : NULL;
}
/* Return the number of active connections. */
int
get_agent_active_connection_count (void)
{
return active_connections;
}
/* Under W32, this function returns the handle of the scdaemon
notification event. Calling it the first time creates that
event. */
#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
void *
get_agent_scd_notify_event (void)
{
static HANDLE the_event = INVALID_HANDLE_VALUE;
if (the_event == INVALID_HANDLE_VALUE)
{
HANDLE h, h2;
SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
/* We need to use a manual reset event object due to the way our
w32-pth wait function works: If we would use an automatic
reset event we are not able to figure out which handle has
been signaled because at the time we single out the signaled
handles using WFSO the event has already been reset due to
the WFMO. */
h = CreateEvent (&sa, TRUE, FALSE, NULL);
if (!h)
log_error ("can't create scd notify event: %s\n", w32_strerror (-1) );
else if (!DuplicateHandle (GetCurrentProcess(), h,
GetCurrentProcess(), &h2,
EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
{
log_error ("setting syncronize for scd notify event failed: %s\n",
w32_strerror (-1) );
CloseHandle (h);
}
else
{
CloseHandle (h);
the_event = h2;
}
}
return the_event;
}
#endif /*HAVE_W32_SYSTEM && !HAVE_W32CE_SYSTEM*/
/* Create a name for the socket in the home directory as using
STANDARD_NAME. We also check for valid characters as well as
against a maximum allowed length for a unix domain socket is done.
The function terminates the process in case of an error. Returns:
Pointer to an allocated string with the absolute name of the socket
used. */
static char *
create_socket_name (char *standard_name, int with_homedir)
{
char *name;
if (with_homedir)
name = make_filename (gnupg_socketdir (), standard_name, NULL);
else
name = make_filename (standard_name, NULL);
if (strchr (name, PATHSEP_C))
{
log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
agent_exit (2);
}
return name;
}
/* Create a Unix domain socket with NAME. Returns the file descriptor
or terminates the process in case of an error. Note that this
function needs to be used for the regular socket first (indicated
by PRIMARY) and only then for the extra and the ssh sockets. If
the socket has been redirected the name of the real socket is
stored as a malloced string at R_REDIR_NAME. If CYGWIN is set a
Cygwin compatible socket is created (Windows only). */
static gnupg_fd_t
create_server_socket (char *name, int primary, int cygwin,
char **r_redir_name, assuan_sock_nonce_t *nonce)
{
struct sockaddr *addr;
struct sockaddr_un *unaddr;
socklen_t len;
gnupg_fd_t fd;
int rc;
xfree (*r_redir_name);
*r_redir_name = NULL;
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD)
{
log_error (_("can't create socket: %s\n"), strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
if (cygwin)
assuan_sock_set_flag (fd, "cygwin", 1);
unaddr = xmalloc (sizeof *unaddr);
addr = (struct sockaddr*)unaddr;
{
int redirected;
if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
{
if (errno == ENAMETOOLONG)
log_error (_("socket name '%s' is too long\n"), name);
else
log_error ("error preparing socket '%s': %s\n",
name, gpg_strerror (gpg_error_from_syserror ()));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
if (redirected)
{
*r_redir_name = xstrdup (unaddr->sun_path);
if (opt.verbose)
log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
}
}
len = SUN_LEN (unaddr);
rc = assuan_sock_bind (fd, addr, len);
/* Our error code mapping on W32CE returns EEXIST thus we also test
for this. */
if (rc == -1
&& (errno == EADDRINUSE
#ifdef HAVE_W32_SYSTEM
|| errno == EEXIST
#endif
))
{
/* Check whether a gpg-agent is already running. We do this
test only if this is the primary socket. For secondary
sockets we assume that a test for gpg-agent has already been
done and reuse the requested socket. Testing the ssh-socket
is not possible because at this point, though we know the new
Assuan socket, the Assuan server and thus the ssh-agent
server is not yet operational; this would lead to a hang. */
if (primary && !check_for_running_agent (1))
{
log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX);
log_set_file (NULL);
log_error (_("a gpg-agent is already running - "
"not starting a new one\n"));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
assuan_sock_close (fd);
agent_exit (2);
}
gnupg_remove (unaddr->sun_path);
rc = assuan_sock_bind (fd, addr, len);
}
if (rc != -1 && (rc=assuan_sock_get_nonce (addr, len, nonce)))
log_error (_("error getting nonce for the socket\n"));
if (rc == -1)
{
/* We use gpg_strerror here because it allows us to get strings
for some W32 socket error codes. */
log_error (_("error binding socket to '%s': %s\n"),
unaddr->sun_path,
gpg_strerror (gpg_error_from_syserror ()));
assuan_sock_close (fd);
*name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
if (gnupg_chmod (unaddr->sun_path, "-rwx"))
log_error (_("can't set permissions of '%s': %s\n"),
unaddr->sun_path, strerror (errno));
if (listen (FD2INT(fd), 5 ) == -1)
{
log_error (_("listen() failed: %s\n"), strerror (errno));
*name = 0; /* Inhibit removal of the socket by cleanup(). */
assuan_sock_close (fd);
agent_exit (2);
}
if (opt.verbose)
log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
return fd;
}
/* Check that the directory for storing the private keys exists and
create it if not. This function won't fail as it is only a
convenience function and not strictly necessary. */
static void
create_private_keys_directory (const char *home)
{
char *fname;
struct stat statbuf;
fname = make_filename (home, GNUPG_PRIVATE_KEYS_DIR, NULL);
if (stat (fname, &statbuf) && errno == ENOENT)
{
if (gnupg_mkdir (fname, "-rwx"))
log_error (_("can't create directory '%s': %s\n"),
fname, strerror (errno) );
else if (!opt.quiet)
log_info (_("directory '%s' created\n"), fname);
}
if (gnupg_chmod (fname, "-rwx"))
log_error (_("can't set permissions of '%s': %s\n"),
fname, strerror (errno));
xfree (fname);
}
/* Create the directory only if the supplied directory name is the
same as the default one. This way we avoid to create arbitrary
directories when a non-default home directory is used. To cope
with HOME, we compare only the suffix if we see that the default
homedir does start with a tilde. We don't stop here in case of
problems because other functions will throw an error anyway.*/
static void
create_directories (void)
{
struct stat statbuf;
const char *defhome = standard_homedir ();
char *home;
home = make_filename (gnupg_homedir (), NULL);
if ( stat (home, &statbuf) )
{
if (errno == ENOENT)
{
if (
#ifdef HAVE_W32_SYSTEM
( !compare_filenames (home, defhome) )
#else
(*defhome == '~'
&& (strlen (home) >= strlen (defhome+1)
&& !strcmp (home + strlen(home)
- strlen (defhome+1), defhome+1)))
|| (*defhome != '~' && !strcmp (home, defhome) )
#endif
)
{
if (gnupg_mkdir (home, "-rwx"))
log_error (_("can't create directory '%s': %s\n"),
home, strerror (errno) );
else
{
if (!opt.quiet)
log_info (_("directory '%s' created\n"), home);
create_private_keys_directory (home);
}
}
}
else
log_error (_("stat() failed for '%s': %s\n"), home, strerror (errno));
}
else if ( !S_ISDIR(statbuf.st_mode))
{
log_error (_("can't use '%s' as home directory\n"), home);
}
else /* exists and is a directory. */
{
create_private_keys_directory (home);
}
xfree (home);
}
/* This is the worker for the ticker. It is called every few seconds
and may only do fast operations. */
static void
handle_tick (void)
{
static time_t last_minute;
if (!last_minute)
last_minute = time (NULL);
/* Check whether the scdaemon has died and cleanup in this case. */
agent_scd_check_aliveness ();
/* If we are running as a child of another process, check whether
the parent is still alive and shutdown if not. */
#ifndef HAVE_W32_SYSTEM
if (parent_pid != (pid_t)(-1))
{
if (kill (parent_pid, 0))
{
shutdown_pending = 2;
log_info ("parent process died - shutting down\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
agent_exit (0);
}
}
#endif /*HAVE_W32_SYSTEM*/
/* Code to be run from time to time. */
#if CHECK_OWN_SOCKET_INTERVAL > 0
if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL))
{
check_own_socket ();
last_minute = time (NULL);
}
#endif
}
/* A global function which allows us to call the reload stuff from
other places too. This is only used when build for W32. */
void
agent_sighup_action (void)
{
log_info ("SIGHUP received - "
"re-reading configuration and flushing cache\n");
agent_flush_cache ();
reread_configuration ();
agent_reload_trustlist ();
/* We flush the module name cache so that after installing a
"pinentry" binary that one can be used in case the
"pinentry-basic" fallback was in use. */
gnupg_module_name_flush_some ();
}
/* A helper function to handle SIGUSR2. */
static void
agent_sigusr2_action (void)
{
if (opt.verbose)
log_info ("SIGUSR2 received - updating card event counter\n");
/* Nothing to check right now. We only increment a counter. */
bump_card_eventcounter ();
}
#ifndef HAVE_W32_SYSTEM
/* The signal handler for this program. It is expected to be run in
its own trhead and not in the context of a signal handler. */
static void
handle_signal (int signo)
{
switch (signo)
{
#ifndef HAVE_W32_SYSTEM
case SIGHUP:
agent_sighup_action ();
break;
case SIGUSR1:
log_info ("SIGUSR1 received - printing internal information:\n");
/* Fixme: We need to see how to integrate pth dumping into our
logging system. */
/* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
agent_query_dump_state ();
agent_scd_dump_state ();
break;
case SIGUSR2:
agent_sigusr2_action ();
break;
case SIGTERM:
if (!shutdown_pending)
log_info ("SIGTERM received - shutting down ...\n");
else
log_info ("SIGTERM received - still %i open connections\n",
active_connections);
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info ("shutdown forced\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
agent_exit (0);
}
break;
case SIGINT:
log_info ("SIGINT received - immediate shutdown\n");
log_info( "%s %s stopped\n", strusage(11), strusage(13));
cleanup ();
agent_exit (0);
break;
#endif
default:
log_info ("signal %d received - no action defined\n", signo);
}
}
#endif
/* Check the nonce on a new connection. This is a NOP unless we we
are using our Unix domain socket emulation under Windows. */
static int
check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce)
{
if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce))
{
log_info (_("error reading nonce on fd %d: %s\n"),
FD2INT(ctrl->thread_startup.fd), strerror (errno));
assuan_sock_close (ctrl->thread_startup.fd);
xfree (ctrl);
return -1;
}
else
return 0;
}
#ifdef HAVE_W32_SYSTEM
/* The window message processing function for Putty. Warning: This
code runs as a native Windows thread. Use of our own functions
needs to be bracket with pth_leave/pth_enter. */
static LRESULT CALLBACK
putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
int ret = 0;
int w32rc;
COPYDATASTRUCT *cds;
const char *mapfile;
HANDLE maphd;
PSID mysid = NULL;
PSID mapsid = NULL;
void *data = NULL;
PSECURITY_DESCRIPTOR psd = NULL;
ctrl_t ctrl = NULL;
if (msg != WM_COPYDATA)
{
return DefWindowProc (hwnd, msg, wparam, lparam);
}
cds = (COPYDATASTRUCT*)lparam;
if (cds->dwData != PUTTY_IPC_MAGIC)
return 0; /* Ignore data with the wrong magic. */
mapfile = cds->lpData;
if (!cds->cbData || mapfile[cds->cbData - 1])
return 0; /* Ignore empty and non-properly terminated strings. */
if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map file '%s'", mapfile);
npth_unprotect ();
}
maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map handle %p\n", maphd);
npth_unprotect ();
}
if (!maphd || maphd == INVALID_HANDLE_VALUE)
return 0;
npth_protect ();
mysid = w32_get_user_sid ();
if (!mysid)
{
log_error ("error getting my sid\n");
goto leave;
}
w32rc = GetSecurityInfo (maphd, SE_KERNEL_OBJECT,
OWNER_SECURITY_INFORMATION,
&mapsid, NULL, NULL, NULL,
&psd);
if (w32rc)
{
log_error ("error getting sid of ssh map file: rc=%d", w32rc);
goto leave;
}
if (DBG_IPC)
{
char *sidstr;
if (!ConvertSidToStringSid (mysid, &sidstr))
sidstr = NULL;
log_debug (" my sid: '%s'", sidstr? sidstr: "[error]");
LocalFree (sidstr);
if (!ConvertSidToStringSid (mapsid, &sidstr))
sidstr = NULL;
log_debug ("ssh map file sid: '%s'", sidstr? sidstr: "[error]");
LocalFree (sidstr);
}
if (!EqualSid (mysid, mapsid))
{
log_error ("ssh map file has a non-matching sid\n");
goto leave;
}
data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (DBG_IPC)
log_debug ("ssh IPC buffer at %p\n", data);
if (!data)
goto leave;
/* log_printhex ("request:", data, 20); */
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno) );
goto leave;
}
ctrl->session_env = session_env_new ();
if (!ctrl->session_env)
{
log_error ("error allocating session environment block: %s\n",
strerror (errno) );
goto leave;
}
agent_init_default_ctrl (ctrl);
if (!serve_mmapped_ssh_request (ctrl, data, PUTTY_IPC_MAXLEN))
ret = 1; /* Valid ssh message has been constructed. */
agent_deinit_default_ctrl (ctrl);
/* log_printhex (" reply:", data, 20); */
leave:
xfree (ctrl);
if (data)
UnmapViewOfFile (data);
xfree (mapsid);
if (psd)
LocalFree (psd);
xfree (mysid);
CloseHandle (maphd);
npth_unprotect ();
return ret;
}
#endif /*HAVE_W32_SYSTEM*/
#ifdef HAVE_W32_SYSTEM
/* The thread handling Putty's IPC requests. */
static void *
putty_message_thread (void *arg)
{
WNDCLASS wndwclass = {0, putty_message_proc, 0, 0,
NULL, NULL, NULL, NULL, NULL, "Pageant"};
HWND hwnd;
MSG msg;
(void)arg;
if (opt.verbose)
log_info ("putty message loop thread started\n");
/* The message loop runs as thread independent from our nPth system.
This also means that we need to make sure that we switch back to
our system before calling any no-windows function. */
npth_unprotect ();
/* First create a window to make sure that a message queue exists
for this thread. */
if (!RegisterClass (&wndwclass))
{
npth_protect ();
log_error ("error registering Pageant window class");
return NULL;
}
hwnd = CreateWindowEx (0, "Pageant", "Pageant", 0,
0, 0, 0, 0,
HWND_MESSAGE, /* hWndParent */
NULL, /* hWndMenu */
NULL, /* hInstance */
NULL); /* lpParm */
if (!hwnd)
{
npth_protect ();
log_error ("error creating Pageant window");
return NULL;
}
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* Back to nPth. */
npth_protect ();
if (opt.verbose)
log_info ("putty message loop thread stopped\n");
return NULL;
}
#endif /*HAVE_W32_SYSTEM*/
static void *
do_start_connection_thread (ctrl_t ctrl)
{
active_connections++;
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d started\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d terminated\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
active_connections--;
return NULL;
}
/* This is the standard connection thread's main function. */
static void *
start_connection_thread_std (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
return do_start_connection_thread (ctrl);
}
/* This is the extra socket connection thread's main function. */
static void *
start_connection_thread_extra (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_extra))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
ctrl->restricted = 1;
return do_start_connection_thread (ctrl);
}
/* This is the browser socket connection thread's main function. */
static void *
start_connection_thread_browser (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_browser))
{
log_error ("handler 0x%lx nonce check FAILED\n",
(unsigned long) npth_self());
return NULL;
}
ctrl->restricted = 2;
return do_start_connection_thread (ctrl);
}
/* This is the ssh connection thread's main function. */
static void *
start_connection_thread_ssh (void *arg)
{
ctrl_t ctrl = arg;
if (check_nonce (ctrl, &socket_nonce_ssh))
return NULL;
active_connections++;
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d started\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
start_command_handler_ssh (ctrl, ctrl->thread_startup.fd);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
(unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd));
agent_deinit_default_ctrl (ctrl);
xfree (ctrl);
active_connections--;
return NULL;
}
/* Connection handler loop. Wait for connection requests and spawn a
thread after accepting a connection. */
static void
handle_connections (gnupg_fd_t listen_fd,
gnupg_fd_t listen_fd_extra,
gnupg_fd_t listen_fd_browser,
gnupg_fd_t listen_fd_ssh)
{
npth_attr_t tattr;
struct sockaddr_un paddr;
socklen_t plen;
fd_set fdset, read_fdset;
int ret;
gnupg_fd_t fd;
int nfd;
int saved_errno;
struct timespec abstime;
struct timespec curtime;
struct timespec timeout;
#ifdef HAVE_W32_SYSTEM
HANDLE events[2];
unsigned int events_set;
#endif
struct {
const char *name;
void *(*func) (void *arg);
gnupg_fd_t l_fd;
} listentbl[] = {
{ "std", start_connection_thread_std },
{ "extra", start_connection_thread_extra },
{ "browser", start_connection_thread_browser },
{ "ssh", start_connection_thread_ssh }
};
ret = npth_attr_init(&tattr);
if (ret)
log_fatal ("error allocating thread attributes: %s\n",
strerror (ret));
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
#ifndef HAVE_W32_SYSTEM
npth_sigev_init ();
npth_sigev_add (SIGHUP);
npth_sigev_add (SIGUSR1);
npth_sigev_add (SIGUSR2);
npth_sigev_add (SIGINT);
npth_sigev_add (SIGTERM);
npth_sigev_fini ();
#else
# ifdef HAVE_W32CE_SYSTEM
/* Use a dummy event. */
sigs = 0;
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
# else
events[0] = get_agent_scd_notify_event ();
events[1] = INVALID_HANDLE_VALUE;
# endif
#endif
/* On Windows we need to fire up a separate thread to listen for
requests from Putty (an SSH client), so we can replace Putty's
Pageant (its ssh-agent implementation). */
#ifdef HAVE_W32_SYSTEM
if (putty_support)
{
npth_t thread;
ret = npth_create (&thread, &tattr, putty_message_thread, NULL);
if (ret)
{
log_error ("error spawning putty message loop: %s\n", strerror (ret));
}
}
#endif /*HAVE_W32_SYSTEM*/
/* Set a flag to tell call-scd.c that it may enable event
notifications. */
opt.sigusr2_enabled = 1;
FD_ZERO (&fdset);
FD_SET (FD2INT (listen_fd), &fdset);
nfd = FD2INT (listen_fd);
if (listen_fd_extra != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_extra), &fdset);
if (FD2INT (listen_fd_extra) > nfd)
nfd = FD2INT (listen_fd_extra);
}
if (listen_fd_browser != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_browser), &fdset);
if (FD2INT (listen_fd_browser) > nfd)
nfd = FD2INT (listen_fd_browser);
}
if (listen_fd_ssh != GNUPG_INVALID_FD)
{
FD_SET ( FD2INT(listen_fd_ssh), &fdset);
if (FD2INT (listen_fd_ssh) > nfd)
nfd = FD2INT (listen_fd_ssh);
}
listentbl[0].l_fd = listen_fd;
listentbl[1].l_fd = listen_fd_extra;
listentbl[2].l_fd = listen_fd_browser;
listentbl[3].l_fd = listen_fd_ssh;
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
for (;;)
{
/* Shutdown test. */
if (shutdown_pending)
{
if (active_connections == 0)
break; /* ready */
/* Do not accept new connections but keep on running the
loop to cope with the timer events. */
FD_ZERO (&fdset);
}
/* POSIX says that fd_set should be implemented as a structure,
thus a simple assignment is fine to copy the entire set. */
read_fdset = fdset;
npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <)))
{
/* Timeout. */
handle_tick ();
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
}
npth_timersub (&abstime, &curtime, &timeout);
#ifndef HAVE_W32_SYSTEM
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
npth_sigev_sigmask ());
saved_errno = errno;
{
int signo;
while (npth_sigev_get_pending (&signo))
handle_signal (signo);
}
#else
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
events, &events_set);
saved_errno = errno;
/* This is valid even if npth_eselect returns an error. */
if (events_set & 1)
agent_sigusr2_action ();
#endif
if (ret == -1 && saved_errno != EINTR)
{
log_error (_("npth_pselect failed: %s - waiting 1s\n"),
strerror (saved_errno));
npth_sleep (1);
continue;
}
if (ret <= 0)
/* Interrupt or timeout. Will be handled when calculating the
next timeout. */
continue;
if (!shutdown_pending)
{
int idx;
ctrl_t ctrl;
npth_t thread;
for (idx=0; idx < DIM(listentbl); idx++)
{
if (listentbl[idx].l_fd == GNUPG_INVALID_FD)
continue;
if (!FD_ISSET (FD2INT (listentbl[idx].l_fd), &read_fdset))
continue;
plen = sizeof paddr;
fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd),
(struct sockaddr *)&paddr, &plen));
if (fd == GNUPG_INVALID_FD)
{
log_error ("accept failed for %s: %s\n",
listentbl[idx].name, strerror (errno));
}
else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
{
log_error ("error allocating connection data for %s: %s\n",
listentbl[idx].name, strerror (errno) );
assuan_sock_close (fd);
}
else if ( !(ctrl->session_env = session_env_new ()))
{
log_error ("error allocating session env block for %s: %s\n",
listentbl[idx].name, strerror (errno) );
xfree (ctrl);
assuan_sock_close (fd);
}
else
{
ctrl->thread_startup.fd = fd;
ret = npth_create (&thread, &tattr,
listentbl[idx].func, ctrl);
if (ret)
{
log_error ("error spawning connection handler for %s:"
" %s\n", listentbl[idx].name, strerror (ret));
assuan_sock_close (fd);
xfree (ctrl);
}
}
fd = GNUPG_INVALID_FD;
}
}
}
cleanup ();
log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
npth_attr_destroy (&tattr);
}
/* Helper for check_own_socket. */
static gpg_error_t
check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length)
{
membuf_t *mb = opaque;
put_membuf (mb, buffer, length);
return 0;
}
/* The thread running the actual check. We need to run this in a
separate thread so that check_own_thread can be called from the
timer tick. */
static void *
check_own_socket_thread (void *arg)
{
int rc;
char *sockname = arg;
assuan_context_t ctx = NULL;
membuf_t mb;
char *buffer;
check_own_socket_running++;
rc = assuan_new (&ctx);
if (rc)
{
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
goto leave;
}
rc = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
if (rc)
{
log_error ("can't connect my own socket: %s\n", gpg_strerror (rc));
goto leave;
}
init_membuf (&mb, 100);
rc = assuan_transact (ctx, "GETINFO pid", check_own_socket_pid_cb, &mb,
NULL, NULL, NULL, NULL);
put_membuf (&mb, "", 1);
buffer = get_membuf (&mb, NULL);
if (rc || !buffer)
{
log_error ("sending command \"%s\" to my own socket failed: %s\n",
"GETINFO pid", gpg_strerror (rc));
rc = 1;
}
else if ( (pid_t)strtoul (buffer, NULL, 10) != getpid ())
{
log_error ("socket is now serviced by another server\n");
rc = 1;
}
else if (opt.verbose > 1)
log_error ("socket is still served by this server\n");
xfree (buffer);
leave:
xfree (sockname);
if (ctx)
assuan_release (ctx);
if (rc)
{
/* We may not remove the socket as it is now in use by another
server. Setting the name to empty does this. */
if (socket_name)
*socket_name = 0;
if (socket_name_ssh)
*socket_name_ssh = 0;
shutdown_pending = 2;
log_info ("this process is useless - shutting down\n");
}
check_own_socket_running--;
return NULL;
}
/* Check whether we are still listening on our own socket. In case
another gpg-agent process started after us has taken ownership of
our socket, we would linger around without any real task. Thus we
better check once in a while whether we are really needed. */
static void
check_own_socket (void)
{
char *sockname;
npth_t thread;
npth_attr_t tattr;
int err;
if (disable_check_own_socket)
return;
if (check_own_socket_running || shutdown_pending)
return; /* Still running or already shutting down. */
sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
if (!sockname)
return; /* Out of memory. */
err = npth_attr_init (&tattr);
if (err)
return;
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
err = npth_create (&thread, &tattr, check_own_socket_thread, sockname);
if (err)
log_error ("error spawning check_own_socket_thread: %s\n", strerror (err));
npth_attr_destroy (&tattr);
}
/* Figure out whether an agent is available and running. Prints an
error if not. If SILENT is true, no messages are printed.
Returns 0 if the agent is running. */
static int
check_for_running_agent (int silent)
{
gpg_error_t err;
char *sockname;
assuan_context_t ctx = NULL;
sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL);
if (!sockname)
return gpg_error_from_syserror ();
err = assuan_new (&ctx);
if (!err)
err = assuan_socket_connect (ctx, sockname, (pid_t)(-1), 0);
xfree (sockname);
if (err)
{
if (!silent)
log_error (_("no gpg-agent running in this session\n"));
if (ctx)
assuan_release (ctx);
return -1;
}
if (!opt.quiet && !silent)
log_info ("gpg-agent running and available\n");
assuan_release (ctx);
return 0;
}
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index c2bf87d4a..f41cc0b5f 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -1,826 +1,819 @@
/* protect-tool.c - A tool to test the secret key protection
* Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
*
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_LOCALE_H
#include
#endif
#ifdef HAVE_LANGINFO_CODESET
#include
#endif
#ifdef HAVE_DOSISH_SYSTEM
#include /* for setmode() */
#endif
#include "agent.h"
#include "i18n.h"
#include "get-passphrase.h"
#include "sysutils.h"
#include "../common/init.h"
enum cmd_and_opt_values
{
aNull = 0,
oVerbose = 'v',
oArmor = 'a',
oPassphrase = 'P',
oProtect = 'p',
oUnprotect = 'u',
oNoVerbose = 500,
oShadow,
oShowShadowInfo,
oShowKeygrip,
oS2Kcalibration,
oCanonical,
oStore,
oForce,
oHaveCert,
oNoFailOnExist,
oHomedir,
oPrompt,
oStatusMsg,
oDebugUseOCB,
oAgentProgram
};
struct rsa_secret_key_s
{
gcry_mpi_t n; /* public modulus */
gcry_mpi_t e; /* public exponent */
gcry_mpi_t d; /* exponent */
gcry_mpi_t p; /* prime p. */
gcry_mpi_t q; /* prime q. */
gcry_mpi_t u; /* inverse of p mod q. */
};
static int opt_armor;
static int opt_canonical;
static int opt_store;
static int opt_force;
static int opt_no_fail_on_exist;
static int opt_have_cert;
static const char *opt_passphrase;
static char *opt_prompt;
static int opt_status_msg;
static const char *opt_agent_program;
static int opt_debug_use_ocb;
static char *get_passphrase (int promptno);
static void release_passphrase (char *pw);
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (oProtect, "protect", "protect a private key"),
ARGPARSE_c (oUnprotect, "unprotect", "unprotect a private key"),
ARGPARSE_c (oShadow, "shadow", "create a shadow entry for a public key"),
ARGPARSE_c (oShowShadowInfo, "show-shadow-info", "return the shadow info"),
ARGPARSE_c (oShowKeygrip, "show-keygrip", "show the \"keygrip\""),
ARGPARSE_c (oS2Kcalibration, "s2k-calibration", "@"),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oVerbose, "verbose", "verbose"),
ARGPARSE_s_n (oArmor, "armor", "write output in advanced format"),
ARGPARSE_s_n (oCanonical, "canonical", "write output in canonical format"),
ARGPARSE_s_s (oPassphrase, "passphrase", "|STRING|use passphrase STRING"),
ARGPARSE_s_n (oHaveCert, "have-cert",
"certificate to export provided on STDIN"),
ARGPARSE_s_n (oStore, "store",
"store the created key in the appropriate place"),
ARGPARSE_s_n (oForce, "force",
"force overwriting"),
ARGPARSE_s_n (oNoFailOnExist, "no-fail-on-exist", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oPrompt, "prompt",
"|ESCSTRING|use ESCSTRING as prompt in pinentry"),
ARGPARSE_s_n (oStatusMsg, "enable-status-msg", "@"),
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
ARGPARSE_s_n (oDebugUseOCB, "debug-use-ocb", "@"), /* For hacking only. */
ARGPARSE_end ()
};
static const char *
my_strusage (int level)
{
const char *p;
switch (level)
{
case 11: p = "gpg-protect-tool (" GNUPG_NAME ")";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 1:
case 40: p = _("Usage: gpg-protect-tool [options] (-h for help)\n");
break;
case 41: p = _("Syntax: gpg-protect-tool [options] [args]\n"
"Secret key maintenance tool\n");
break;
default: p = NULL;
}
return p;
}
/* static void */
/* print_mpi (const char *text, gcry_mpi_t a) */
/* { */
/* char *buf; */
/* void *bufaddr = &buf; */
/* int rc; */
/* rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); */
/* if (rc) */
/* log_info ("%s: [error printing number: %s]\n", text, gpg_strerror (rc)); */
/* else */
/* { */
/* log_info ("%s: %s\n", text, buf); */
/* gcry_free (buf); */
/* } */
/* } */
static unsigned char *
make_canonical (const char *fname, const char *buf, size_t buflen)
{
int rc;
size_t erroff, len;
gcry_sexp_t sexp;
unsigned char *result;
rc = gcry_sexp_sscan (&sexp, &erroff, buf, buflen);
if (rc)
{
log_error ("invalid S-Expression in '%s' (off=%u): %s\n",
fname, (unsigned int)erroff, gpg_strerror (rc));
return NULL;
}
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
result = xmalloc (len);
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, result, len);
assert (len);
gcry_sexp_release (sexp);
return result;
}
static char *
make_advanced (const unsigned char *buf, size_t buflen)
{
int rc;
size_t erroff, len;
gcry_sexp_t sexp;
char *result;
rc = gcry_sexp_sscan (&sexp, &erroff, (const char*)buf, buflen);
if (rc)
{
log_error ("invalid canonical S-Expression (off=%u): %s\n",
(unsigned int)erroff, gpg_strerror (rc));
return NULL;
}
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
assert (len);
result = xmalloc (len);
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
assert (len);
gcry_sexp_release (sexp);
return result;
}
static char *
read_file (const char *fname, size_t *r_length)
{
FILE *fp;
char *buf;
size_t buflen;
if (!strcmp (fname, "-"))
{
size_t nread, bufsize = 0;
fp = stdin;
#ifdef HAVE_DOSISH_SYSTEM
setmode ( fileno(fp) , O_BINARY );
#endif
buf = NULL;
buflen = 0;
#define NCHUNK 8192
do
{
bufsize += NCHUNK;
if (!buf)
buf = xmalloc (bufsize);
else
buf = xrealloc (buf, bufsize);
nread = fread (buf+buflen, 1, NCHUNK, fp);
if (nread < NCHUNK && ferror (fp))
{
log_error ("error reading '[stdin]': %s\n", strerror (errno));
xfree (buf);
return NULL;
}
buflen += nread;
}
while (nread == NCHUNK);
#undef NCHUNK
}
else
{
struct stat st;
fp = fopen (fname, "rb");
if (!fp)
{
log_error ("can't open '%s': %s\n", fname, strerror (errno));
return NULL;
}
if (fstat (fileno(fp), &st))
{
log_error ("can't stat '%s': %s\n", fname, strerror (errno));
fclose (fp);
return NULL;
}
buflen = st.st_size;
buf = xmalloc (buflen+1);
if (fread (buf, buflen, 1, fp) != 1)
{
log_error ("error reading '%s': %s\n", fname, strerror (errno));
fclose (fp);
xfree (buf);
return NULL;
}
fclose (fp);
}
*r_length = buflen;
return buf;
}
static unsigned char *
read_key (const char *fname)
{
char *buf;
size_t buflen;
unsigned char *key;
buf = read_file (fname, &buflen);
if (!buf)
return NULL;
key = make_canonical (fname, buf, buflen);
xfree (buf);
return key;
}
static void
read_and_protect (const char *fname)
{
int rc;
unsigned char *key;
unsigned char *result;
size_t resultlen;
char *pw;
key = read_key (fname);
if (!key)
return;
pw = get_passphrase (1);
rc = agent_protect (key, pw, &result, &resultlen, 0,
opt_debug_use_ocb? 1 : -1);
release_passphrase (pw);
xfree (key);
if (rc)
{
log_error ("protecting the key failed: %s\n", gpg_strerror (rc));
return;
}
if (opt_armor)
{
char *p = make_advanced (result, resultlen);
xfree (result);
if (!p)
return;
result = (unsigned char*)p;
resultlen = strlen (p);
}
fwrite (result, resultlen, 1, stdout);
xfree (result);
}
static void
read_and_unprotect (ctrl_t ctrl, const char *fname)
{
int rc;
unsigned char *key;
unsigned char *result;
size_t resultlen;
char *pw;
gnupg_isotime_t protected_at;
key = read_key (fname);
if (!key)
return;
rc = agent_unprotect (ctrl, key, (pw=get_passphrase (1)),
protected_at, &result, &resultlen);
release_passphrase (pw);
xfree (key);
if (rc)
{
if (opt_status_msg)
log_info ("[PROTECT-TOOL:] bad-passphrase\n");
log_error ("unprotecting the key failed: %s\n", gpg_strerror (rc));
return;
}
if (opt.verbose)
{
if (*protected_at)
log_info ("key protection done at %.4s-%.2s-%.2s %.2s:%.2s:%s\n",
protected_at, protected_at+4, protected_at+6,
protected_at+9, protected_at+11, protected_at+13);
else
log_info ("key protection done at [unknown]\n");
}
if (opt_armor)
{
char *p = make_advanced (result, resultlen);
xfree (result);
if (!p)
return;
result = (unsigned char*)p;
resultlen = strlen (p);
}
fwrite (result, resultlen, 1, stdout);
xfree (result);
}
static void
read_and_shadow (const char *fname)
{
int rc;
unsigned char *key;
unsigned char *result;
size_t resultlen;
unsigned char dummy_info[] = "(8:313233342:43)";
key = read_key (fname);
if (!key)
return;
rc = agent_shadow_key (key, dummy_info, &result);
xfree (key);
if (rc)
{
log_error ("shadowing the key failed: %s\n", gpg_strerror (rc));
return;
}
resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL);
assert (resultlen);
if (opt_armor)
{
char *p = make_advanced (result, resultlen);
xfree (result);
if (!p)
return;
result = (unsigned char*)p;
resultlen = strlen (p);
}
fwrite (result, resultlen, 1, stdout);
xfree (result);
}
static void
show_shadow_info (const char *fname)
{
int rc;
unsigned char *key;
const unsigned char *info;
size_t infolen;
key = read_key (fname);
if (!key)
return;
rc = agent_get_shadow_info (key, &info);
xfree (key);
if (rc)
{
log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc));
return;
}
infolen = gcry_sexp_canon_len (info, 0, NULL,NULL);
assert (infolen);
if (opt_armor)
{
char *p = make_advanced (info, infolen);
if (!p)
return;
fwrite (p, strlen (p), 1, stdout);
xfree (p);
}
else
fwrite (info, infolen, 1, stdout);
}
static void
show_file (const char *fname)
{
unsigned char *key;
size_t keylen;
char *p;
key = read_key (fname);
if (!key)
return;
keylen = gcry_sexp_canon_len (key, 0, NULL,NULL);
assert (keylen);
if (opt_canonical)
{
fwrite (key, keylen, 1, stdout);
}
else
{
p = make_advanced (key, keylen);
if (p)
{
fwrite (p, strlen (p), 1, stdout);
xfree (p);
}
}
xfree (key);
}
static void
show_keygrip (const char *fname)
{
unsigned char *key;
gcry_sexp_t private;
unsigned char grip[20];
int i;
key = read_key (fname);
if (!key)
return;
if (gcry_sexp_new (&private, key, 0, 0))
{
log_error ("gcry_sexp_new failed\n");
return;
}
xfree (key);
if (!gcry_pk_get_keygrip (private, grip))
{
log_error ("can't calculate keygrip\n");
return;
}
gcry_sexp_release (private);
for (i=0; i < 20; i++)
printf ("%02X", grip[i]);
putchar ('\n');
}
int
main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int cmd = 0;
const char *fname;
ctrl_t ctrl;
early_system_init ();
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
log_set_prefix ("gpg-protect-tool", 1);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
- if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
- {
- log_fatal( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
- NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
- }
-
setup_libgcrypt_logging ();
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
-
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* (do not remove the args) */
while (arg_parse (&pargs, opts) )
{
switch (pargs.r_opt)
{
case oVerbose: opt.verbose++; break;
case oArmor: opt_armor=1; break;
case oCanonical: opt_canonical=1; break;
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oAgentProgram: opt_agent_program = pargs.r.ret_str; break;
case oProtect: cmd = oProtect; break;
case oUnprotect: cmd = oUnprotect; break;
case oShadow: cmd = oShadow; break;
case oShowShadowInfo: cmd = oShowShadowInfo; break;
case oShowKeygrip: cmd = oShowKeygrip; break;
case oS2Kcalibration: cmd = oS2Kcalibration; break;
case oPassphrase: opt_passphrase = pargs.r.ret_str; break;
case oStore: opt_store = 1; break;
case oForce: opt_force = 1; break;
case oNoFailOnExist: opt_no_fail_on_exist = 1; break;
case oHaveCert: opt_have_cert = 1; break;
case oPrompt: opt_prompt = pargs.r.ret_str; break;
case oStatusMsg: opt_status_msg = 1; break;
case oDebugUseOCB: opt_debug_use_ocb = 1; break;
default: pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
if (log_get_errorcount (0))
exit (2);
fname = "-";
if (argc == 1)
fname = *argv;
else if (argc > 1)
usage (1);
/* Allocate an CTRL object. An empty object should sufficent. */
ctrl = xtrycalloc (1, sizeof *ctrl);
if (!ctrl)
{
log_error ("error allocating connection control data: %s\n",
strerror (errno));
agent_exit (1);
}
/* Set the information which can't be taken from envvars. */
gnupg_prepare_get_passphrase (GPG_ERR_SOURCE_DEFAULT,
opt.verbose,
opt_agent_program,
NULL, NULL, NULL);
if (opt_prompt)
opt_prompt = percent_plus_unescape (opt_prompt, 0);
if (cmd == oProtect)
read_and_protect (fname);
else if (cmd == oUnprotect)
read_and_unprotect (ctrl, fname);
else if (cmd == oShadow)
read_and_shadow (fname);
else if (cmd == oShowShadowInfo)
show_shadow_info (fname);
else if (cmd == oShowKeygrip)
show_keygrip (fname);
else if (cmd == oS2Kcalibration)
{
if (!opt.verbose)
opt.verbose++; /* We need to see something. */
get_standard_s2k_count ();
}
else
show_file (fname);
xfree (ctrl);
agent_exit (0);
return 8; /*NOTREACHED*/
}
void
agent_exit (int rc)
{
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
exit (rc);
}
/* Return the passphrase string and ask the agent if it has not been
set from the command line PROMPTNO select the prompt to display:
0 = default
1 = taken from the option --prompt
2 = for unprotecting a pkcs#12 object
3 = for protecting a new pkcs#12 object
4 = for protecting an imported pkcs#12 in our system
*/
static char *
get_passphrase (int promptno)
{
char *pw;
int err;
const char *desc;
char *orig_codeset;
int repeat = 0;
if (opt_passphrase)
return xstrdup (opt_passphrase);
orig_codeset = i18n_switchto_utf8 ();
if (promptno == 1 && opt_prompt)
{
desc = opt_prompt;
}
else if (promptno == 2)
{
desc = _("Please enter the passphrase to unprotect the "
"PKCS#12 object.");
}
else if (promptno == 3)
{
desc = _("Please enter the passphrase to protect the "
"new PKCS#12 object.");
repeat = 1;
}
else if (promptno == 4)
{
desc = _("Please enter the passphrase to protect the "
"imported object within the GnuPG system.");
repeat = 1;
}
else
desc = _("Please enter the passphrase or the PIN\n"
"needed to complete this operation.");
i18n_switchback (orig_codeset);
err = gnupg_get_passphrase (NULL, NULL, _("Passphrase:"), desc,
repeat, repeat, 1, &pw);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_CANCELED
|| gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
log_info (_("cancelled\n"));
else
log_error (_("error while asking for the passphrase: %s\n"),
gpg_strerror (err));
agent_exit (0);
}
assert (pw);
return pw;
}
static void
release_passphrase (char *pw)
{
if (pw)
{
wipememory (pw, strlen (pw));
xfree (pw);
}
}
/* Stub function. */
int
agent_key_available (const unsigned char *grip)
{
(void)grip;
return -1; /* Not available. */
}
char *
agent_get_cache (const char *key, cache_mode_t cache_mode)
{
(void)key;
(void)cache_mode;
return NULL;
}
gpg_error_t
agent_askpin (ctrl_t ctrl,
const char *desc_text, const char *prompt_text,
const char *initial_errtext,
struct pin_entry_info_s *pininfo,
const char *keyinfo, cache_mode_t cache_mode)
{
gpg_error_t err;
unsigned char *passphrase;
size_t size;
(void)ctrl;
(void)desc_text;
(void)prompt_text;
(void)initial_errtext;
(void)keyinfo;
(void)cache_mode;
*pininfo->pin = 0; /* Reset the PIN. */
passphrase = get_passphrase (0);
size = strlen (passphrase);
if (size >= pininfo->max_length)
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (&pininfo->pin, passphrase, size);
xfree (passphrase);
pininfo->pin[size] = 0;
if (pininfo->check_cb)
{
/* More checks by utilizing the optional callback. */
pininfo->cb_errtext = NULL;
err = pininfo->check_cb (pininfo);
}
else
err = 0;
return err;
}
/* Replacement for the function in findkey.c. Here we write the key
* to stdout. */
int
agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force)
{
char hexgrip[40+4+1];
char *p;
(void)force;
bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
p = make_advanced (buffer, length);
if (p)
{
printf ("# Begin dump of %s\n%s%s# End dump of %s\n",
hexgrip, p, (*p && p[strlen(p)-1] == '\n')? "":"\n", hexgrip);
xfree (p);
}
return 0;
}
diff --git a/common/init.c b/common/init.c
index c406ffe58..591c85468 100644
--- a/common/init.c
+++ b/common/init.c
@@ -1,289 +1,297 @@
/* init.c - Various initializations
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file 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 .
*/
#include
#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
#undef HAVE_NPTH
#undef USE_NPTH
#endif
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#endif
#ifdef HAVE_NPTH
# include
#endif
#ifdef HAVE_W32CE_SYSTEM
# include /* For _assuan_w32ce_finish_pipe. */
#endif
+#include
#include "util.h"
+#include "i18n.h"
/* This object is used to register memory cleanup functions.
Technically they are not needed but they can avoid frequent
questions about un-released memory. Note that we use the system
malloc and not any wrappers. */
struct mem_cleanup_item_s;
typedef struct mem_cleanup_item_s *mem_cleanup_item_t;
struct mem_cleanup_item_s
{
mem_cleanup_item_t next;
void (*func) (void);
};
static mem_cleanup_item_t mem_cleanup_list;
/* The default error source of the application. This is different
from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the
source file and thus is usable in code shared by applications.
Note that we need to initialize it because otherwise some linkers
(OS X at least) won't find the symbol when linking the t-*.c
files. */
gpg_err_source_t default_errsource = 0;
#ifdef HAVE_W32CE_SYSTEM
static void parse_std_file_handles (int *argcp, char ***argvp);
static void
sleep_on_exit (void)
{
/* The sshd on CE swallows some of the command output. Sleeping a
while usually helps. */
Sleep (400);
}
#endif /*HAVE_W32CE_SYSTEM*/
static void
run_mem_cleanup (void)
{
mem_cleanup_item_t next;
while (mem_cleanup_list)
{
next = mem_cleanup_list->next;
mem_cleanup_list->func ();
free (mem_cleanup_list);
mem_cleanup_list = next;
}
}
void
register_mem_cleanup_func (void (*func)(void))
{
mem_cleanup_item_t item;
for (item = mem_cleanup_list; item; item = item->next)
if (item->func == func)
return; /* Function has already been registered. */
item = malloc (sizeof *item);
if (item)
{
item->func = func;
item->next = mem_cleanup_list;
mem_cleanup_list = item;
}
}
/* If STRING is not NULL write string to es_stdout or es_stderr. MODE
must be 1 or 2. If STRING is NULL flush the respective stream. */
static int
writestring_via_estream (int mode, const char *string)
{
if (mode == 1 || mode == 2)
{
if (string)
return es_fputs (string, mode == 1? es_stdout : es_stderr);
else
return es_fflush (mode == 1? es_stdout : es_stderr);
}
else
return -1;
}
/* This function should be the first called after main. */
void
early_system_init (void)
{
}
/* This function is to be used early at program startup to make sure
that some subsystems are initialized. This is in particular
important for W32 to initialize the sockets so that our socket
emulation code used directly as well as in libassuan may be used.
It should best be called before any I/O is done so that setup
required for logging is ready. ARGCP and ARGVP are the addresses
of the parameters given to main. This function may modify them.
This function should be called only via the macro
init_common_subsystems.
CAUTION: This might be called while running suid(root). */
void
_init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp)
{
/* Store the error source in a global variable. */
default_errsource = errsource;
atexit (run_mem_cleanup);
/* Try to auto set the character set. */
set_native_charset (NULL);
#ifdef HAVE_W32_SYSTEM
/* For W32 we need to initialize the socket layer. This is because
we use recv and send in libassuan as well as at some other
places. */
{
WSADATA wsadat;
WSAStartup (0x202, &wsadat);
}
#endif
#ifdef HAVE_W32CE_SYSTEM
/* Register the sleep exit function before the estream init so that
the sleep will be called after the estream registered atexit
function which flushes the left open estream streams and in
particular es_stdout. */
atexit (sleep_on_exit);
#endif
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
+ {
+ log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL));
+ }
+
/* Initialize the Estream library. */
gpgrt_init ();
gpgrt_set_alloc_func (gcry_realloc);
#ifdef USE_NPTH
gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
#endif
/* Special hack for Windows CE: We extract some options from arg
to setup the standard handles. */
#ifdef HAVE_W32CE_SYSTEM
parse_std_file_handles (argcp, argvp);
#else
(void)argcp;
(void)argvp;
#endif
/* Access the standard estreams as early as possible. If we don't
do this the original stdio streams may have been closed when
_es_get_std_stream is first use and in turn it would connect to
the bit bucket. */
{
int i;
for (i=0; i < 3; i++)
(void)_gpgrt_get_std_stream (i);
}
/* --version et al shall use estream as well. */
argparse_register_outfnc (writestring_via_estream);
}
/* WindowsCE uses a very strange way of handling the standard streams.
There is a function SetStdioPath to associate a standard stream
with a file or a device but what we really want is to use pipes as
standard streams. Despite that we implement pipes using a device,
we would have some limitations on the number of open pipes due to
the 3 character limit of device file name. Thus we don't take this
path. Another option would be to install a file system driver with
support for pipes; this would allow us to get rid of the device
name length limitation. However, with GnuPG we can get away be
redefining the standard streams and passing the handles to be used
on the command line. This has also the advantage that it makes
creating a process much easier and does not require the
SetStdioPath set and restore game. The caller needs to pass the
rendezvous ids using up to three options:
-&S0= -&S1= -&S2=
They are all optional but they must be the first arguments on the
command line. Parsing stops as soon as an invalid option is found.
These rendezvous ids are then used to finish the pipe creation.*/
#ifdef HAVE_W32CE_SYSTEM
static void
parse_std_file_handles (int *argcp, char ***argvp)
{
int argc = *argcp;
char **argv = *argvp;
const char *s;
assuan_fd_t fd;
int i;
int fixup = 0;
if (!argc)
return;
for (argc--, argv++; argc; argc--, argv++)
{
s = *argv;
if (*s == '-' && s[1] == '&' && s[2] == 'S'
&& (s[3] == '0' || s[3] == '1' || s[3] == '2')
&& s[4] == '='
&& (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null")))
{
if (s[5] == 'n')
fd = ASSUAN_INVALID_FD;
else
fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0');
_es_set_std_fd (s[3] - '0', (int)fd);
fixup++;
}
else
break;
}
if (fixup)
{
argc = *argcp;
argc -= fixup;
*argcp = argc;
argv = *argvp;
for (i=1; i < argc; i++)
argv[i] = argv[i + fixup];
for (; i < argc + fixup; i++)
argv[i] = NULL;
}
}
#endif /*HAVE_W32CE_SYSTEM*/
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index aaa905089..64bc05868 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -1,151 +1,151 @@
# Makefile.am - dirmngr
# Copyright (C) 2002 Klarälvdalens Datakonsult AB
# Copyright (C) 2004, 2007, 2010 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 .
## Process this file with automake to produce Makefile.in
EXTRA_DIST = OAUTHORS ONEWS ChangeLog-2011 tls-ca.pem
dist_pkgdata_DATA = sks-keyservers.netCA.pem
bin_PROGRAMS = dirmngr dirmngr-client
if USE_LDAPWRAPPER
libexec_PROGRAMS = dirmngr_ldap
endif
noinst_PROGRAMS = $(module_tests) $(module_maint_tests)
TESTS = $(module_tests)
AM_CPPFLAGS = -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) \
$(GPG_ERROR_CFLAGS) $(NPTH_CFLAGS) $(NTBTLS_CFLAGS) \
$(LIBGNUTLS_CFLAGS)
BUILT_SOURCES = no-libgcrypt.c
CLEANFILES = no-libgcrypt.c
if HAVE_W32_SYSTEM
ldap_url = ldap-url.h ldap-url.c
else
ldap_url =
endif
if USE_LDAPWRAPPER
extraldap_src = ldap-wrapper.c
else
extraldap_src = ldap-wrapper-ce.c dirmngr_ldap.c
endif
noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
certcache.c certcache.h \
cdb.h cdblib.c misc.c dirmngr-err.h \
ocsp.c ocsp.h validate.c validate.h \
dns-stuff.c dns-stuff.h \
http.c http.h \
ks-action.c ks-action.h ks-engine.h \
ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c
if USE_LDAP
dirmngr_SOURCES += ldapserver.h ldapserver.c ldap.c w32-ldap-help.h \
ldap-wrapper.h ldap-parse-uri.c ldap-parse-uri.h \
ks-engine-ldap.c $(ldap_url) $(extraldap_src)
ldaplibs = $(LDAPLIBS)
else
ldaplibs =
endif
dirmngr_LDADD = $(libcommonpth) \
$(DNSLIBS) $(LIBASSUAN_LIBS) \
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(NPTH_LIBS) \
$(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(LIBINTL) $(LIBICONV)
if USE_LDAP
dirmngr_LDADD += $(ldaplibs)
endif
if !USE_LDAPWRAPPER
dirmngr_LDADD += $(ldaplibs)
endif
dirmngr_LDFLAGS = $(extra_bin_ldflags)
if USE_LDAPWRAPPER
dirmngr_ldap_SOURCES = dirmngr_ldap.c $(ldap_url)
dirmngr_ldap_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS)
dirmngr_ldap_LDFLAGS =
dirmngr_ldap_LDADD = $(libcommon) no-libgcrypt.o \
- $(GPG_ERROR_LIBS) $(LDAPLIBS) $(LBER_LIBS) $(LIBINTL) \
- $(LIBICONV)
+ $(GPG_ERROR_LIBS) $(LIBGCRYPT_LIBS) $(LDAPLIBS) \
+ $(LBER_LIBS) $(LIBINTL) $(LIBICONV)
endif
dirmngr_client_SOURCES = dirmngr-client.c
dirmngr_client_LDADD = $(libcommon) \
$(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
$(LIBGCRYPT_LIBS) $(NETLIBS) $(LIBINTL) $(LIBICONV)
dirmngr_client_LDFLAGS = $(extra_bin_ldflags)
no-libgcrypt.c : $(top_srcdir)/tools/no-libgcrypt.c
cat $(top_srcdir)/tools/no-libgcrypt.c > no-libgcrypt.c
t_common_src = t-support.h
t_common_ldadd = $(libcommon) $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \
$(GPG_ERROR_LIBS) $(NETLIBS) \
$(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) \
$(DNSLIBS) $(LIBINTL) $(LIBICONV)
module_tests = t-dns-stuff
if USE_LDAP
module_tests += t-ldap-parse-uri
endif
if MAINTAINER_MODE
module_maint_tests = t-http
else
module_maint_tests =
endif
# http tests
t_http_SOURCES = t-http.c http.c dns-stuff.c
t_http_CFLAGS = -DWITHOUT_NPTH=1 \
$(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \
$(GPG_ERROR_CFLAGS)
t_http_LDADD = $(t_common_ldadd) \
$(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS)
t_ldap_parse_uri_SOURCES = \
t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \
http.c dns-stuff.c \
$(ldap_url) $(t_common_src)
t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 \
$(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS)
t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 \
$(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
t_dns_stuff_SOURCES = t-dns-stuff.c dns-stuff.c
t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS)
$(PROGRAMS) : $(libcommon) $(libcommonpth)
diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
index 7e629db96..007fa1002 100644
--- a/dirmngr/dirmngr.c
+++ b/dirmngr/dirmngr.c
@@ -1,2149 +1,2145 @@
/* dirmngr.c - Keyserver and X.509 LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2006, 2007, 2008, 2010, 2011 g10 Code GmbH
* Copyright (C) 2014 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef HAVE_W32_SYSTEM
#include
#include
#endif
#include
#include
#ifdef HAVE_SIGNAL_H
# include
#endif
#include
#include "dirmngr-err.h"
#if HTTP_USE_NTBTLS
# include
#elif HTTP_USE_GNUTLS
# include
#endif /*HTTP_USE_GNUTLS*/
#define GNUPG_COMMON_NEED_AFLOCAL
#include "dirmngr.h"
#include
#include "certcache.h"
#include "crlcache.h"
#include "crlfetch.h"
#include "misc.h"
#if USE_LDAP
# include "ldapserver.h"
#endif
#include "asshelp.h"
#if USE_LDAP
# include "ldap-wrapper.h"
#endif
#include "../common/init.h"
#include "gc-opt-flags.h"
#include "dns-stuff.h"
/* The plain Windows version uses the windows service system. For
example to start the service you may use "sc start dirmngr".
WindowsCE does not support this; the service system over there is
based on a single process with all services being DLLs - we can't
support this easily. */
#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM)
# define USE_W32_SERVICE 1
#endif
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
enum cmd_and_opt_values {
aNull = 0,
oCsh = 'c',
oQuiet = 'q',
oSh = 's',
oVerbose = 'v',
oNoVerbose = 500,
aServer,
aDaemon,
aService,
aListCRLs,
aLoadCRL,
aFetchCRL,
aShutdown,
aFlush,
aGPGConfList,
aGPGConfTest,
oOptions,
oDebug,
oDebugAll,
oDebugWait,
oDebugLevel,
oGnutlsDebug,
oNoGreeting,
oNoOptions,
oHomedir,
oNoDetach,
oLogFile,
oBatch,
oDisableHTTP,
oDisableLDAP,
oIgnoreLDAPDP,
oIgnoreHTTPDP,
oIgnoreOCSPSvcUrl,
oHonorHTTPProxy,
oHTTPProxy,
oLDAPProxy,
oOnlyLDAPProxy,
oLDAPFile,
oLDAPTimeout,
oLDAPAddServers,
oOCSPResponder,
oOCSPSigner,
oOCSPMaxClockSkew,
oOCSPMaxPeriod,
oOCSPCurrentPeriod,
oMaxReplies,
oHkpCaCert,
oFakedSystemTime,
oForce,
oAllowOCSP,
oSocketName,
oLDAPWrapperProgram,
oHTTPWrapperProgram,
oIgnoreCertExtension,
oUseTor,
oKeyServer,
oNameServer,
aTest
};
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (aServer, "server", N_("run in server mode (foreground)") ),
ARGPARSE_c (aDaemon, "daemon", N_("run in daemon mode (background)") ),
#ifdef USE_W32_SERVICE
ARGPARSE_c (aService, "service", N_("run as windows service (background)")),
#endif
ARGPARSE_c (aListCRLs, "list-crls", N_("list the contents of the CRL cache")),
ARGPARSE_c (aLoadCRL, "load-crl", N_("|FILE|load CRL from FILE into cache")),
ARGPARSE_c (aFetchCRL, "fetch-crl", N_("|URL|fetch a CRL from URL")),
ARGPARSE_c (aShutdown, "shutdown", N_("shutdown the dirmngr")),
ARGPARSE_c (aFlush, "flush", N_("flush the cache")),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")),
ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")),
ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
ARGPARSE_s_s (oDebugLevel, "debug-level",
N_("|LEVEL|set the debugging level to LEVEL")),
ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_s (oLogFile, "log-file",
N_("|FILE|write server mode logs to FILE")),
ARGPARSE_s_n (oBatch, "batch", N_("run without asking a user")),
ARGPARSE_s_n (oForce, "force", N_("force loading of outdated CRLs")),
ARGPARSE_s_n (oAllowOCSP, "allow-ocsp", N_("allow sending OCSP requests")),
ARGPARSE_s_n (oDisableHTTP, "disable-http", N_("inhibit the use of HTTP")),
ARGPARSE_s_n (oDisableLDAP, "disable-ldap", N_("inhibit the use of LDAP")),
ARGPARSE_s_n (oIgnoreHTTPDP,"ignore-http-dp",
N_("ignore HTTP CRL distribution points")),
ARGPARSE_s_n (oIgnoreLDAPDP,"ignore-ldap-dp",
N_("ignore LDAP CRL distribution points")),
ARGPARSE_s_n (oIgnoreOCSPSvcUrl, "ignore-ocsp-service-url",
N_("ignore certificate contained OCSP service URLs")),
ARGPARSE_s_s (oHTTPProxy, "http-proxy",
N_("|URL|redirect all HTTP requests to URL")),
ARGPARSE_s_s (oLDAPProxy, "ldap-proxy",
N_("|HOST|use HOST for LDAP queries")),
ARGPARSE_s_n (oOnlyLDAPProxy, "only-ldap-proxy",
N_("do not use fallback hosts with --ldap-proxy")),
ARGPARSE_s_s (oLDAPFile, "ldapserverlist-file",
N_("|FILE|read LDAP server list from FILE")),
ARGPARSE_s_n (oLDAPAddServers, "add-servers",
N_("add new servers discovered in CRL distribution"
" points to serverlist")),
ARGPARSE_s_i (oLDAPTimeout, "ldaptimeout",
N_("|N|set LDAP timeout to N seconds")),
ARGPARSE_s_s (oOCSPResponder, "ocsp-responder",
N_("|URL|use OCSP responder at URL")),
ARGPARSE_s_s (oOCSPSigner, "ocsp-signer",
N_("|FPR|OCSP response signed by FPR")),
ARGPARSE_s_i (oOCSPMaxClockSkew, "ocsp-max-clock-skew", "@"),
ARGPARSE_s_i (oOCSPMaxPeriod, "ocsp-max-period", "@"),
ARGPARSE_s_i (oOCSPCurrentPeriod, "ocsp-current-period", "@"),
ARGPARSE_s_i (oMaxReplies, "max-replies",
N_("|N|do not return more than N items in one query")),
ARGPARSE_s_s (oNameServer, "nameserver", "@"),
ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
ARGPARSE_s_s (oHkpCaCert, "hkp-cacert",
N_("|FILE|use the CA certificates in FILE for HKP over TLS")),
ARGPARSE_s_n (oUseTor, "use-tor", N_("route all network traffic via Tor")),
ARGPARSE_s_s (oSocketName, "socket-name", "@"), /* Only for debugging. */
ARGPARSE_s_u (oFakedSystemTime, "faked-system-time", "@"), /*(epoch time)*/
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_i (oGnutlsDebug, "gnutls-debug", "@"),
ARGPARSE_s_i (oGnutlsDebug, "tls-debug", "@"),
ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oLDAPWrapperProgram, "ldap-wrapper-program", "@"),
ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
ARGPARSE_s_n (oHonorHTTPProxy, "honor-http-proxy", "@"),
ARGPARSE_s_s (oIgnoreCertExtension,"ignore-cert-extension", "@"),
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
"of all commands and options)\n")),
ARGPARSE_end ()
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_X509_VALUE , "x509" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_IPC_VALUE , "ipc" },
{ DBG_LOOKUP_VALUE , "lookup" },
{ 77, NULL } /* 77 := Do not exit on "help" or "?". */
};
#define DEFAULT_MAX_REPLIES 10
#define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */
/* For the cleanup handler we need to keep track of the socket's name. */
static const char *socket_name;
/* If the socket has been redirected, this is the name of the
redirected socket.. */
static const char *redir_socket_name;
/* We need to keep track of the server's nonces (these are dummies for
POSIX systems). */
static assuan_sock_nonce_t socket_nonce;
/* Only if this flag has been set will we remove the socket file. */
static int cleanup_socket;
/* Keep track of the current log file so that we can avoid updating
the log file after a SIGHUP if it didn't changed. Malloced. */
static char *current_logfile;
/* Helper to implement --debug-level. */
static const char *debug_level;
/* Helper to set the NTBTLS or GNUTLS log level. */
static int opt_gnutls_debug = -1;
/* Flag indicating that a shutdown has been requested. */
static volatile int shutdown_pending;
/* Counter for the active connections. */
static int active_connections;
/* The timer tick used for housekeeping stuff. For Windows we use a
longer period as the SetWaitableTimer seems to signal earlier than
the 2 seconds. All values are in seconds. */
#if defined(HAVE_W32CE_SYSTEM)
# define TIMERTICK_INTERVAL (60)
#elif defined(HAVE_W32_SYSTEM)
# define TIMERTICK_INTERVAL (4)
#else
# define TIMERTICK_INTERVAL (2)
#endif
#define HOUSEKEEPING_INTERVAL (600)
/* This union is used to avoid compiler warnings in case a pointer is
64 bit and an int 32 bit. We store an integer in a pointer and get
it back later (npth_getspecific et al.). */
union int_and_ptr_u
{
int aint;
assuan_fd_t afd;
void *aptr;
};
/* The key used to store the current file descriptor in the thread
local storage. We use this in conjunction with the
log_set_pid_suffix_cb feature. */
#ifndef HAVE_W32_SYSTEM
static int my_tlskey_current_fd;
#endif
/* Prototypes. */
static void cleanup (void);
#if USE_LDAP
static ldap_server_t parse_ldapserver_file (const char* filename);
#endif /*USE_LDAP*/
static fingerprint_list_t parse_ocsp_signer (const char *string);
static void handle_connections (assuan_fd_t listen_fd);
/* NPth wrapper function definitions. */
ASSUAN_SYSTEM_NPTH_IMPL;
static const char *
my_strusage( int level )
{
const char *p;
switch ( level )
{
case 11: p = "@DIRMNGR@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
/* TRANSLATORS: @EMAIL@ will get replaced by the actual bug
reporting address. This is so that we can change the
reporting address without breaking the translations. */
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 49: p = PACKAGE_BUGREPORT; break;
case 1:
case 40: p = _("Usage: @DIRMNGR@ [options] (-h for help)");
break;
case 41: p = _("Syntax: @DIRMNGR@ [options] [command [args]]\n"
"Keyserver, CRL, and OCSP access for @GNUPG@\n");
break;
default: p = NULL;
}
return p;
}
/* Callback from libksba to hash a provided buffer. Our current
implementation does only allow SHA-1 for hashing. This may be
extended by mapping the name, testing for algorithm availibility
and adjust the length checks accordingly. */
static gpg_error_t
my_ksba_hash_buffer (void *arg, const char *oid,
const void *buffer, size_t length, size_t resultsize,
unsigned char *result, size_t *resultlen)
{
(void)arg;
if (oid && strcmp (oid, "1.3.14.3.2.26"))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
if (resultsize < 20)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
gcry_md_hash_buffer (2, result, buffer, length);
*resultlen = 20;
return 0;
}
/* GNUTLS log function callback. */
#ifdef HTTP_USE_GNUTLS
static void
my_gnutls_log (int level, const char *text)
{
int n;
n = strlen (text);
while (n && text[n-1] == '\n')
n--;
log_debug ("gnutls:L%d: %.*s\n", level, n, text);
}
#endif /*HTTP_USE_GNUTLS*/
/* Setup the debugging. With a LEVEL of NULL only the active debug
flags are propagated to the subsystems. With LEVEL set, a specific
set of debug flags is set; thus overriding all flags already
set. */
static void
set_debug (void)
{
int numok = (debug_level && digitp (debug_level));
int numlvl = numok? atoi (debug_level) : 0;
if (!debug_level)
;
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE);
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE|DBG_LOOKUP_VALUE
|DBG_CACHE_VALUE|DBG_CRYPTO_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), debug_level);
log_info (_("valid debug levels are: %s\n"),
"none, basic, advanced, expert, guru");
opt.debug = 0; /* Reset debugging, so that prior debug
statements won't have an undesired effect. */
}
if (opt.debug && !opt.verbose)
{
opt.verbose = 1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
}
if (opt.debug && opt.quiet)
opt.quiet = 0;
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
#if HTTP_USE_NTBTLS
if (opt_gnutls_debug >= 0)
{
ntbtls_set_debug (opt_gnutls_debug, NULL, NULL);
}
#elif HTTP_USE_GNUTLS
if (opt_gnutls_debug >= 0)
{
gnutls_global_set_log_function (my_gnutls_log);
gnutls_global_set_log_level (opt_gnutls_debug);
}
#endif /*HTTP_USE_GNUTLS*/
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
}
static void
set_tor_mode (void)
{
if (opt.use_tor)
{
if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1))
{
log_error ("error enabling Tor mode: %s\n", strerror (errno));
log_info ("(is your Libassuan recent enough?)\n");
}
}
}
static void
wrong_args (const char *text)
{
es_fprintf (es_stderr, _("usage: %s [options] "), DIRMNGR_NAME);
es_fputs (text, es_stderr);
es_putc ('\n', es_stderr);
dirmngr_exit (2);
}
/* Helper to stop the reaper thread for the ldap wrapper. */
static void
shutdown_reaper (void)
{
#if USE_LDAP
ldap_wrapper_wait_connections ();
#endif
}
/* Handle options which are allowed to be reset after program start.
Return true if the current option in PARGS could be handled and
false if not. As a special feature, passing a value of NULL for
PARGS, resets the options to the default. REREAD should be set
true if it is not the initial option parsing. */
static int
parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
{
if (!pargs)
{ /* Reset mode. */
opt.quiet = 0;
opt.verbose = 0;
opt.debug = 0;
opt.ldap_wrapper_program = NULL;
opt.disable_http = 0;
opt.disable_ldap = 0;
opt.honor_http_proxy = 0;
opt.http_proxy = NULL;
opt.ldap_proxy = NULL;
opt.only_ldap_proxy = 0;
opt.ignore_http_dp = 0;
opt.ignore_ldap_dp = 0;
opt.ignore_ocsp_service_url = 0;
opt.allow_ocsp = 0;
opt.ocsp_responder = NULL;
opt.ocsp_max_clock_skew = 10 * 60; /* 10 minutes. */
opt.ocsp_max_period = 90 * 86400; /* 90 days. */
opt.ocsp_current_period = 3 * 60 * 60; /* 3 hours. */
opt.max_replies = DEFAULT_MAX_REPLIES;
while (opt.ocsp_signer)
{
fingerprint_list_t tmp = opt.ocsp_signer->next;
xfree (opt.ocsp_signer);
opt.ocsp_signer = tmp;
}
FREE_STRLIST (opt.ignored_cert_extensions);
http_register_tls_ca (NULL);
FREE_STRLIST (opt.keyserver);
/* Note: We do not allow resetting of opt.use_tor at runtime. */
return 1;
}
switch (pargs->r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oDebug:
parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags);
break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs->r.ret_str; break;
case oGnutlsDebug: opt_gnutls_debug = pargs->r.ret_int; break;
case oLogFile:
if (!reread)
return 0; /* Not handled. */
if (!current_logfile || !pargs->r.ret_str
|| strcmp (current_logfile, pargs->r.ret_str))
{
log_set_file (pargs->r.ret_str);
xfree (current_logfile);
current_logfile = xtrystrdup (pargs->r.ret_str);
}
break;
case oLDAPWrapperProgram:
opt.ldap_wrapper_program = pargs->r.ret_str;
break;
case oHTTPWrapperProgram:
opt.http_wrapper_program = pargs->r.ret_str;
break;
case oDisableHTTP: opt.disable_http = 1; break;
case oDisableLDAP: opt.disable_ldap = 1; break;
case oHonorHTTPProxy: opt.honor_http_proxy = 1; break;
case oHTTPProxy: opt.http_proxy = pargs->r.ret_str; break;
case oLDAPProxy: opt.ldap_proxy = pargs->r.ret_str; break;
case oOnlyLDAPProxy: opt.only_ldap_proxy = 1; break;
case oIgnoreHTTPDP: opt.ignore_http_dp = 1; break;
case oIgnoreLDAPDP: opt.ignore_ldap_dp = 1; break;
case oIgnoreOCSPSvcUrl: opt.ignore_ocsp_service_url = 1; break;
case oAllowOCSP: opt.allow_ocsp = 1; break;
case oOCSPResponder: opt.ocsp_responder = pargs->r.ret_str; break;
case oOCSPSigner:
opt.ocsp_signer = parse_ocsp_signer (pargs->r.ret_str);
break;
case oOCSPMaxClockSkew: opt.ocsp_max_clock_skew = pargs->r.ret_int; break;
case oOCSPMaxPeriod: opt.ocsp_max_period = pargs->r.ret_int; break;
case oOCSPCurrentPeriod: opt.ocsp_current_period = pargs->r.ret_int; break;
case oMaxReplies: opt.max_replies = pargs->r.ret_int; break;
case oHkpCaCert:
{
char *tmpname;
/* Do tilde expansion and print a warning if the file can't be
accessed. */
tmpname = make_absfilename_try (pargs->r.ret_str, NULL);
if (!tmpname || access (tmpname, F_OK))
log_info (_("can't access '%s': %s\n"),
tmpname? tmpname : pargs->r.ret_str,
gpg_strerror (gpg_error_from_syserror()));
else
http_register_tls_ca (tmpname);
xfree (tmpname);
}
break;
case oIgnoreCertExtension:
add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str);
break;
case oUseTor: opt.use_tor = 1; break;
case oKeyServer:
if (*pargs->r.ret_str)
add_to_strlist (&opt.keyserver, pargs->r.ret_str);
break;
case oNameServer:
set_dns_nameserver (pargs->r.ret_str);
break;
default:
return 0; /* Not handled. */
}
return 1; /* Handled. */
}
#ifdef USE_W32_SERVICE
/* The global status of our service. */
SERVICE_STATUS_HANDLE service_handle;
SERVICE_STATUS service_status;
DWORD WINAPI
w32_service_control (DWORD control, DWORD event_type, LPVOID event_data,
LPVOID context)
{
(void)event_type;
(void)event_data;
(void)context;
/* event_type and event_data are not used here. */
switch (control)
{
case SERVICE_CONTROL_SHUTDOWN:
/* For shutdown we will try to force termination. */
service_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (service_handle, &service_status);
shutdown_pending = 3;
break;
case SERVICE_CONTROL_STOP:
service_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus (service_handle, &service_status);
shutdown_pending = 1;
break;
default:
break;
}
return 0;
}
#endif /*USE_W32_SERVICE*/
#ifndef HAVE_W32_SYSTEM
static int
pid_suffix_callback (unsigned long *r_suffix)
{
union int_and_ptr_u value;
memset (&value, 0, sizeof value);
value.aptr = npth_getspecific (my_tlskey_current_fd);
*r_suffix = value.aint;
return (*r_suffix != -1); /* Use decimal representation. */
}
#endif /*!HAVE_W32_SYSTEM*/
#ifdef USE_W32_SERVICE
# define main real_main
#endif
int
main (int argc, char **argv)
{
#ifdef USE_W32_SERVICE
# undef main
#endif
enum cmd_and_opt_values cmd = 0;
ARGPARSE_ARGS pargs;
int orig_argc;
char **orig_argv;
FILE *configfp = NULL;
char *configname = NULL;
const char *shell;
unsigned configlineno;
int parse_debug = 0;
int default_config =1;
int greeting = 0;
int nogreeting = 0;
int nodetach = 0;
int csh_style = 0;
char *logfile = NULL;
#if USE_LDAP
char *ldapfile = NULL;
#endif /*USE_LDAP*/
int debug_wait = 0;
int rc;
int homedir_seen = 0;
struct assuan_malloc_hooks malloc_hooks;
early_system_init ();
#ifdef USE_W32_SERVICE
/* The option will be set by main() below if we should run as a
system daemon. */
if (opt.system_service)
{
service_handle
= RegisterServiceCtrlHandlerEx ("DirMngr",
&w32_service_control, NULL /*FIXME*/);
if (service_handle == 0)
log_error ("failed to register service control handler: ec=%d",
(int) GetLastError ());
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
service_status.dwCurrentState = SERVICE_START_PENDING;
service_status.dwControlsAccepted
= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
service_status.dwWin32ExitCode = NO_ERROR;
service_status.dwServiceSpecificExitCode = NO_ERROR;
service_status.dwCheckPoint = 0;
service_status.dwWaitHint = 10000; /* 10 seconds timeout. */
SetServiceStatus (service_handle, &service_status);
}
#endif /*USE_W32_SERVICE*/
set_strusage (my_strusage);
log_set_prefix (DIRMNGR_NAME, 1|4);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
npth_init ();
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
/* Check that the libraries are suitable. Do it here because
the option parsing may need services of the libraries. */
-
- if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
- log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
- NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
if (!ksba_check_version (NEED_KSBA_VERSION) )
log_fatal( _("%s is too old (need %s, have %s)\n"), "libksba",
NEED_KSBA_VERSION, ksba_check_version (NULL) );
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
ksba_set_hash_buffer_function (my_ksba_hash_buffer, NULL);
/* Init TLS library. */
#if HTTP_USE_NTBTLS
if (!ntbtls_check_version (NEED_NTBTLS_VERSION) )
log_fatal( _("%s is too old (need %s, have %s)\n"), "ntbtls",
NEED_NTBTLS_VERSION, ntbtls_check_version (NULL) );
#elif HTTP_USE_GNUTLS
rc = gnutls_global_init ();
if (rc)
log_fatal ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));
#endif /*HTTP_USE_GNUTLS*/
/* Init Assuan. */
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
assuan_set_assuan_log_prefix (log_get_prefix (NULL));
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
assuan_sock_init ();
setup_libassuan_logging (&opt.debug);
setup_libgcrypt_logging ();
/* Setup defaults. */
shell = getenv ("SHELL");
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
csh_style = 1;
/* Now with NPth running we can set the logging callback. Our
windows implementation does not yet feature the NPth TLS
functions. */
#ifndef HAVE_W32_SYSTEM
if (npth_key_create (&my_tlskey_current_fd, NULL) == 0)
if (npth_setspecific (my_tlskey_current_fd, NULL) == 0)
log_set_pid_suffix_cb (pid_suffix_callback);
#endif /*!HAVE_W32_SYSTEM*/
/* Reset rereadable options to default values. */
parse_rereadable_options (NULL, 0);
/* LDAP defaults. */
opt.add_new_ldapservers = 0;
opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT;
/* Other defaults. */
/* Check whether we have a config file given on the commandline */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
while (arg_parse( &pargs, opts))
{
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
parse_debug++;
else if (pargs.r_opt == oOptions)
{ /* Yes there is one, so we do not try the default one, but
read the option file when it is encountered at the
commandline */
default_config = 0;
}
else if (pargs.r_opt == oNoOptions)
default_config = 0; /* --no-options */
else if (pargs.r_opt == oHomedir)
{
gnupg_set_homedir (pargs.r.ret_str);
homedir_seen = 1;
}
else if (pargs.r_opt == aDaemon)
opt.system_daemon = 1;
else if (pargs.r_opt == aService)
{
/* Redundant. The main function takes care of it. */
opt.system_service = 1;
opt.system_daemon = 1;
}
#ifdef HAVE_W32_SYSTEM
else if (pargs.r_opt == aGPGConfList || pargs.r_opt == aGPGConfTest)
/* We set this so we switch to the system configuration
directory below. This is a crutch to solve the problem
that the user configuration is never used on Windows. Also
see below at aGPGConfList. */
opt.system_daemon = 1;
#endif
}
/* If --daemon has been given on the command line but not --homedir,
we switch to /etc/gnupg as default home directory. Note, that
this also overrides the GNUPGHOME environment variable. */
if (opt.system_daemon && !homedir_seen)
{
#ifdef HAVE_W32CE_SYSTEM
gnupg_set_homedir (DIRSEP_S "gnupg");
#else
gnupg_set_homedir (gnupg_sysconfdir ());
#endif
opt.homedir_cache = gnupg_cachedir ();
socket_name = dirmngr_sys_socket_name ();
}
else if (dirmngr_user_socket_name ())
socket_name = dirmngr_user_socket_name ();
else
socket_name = dirmngr_sys_socket_name ();
if (default_config)
configname = make_filename (gnupg_homedir (), DIRMNGR_NAME".conf", NULL );
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
next_pass:
if (configname)
{
configlineno = 0;
configfp = fopen (configname, "r");
if (!configfp)
{
if (default_config)
{
if( parse_debug )
log_info (_("Note: no default option file '%s'\n"),
configname );
}
else
{
log_error (_("option file '%s': %s\n"),
configname, strerror(errno) );
exit(2);
}
xfree (configname);
configname = NULL;
}
if (parse_debug && configname )
log_info (_("reading options from '%s'\n"), configname );
default_config = 0;
}
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
{
if (parse_rereadable_options (&pargs, 0))
continue; /* Already handled */
switch (pargs.r_opt)
{
case aServer:
case aDaemon:
case aService:
case aShutdown:
case aFlush:
case aListCRLs:
case aLoadCRL:
case aFetchCRL:
case aGPGConfList:
case aGPGConfTest:
cmd = pargs.r_opt;
break;
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
case oBatch: opt.batch=1; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break;
case oOptions:
/* Config files may not be nested (silently ignore them) */
if (!configfp)
{
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoGreeting: nogreeting = 1; break;
case oNoVerbose: opt.verbose = 0; break;
case oNoOptions: break; /* no-options */
case oHomedir: /* Ignore this option here. */; break;
case oNoDetach: nodetach = 1; break;
case oLogFile: logfile = pargs.r.ret_str; break;
case oCsh: csh_style = 1; break;
case oSh: csh_style = 0; break;
case oLDAPFile:
# if USE_LDAP
ldapfile = pargs.r.ret_str;
# endif /*USE_LDAP*/
break;
case oLDAPAddServers: opt.add_new_ldapservers = 1; break;
case oLDAPTimeout:
opt.ldaptimeout = pargs.r.ret_int;
break;
case oFakedSystemTime:
gnupg_set_time ((time_t)pargs.r.ret_ulong, 0);
break;
case oForce: opt.force = 1; break;
case oSocketName: socket_name = pargs.r.ret_str; break;
default : pargs.err = configfp? 1:2; break;
}
}
if (configfp)
{
fclose (configfp);
configfp = NULL;
/* Keep a copy of the name so that it can be read on SIGHUP. */
opt.config_filename = configname;
configname = NULL;
goto next_pass;
}
xfree (configname);
configname = NULL;
if (log_get_errorcount(0))
exit(2);
if (nogreeting )
greeting = 0;
if (!opt.homedir_cache)
opt.homedir_cache = xstrdup (gnupg_homedir ());
if (greeting)
{
es_fprintf (es_stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
es_fprintf (es_stderr, "%s\n", strusage(15) );
}
#ifdef IS_DEVELOPMENT_VERSION
log_info ("NOTE: this is a development version!\n");
#endif
if (opt.use_tor)
{
log_info ("WARNING: ***************************************\n");
log_info ("WARNING: Tor mode (--use-tor) MAY NOT FULLY WORK!\n");
log_info ("WARNING: ***************************************\n");
}
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
if (!access ("/etc/"DIRMNGR_NAME, F_OK)
&& !strncmp (gnupg_homedir (), "/etc/", 5))
log_info
("NOTE: DirMngr is now a proper part of %s. The configuration and"
" other directory names changed. Please check that no other version"
" of dirmngr is still installed. To disable this warning, remove the"
" directory '/etc/dirmngr'.\n", GNUPG_NAME);
if (gnupg_faked_time_p ())
{
gnupg_isotime_t tbuf;
log_info (_("WARNING: running with faked system time: "));
gnupg_get_isotime (tbuf);
dump_isotime (tbuf);
log_printf ("\n");
}
set_debug ();
set_tor_mode ();
/* Get LDAP server list from file. */
#if USE_LDAP
if (!ldapfile)
{
ldapfile = make_filename (gnupg_homedir (),
opt.system_daemon?
"ldapservers.conf":"dirmngr_ldapservers.conf",
NULL);
opt.ldapservers = parse_ldapserver_file (ldapfile);
xfree (ldapfile);
}
else
opt.ldapservers = parse_ldapserver_file (ldapfile);
#endif /*USE_LDAP*/
#ifndef HAVE_W32_SYSTEM
/* We need to ignore the PIPE signal because the we might log to a
socket and that code handles EPIPE properly. The ldap wrapper
also requires us to ignore this silly signal. Assuan would set
this signal to ignore anyway.*/
signal (SIGPIPE, SIG_IGN);
#endif
/* Ready. Now to our duties. */
if (!cmd && opt.system_service)
cmd = aDaemon;
else if (!cmd)
cmd = aServer;
rc = 0;
if (cmd == aServer)
{
/* Note that this server mode is mainly useful for debugging. */
if (argc)
wrong_args ("--server");
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, 2|4);
}
if (debug_wait)
{
log_debug ("waiting for debugger - my pid is %u .....\n",
(unsigned int)getpid());
gnupg_sleep (debug_wait);
log_debug ("... okay\n");
}
#if USE_LDAP
ldap_wrapper_launch_thread ();
#endif /*USE_LDAP*/
cert_cache_init ();
crl_cache_init ();
start_command_handler (ASSUAN_INVALID_FD);
shutdown_reaper ();
}
else if (cmd == aDaemon)
{
assuan_fd_t fd;
pid_t pid;
int len;
struct sockaddr_un serv_addr;
if (argc)
wrong_args ("--daemon");
/* Now start with logging to a file if this is desired. */
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
|GPGRT_LOG_WITH_TIME
|GPGRT_LOG_WITH_PID));
current_logfile = xstrdup (logfile);
}
#ifndef HAVE_W32_SYSTEM
if (strchr (socket_name, ':'))
{
log_error (_("colons are not allowed in the socket name\n"));
dirmngr_exit (1);
}
#endif
fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
if (fd == ASSUAN_INVALID_FD)
{
log_error (_("can't create socket: %s\n"), strerror (errno));
cleanup ();
dirmngr_exit (1);
}
{
int redirected;
if (assuan_sock_set_sockaddr_un (socket_name,
(struct sockaddr*)&serv_addr,
&redirected))
{
if (errno == ENAMETOOLONG)
log_error (_("socket name '%s' is too long\n"), socket_name);
else
log_error ("error preparing socket '%s': %s\n",
socket_name,
gpg_strerror (gpg_error_from_syserror ()));
dirmngr_exit (1);
}
if (redirected)
{
redir_socket_name = xstrdup (serv_addr.sun_path);
if (opt.verbose)
log_info ("redirecting socket '%s' to '%s'\n",
socket_name, redir_socket_name);
}
}
len = SUN_LEN (&serv_addr);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len);
if (rc == -1
&& (errno == EADDRINUSE
#ifdef HAVE_W32_SYSTEM
|| errno == EEXIST
#endif
))
{
/* Fixme: We should test whether a dirmngr is already running. */
gnupg_remove (redir_socket_name? redir_socket_name : socket_name);
rc = assuan_sock_bind (fd, (struct sockaddr*) &serv_addr, len);
}
if (rc != -1
&& (rc = assuan_sock_get_nonce ((struct sockaddr*) &serv_addr, len, &socket_nonce)))
log_error (_("error getting nonce for the socket\n"));
if (rc == -1)
{
log_error (_("error binding socket to '%s': %s\n"),
serv_addr.sun_path,
gpg_strerror (gpg_error_from_errno (errno)));
assuan_sock_close (fd);
dirmngr_exit (1);
}
cleanup_socket = 1;
if (gnupg_chmod (serv_addr.sun_path, "-rwx"))
log_error (_("can't set permissions of '%s': %s\n"),
serv_addr.sun_path, strerror (errno));
if (listen (FD2INT (fd), 5) == -1)
{
log_error (_("listen() failed: %s\n"), strerror (errno));
assuan_sock_close (fd);
dirmngr_exit (1);
}
if (opt.verbose)
log_info (_("listening on socket '%s'\n"), serv_addr.sun_path);
es_fflush (NULL);
/* Note: We keep the dirmngr_info output only for the sake of
existing scripts which might use this to detect a successful
start of the dirmngr. */
#ifdef HAVE_W32_SYSTEM
(void)csh_style;
(void)nodetach;
pid = getpid ();
es_printf ("set %s=%s;%lu;1\n",
DIRMNGR_INFO_NAME, socket_name, (ulong) pid);
#else
pid = fork();
if (pid == (pid_t)-1)
{
log_fatal (_("error forking process: %s\n"), strerror (errno));
dirmngr_exit (1);
}
if (pid)
{ /* We are the parent */
char *infostr;
/* Don't let cleanup() remove the socket - the child is
responsible for doing that. */
cleanup_socket = 0;
close (fd);
/* Create the info string: :: */
if (asprintf (&infostr, "%s=%s:%lu:1",
DIRMNGR_INFO_NAME, serv_addr.sun_path, (ulong)pid ) < 0)
{
log_error (_("out of core\n"));
kill (pid, SIGTERM);
dirmngr_exit (1);
}
/* Print the environment string, so that the caller can use
shell's eval to set it. But see above. */
if (csh_style)
{
*strchr (infostr, '=') = ' ';
es_printf ( "setenv %s;\n", infostr);
}
else
{
es_printf ( "%s; export %s;\n", infostr, DIRMNGR_INFO_NAME);
}
free (infostr);
exit (0);
/*NEVER REACHED*/
} /* end parent */
/*
This is the child
*/
/* Detach from tty and put process into a new session */
if (!nodetach )
{
int i;
unsigned int oldflags;
/* Close stdin, stdout and stderr unless it is the log stream */
for (i=0; i <= 2; i++)
{
if (!log_test_fd (i) && i != fd )
close (i);
}
if (setsid() == -1)
{
log_error ("setsid() failed: %s\n", strerror(errno) );
dirmngr_exit (1);
}
log_get_prefix (&oldflags);
log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
opt.running_detached = 1;
if (chdir("/"))
{
log_error ("chdir to / failed: %s\n", strerror (errno));
dirmngr_exit (1);
}
}
#endif
#if USE_LDAP
ldap_wrapper_launch_thread ();
#endif /*USE_LDAP*/
cert_cache_init ();
crl_cache_init ();
#ifdef USE_W32_SERVICE
if (opt.system_service)
{
service_status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (service_handle, &service_status);
}
#endif
handle_connections (fd);
assuan_sock_close (fd);
shutdown_reaper ();
#ifdef USE_W32_SERVICE
if (opt.system_service)
{
service_status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (service_handle, &service_status);
}
#endif
}
else if (cmd == aListCRLs)
{
/* Just list the CRL cache and exit. */
if (argc)
wrong_args ("--list-crls");
#if USE_LDAP
ldap_wrapper_launch_thread ();
#endif /*USE_LDAP*/
crl_cache_init ();
crl_cache_list (es_stdout);
}
else if (cmd == aLoadCRL)
{
struct server_control_s ctrlbuf;
memset (&ctrlbuf, 0, sizeof ctrlbuf);
dirmngr_init_default_ctrl (&ctrlbuf);
#if USE_LDAP
ldap_wrapper_launch_thread ();
#endif /*USE_LDAP*/
cert_cache_init ();
crl_cache_init ();
if (!argc)
rc = crl_cache_load (&ctrlbuf, NULL);
else
{
for (; !rc && argc; argc--, argv++)
rc = crl_cache_load (&ctrlbuf, *argv);
}
dirmngr_deinit_default_ctrl (&ctrlbuf);
}
else if (cmd == aFetchCRL)
{
ksba_reader_t reader;
struct server_control_s ctrlbuf;
if (argc != 1)
wrong_args ("--fetch-crl URL");
memset (&ctrlbuf, 0, sizeof ctrlbuf);
dirmngr_init_default_ctrl (&ctrlbuf);
#if USE_LDAP
ldap_wrapper_launch_thread ();
#endif /*USE_LDAP*/
cert_cache_init ();
crl_cache_init ();
rc = crl_fetch (&ctrlbuf, argv[0], &reader);
if (rc)
log_error (_("fetching CRL from '%s' failed: %s\n"),
argv[0], gpg_strerror (rc));
else
{
rc = crl_cache_insert (&ctrlbuf, argv[0], reader);
if (rc)
log_error (_("processing CRL from '%s' failed: %s\n"),
argv[0], gpg_strerror (rc));
crl_close_reader (reader);
}
dirmngr_deinit_default_ctrl (&ctrlbuf);
}
else if (cmd == aFlush)
{
/* Delete cache and exit. */
if (argc)
wrong_args ("--flush");
rc = crl_cache_flush();
}
else if (cmd == aGPGConfTest)
dirmngr_exit (0);
else if (cmd == aGPGConfList)
{
unsigned long flags = 0;
char *filename;
char *filename_esc;
#ifdef HAVE_W32_SYSTEM
/* On Windows systems, dirmngr always runs as system daemon, and
the per-user configuration is never used. So we short-cut
everything to use the global system configuration of dirmngr
above, and here we set the no change flag to make these
read-only. */
flags |= GC_OPT_FLAG_NO_CHANGE;
#endif
/* First the configuration file. This is not an option, but it
is vital information for GPG Conf. */
if (!opt.config_filename)
opt.config_filename = make_filename (gnupg_homedir (),
"dirmngr.conf", NULL );
filename = percent_escape (opt.config_filename, NULL);
es_printf ("gpgconf-dirmngr.conf:%lu:\"%s\n",
GC_OPT_FLAG_DEFAULT, filename);
xfree (filename);
es_printf ("verbose:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("quiet:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none\n", flags | GC_OPT_FLAG_DEFAULT);
es_printf ("log-file:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("force:%lu:\n", flags | GC_OPT_FLAG_NONE);
/* --csh and --sh are mutually exclusive, something we can not
express in GPG Conf. --options is only usable from the
command line, really. --debug-all interacts with --debug,
and having both of them is thus problematic. --no-detach is
also only usable on the command line. --batch is unused. */
filename = make_filename (gnupg_homedir (),
opt.system_daemon?
"ldapservers.conf":"dirmngr_ldapservers.conf",
NULL);
filename_esc = percent_escape (filename, NULL);
es_printf ("ldapserverlist-file:%lu:\"%s\n", flags | GC_OPT_FLAG_DEFAULT,
filename_esc);
xfree (filename_esc);
xfree (filename);
es_printf ("ldaptimeout:%lu:%u\n",
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_LDAP_TIMEOUT);
es_printf ("max-replies:%lu:%u\n",
flags | GC_OPT_FLAG_DEFAULT, DEFAULT_MAX_REPLIES);
es_printf ("allow-ocsp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ocsp-responder:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ocsp-signer:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("faked-system-time:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("no-greeting:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("disable-http:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("disable-ldap:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("honor-http-proxy:%lu\n", flags | GC_OPT_FLAG_NONE);
es_printf ("http-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("only-ldap-proxy:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-ldap-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-http-dp:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("ignore-ocsp-service-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
/* Note: The next one is to fix a typo in gpgconf - should be
removed eventually. */
es_printf ("ignore-ocsp-servic-url:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("use-tor:%lu:\n", flags | GC_OPT_FLAG_NONE);
es_printf ("keyserver:%lu:\n", flags | GC_OPT_FLAG_NONE);
}
cleanup ();
return !!rc;
}
#ifdef USE_W32_SERVICE
static void WINAPI
call_real_main (DWORD argc, LPSTR *argv)
{
real_main (argc, argv);
}
int
main (int argc, char *argv[])
{
int i;
/* Find out if we run in daemon mode or on the command line. */
for (i = 1; i < argc; i++)
if (!strcmp (argv[i], "--service"))
{
opt.system_service = 1;
opt.system_daemon = 1;
break;
}
if (!opt.system_service)
return real_main (argc, argv);
else
{
SERVICE_TABLE_ENTRY DispatchTable [] =
{
{ "DirMngr", &call_real_main },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher (DispatchTable))
return 1;
return 0;
}
}
#endif /*USE_W32_SERVICE*/
static void
cleanup (void)
{
crl_cache_deinit ();
cert_cache_deinit (1);
#if USE_LDAP
ldapserver_list_free (opt.ldapservers);
#endif /*USE_LDAP*/
opt.ldapservers = NULL;
if (cleanup_socket)
{
cleanup_socket = 0;
if (redir_socket_name)
gnupg_remove (redir_socket_name);
else if (socket_name && *socket_name)
gnupg_remove (socket_name);
}
}
void
dirmngr_exit (int rc)
{
cleanup ();
exit (rc);
}
void
dirmngr_init_default_ctrl (ctrl_t ctrl)
{
if (opt.http_proxy)
ctrl->http_proxy = xstrdup (opt.http_proxy);
}
void
dirmngr_deinit_default_ctrl (ctrl_t ctrl)
{
if (!ctrl)
return;
xfree (ctrl->http_proxy);
ctrl->http_proxy = NULL;
}
/* Create a list of LDAP servers from the file FILENAME. Returns the
list or NULL in case of errors.
The format fo such a file is line oriented where empty lines and
lines starting with a hash mark are ignored. All other lines are
assumed to be colon seprated with these fields:
1. field: Hostname
2. field: Portnumber
3. field: Username
4. field: Password
5. field: Base DN
*/
#if USE_LDAP
static ldap_server_t
parse_ldapserver_file (const char* filename)
{
char buffer[1024];
char *p;
ldap_server_t server, serverstart, *serverend;
int c;
unsigned int lineno = 0;
estream_t fp;
fp = es_fopen (filename, "r");
if (!fp)
{
log_error (_("error opening '%s': %s\n"), filename, strerror (errno));
return NULL;
}
serverstart = NULL;
serverend = &serverstart;
while (es_fgets (buffer, sizeof buffer, fp))
{
lineno++;
if (!*buffer || buffer[strlen(buffer)-1] != '\n')
{
if (*buffer && es_feof (fp))
; /* Last line not terminated - continue. */
else
{
log_error (_("%s:%u: line too long - skipped\n"),
filename, lineno);
while ( (c=es_fgetc (fp)) != EOF && c != '\n')
; /* Skip until end of line. */
continue;
}
}
/* Skip empty and comment lines.*/
for (p=buffer; spacep (p); p++)
;
if (!*p || *p == '\n' || *p == '#')
continue;
/* Parse the colon separated fields. */
server = ldapserver_parse_one (buffer, filename, lineno);
if (server)
{
*serverend = server;
serverend = &server->next;
}
}
if (es_ferror (fp))
log_error (_("error reading '%s': %s\n"), filename, strerror (errno));
es_fclose (fp);
return serverstart;
}
#endif /*USE_LDAP*/
static fingerprint_list_t
parse_ocsp_signer (const char *string)
{
gpg_error_t err;
char *fname;
estream_t fp;
char line[256];
char *p;
fingerprint_list_t list, *list_tail, item;
unsigned int lnr = 0;
int c, i, j;
int errflag = 0;
/* Check whether this is not a filename and treat it as a direct
fingerprint specification. */
if (!strpbrk (string, "/.~\\"))
{
item = xcalloc (1, sizeof *item);
for (i=j=0; (string[i] == ':' || hexdigitp (string+i)) && j < 40; i++)
if ( string[i] != ':' )
item->hexfpr[j++] = string[i] >= 'a'? (string[i] & 0xdf): string[i];
item->hexfpr[j] = 0;
if (j != 40 || !(spacep (string+i) || !string[i]))
{
log_error (_("%s:%u: invalid fingerprint detected\n"),
"--ocsp-signer", 0);
xfree (item);
return NULL;
}
return item;
}
/* Well, it is a filename. */
if (*string == '/' || (*string == '~' && string[1] == '/'))
fname = make_filename (string, NULL);
else
{
if (string[0] == '.' && string[1] == '/' )
string += 2;
fname = make_filename (gnupg_homedir (), string, NULL);
}
fp = es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
xfree (fname);
return NULL;
}
list = NULL;
list_tail = &list;
for (;;)
{
if (!es_fgets (line, DIM(line)-1, fp) )
{
if (!es_feof (fp))
{
err = gpg_error_from_syserror ();
log_error (_("%s:%u: read error: %s\n"),
fname, lnr, gpg_strerror (err));
errflag = 1;
}
es_fclose (fp);
if (errflag)
{
while (list)
{
fingerprint_list_t tmp = list->next;
xfree (list);
list = tmp;
}
}
xfree (fname);
return list; /* Ready. */
}
lnr++;
if (!*line || line[strlen(line)-1] != '\n')
{
/* Eat until end of line. */
while ( (c=es_getc (fp)) != EOF && c != '\n')
;
err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
/* */: GPG_ERR_INCOMPLETE_LINE);
log_error (_("%s:%u: read error: %s\n"),
fname, lnr, gpg_strerror (err));
errflag = 1;
continue;
}
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
if (!*p || *p == '\n' || *p == '#')
continue;
item = xcalloc (1, sizeof *item);
*list_tail = item;
list_tail = &item->next;
for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
if ( p[i] != ':' )
item->hexfpr[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
item->hexfpr[j] = 0;
if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
{
log_error (_("%s:%u: invalid fingerprint detected\n"), fname, lnr);
errflag = 1;
}
i++;
while (spacep (p+i))
i++;
if (p[i] && p[i] != '\n')
log_info (_("%s:%u: garbage at end of line ignored\n"), fname, lnr);
}
/*NOTREACHED*/
}
/*
Stuff used in daemon mode.
*/
/* Reread parts of the configuration. Note, that this function is
obviously not thread-safe and should only be called from the NPTH
signal handler.
Fixme: Due to the way the argument parsing works, we create a
memory leak here for all string type arguments. There is currently
no clean way to tell whether the memory for the argument has been
allocated or points into the process' original arguments. Unless
we have a mechanism to tell this, we need to live on with this. */
static void
reread_configuration (void)
{
ARGPARSE_ARGS pargs;
FILE *fp;
unsigned int configlineno = 0;
int dummy;
if (!opt.config_filename)
return; /* No config file. */
fp = fopen (opt.config_filename, "r");
if (!fp)
{
log_error (_("option file '%s': %s\n"),
opt.config_filename, strerror(errno) );
return;
}
parse_rereadable_options (NULL, 1); /* Start from the default values. */
memset (&pargs, 0, sizeof pargs);
dummy = 0;
pargs.argc = &dummy;
pargs.flags = 1; /* do not remove the args */
while (optfile_parse (fp, opt.config_filename, &configlineno, &pargs, opts) )
{
if (pargs.r_opt < -1)
pargs.err = 1; /* Print a warning. */
else /* Try to parse this option - ignore unchangeable ones. */
parse_rereadable_options (&pargs, 1);
}
fclose (fp);
set_debug ();
set_tor_mode ();
}
/* A global function which allows us to trigger the reload stuff from
other places. */
void
dirmngr_sighup_action (void)
{
log_info (_("SIGHUP received - "
"re-reading configuration and flushing caches\n"));
reread_configuration ();
cert_cache_deinit (0);
crl_cache_deinit ();
cert_cache_init ();
crl_cache_init ();
}
/* The signal handler. */
#ifndef HAVE_W32_SYSTEM
static void
handle_signal (int signo)
{
switch (signo)
{
case SIGHUP:
dirmngr_sighup_action ();
break;
case SIGUSR1:
cert_cache_print_stats ();
break;
case SIGUSR2:
log_info (_("SIGUSR2 received - no action defined\n"));
break;
case SIGTERM:
if (!shutdown_pending)
log_info (_("SIGTERM received - shutting down ...\n"));
else
log_info (_("SIGTERM received - still %d active connections\n"),
active_connections);
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info (_("shutdown forced\n"));
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
dirmngr_exit (0);
}
break;
case SIGINT:
log_info (_("SIGINT received - immediate shutdown\n"));
log_info( "%s %s stopped\n", strusage(11), strusage(13));
cleanup ();
dirmngr_exit (0);
break;
default:
log_info (_("signal %d received - no action defined\n"), signo);
}
}
#endif /*!HAVE_W32_SYSTEM*/
/* Thread to do the housekeeping. */
static void *
housekeeping_thread (void *arg)
{
static int sentinel;
time_t curtime;
(void)arg;
curtime = gnupg_get_time ();
if (sentinel)
{
log_info ("housekeeping is already going on\n");
return NULL;
}
sentinel++;
if (opt.verbose)
log_info ("starting housekeeping\n");
ks_hkp_housekeeping (curtime);
if (opt.verbose)
log_info ("ready with housekeeping\n");
sentinel--;
return NULL;
}
#if GPGRT_GCC_HAVE_PUSH_PRAGMA
# pragma GCC push_options
# pragma GCC optimize ("no-strict-overflow")
#endif
static int
time_for_housekeeping_p (time_t curtime)
{
static time_t last_housekeeping;
if (!last_housekeeping)
last_housekeeping = curtime;
if (last_housekeeping + HOUSEKEEPING_INTERVAL <= curtime
|| last_housekeeping > curtime /*(be prepared for y2038)*/)
{
last_housekeeping = curtime;
return 1;
}
return 0;
}
#if GPGRT_GCC_HAVE_PUSH_PRAGMA
# pragma GCC pop_options
#endif
/* This is the worker for the ticker. It is called every few seconds
and may only do fast operations. */
static void
handle_tick (void)
{
/* Under Windows we don't use signals and need a way for the loop to
check for the shutdown flag. */
#ifdef HAVE_W32_SYSTEM
if (shutdown_pending)
log_info (_("SIGTERM received - shutting down ...\n"));
if (shutdown_pending > 2)
{
log_info (_("shutdown forced\n"));
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
cleanup ();
dirmngr_exit (0);
}
#endif /*HAVE_W32_SYSTEM*/
if (time_for_housekeeping_p (gnupg_get_time ()))
{
npth_t thread;
npth_attr_t tattr;
int err;
err = npth_attr_init (&tattr);
if (err)
log_error ("error preparing housekeeping thread: %s\n", strerror (err));
else
{
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
err = npth_create (&thread, &tattr, housekeeping_thread, NULL);
if (err)
log_error ("error spawning housekeeping thread: %s\n",
strerror (err));
npth_attr_destroy (&tattr);
}
}
}
/* Check the nonce on a new connection. This is a NOP unless we are
using our Unix domain socket emulation under Windows. */
static int
check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce)
{
if (assuan_sock_check_nonce (fd, nonce))
{
log_info (_("error reading nonce on fd %d: %s\n"),
FD2INT (fd), strerror (errno));
assuan_sock_close (fd);
return -1;
}
else
return 0;
}
/* Helper to call a connection's main function. */
static void *
start_connection_thread (void *arg)
{
union int_and_ptr_u argval;
gnupg_fd_t fd;
memset (&argval, 0, sizeof argval);
argval.aptr = arg;
fd = argval.afd;
if (check_nonce (fd, &socket_nonce))
{
log_error ("handler nonce check FAILED\n");
return NULL;
}
#ifndef HAVE_W32_SYSTEM
npth_setspecific (my_tlskey_current_fd, argval.aptr);
#endif
active_connections++;
if (opt.verbose)
log_info (_("handler for fd %d started\n"), FD2INT (fd));
start_command_handler (fd);
if (opt.verbose)
log_info (_("handler for fd %d terminated\n"), FD2INT (fd));
active_connections--;
#ifndef HAVE_W32_SYSTEM
argval.afd = ASSUAN_INVALID_FD;
npth_setspecific (my_tlskey_current_fd, argval.aptr);
#endif
return NULL;
}
/* Main loop in daemon mode. */
static void
handle_connections (assuan_fd_t listen_fd)
{
npth_attr_t tattr;
#ifndef HAVE_W32_SYSTEM
int signo;
#endif
struct sockaddr_un paddr;
socklen_t plen = sizeof( paddr );
gnupg_fd_t fd;
int nfd, ret;
fd_set fdset, read_fdset;
struct timespec abstime;
struct timespec curtime;
struct timespec timeout;
int saved_errno;
npth_attr_init (&tattr);
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
#ifndef HAVE_W32_SYSTEM /* FIXME */
npth_sigev_init ();
npth_sigev_add (SIGHUP);
npth_sigev_add (SIGUSR1);
npth_sigev_add (SIGUSR2);
npth_sigev_add (SIGINT);
npth_sigev_add (SIGTERM);
npth_sigev_fini ();
#endif
/* Setup the fdset. It has only one member. This is because we use
pth_select instead of pth_accept to properly sync timeouts with
to full second. */
FD_ZERO (&fdset);
FD_SET (FD2INT (listen_fd), &fdset);
nfd = FD2INT (listen_fd);
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
/* Main loop. */
for (;;)
{
/* Shutdown test. */
if (shutdown_pending)
{
if (!active_connections)
break; /* ready */
/* Do not accept new connections but keep on running the
loop to cope with the timer events. */
FD_ZERO (&fdset);
}
/* Take a copy of the fdset. */
read_fdset = fdset;
npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <)))
{
/* Timeout. */
handle_tick ();
npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL;
}
npth_timersub (&abstime, &curtime, &timeout);
#ifndef HAVE_W32_SYSTEM
ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
saved_errno = errno;
while (npth_sigev_get_pending(&signo))
handle_signal (signo);
#else
ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
saved_errno = errno;
#endif
if (ret == -1 && saved_errno != EINTR)
{
log_error (_("npth_pselect failed: %s - waiting 1s\n"),
strerror (saved_errno));
npth_sleep (1);
continue;
}
if (ret <= 0)
/* Interrupt or timeout. Will be handled when calculating the
next timeout. */
continue;
if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
{
plen = sizeof paddr;
fd = INT2FD (npth_accept (FD2INT(listen_fd),
(struct sockaddr *)&paddr, &plen));
if (fd == GNUPG_INVALID_FD)
{
log_error ("accept failed: %s\n", strerror (errno));
}
else
{
char threadname[50];
union int_and_ptr_u argval;
npth_t thread;
memset (&argval, 0, sizeof argval);
argval.afd = fd;
snprintf (threadname, sizeof threadname-1,
"conn fd=%d", FD2INT(fd));
threadname[sizeof threadname -1] = 0;
ret = npth_create (&thread, &tattr,
start_connection_thread, argval.aptr);
if (ret)
{
log_error ("error spawning connection handler: %s\n",
strerror (ret) );
assuan_sock_close (fd);
}
npth_setname_np (thread, threadname);
}
fd = GNUPG_INVALID_FD;
}
}
npth_attr_destroy (&tattr);
cleanup ();
log_info ("%s %s stopped\n", strusage(11), strusage(13));
}
diff --git a/g10/gpg.c b/g10/gpg.c
index b33b61b7c..91f34720f 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,5129 +1,5121 @@
/* gpg.c - The GnuPG utility (main for gpg)
* Copyright (C) 1998-2011 Free Software Foundation, Inc.
* Copyright (C) 1997-2016 Werner Koch
* Copyright (C) 2015-2016 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_STAT
#include /* for stat() */
#endif
#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include
#include "../common/iobuf.h"
#include "util.h"
#include "packet.h"
#include "membuf.h"
#include "main.h"
#include "options.h"
#include "keydb.h"
#include "trustdb.h"
#include "filter.h"
#include "ttyio.h"
#include "i18n.h"
#include "sysutils.h"
#include "status.h"
#include "keyserver-internal.h"
#include "exec.h"
#include "gc-opt-flags.h"
#include "asshelp.h"
#include "call-dirmngr.h"
#include "tofu.h"
#include "../common/init.h"
#include "../common/shareddefs.h"
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
#define MY_O_BINARY O_BINARY
#ifndef S_IRGRP
# define S_IRGRP 0
# define S_IWGRP 0
#endif
#else
#define MY_O_BINARY 0
#endif
enum cmd_and_opt_values
{
aNull = 0,
oArmor = 'a',
aDetachedSign = 'b',
aSym = 'c',
aDecrypt = 'd',
aEncr = 'e',
oRecipientFile = 'f',
oHiddenRecipientFile = 'F',
oInteractive = 'i',
aListKeys = 'k',
oDryRun = 'n',
oOutput = 'o',
oQuiet = 'q',
oRecipient = 'r',
oHiddenRecipient = 'R',
aSign = 's',
oTextmodeShort= 't',
oLocalUser = 'u',
oVerbose = 'v',
oCompress = 'z',
oSetNotation = 'N',
aListSecretKeys = 'K',
oBatch = 500,
oMaxOutput,
oSigNotation,
oCertNotation,
oShowNotation,
oNoShowNotation,
aEncrFiles,
aEncrSym,
aDecryptFiles,
aClearsign,
aStore,
aQuickKeygen,
aFullKeygen,
aKeygen,
aSignEncr,
aSignEncrSym,
aSignSym,
aSignKey,
aLSignKey,
aQuickSignKey,
aQuickLSignKey,
aQuickAddUid,
aQuickAddKey,
aQuickRevUid,
aListConfig,
aListGcryptConfig,
aGPGConfList,
aGPGConfTest,
aListPackets,
aEditKey,
aDeleteKeys,
aDeleteSecretKeys,
aDeleteSecretAndPublicKeys,
aImport,
aFastImport,
aVerify,
aVerifyFiles,
aListSigs,
aSendKeys,
aRecvKeys,
aLocateKeys,
aSearchKeys,
aRefreshKeys,
aFetchKeys,
aExport,
aExportSecret,
aExportSecretSub,
aExportSshKey,
aCheckKeys,
aGenRevoke,
aDesigRevoke,
aPrimegen,
aPrintMD,
aPrintMDs,
aCheckTrustDB,
aUpdateTrustDB,
aFixTrustDB,
aListTrustDB,
aListTrustPath,
aExportOwnerTrust,
aImportOwnerTrust,
aDeArmor,
aEnArmor,
aGenRandom,
aRebuildKeydbCaches,
aCardStatus,
aCardEdit,
aChangePIN,
aPasswd,
aServer,
aTOFUPolicy,
oMimemode,
oTextmode,
oNoTextmode,
oExpert,
oNoExpert,
oDefSigExpire,
oAskSigExpire,
oNoAskSigExpire,
oDefCertExpire,
oAskCertExpire,
oNoAskCertExpire,
oDefCertLevel,
oMinCertLevel,
oAskCertLevel,
oNoAskCertLevel,
oFingerprint,
oWithFingerprint,
oWithSubkeyFingerprint,
oWithICAOSpelling,
oWithKeygrip,
oWithSecret,
oWithWKDHash,
oAnswerYes,
oAnswerNo,
oKeyring,
oPrimaryKeyring,
oSecretKeyring,
oShowKeyring,
oDefaultKey,
oDefRecipient,
oDefRecipientSelf,
oNoDefRecipient,
oTrySecretKey,
oOptions,
oDebug,
oDebugLevel,
oDebugAll,
oDebugIOLBF,
oStatusFD,
oStatusFile,
oAttributeFD,
oAttributeFile,
oEmitVersion,
oNoEmitVersion,
oCompletesNeeded,
oMarginalsNeeded,
oMaxCertDepth,
oLoadExtension,
oGnuPG,
oRFC2440,
oRFC4880,
oRFC4880bis,
oOpenPGP,
oPGP6,
oPGP7,
oPGP8,
oRFC2440Text,
oNoRFC2440Text,
oCipherAlgo,
oDigestAlgo,
oCertDigestAlgo,
oCompressAlgo,
oCompressLevel,
oBZ2CompressLevel,
oBZ2DecompressLowmem,
oPassphrase,
oPassphraseFD,
oPassphraseFile,
oPassphraseRepeat,
oPinentryMode,
oCommandFD,
oCommandFile,
oQuickRandom,
oNoVerbose,
oTrustDBName,
oNoSecmemWarn,
oRequireSecmem,
oNoRequireSecmem,
oNoPermissionWarn,
oNoMDCWarn,
oNoArmor,
oNoDefKeyring,
oNoKeyring,
oNoGreeting,
oNoTTY,
oNoOptions,
oNoBatch,
oHomedir,
oWithColons,
oWithKeyData,
oWithSigList,
oWithSigCheck,
oSkipVerify,
oSkipHiddenRecipients,
oNoSkipHiddenRecipients,
oAlwaysTrust,
oTrustModel,
oForceOwnertrust,
oSetFilename,
oForYourEyesOnly,
oNoForYourEyesOnly,
oSetPolicyURL,
oSigPolicyURL,
oCertPolicyURL,
oShowPolicyURL,
oNoShowPolicyURL,
oSigKeyserverURL,
oUseEmbeddedFilename,
oNoUseEmbeddedFilename,
oComment,
oDefaultComment,
oNoComments,
oThrowKeyids,
oNoThrowKeyids,
oShowPhotos,
oNoShowPhotos,
oPhotoViewer,
oForceMDC,
oNoForceMDC,
oDisableMDC,
oNoDisableMDC,
oS2KMode,
oS2KDigest,
oS2KCipher,
oS2KCount,
oDisplayCharset,
oNotDashEscaped,
oEscapeFrom,
oNoEscapeFrom,
oLockOnce,
oLockMultiple,
oLockNever,
oKeyServer,
oKeyServerOptions,
oImportOptions,
oImportFilter,
oExportOptions,
oExportFilter,
oListOptions,
oVerifyOptions,
oTempDir,
oExecPath,
oEncryptTo,
oHiddenEncryptTo,
oNoEncryptTo,
oEncryptToDefaultKey,
oLoggerFD,
oLoggerFile,
oUtf8Strings,
oNoUtf8Strings,
oDisableCipherAlgo,
oDisablePubkeyAlgo,
oAllowNonSelfsignedUID,
oNoAllowNonSelfsignedUID,
oAllowFreeformUID,
oNoAllowFreeformUID,
oAllowSecretKeyImport,
oEnableSpecialFilenames,
oNoLiteral,
oSetFilesize,
oHonorHttpProxy,
oFastListMode,
oListOnly,
oIgnoreTimeConflict,
oIgnoreValidFrom,
oIgnoreCrcError,
oIgnoreMDCError,
oShowSessionKey,
oOverrideSessionKey,
oNoRandomSeedFile,
oAutoKeyRetrieve,
oNoAutoKeyRetrieve,
oUseAgent,
oNoUseAgent,
oGpgAgentInfo,
oMergeOnly,
oTryAllSecrets,
oTrustedKey,
oNoExpensiveTrustChecks,
oFixedListMode,
oLegacyListMode,
oNoSigCache,
oAutoCheckTrustDB,
oNoAutoCheckTrustDB,
oPreservePermissions,
oDefaultPreferenceList,
oDefaultKeyserverURL,
oPersonalCipherPreferences,
oPersonalDigestPreferences,
oPersonalCompressPreferences,
oAgentProgram,
oDirmngrProgram,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oGroup,
oUnGroup,
oNoGroups,
oStrict,
oNoStrict,
oMangleDosFilenames,
oNoMangleDosFilenames,
oEnableProgressFilter,
oMultifile,
oKeyidFormat,
oExitOnStatusWriteError,
oLimitCardInsertTries,
oReaderPort,
octapiDriver,
opcscDriver,
oDisableCCID,
oRequireCrossCert,
oNoRequireCrossCert,
oAutoKeyLocate,
oNoAutoKeyLocate,
oAllowMultisigVerification,
oEnableLargeRSA,
oDisableLargeRSA,
oEnableDSA2,
oDisableDSA2,
oAllowMultipleMessages,
oNoAllowMultipleMessages,
oAllowWeakDigestAlgos,
oFakedSystemTime,
oNoAutostart,
oPrintPKARecords,
oPrintDANERecords,
oTOFUDefaultPolicy,
oTOFUDBFormat,
oWeakDigest,
oUnwrap,
oOnlySignTextIDs,
oDisableSignerUID,
oNoop
};
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@Commands:\n ")),
ARGPARSE_c (aSign, "sign", N_("make a signature")),
ARGPARSE_c (aClearsign, "clearsign", N_("make a clear text signature")),
ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")),
ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")),
ARGPARSE_c (aEncrFiles, "encrypt-files", "@"),
ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),
ARGPARSE_c (aStore, "store", "@"),
ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")),
ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"),
ARGPARSE_c (aVerify, "verify" , N_("verify a signature")),
ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ),
ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
ARGPARSE_c (aListKeys, "list-public-keys", "@" ),
ARGPARSE_c (aListSigs, "list-sigs", N_("list keys and signatures")),
ARGPARSE_c (aCheckKeys, "check-sigs",N_("list and check key signatures")),
ARGPARSE_c (oFingerprint, "fingerprint", N_("list keys and fingerprints")),
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
ARGPARSE_c (aKeygen, "gen-key",
N_("generate a new key pair")),
ARGPARSE_c (aQuickKeygen, "quick-gen-key" ,
N_("quickly generate a new key pair")),
ARGPARSE_c (aQuickAddUid, "quick-adduid",
N_("quickly add a new user-id")),
ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"),
ARGPARSE_c (aQuickRevUid, "quick-revuid",
N_("quickly revoke a user-id")),
ARGPARSE_c (aFullKeygen, "full-gen-key" ,
N_("full featured key pair generation")),
ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
ARGPARSE_c (aDeleteKeys,"delete-keys",
N_("remove keys from the public keyring")),
ARGPARSE_c (aDeleteSecretKeys, "delete-secret-keys",
N_("remove keys from the secret keyring")),
ARGPARSE_c (aQuickSignKey, "quick-sign-key" ,
N_("quickly sign a key")),
ARGPARSE_c (aQuickLSignKey, "quick-lsign-key",
N_("quickly sign a key locally")),
ARGPARSE_c (aSignKey, "sign-key" ,N_("sign a key")),
ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")),
ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")),
ARGPARSE_c (aEditKey, "key-edit" ,"@"),
ARGPARSE_c (aPasswd, "passwd", N_("change a passphrase")),
ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ),
ARGPARSE_c (aExport, "export" , N_("export keys") ),
ARGPARSE_c (aSendKeys, "send-keys" , N_("export keys to a keyserver") ),
ARGPARSE_c (aRecvKeys, "recv-keys" , N_("import keys from a keyserver") ),
ARGPARSE_c (aSearchKeys, "search-keys" ,
N_("search for keys on a keyserver") ),
ARGPARSE_c (aRefreshKeys, "refresh-keys",
N_("update all keys from a keyserver")),
ARGPARSE_c (aLocateKeys, "locate-keys", "@"),
ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ),
ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ),
ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ),
ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ),
ARGPARSE_c (aImport, "import", N_("import/merge keys")),
ARGPARSE_c (aFastImport, "fast-import", "@"),
#ifdef ENABLE_CARD_SUPPORT
ARGPARSE_c (aCardStatus, "card-status", N_("print the card status")),
ARGPARSE_c (aCardEdit, "card-edit", N_("change data on a card")),
ARGPARSE_c (aChangePIN, "change-pin", N_("change a card's PIN")),
#endif
ARGPARSE_c (aListConfig, "list-config", "@"),
ARGPARSE_c (aListGcryptConfig, "list-gcrypt-config", "@"),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@" ),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@" ),
ARGPARSE_c (aListPackets, "list-packets","@"),
#ifndef NO_TRUST_MODELS
ARGPARSE_c (aExportOwnerTrust, "export-ownertrust", "@"),
ARGPARSE_c (aImportOwnerTrust, "import-ownertrust", "@"),
ARGPARSE_c (aUpdateTrustDB,"update-trustdb",
N_("update the trust database")),
ARGPARSE_c (aCheckTrustDB, "check-trustdb", "@"),
ARGPARSE_c (aFixTrustDB, "fix-trustdb", "@"),
#endif
ARGPARSE_c (aDeArmor, "dearmor", "@"),
ARGPARSE_c (aDeArmor, "dearmour", "@"),
ARGPARSE_c (aEnArmor, "enarmor", "@"),
ARGPARSE_c (aEnArmor, "enarmour", "@"),
ARGPARSE_c (aPrintMD, "print-md", N_("print message digests")),
ARGPARSE_c (aPrimegen, "gen-prime", "@" ),
ARGPARSE_c (aGenRandom,"gen-random", "@" ),
ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aTOFUPolicy, "tofu-policy",
N_("|VALUE|set the TOFU policy for a key")),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")),
ARGPARSE_s_n (oArmor, "armour", "@"),
ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
ARGPARSE_s_s (oHiddenRecipient, "hidden-recipient", "@"),
ARGPARSE_s_s (oRecipientFile, "recipient-file", "@"),
ARGPARSE_s_s (oHiddenRecipientFile, "hidden-recipient-file", "@"),
ARGPARSE_s_s (oRecipient, "remote-user", "@"), /* (old option name) */
ARGPARSE_s_s (oDefRecipient, "default-recipient", "@"),
ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", "@"),
ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"),
ARGPARSE_s_s (oTempDir, "temp-directory", "@"),
ARGPARSE_s_s (oExecPath, "exec-path", "@"),
ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"),
ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"),
ARGPARSE_s_s (oHiddenEncryptTo, "hidden-encrypt-to", "@"),
ARGPARSE_s_n (oEncryptToDefaultKey, "encrypt-to-default-key", "@"),
ARGPARSE_s_s (oLocalUser, "local-user",
N_("|USER-ID|use USER-ID to sign or decrypt")),
ARGPARSE_s_s (oTrySecretKey, "try-secret-key", "@"),
ARGPARSE_s_i (oCompress, NULL,
N_("|N|set compress level to N (0 disables)")),
ARGPARSE_s_i (oCompressLevel, "compress-level", "@"),
ARGPARSE_s_i (oBZ2CompressLevel, "bzip2-compress-level", "@"),
ARGPARSE_s_n (oBZ2DecompressLowmem, "bzip2-decompress-lowmem", "@"),
ARGPARSE_s_n (oMimemode, "mimemode", "@"),
ARGPARSE_s_n (oTextmodeShort, NULL, "@"),
ARGPARSE_s_n (oTextmode, "textmode", N_("use canonical text mode")),
ARGPARSE_s_n (oNoTextmode, "no-textmode", "@"),
ARGPARSE_s_n (oExpert, "expert", "@"),
ARGPARSE_s_n (oNoExpert, "no-expert", "@"),
ARGPARSE_s_s (oDefSigExpire, "default-sig-expire", "@"),
ARGPARSE_s_n (oAskSigExpire, "ask-sig-expire", "@"),
ARGPARSE_s_n (oNoAskSigExpire, "no-ask-sig-expire", "@"),
ARGPARSE_s_s (oDefCertExpire, "default-cert-expire", "@"),
ARGPARSE_s_n (oAskCertExpire, "ask-cert-expire", "@"),
ARGPARSE_s_n (oNoAskCertExpire, "no-ask-cert-expire", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-level", "@"),
ARGPARSE_s_i (oMinCertLevel, "min-cert-level", "@"),
ARGPARSE_s_n (oAskCertLevel, "ask-cert-level", "@"),
ARGPARSE_s_n (oNoAskCertLevel, "no-ask-cert-level", "@"),
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
ARGPARSE_p_u (oMaxOutput, "max-output", "@"),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", "@"),
ARGPARSE_s_n (oNoTTY, "no-tty", "@"),
ARGPARSE_s_n (oForceMDC, "force-mdc", "@"),
ARGPARSE_s_n (oNoForceMDC, "no-force-mdc", "@"),
ARGPARSE_s_n (oDisableMDC, "disable-mdc", "@"),
ARGPARSE_s_n (oNoDisableMDC, "no-disable-mdc", "@"),
ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"),
ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
ARGPARSE_s_n (oInteractive, "interactive", N_("prompt before overwriting")),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_n (oAnswerYes, "yes", "@"),
ARGPARSE_s_n (oAnswerNo, "no", "@"),
ARGPARSE_s_s (oKeyring, "keyring", "@"),
ARGPARSE_s_s (oPrimaryKeyring, "primary-keyring", "@"),
ARGPARSE_s_s (oSecretKeyring, "secret-keyring", "@"),
ARGPARSE_s_n (oShowKeyring, "show-keyring", "@"),
ARGPARSE_s_s (oDefaultKey, "default-key", "@"),
ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"),
ARGPARSE_s_s (oImportOptions, "import-options", "@"),
ARGPARSE_s_s (oImportFilter, "import-filter", "@"),
ARGPARSE_s_s (oExportOptions, "export-options", "@"),
ARGPARSE_s_s (oExportFilter, "export-filter", "@"),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"),
ARGPARSE_s_s (oDisplayCharset, "display-charset", "@"),
ARGPARSE_s_s (oDisplayCharset, "charset", "@"),
ARGPARSE_s_s (oOptions, "options", "@"),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_n (oDebugIOLBF, "debug-iolbf", "@"),
ARGPARSE_s_i (oStatusFD, "status-fd", "@"),
ARGPARSE_s_s (oStatusFile, "status-file", "@"),
ARGPARSE_s_i (oAttributeFD, "attribute-fd", "@"),
ARGPARSE_s_s (oAttributeFile, "attribute-file", "@"),
ARGPARSE_s_i (oCompletesNeeded, "completes-needed", "@"),
ARGPARSE_s_i (oMarginalsNeeded, "marginals-needed", "@"),
ARGPARSE_s_i (oMaxCertDepth, "max-cert-depth", "@" ),
ARGPARSE_s_s (oTrustedKey, "trusted-key", "@"),
ARGPARSE_s_s (oLoadExtension, "load-extension", "@"), /* Dummy. */
ARGPARSE_s_n (oGnuPG, "gnupg", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp2", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp6", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp7", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp8", "@"),
ARGPARSE_s_n (oRFC2440, "rfc2440", "@"),
ARGPARSE_s_n (oRFC4880, "rfc4880", "@"),
ARGPARSE_s_n (oRFC4880bis, "rfc4880bis", "@"),
ARGPARSE_s_n (oOpenPGP, "openpgp", N_("use strict OpenPGP behavior")),
ARGPARSE_s_n (oPGP6, "pgp6", "@"),
ARGPARSE_s_n (oPGP7, "pgp7", "@"),
ARGPARSE_s_n (oPGP8, "pgp8", "@"),
ARGPARSE_s_n (oRFC2440Text, "rfc2440-text", "@"),
ARGPARSE_s_n (oNoRFC2440Text, "no-rfc2440-text", "@"),
ARGPARSE_s_i (oS2KMode, "s2k-mode", "@"),
ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"),
ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"),
ARGPARSE_s_i (oS2KCount, "s2k-count", "@"),
ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"),
ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"),
ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"),
ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"),
ARGPARSE_s_s (oCompressAlgo, "compression-algo", "@"), /* Alias */
ARGPARSE_s_n (oThrowKeyids, "throw-keyids", "@"),
ARGPARSE_s_n (oNoThrowKeyids, "no-throw-keyids", "@"),
ARGPARSE_s_n (oShowPhotos, "show-photos", "@"),
ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"),
ARGPARSE_s_s (oPhotoViewer, "photo-viewer", "@"),
ARGPARSE_s_s (oSetNotation, "set-notation", "@"),
ARGPARSE_s_s (oSigNotation, "sig-notation", "@"),
ARGPARSE_s_s (oCertNotation, "cert-notation", "@"),
ARGPARSE_group (302, N_(
"@\n(See the man page for a complete listing of all commands and options)\n"
)),
ARGPARSE_group (303, N_("@\nExamples:\n\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
" --clearsign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n")),
/* More hidden commands and options. */
ARGPARSE_c (aPrintMDs, "print-mds", "@"), /* old */
#ifndef NO_TRUST_MODELS
ARGPARSE_c (aListTrustDB, "list-trustdb", "@"),
#endif
/* Not yet used:
ARGPARSE_c (aListTrustPath, "list-trust-path", "@"), */
ARGPARSE_c (aDeleteSecretAndPublicKeys,
"delete-secret-and-public-keys", "@"),
ARGPARSE_c (aRebuildKeydbCaches, "rebuild-keydb-caches", "@"),
ARGPARSE_s_s (oPassphrase, "passphrase", "@"),
ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"),
ARGPARSE_s_s (oPassphraseFile, "passphrase-file", "@"),
ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"),
ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"),
ARGPARSE_s_i (oCommandFD, "command-fd", "@"),
ARGPARSE_s_s (oCommandFile, "command-file", "@"),
ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"),
ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
#ifndef NO_TRUST_MODELS
ARGPARSE_s_s (oTrustDBName, "trustdb-name", "@"),
ARGPARSE_s_n (oAutoCheckTrustDB, "auto-check-trustdb", "@"),
ARGPARSE_s_n (oNoAutoCheckTrustDB, "no-auto-check-trustdb", "@"),
ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"),
#endif
ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
ARGPARSE_s_n (oRequireSecmem, "require-secmem", "@"),
ARGPARSE_s_n (oNoRequireSecmem, "no-require-secmem", "@"),
ARGPARSE_s_n (oNoPermissionWarn, "no-permission-warning", "@"),
ARGPARSE_s_n (oNoMDCWarn, "no-mdc-warning", "@"),
ARGPARSE_s_n (oNoArmor, "no-armor", "@"),
ARGPARSE_s_n (oNoArmor, "no-armour", "@"),
ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"),
ARGPARSE_s_n (oNoKeyring, "no-keyring", "@"),
ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"),
ARGPARSE_s_n (oNoOptions, "no-options", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
ARGPARSE_s_n (oWithColons, "with-colons", "@"),
ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"),
ARGPARSE_s_n (oWithSigList,"with-sig-list", "@"),
ARGPARSE_s_n (oWithSigCheck,"with-sig-check", "@"),
ARGPARSE_s_n (aListKeys, "list-key", "@"), /* alias */
ARGPARSE_s_n (aListSigs, "list-sig", "@"), /* alias */
ARGPARSE_s_n (aCheckKeys, "check-sig", "@"), /* alias */
ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"),
ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"),
ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-check-level", "@"), /* old */
#ifndef NO_TRUST_MODELS
ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"),
#endif
ARGPARSE_s_s (oTrustModel, "trust-model", "@"),
ARGPARSE_s_s (oTOFUDefaultPolicy, "tofu-default-policy", "@"),
ARGPARSE_s_s (oTOFUDBFormat, "tofu-db-format", "@"),
ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
ARGPARSE_s_n (oForYourEyesOnly, "for-your-eyes-only", "@"),
ARGPARSE_s_n (oNoForYourEyesOnly, "no-for-your-eyes-only", "@"),
ARGPARSE_s_s (oSetPolicyURL, "set-policy-url", "@"),
ARGPARSE_s_s (oSigPolicyURL, "sig-policy-url", "@"),
ARGPARSE_s_s (oCertPolicyURL, "cert-policy-url", "@"),
ARGPARSE_s_n (oShowPolicyURL, "show-policy-url", "@"),
ARGPARSE_s_n (oNoShowPolicyURL, "no-show-policy-url", "@"),
ARGPARSE_s_s (oSigKeyserverURL, "sig-keyserver-url", "@"),
ARGPARSE_s_n (oShowNotation, "show-notation", "@"),
ARGPARSE_s_n (oNoShowNotation, "no-show-notation", "@"),
ARGPARSE_s_s (oComment, "comment", "@"),
ARGPARSE_s_n (oDefaultComment, "default-comment", "@"),
ARGPARSE_s_n (oNoComments, "no-comments", "@"),
ARGPARSE_s_n (oEmitVersion, "emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-version", "@"), /* alias */
ARGPARSE_s_n (oNotDashEscaped, "not-dash-escaped", "@"),
ARGPARSE_s_n (oEscapeFrom, "escape-from-lines", "@"),
ARGPARSE_s_n (oNoEscapeFrom, "no-escape-from-lines", "@"),
ARGPARSE_s_n (oLockOnce, "lock-once", "@"),
ARGPARSE_s_n (oLockMultiple, "lock-multiple", "@"),
ARGPARSE_s_n (oLockNever, "lock-never", "@"),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
ARGPARSE_s_s (oLoggerFile, "log-file", "@"),
ARGPARSE_s_s (oLoggerFile, "logger-file", "@"), /* 1.4 compatibility. */
ARGPARSE_s_n (oUseEmbeddedFilename, "use-embedded-filename", "@"),
ARGPARSE_s_n (oNoUseEmbeddedFilename, "no-use-embedded-filename", "@"),
ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"),
ARGPARSE_s_n (oNoUtf8Strings, "no-utf8-strings", "@"),
ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"),
ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"),
ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"),
ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"),
ARGPARSE_s_n (oWithSecret, "with-secret", "@"),
ARGPARSE_s_n (oWithWKDHash, "with-wkd-hash", "@"),
ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"),
ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"),
ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"),
ARGPARSE_s_n (oNoAllowFreeformUID, "no-allow-freeform-uid", "@"),
ARGPARSE_s_n (oNoLiteral, "no-literal", "@"),
ARGPARSE_p_u (oSetFilesize, "set-filesize", "@"),
ARGPARSE_s_n (oFastListMode, "fast-list-mode", "@"),
ARGPARSE_s_n (oFixedListMode, "fixed-list-mode", "@"),
ARGPARSE_s_n (oLegacyListMode, "legacy-list-mode", "@"),
ARGPARSE_s_n (oListOnly, "list-only", "@"),
ARGPARSE_s_n (oPrintPKARecords, "print-pka-records", "@"),
ARGPARSE_s_n (oPrintDANERecords, "print-dane-records", "@"),
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"),
ARGPARSE_s_n (oIgnoreValidFrom, "ignore-valid-from", "@"),
ARGPARSE_s_n (oIgnoreCrcError, "ignore-crc-error", "@"),
ARGPARSE_s_n (oIgnoreMDCError, "ignore-mdc-error", "@"),
ARGPARSE_s_n (oShowSessionKey, "show-session-key", "@"),
ARGPARSE_s_s (oOverrideSessionKey, "override-session-key", "@"),
ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
ARGPARSE_s_n (oAutoKeyRetrieve, "auto-key-retrieve", "@"),
ARGPARSE_s_n (oNoAutoKeyRetrieve, "no-auto-key-retrieve", "@"),
ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"),
ARGPARSE_s_n (oMergeOnly, "merge-only", "@" ),
ARGPARSE_s_n (oAllowSecretKeyImport, "allow-secret-key-import", "@"),
ARGPARSE_s_n (oTryAllSecrets, "try-all-secrets", "@"),
ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
ARGPARSE_s_n (oNoExpensiveTrustChecks, "no-expensive-trust-checks", "@"),
ARGPARSE_s_n (oPreservePermissions, "preserve-permissions", "@"),
ARGPARSE_s_s (oDefaultPreferenceList, "default-preference-list", "@"),
ARGPARSE_s_s (oDefaultKeyserverURL, "default-keyserver-url", "@"),
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-preferences","@"),
ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-preferences","@"),
ARGPARSE_s_s (oPersonalCompressPreferences,
"personal-compress-preferences", "@"),
ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
ARGPARSE_s_s (oWeakDigest, "weak-digest","@"),
ARGPARSE_s_n (oUnwrap, "unwrap", "@"),
ARGPARSE_s_n (oOnlySignTextIDs, "only-sign-text-ids", "@"),
/* Aliases. I constantly mistype these, and assume other people do
as well. */
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-prefs", "@"),
ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-prefs", "@"),
ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-prefs", "@"),
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"),
ARGPARSE_s_s (oDisplay, "display", "@"),
ARGPARSE_s_s (oTTYname, "ttyname", "@"),
ARGPARSE_s_s (oTTYtype, "ttytype", "@"),
ARGPARSE_s_s (oLCctype, "lc-ctype", "@"),
ARGPARSE_s_s (oLCmessages, "lc-messages","@"),
ARGPARSE_s_s (oXauthority, "xauthority", "@"),
ARGPARSE_s_s (oGroup, "group", "@"),
ARGPARSE_s_s (oUnGroup, "ungroup", "@"),
ARGPARSE_s_n (oNoGroups, "no-groups", "@"),
ARGPARSE_s_n (oStrict, "strict", "@"),
ARGPARSE_s_n (oNoStrict, "no-strict", "@"),
ARGPARSE_s_n (oMangleDosFilenames, "mangle-dos-filenames", "@"),
ARGPARSE_s_n (oNoMangleDosFilenames, "no-mangle-dos-filenames", "@"),
ARGPARSE_s_n (oEnableProgressFilter, "enable-progress-filter", "@"),
ARGPARSE_s_n (oMultifile, "multifile", "@"),
ARGPARSE_s_s (oKeyidFormat, "keyid-format", "@"),
ARGPARSE_s_n (oExitOnStatusWriteError, "exit-on-status-write-error", "@"),
ARGPARSE_s_i (oLimitCardInsertTries, "limit-card-insert-tries", "@"),
ARGPARSE_s_n (oAllowMultisigVerification,
"allow-multisig-verification", "@"),
ARGPARSE_s_n (oEnableLargeRSA, "enable-large-rsa", "@"),
ARGPARSE_s_n (oDisableLargeRSA, "disable-large-rsa", "@"),
ARGPARSE_s_n (oEnableDSA2, "enable-dsa2", "@"),
ARGPARSE_s_n (oDisableDSA2, "disable-dsa2", "@"),
ARGPARSE_s_n (oAllowMultipleMessages, "allow-multiple-messages", "@"),
ARGPARSE_s_n (oNoAllowMultipleMessages, "no-allow-multiple-messages", "@"),
ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"),
/* These two are aliases to help users of the PGP command line
product use gpg with minimal pain. Many commands are common
already as they seem to have borrowed commands from us. Now I'm
returning the favor. */
ARGPARSE_s_s (oLocalUser, "sign-with", "@"),
ARGPARSE_s_s (oRecipient, "user", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-cross-certification", "@"),
/* New options. Fixme: Should go more to the top. */
ARGPARSE_s_s (oAutoKeyLocate, "auto-key-locate", "@"),
ARGPARSE_s_n (oNoAutoKeyLocate, "no-auto-key-locate", "@"),
ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"),
/* Dummy options with warnings. */
ARGPARSE_s_n (oUseAgent, "use-agent", "@"),
ARGPARSE_s_n (oNoUseAgent, "no-use-agent", "@"),
ARGPARSE_s_s (oGpgAgentInfo, "gpg-agent-info", "@"),
ARGPARSE_s_s (oReaderPort, "reader-port", "@"),
ARGPARSE_s_s (octapiDriver, "ctapi-driver", "@"),
ARGPARSE_s_s (opcscDriver, "pcsc-driver", "@"),
ARGPARSE_s_n (oDisableCCID, "disable-ccid", "@"),
ARGPARSE_s_n (oHonorHttpProxy, "honor-http-proxy", "@"),
/* Dummy options. */
ARGPARSE_s_n (oNoop, "sk-comments", "@"),
ARGPARSE_s_n (oNoop, "no-sk-comments", "@"),
ARGPARSE_s_n (oNoop, "compress-keys", "@"),
ARGPARSE_s_n (oNoop, "compress-sigs", "@"),
ARGPARSE_s_n (oNoop, "force-v3-sigs", "@"),
ARGPARSE_s_n (oNoop, "no-force-v3-sigs", "@"),
ARGPARSE_s_n (oNoop, "force-v4-certs", "@"),
ARGPARSE_s_n (oNoop, "no-force-v4-certs", "@"),
ARGPARSE_end ()
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_PACKET_VALUE , "packet" },
{ DBG_MPI_VALUE , "mpi" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_FILTER_VALUE , "filter" },
{ DBG_IOBUF_VALUE , "iobuf" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_CACHE_VALUE , "cache" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_TRUST_VALUE , "trust" },
{ DBG_HASHING_VALUE, "hashing" },
{ DBG_CARD_IO_VALUE, "cardio" },
{ DBG_IPC_VALUE , "ipc" },
{ DBG_CLOCK_VALUE , "clock" },
{ DBG_LOOKUP_VALUE , "lookup" },
{ DBG_EXTPROG_VALUE, "extprog" },
{ 0, NULL }
};
#ifdef ENABLE_SELINUX_HACKS
#define ALWAYS_ADD_KEYRINGS 1
#else
#define ALWAYS_ADD_KEYRINGS 0
#endif
int g10_errors_seen = 0;
static int utf8_strings = 0;
static int maybe_setuid = 1;
static char *build_list( const char *text, char letter,
const char *(*mapf)(int), int (*chkf)(int) );
static void set_cmd( enum cmd_and_opt_values *ret_cmd,
enum cmd_and_opt_values new_cmd );
static void print_mds( const char *fname, int algo );
static void add_notation_data( const char *string, int which );
static void add_policy_url( const char *string, int which );
static void add_keyserver_url( const char *string, int which );
static void emergency_cleanup (void);
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
if (maybe_setuid)
{
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
maybe_setuid = 0;
}
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
static int
build_list_pk_test_algo (int algo)
{
/* Show only one "RSA" string. If RSA_E or RSA_S is available RSA
is also available. */
if (algo == PUBKEY_ALGO_RSA_E
|| algo == PUBKEY_ALGO_RSA_S)
return GPG_ERR_DIGEST_ALGO;
return openpgp_pk_test_algo (algo);
}
static const char *
build_list_pk_algo_name (int algo)
{
return openpgp_pk_algo_name (algo);
}
static int
build_list_cipher_test_algo (int algo)
{
return openpgp_cipher_test_algo (algo);
}
static const char *
build_list_cipher_algo_name (int algo)
{
return openpgp_cipher_algo_name (algo);
}
static int
build_list_md_test_algo (int algo)
{
/* By default we do not accept MD5 based signatures. To avoid
confusion we do not announce support for it either. */
if (algo == DIGEST_ALGO_MD5)
return GPG_ERR_DIGEST_ALGO;
return openpgp_md_test_algo (algo);
}
static const char *
build_list_md_algo_name (int algo)
{
return openpgp_md_algo_name (algo);
}
static const char *
my_strusage( int level )
{
static char *digests, *pubkeys, *ciphers, *zips, *ver_gcry;
const char *p;
switch( level ) {
case 11: p = "@GPG@ (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
#ifdef IS_DEVELOPMENT_VERSION
case 25:
p="NOTE: THIS IS A DEVELOPMENT VERSION!";
break;
case 26:
p="It is only intended for test purposes and should NOT be";
break;
case 27:
p="used in a production environment or with production keys!";
break;
#endif
case 1:
case 40: p =
_("Usage: @GPG@ [options] [files] (-h for help)");
break;
case 41: p =
_("Syntax: @GPG@ [options] [files]\n"
"Sign, check, encrypt or decrypt\n"
"Default operation depends on the input data\n");
break;
case 31: p = "\nHome: "; break;
#ifndef __riscos__
case 32: p = gnupg_homedir (); break;
#else /* __riscos__ */
case 32: p = make_filename(gnupg_homedir (), NULL); break;
#endif /* __riscos__ */
case 33: p = _("\nSupported algorithms:\n"); break;
case 34:
if (!pubkeys)
pubkeys = build_list (_("Pubkey: "), 1,
build_list_pk_algo_name,
build_list_pk_test_algo );
p = pubkeys;
break;
case 35:
if( !ciphers )
ciphers = build_list(_("Cipher: "), 'S',
build_list_cipher_algo_name,
build_list_cipher_test_algo );
p = ciphers;
break;
case 36:
if( !digests )
digests = build_list(_("Hash: "), 'H',
build_list_md_algo_name,
build_list_md_test_algo );
p = digests;
break;
case 37:
if( !zips )
zips = build_list(_("Compression: "),'Z',
compress_algo_to_string,
check_compress_algo);
p = zips;
break;
default: p = NULL;
}
return p;
}
static char *
build_list (const char *text, char letter,
const char * (*mapf)(int), int (*chkf)(int))
{
membuf_t mb;
int indent;
int i, j, len;
const char *s;
char *string;
if (maybe_setuid)
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
indent = utf8_charcount (text, -1);
len = 0;
init_membuf (&mb, 512);
for (i=0; i <= 110; i++ )
{
if (!chkf (i) && (s = mapf (i)))
{
if (mb.len - len > 60)
{
put_membuf_str (&mb, ",\n");
len = mb.len;
for (j=0; j < indent; j++)
put_membuf_str (&mb, " ");
}
else if (mb.len)
put_membuf_str (&mb, ", ");
else
put_membuf_str (&mb, text);
put_membuf_str (&mb, s);
if (opt.verbose && letter)
{
char num[20];
if (letter == 1)
snprintf (num, sizeof num, " (%d)", i);
else
snprintf (num, sizeof num, " (%c%d)", letter, i);
put_membuf_str (&mb, num);
}
}
}
if (mb.len)
put_membuf_str (&mb, "\n");
put_membuf (&mb, "", 1);
string = get_membuf (&mb, NULL);
return xrealloc (string, strlen (string)+1);
}
static void
wrong_args( const char *text)
{
es_fprintf (es_stderr, _("usage: %s [options] %s\n"), GPG_NAME, text);
g10_exit(2);
}
static char *
make_username( const char *string )
{
char *p;
if( utf8_strings )
p = xstrdup(string);
else
p = native_to_utf8( string );
return p;
}
static void
set_opt_session_env (const char *name, const char *value)
{
gpg_error_t err;
err = session_env_setenv (opt.session_env, name, value);
if (err)
log_fatal ("error setting session environment: %s\n",
gpg_strerror (err));
}
/* Setup the debugging. With a LEVEL of NULL only the active debug
flags are propagated to the subsystems. With LEVEL set, a specific
set of debug flags is set; thus overriding all flags already
set. */
static void
set_debug (const char *level)
{
int numok = (level && digitp (level));
int numlvl = numok? atoi (level) : 0;
if (!level)
;
else if (!strcmp (level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_MEMSTAT_VALUE;
else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE;
else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE
|DBG_CACHE_VALUE|DBG_LOOKUP|DBG_FILTER_VALUE|DBG_PACKET_VALUE);
else if (!strcmp (level, "guru") || numok)
{
opt.debug = ~0;
/* Unless the "guru" string has been used we don't want to allow
hashing debugging. The rationale is that people tend to
select the highest debug value and would then clutter their
disk with debug files which may reveal confidential data. */
if (numok)
opt.debug &= ~(DBG_HASHING_VALUE);
}
else
{
log_error (_("invalid debug-level '%s' given\n"), level);
g10_exit (2);
}
if (opt.debug & DBG_MEMORY_VALUE )
memory_debug_mode = 1;
if (opt.debug & DBG_MEMSTAT_VALUE )
memory_stat_debug_mode = 1;
if (opt.debug & DBG_MPI_VALUE)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
if (opt.debug & DBG_IOBUF_VALUE )
iobuf_debug_mode = 1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
}
/* We set the screen dimensions for UI purposes. Do not allow screens
smaller than 80x24 for the sake of simplicity. */
static void
set_screen_dimensions(void)
{
#ifndef HAVE_W32_SYSTEM
char *str;
str=getenv("COLUMNS");
if(str)
opt.screen_columns=atoi(str);
str=getenv("LINES");
if(str)
opt.screen_lines=atoi(str);
#endif
if(opt.screen_columns<80 || opt.screen_columns>255)
opt.screen_columns=80;
if(opt.screen_lines<24 || opt.screen_lines>255)
opt.screen_lines=24;
}
/* Helper to open a file FNAME either for reading or writing to be
used with --status-file etc functions. Not generally useful but it
avoids the riscos specific functions and well some Windows people
might like it too. Prints an error message and returns -1 on
error. On success the file descriptor is returned. */
static int
open_info_file (const char *fname, int for_write, int binary)
{
#ifdef __riscos__
return riscos_fdopenfile (fname, for_write);
#elif defined (ENABLE_SELINUX_HACKS)
/* We can't allow these even when testing for a secured filename
because files to be secured might not yet been secured. This is
similar to the option file but in that case it is unlikely that
sensitive information may be retrieved by means of error
messages. */
(void)fname;
(void)for_write;
(void)binary;
return -1;
#else
int fd;
if (binary)
binary = MY_O_BINARY;
/* if (is_secured_filename (fname)) */
/* { */
/* fd = -1; */
/* gpg_err_set_errno (EPERM); */
/* } */
/* else */
/* { */
do
{
if (for_write)
fd = open (fname, O_CREAT | O_TRUNC | O_WRONLY | binary,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
else
fd = open (fname, O_RDONLY | binary);
}
while (fd == -1 && errno == EINTR);
/* } */
if ( fd == -1)
log_error ( for_write? _("can't create '%s': %s\n")
: _("can't open '%s': %s\n"), fname, strerror(errno));
return fd;
#endif
}
static void
set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd )
{
enum cmd_and_opt_values cmd = *ret_cmd;
if( !cmd || cmd == new_cmd )
cmd = new_cmd;
else if( cmd == aSign && new_cmd == aEncr )
cmd = aSignEncr;
else if( cmd == aEncr && new_cmd == aSign )
cmd = aSignEncr;
else if( cmd == aSign && new_cmd == aSym )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aSign )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aEncr )
cmd = aEncrSym;
else if( cmd == aEncr && new_cmd == aSym )
cmd = aEncrSym;
else if (cmd == aSignEncr && new_cmd == aSym)
cmd = aSignEncrSym;
else if (cmd == aSignSym && new_cmd == aEncr)
cmd = aSignEncrSym;
else if (cmd == aEncrSym && new_cmd == aSign)
cmd = aSignEncrSym;
else if( ( cmd == aSign && new_cmd == aClearsign )
|| ( cmd == aClearsign && new_cmd == aSign ) )
cmd = aClearsign;
else {
log_error(_("conflicting commands\n"));
g10_exit(2);
}
*ret_cmd = cmd;
}
static void
add_group(char *string)
{
char *name,*value;
struct groupitem *item;
/* Break off the group name */
name=strsep(&string,"=");
if(string==NULL)
{
log_error(_("no = sign found in group definition '%s'\n"),name);
return;
}
trim_trailing_ws(name,strlen(name));
/* Does this group already exist? */
for(item=opt.grouplist;item;item=item->next)
if(strcasecmp(item->name,name)==0)
break;
if(!item)
{
item=xmalloc(sizeof(struct groupitem));
item->name=name;
item->next=opt.grouplist;
item->values=NULL;
opt.grouplist=item;
}
/* Break apart the values */
while ((value= strsep(&string," \t")))
{
if (*value)
add_to_strlist2(&item->values,value,utf8_strings);
}
}
static void
rm_group(char *name)
{
struct groupitem *item,*last=NULL;
trim_trailing_ws(name,strlen(name));
for(item=opt.grouplist;item;last=item,item=item->next)
{
if(strcasecmp(item->name,name)==0)
{
if(last)
last->next=item->next;
else
opt.grouplist=item->next;
free_strlist(item->values);
xfree(item);
break;
}
}
}
/* We need to check three things.
0) The homedir. It must be x00, a directory, and owned by the
user.
1) The options/gpg.conf file. Okay unless it or its containing
directory is group or other writable or not owned by us. Disable
exec in this case.
2) Extensions. Same as #1.
Returns true if the item is unsafe. */
static int
check_permissions (const char *path, int item)
{
#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
static int homedir_cache=-1;
char *tmppath,*dir;
struct stat statbuf,dirbuf;
int homedir=0,ret=0,checkonly=0;
int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0;
if(opt.no_perm_warn)
return 0;
log_assert(item==0 || item==1 || item==2);
/* extensions may attach a path */
if(item==2 && path[0]!=DIRSEP_C)
{
if(strchr(path,DIRSEP_C))
tmppath=make_filename(path,NULL);
else
tmppath=make_filename(gnupg_libdir (),path,NULL);
}
else
tmppath=xstrdup(path);
/* If the item is located in the homedir, but isn't the homedir,
don't continue if we already checked the homedir itself. This is
to avoid user confusion with an extra options file warning which
could be rectified if the homedir itself had proper
permissions. */
if(item!=0 && homedir_cache>-1
&& !ascii_strncasecmp (gnupg_homedir (), tmppath,
strlen (gnupg_homedir ())))
{
ret=homedir_cache;
goto end;
}
/* It's okay if the file or directory doesn't exist */
if(stat(tmppath,&statbuf)!=0)
{
ret=0;
goto end;
}
/* Now check the enclosing directory. Theoretically, we could walk
this test up to the root directory /, but for the sake of sanity,
I'm stopping at one level down. */
dir=make_dirname(tmppath);
if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode))
{
/* Weird error */
ret=1;
goto end;
}
xfree(dir);
/* Assume failure */
ret=1;
if(item==0)
{
/* The homedir must be x00, a directory, and owned by the user. */
if(S_ISDIR(statbuf.st_mode))
{
if(statbuf.st_uid==getuid())
{
if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=1;
}
else
own=1;
homedir_cache=ret;
}
}
else if(item==1 || item==2)
{
/* The options or extension file. Okay unless it or its
containing directory is group or other writable or not owned
by us or root. */
if(S_ISREG(statbuf.st_mode))
{
if(statbuf.st_uid==getuid() || statbuf.st_uid==0)
{
if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
{
/* it's not writable, so make sure the enclosing
directory is also not writable */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
ret=0;
else
enc_dir_perm=1;
}
else
enc_dir_own=1;
}
else
{
/* it's writable, so the enclosing directory had
better not let people get to it. */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=enc_dir_perm=1; /* unclear which one to fix! */
}
else
enc_dir_own=1;
}
}
else
own=1;
}
}
else
BUG();
if(!checkonly)
{
if(own)
{
if(item==0)
log_info(_("WARNING: unsafe ownership on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe ownership on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe ownership on"
" extension '%s'\n"),tmppath);
}
if(perm)
{
if(item==0)
log_info(_("WARNING: unsafe permissions on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe permissions on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe permissions on"
" extension '%s'\n"),tmppath);
}
if(enc_dir_own)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory ownership on"
" extension '%s'\n"),tmppath);
}
if(enc_dir_perm)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" homedir '%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" configuration file '%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory permissions on"
" extension '%s'\n"),tmppath);
}
}
end:
xfree(tmppath);
if(homedir)
homedir_cache=ret;
return ret;
#else /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/
(void)path;
(void)item;
return 0;
#endif /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/
}
/* Print the OpenPGP defined algo numbers. */
static void
print_algo_numbers(int (*checker)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
es_printf (";");
es_printf ("%d",i);
}
}
}
static void
print_algo_names(int (*checker)(int),const char *(*mapper)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
es_printf (";");
es_printf ("%s",mapper(i));
}
}
}
/* In the future, we can do all sorts of interesting configuration
output here. For now, just give "group" as the Enigmail folks need
it, and pubkey, cipher, hash, and compress as they may be useful
for frontends. */
static void
list_config(char *items)
{
int show_all = !items;
char *name = NULL;
const char *s;
struct groupitem *giter;
int first, iter;
if(!opt.with_colons)
return;
while(show_all || (name=strsep(&items," ")))
{
int any=0;
if(show_all || ascii_strcasecmp(name,"group")==0)
{
for (giter = opt.grouplist; giter; giter = giter->next)
{
strlist_t sl;
es_fprintf (es_stdout, "cfg:group:");
es_write_sanitized (es_stdout, giter->name, strlen(giter->name),
":", NULL);
es_putc (':', es_stdout);
for(sl=giter->values; sl; sl=sl->next)
{
es_write_sanitized (es_stdout, sl->d, strlen (sl->d),
":;", NULL);
if(sl->next)
es_printf(";");
}
es_printf("\n");
}
any=1;
}
if(show_all || ascii_strcasecmp(name,"version")==0)
{
es_printf("cfg:version:");
es_write_sanitized (es_stdout, VERSION, strlen(VERSION), ":", NULL);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"pubkey")==0)
{
es_printf ("cfg:pubkey:");
print_algo_numbers (build_list_pk_test_algo);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"pubkeyname")==0)
{
es_printf ("cfg:pubkeyname:");
print_algo_names (build_list_pk_test_algo,
build_list_pk_algo_name);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"cipher")==0)
{
es_printf ("cfg:cipher:");
print_algo_numbers (build_list_cipher_test_algo);
es_printf ("\n");
any=1;
}
if (show_all || !ascii_strcasecmp (name,"ciphername"))
{
es_printf ("cfg:ciphername:");
print_algo_names (build_list_cipher_test_algo,
build_list_cipher_algo_name);
es_printf ("\n");
any = 1;
}
if(show_all
|| ascii_strcasecmp(name,"digest")==0
|| ascii_strcasecmp(name,"hash")==0)
{
es_printf ("cfg:digest:");
print_algo_numbers (build_list_md_test_algo);
es_printf ("\n");
any=1;
}
if (show_all
|| !ascii_strcasecmp(name,"digestname")
|| !ascii_strcasecmp(name,"hashname"))
{
es_printf ("cfg:digestname:");
print_algo_names (build_list_md_test_algo,
build_list_md_algo_name);
es_printf ("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"compress")==0)
{
es_printf ("cfg:compress:");
print_algo_numbers(check_compress_algo);
es_printf ("\n");
any=1;
}
if (show_all || !ascii_strcasecmp(name,"ccid-reader-id"))
{
/* We ignore this for GnuPG 1.4 backward compatibility. */
any=1;
}
if (show_all || !ascii_strcasecmp (name,"curve"))
{
es_printf ("cfg:curve:");
for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first=0)
es_printf ("%s%s", first?"":";", s);
es_printf ("\n");
any=1;
}
/* Curve OIDs are rarely useful and thus only printed if requested. */
if (name && !ascii_strcasecmp (name,"curveoid"))
{
es_printf ("cfg:curveoid:");
for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first = 0)
{
s = openpgp_curve_to_oid (s, NULL);
es_printf ("%s%s", first?"":";", s? s:"[?]");
}
es_printf ("\n");
any=1;
}
if(show_all)
break;
if(!any)
log_error(_("unknown configuration item '%s'\n"),name);
}
}
/* List options and default values in the GPG Conf format. This is a
new tool distributed with gnupg 1.9.x but we also want some limited
support in older gpg versions. The output is the name of the
configuration file and a list of options available for editing by
gpgconf. */
static void
gpgconf_list (const char *configfile)
{
char *configfile_esc = percent_escape (configfile, NULL);
es_printf ("%s-%s.conf:%lu:\"%s\n",
GPGCONF_NAME, GPG_NAME,
GC_OPT_FLAG_DEFAULT,
configfile_esc ? configfile_esc : "/dev/null");
es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("default-key:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("try-secret-key:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("auto-key-locate:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("group:%lu:\n", GC_OPT_FLAG_NONE);
/* The next one is an info only item and should match the macros at
the top of keygen.c */
es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
"RSA-2048");
xfree (configfile_esc);
}
static int
parse_subpacket_list(char *list)
{
char *tok;
byte subpackets[128],i;
int count=0;
if(!list)
{
/* No arguments means all subpackets */
memset(subpackets+1,1,sizeof(subpackets)-1);
count=127;
}
else
{
memset(subpackets,0,sizeof(subpackets));
/* Merge with earlier copy */
if(opt.show_subpackets)
{
byte *in;
for(in=opt.show_subpackets;*in;in++)
{
if(*in>127 || *in<1)
BUG();
if(!subpackets[*in])
count++;
subpackets[*in]=1;
}
}
while((tok=strsep(&list," ,")))
{
if(!*tok)
continue;
i=atoi(tok);
if(i>127 || i<1)
return 0;
if(!subpackets[i])
count++;
subpackets[i]=1;
}
}
xfree(opt.show_subpackets);
opt.show_subpackets=xmalloc(count+1);
opt.show_subpackets[count--]=0;
for(i=1;i<128 && count>=0;i++)
if(subpackets[i])
opt.show_subpackets[count--]=i;
return 1;
}
static int
parse_list_options(char *str)
{
char *subpackets=""; /* something that isn't NULL */
struct parse_options lopts[]=
{
{"show-photos",LIST_SHOW_PHOTOS,NULL,
N_("display photo IDs during key listings")},
{"show-usage",LIST_SHOW_USAGE,NULL,
N_("show key usage information during key listings")},
{"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature listings")},
{"show-notations",LIST_SHOW_NOTATIONS,NULL,
N_("show all notations during signature listings")},
{"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature listings")},
{"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature listings")},
{"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature listings")},
{"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during key listings")},
{"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in key listings")},
{"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL,
N_("show revoked and expired subkeys in key listings")},
{"show-keyring",LIST_SHOW_KEYRING,NULL,
N_("show the keyring name in key listings")},
{"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL,
N_("show expiration dates during signature listings")},
{"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL,
NULL},
{NULL,0,NULL,NULL}
};
/* C99 allows for non-constant initializers, but we'd like to
compile everywhere, so fill in the show-sig-subpackets argument
here. Note that if the parse_options array changes, we'll have
to change the subscript here. */
lopts[13].value=&subpackets;
if(parse_options(str,&opt.list_options,lopts,1))
{
if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS)
{
/* Unset so users can pass multiple lists in. */
opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS;
if(!parse_subpacket_list(subpackets))
return 0;
}
else if(subpackets==NULL && opt.show_subpackets)
{
/* User did 'no-show-subpackets' */
xfree(opt.show_subpackets);
opt.show_subpackets=NULL;
}
return 1;
}
else
return 0;
}
/* Collapses argc/argv into a single string that must be freed */
static char *
collapse_args(int argc,char *argv[])
{
char *str=NULL;
int i,first=1,len=0;
for(i=0;iflags = KEYDB_RESOURCE_FLAG_PRIMARY;
break;
case oShowKeyring:
deprecated_warning(configname,configlineno,"--show-keyring",
"--list-options ","show-keyring");
opt.list_options|=LIST_SHOW_KEYRING;
break;
case oDebug:
if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
{
pargs.r_opt = ARGPARSE_INVALID_ARG;
pargs.err = ARGPARSE_PRINT_ERROR;
}
break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oDebugIOLBF: break; /* Already set in pre-parse step. */
case oStatusFD:
set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oStatusFile:
set_status_fd ( open_info_file (pargs.r.ret_str, 1, 0) );
break;
case oAttributeFD:
set_attrib_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oAttributeFile:
set_attrib_fd ( open_info_file (pargs.r.ret_str, 1, 1) );
break;
case oLoggerFD:
log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oLoggerFile:
logfile = pargs.r.ret_str;
break;
case oWithFingerprint:
opt.with_fingerprint = 1;
opt.fingerprint++;
break;
case oWithSubkeyFingerprint:
opt.with_subkey_fingerprint = 1;
break;
case oWithICAOSpelling:
opt.with_icao_spelling = 1;
break;
case oFingerprint:
opt.fingerprint++;
fpr_maybe_cmd = 1;
break;
case oWithKeygrip:
opt.with_keygrip = 1;
break;
case oWithSecret:
opt.with_secret = 1;
break;
case oWithWKDHash:
opt.with_wkd_hash = 1;
break;
case oSecretKeyring:
/* Ignore this old option. */
break;
case oOptions:
/* config files may not be nested (silently ignore them) */
if( !configfp ) {
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoArmor: opt.no_armor=1; opt.armor=0; break;
case oNoDefKeyring:
if (default_keyring > 0)
default_keyring = 0;
break;
case oNoKeyring:
default_keyring = -1;
break;
case oNoGreeting: nogreeting = 1; break;
case oNoVerbose:
opt.verbose = 0;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
opt.list_sigs=0;
break;
case oQuickRandom:
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
break;
case oEmitVersion: opt.emit_version++; break;
case oNoEmitVersion: opt.emit_version=0; break;
case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break;
case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break;
#ifndef NO_TRUST_MODELS
case oTrustDBName: trustdb_name = pargs.r.ret_str; break;
#endif /*!NO_TRUST_MODELS*/
case oDefaultKey:
sl = add_to_strlist (&opt.def_secret_key, pargs.r.ret_str);
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configfp)
sl->flags |= PK_LIST_CONFIG;
break;
case oDefRecipient:
if( *pargs.r.ret_str )
{
xfree (opt.def_recipient);
opt.def_recipient = make_username(pargs.r.ret_str);
}
break;
case oDefRecipientSelf:
xfree(opt.def_recipient); opt.def_recipient = NULL;
opt.def_recipient_self = 1;
break;
case oNoDefRecipient:
xfree(opt.def_recipient); opt.def_recipient = NULL;
opt.def_recipient_self = 0;
break;
case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */
case oHomedir: break;
case oNoBatch: opt.batch = 0; break;
case oWithKeyData: opt.with_key_data=1; /*FALLTHRU*/
case oWithColons: opt.with_colons=':'; break;
case oWithSigCheck: opt.check_sigs = 1; /*FALLTHRU*/
case oWithSigList: opt.list_sigs = 1; break;
case oSkipVerify: opt.skip_verify=1; break;
case oSkipHiddenRecipients: opt.skip_hidden_recipients = 1; break;
case oNoSkipHiddenRecipients: opt.skip_hidden_recipients = 0; break;
case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break;
#ifndef NO_TRUST_MODELS
/* There are many programs (like mutt) that call gpg with
--always-trust so keep this option around for a long
time. */
case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break;
case oTrustModel:
parse_trust_model(pargs.r.ret_str);
break;
#endif /*!NO_TRUST_MODELS*/
case oTOFUDefaultPolicy:
opt.tofu_default_policy = parse_tofu_policy (pargs.r.ret_str);
break;
case oTOFUDBFormat:
opt.tofu_db_format = parse_tofu_db_format (pargs.r.ret_str);
break;
case oForceOwnertrust:
log_info(_("Note: %s is not for normal use!\n"),
"--force-ownertrust");
opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str);
if(opt.force_ownertrust==-1)
{
log_error("invalid ownertrust '%s'\n",pargs.r.ret_str);
opt.force_ownertrust=0;
}
break;
case oLoadExtension:
/* Dummy so that gpg 1.4 conf files can work. Should
eventually be removed. */
break;
case oRFC4880bis:
opt.flags.rfc4880bis = 1;
/* fall thru. */
case oOpenPGP:
case oRFC4880:
/* This is effectively the same as RFC2440, but with
"--enable-dsa2 --no-rfc2440-text --escape-from-lines
--require-cross-certification". */
opt.compliance = CO_RFC4880;
opt.flags.dsa2 = 1;
opt.flags.require_cross_cert = 1;
opt.rfc2440_text = 0;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.escape_from = 1;
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
break;
case oRFC2440:
opt.compliance = CO_RFC2440;
opt.flags.dsa2 = 0;
opt.rfc2440_text = 1;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.escape_from = 0;
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
break;
case oPGP6: opt.compliance = CO_PGP6; break;
case oPGP7: opt.compliance = CO_PGP7; break;
case oPGP8: opt.compliance = CO_PGP8; break;
case oGnuPG: opt.compliance = CO_GNUPG; break;
case oRFC2440Text: opt.rfc2440_text=1; break;
case oNoRFC2440Text: opt.rfc2440_text=0; break;
case oSetFilename:
if(utf8_strings)
opt.set_filename = pargs.r.ret_str;
else
opt.set_filename = native_to_utf8(pargs.r.ret_str);
break;
case oForYourEyesOnly: eyes_only = 1; break;
case oNoForYourEyesOnly: eyes_only = 0; break;
case oSetPolicyURL:
add_policy_url(pargs.r.ret_str,0);
add_policy_url(pargs.r.ret_str,1);
break;
case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break;
case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break;
case oShowPolicyURL:
deprecated_warning(configname,configlineno,"--show-policy-url",
"--list-options ","show-policy-urls");
deprecated_warning(configname,configlineno,"--show-policy-url",
"--verify-options ","show-policy-urls");
opt.list_options|=LIST_SHOW_POLICY_URLS;
opt.verify_options|=VERIFY_SHOW_POLICY_URLS;
break;
case oNoShowPolicyURL:
deprecated_warning(configname,configlineno,"--no-show-policy-url",
"--list-options ","no-show-policy-urls");
deprecated_warning(configname,configlineno,"--no-show-policy-url",
"--verify-options ","no-show-policy-urls");
opt.list_options&=~LIST_SHOW_POLICY_URLS;
opt.verify_options&=~VERIFY_SHOW_POLICY_URLS;
break;
case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break;
case oUseEmbeddedFilename:
opt.flags.use_embedded_filename=1;
break;
case oNoUseEmbeddedFilename:
opt.flags.use_embedded_filename=0;
break;
case oComment:
if(pargs.r.ret_str[0])
append_to_strlist(&opt.comments,pargs.r.ret_str);
break;
case oDefaultComment:
deprecated_warning(configname,configlineno,
"--default-comment","--no-comments","");
/* fall through */
case oNoComments:
free_strlist(opt.comments);
opt.comments=NULL;
break;
case oThrowKeyids: opt.throw_keyids = 1; break;
case oNoThrowKeyids: opt.throw_keyids = 0; break;
case oShowPhotos:
deprecated_warning(configname,configlineno,"--show-photos",
"--list-options ","show-photos");
deprecated_warning(configname,configlineno,"--show-photos",
"--verify-options ","show-photos");
opt.list_options|=LIST_SHOW_PHOTOS;
opt.verify_options|=VERIFY_SHOW_PHOTOS;
break;
case oNoShowPhotos:
deprecated_warning(configname,configlineno,"--no-show-photos",
"--list-options ","no-show-photos");
deprecated_warning(configname,configlineno,"--no-show-photos",
"--verify-options ","no-show-photos");
opt.list_options&=~LIST_SHOW_PHOTOS;
opt.verify_options&=~VERIFY_SHOW_PHOTOS;
break;
case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
case oForceMDC: opt.force_mdc = 1; break;
case oNoForceMDC: opt.force_mdc = 0; break;
case oDisableMDC: opt.disable_mdc = 1; break;
case oNoDisableMDC: opt.disable_mdc = 0; break;
case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break;
case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break;
case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break;
case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break;
case oS2KCount:
if (pargs.r.ret_int)
opt.s2k_count = encode_s2k_iterations (pargs.r.ret_int);
else
opt.s2k_count = 0; /* Auto-calibrate when needed. */
break;
case oRecipient:
case oHiddenRecipient:
case oRecipientFile:
case oHiddenRecipientFile:
/* Store the recipient. Note that we also store the
* option as private data in the flags. This is achieved
* by shifting the option value to the left so to keep
* enough space for the flags. */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configfp)
sl->flags |= PK_LIST_CONFIG;
if (pargs.r_opt == oHiddenRecipient
|| pargs.r_opt == oHiddenRecipientFile)
sl->flags |= PK_LIST_HIDDEN;
if (pargs.r_opt == oRecipientFile
|| pargs.r_opt == oHiddenRecipientFile)
sl->flags |= PK_LIST_FROM_FILE;
any_explicit_recipient = 1;
break;
case oEncryptTo:
case oHiddenEncryptTo:
/* Store an additional recipient. */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = ((pargs.r_opt << PK_LIST_SHIFT) | PK_LIST_ENCRYPT_TO);
if (configfp)
sl->flags |= PK_LIST_CONFIG;
if (pargs.r_opt == oHiddenEncryptTo)
sl->flags |= PK_LIST_HIDDEN;
break;
case oNoEncryptTo:
opt.no_encrypt_to = 1;
break;
case oEncryptToDefaultKey:
opt.encrypt_to_default_key = configfp ? 2 : 1;
break;
case oTrySecretKey:
add_to_strlist2 (&opt.secret_keys_to_try,
pargs.r.ret_str, utf8_strings);
break;
case oMimemode: opt.mimemode = opt.textmode = 1; break;
case oTextmodeShort: opt.textmode = 2; break;
case oTextmode: opt.textmode=1; break;
case oNoTextmode: opt.textmode=opt.mimemode=0; break;
case oExpert: opt.expert = 1; break;
case oNoExpert: opt.expert = 0; break;
case oDefSigExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("'%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_sig_expire=pargs.r.ret_str;
}
break;
case oAskSigExpire: opt.ask_sig_expire = 1; break;
case oNoAskSigExpire: opt.ask_sig_expire = 0; break;
case oDefCertExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("'%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_cert_expire=pargs.r.ret_str;
}
break;
case oAskCertExpire: opt.ask_cert_expire = 1; break;
case oNoAskCertExpire: opt.ask_cert_expire = 0; break;
case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break;
case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break;
case oAskCertLevel: opt.ask_cert_level = 1; break;
case oNoAskCertLevel: opt.ask_cert_level = 0; break;
case oLocalUser: /* store the local users */
sl = add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
sl->flags = (pargs.r_opt << PK_LIST_SHIFT);
if (configfp)
sl->flags |= PK_LIST_CONFIG;
break;
case oCompress:
/* this is the -z command line option */
opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
break;
case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break;
case oPassphrase:
set_passphrase_from_string(pargs.r.ret_str);
break;
case oPassphraseFD:
pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oPassphraseFile:
pwfd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oPassphraseRepeat:
opt.passphrase_repeat = pargs.r.ret_int;
break;
case oPinentryMode:
opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str);
if (opt.pinentry_mode == -1)
log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str);
break;
case oCommandFD:
opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oCommandFile:
opt.command_fd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oCipherAlgo:
def_cipher_string = xstrdup(pargs.r.ret_str);
break;
case oDigestAlgo:
def_digest_string = xstrdup(pargs.r.ret_str);
break;
case oCompressAlgo:
/* If it is all digits, stick a Z in front of it for
later. This is for backwards compatibility with
versions that took the compress algorithm number. */
{
char *pt=pargs.r.ret_str;
while(*pt)
{
if (!isascii (*pt) || !isdigit (*pt))
break;
pt++;
}
if(*pt=='\0')
{
compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2);
strcpy(compress_algo_string,"Z");
strcat(compress_algo_string,pargs.r.ret_str);
}
else
compress_algo_string = xstrdup(pargs.r.ret_str);
}
break;
case oCertDigestAlgo:
cert_digest_string = xstrdup(pargs.r.ret_str);
break;
case oNoSecmemWarn:
gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
break;
case oRequireSecmem: require_secmem=1; break;
case oNoRequireSecmem: require_secmem=0; break;
case oNoPermissionWarn: opt.no_perm_warn=1; break;
case oNoMDCWarn: opt.no_mdc_warn=1; break;
case oDisplayCharset:
if( set_native_charset( pargs.r.ret_str ) )
log_error(_("'%s' is not a valid character set\n"),
pargs.r.ret_str);
break;
case oNotDashEscaped: opt.not_dash_escaped = 1; break;
case oEscapeFrom: opt.escape_from = 1; break;
case oNoEscapeFrom: opt.escape_from = 0; break;
case oLockOnce: opt.lock_once = 1; break;
case oLockNever:
dotlock_disable ();
break;
case oLockMultiple:
#ifndef __riscos__
opt.lock_once = 0;
#else /* __riscos__ */
riscos_not_implemented("lock-multiple");
#endif /* __riscos__ */
break;
case oKeyServer:
{
keyserver_spec_t keyserver;
keyserver = parse_keyserver_uri (pargs.r.ret_str, 0);
if (!keyserver)
log_error (_("could not parse keyserver URL\n"));
else
{
/* We only support a single keyserver. Later ones
override earlier ones. (Since we parse the
config file first and then the command line
arguments, the command line takes
precedence.) */
if (opt.keyserver)
free_keyserver_spec (opt.keyserver);
opt.keyserver = keyserver;
}
}
break;
case oKeyServerOptions:
if(!parse_keyserver_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid keyserver options\n"),
configname,configlineno);
else
log_error(_("invalid keyserver options\n"));
}
break;
case oImportOptions:
if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1))
{
if(configname)
log_error(_("%s:%d: invalid import options\n"),
configname,configlineno);
else
log_error(_("invalid import options\n"));
}
break;
case oImportFilter:
rc = parse_and_set_import_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oExportOptions:
if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
{
if(configname)
log_error(_("%s:%d: invalid export options\n"),
configname,configlineno);
else
log_error(_("invalid export options\n"));
}
break;
case oExportFilter:
rc = parse_and_set_export_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oListOptions:
if(!parse_list_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid list options\n"),
configname,configlineno);
else
log_error(_("invalid list options\n"));
}
break;
case oVerifyOptions:
{
struct parse_options vopts[]=
{
{"show-photos",VERIFY_SHOW_PHOTOS,NULL,
N_("display photo IDs during signature verification")},
{"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature verification")},
{"show-notations",VERIFY_SHOW_NOTATIONS,NULL,
N_("show all notations during signature verification")},
{"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature verification")},
{"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature verification")},
{"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature verification")},
{"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during signature verification")},
{"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in signature verification")},
{"show-primary-uid-only",VERIFY_SHOW_PRIMARY_UID_ONLY,NULL,
N_("show only the primary user ID in signature verification")},
{"pka-lookups",VERIFY_PKA_LOOKUPS,NULL,
N_("validate signatures with PKA data")},
{"pka-trust-increase",VERIFY_PKA_TRUST_INCREASE,NULL,
N_("elevate the trust of signatures with valid PKA data")},
{NULL,0,NULL,NULL}
};
if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1))
{
if(configname)
log_error(_("%s:%d: invalid verify options\n"),
configname,configlineno);
else
log_error(_("invalid verify options\n"));
}
}
break;
case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
case oExecPath:
if(set_exec_path(pargs.r.ret_str))
log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str);
else
opt.exec_path_set=1;
break;
case oSetNotation:
add_notation_data( pargs.r.ret_str, 0 );
add_notation_data( pargs.r.ret_str, 1 );
break;
case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break;
case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break;
case oShowNotation:
deprecated_warning(configname,configlineno,"--show-notation",
"--list-options ","show-notations");
deprecated_warning(configname,configlineno,"--show-notation",
"--verify-options ","show-notations");
opt.list_options|=LIST_SHOW_NOTATIONS;
opt.verify_options|=VERIFY_SHOW_NOTATIONS;
break;
case oNoShowNotation:
deprecated_warning(configname,configlineno,"--no-show-notation",
"--list-options ","no-show-notations");
deprecated_warning(configname,configlineno,"--no-show-notation",
"--verify-options ","no-show-notations");
opt.list_options&=~LIST_SHOW_NOTATIONS;
opt.verify_options&=~VERIFY_SHOW_NOTATIONS;
break;
case oUtf8Strings: utf8_strings = 1; break;
case oNoUtf8Strings: utf8_strings = 0; break;
case oDisableCipherAlgo:
{
int algo = string_to_cipher_algo (pargs.r.ret_str);
gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo);
}
break;
case oDisablePubkeyAlgo:
{
int algo = gcry_pk_map_name (pargs.r.ret_str);
gcry_pk_ctl (GCRYCTL_DISABLE_ALGO, &algo, sizeof algo);
}
break;
case oNoSigCache: opt.no_sig_cache = 1; break;
case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break;
case oAllowFreeformUID: opt.allow_freeform_uid = 1; break;
case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break;
case oNoLiteral: opt.no_literal = 1; break;
case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break;
case oFastListMode: opt.fast_list_mode = 1; break;
case oFixedListMode: /* Dummy */ break;
case oLegacyListMode: opt.legacy_list_mode = 1; break;
case oPrintPKARecords: print_pka_records = 1; break;
case oPrintDANERecords: print_dane_records = 1; break;
case oListOnly: opt.list_only=1; break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oIgnoreValidFrom: opt.ignore_valid_from = 1; break;
case oIgnoreCrcError: opt.ignore_crc_error = 1; break;
case oIgnoreMDCError: opt.ignore_mdc_error = 1; break;
case oNoRandomSeedFile: use_random_seed = 0; break;
case oAutoKeyRetrieve:
case oNoAutoKeyRetrieve:
if(pargs.r_opt==oAutoKeyRetrieve)
opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE;
else
opt.keyserver_options.options&=~KEYSERVER_AUTO_KEY_RETRIEVE;
break;
case oShowSessionKey: opt.show_session_key = 1; break;
case oOverrideSessionKey:
opt.override_session_key = pargs.r.ret_str;
break;
case oMergeOnly:
deprecated_warning(configname,configlineno,"--merge-only",
"--import-options ","merge-only");
opt.import_options|=IMPORT_MERGE_ONLY;
break;
case oAllowSecretKeyImport: /* obsolete */ break;
case oTryAllSecrets: opt.try_all_secrets = 1; break;
case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break;
case oEnableSpecialFilenames:
iobuf_enable_special_filenames (1);
break;
case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break;
case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break;
case oPreservePermissions: opt.preserve_permissions=1; break;
case oDefaultPreferenceList:
opt.def_preference_list = pargs.r.ret_str;
break;
case oDefaultKeyserverURL:
{
keyserver_spec_t keyserver;
keyserver = parse_keyserver_uri (pargs.r.ret_str,1 );
if (!keyserver)
log_error (_("could not parse keyserver URL\n"));
else
free_keyserver_spec (keyserver);
opt.def_keyserver_url = pargs.r.ret_str;
}
break;
case oPersonalCipherPreferences:
pers_cipher_list=pargs.r.ret_str;
break;
case oPersonalDigestPreferences:
pers_digest_list=pargs.r.ret_str;
break;
case oPersonalCompressPreferences:
pers_compress_list=pargs.r.ret_str;
break;
case oAgentProgram: opt.agent_program = pargs.r.ret_str; break;
case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break;
case oWeakDigest:
additional_weak_digest(pargs.r.ret_str);
break;
case oUnwrap:
opt.unwrap_encryption = 1;
break;
case oOnlySignTextIDs:
opt.only_sign_text_ids = 1;
break;
case oDisplay:
set_opt_session_env ("DISPLAY", pargs.r.ret_str);
break;
case oTTYname:
set_opt_session_env ("GPG_TTY", pargs.r.ret_str);
break;
case oTTYtype:
set_opt_session_env ("TERM", pargs.r.ret_str);
break;
case oXauthority:
set_opt_session_env ("XAUTHORITY", pargs.r.ret_str);
break;
case oLCctype: opt.lc_ctype = pargs.r.ret_str; break;
case oLCmessages: opt.lc_messages = pargs.r.ret_str; break;
case oGroup: add_group(pargs.r.ret_str); break;
case oUnGroup: rm_group(pargs.r.ret_str); break;
case oNoGroups:
while(opt.grouplist)
{
struct groupitem *iter=opt.grouplist;
free_strlist(iter->values);
opt.grouplist=opt.grouplist->next;
xfree(iter);
}
break;
case oStrict:
case oNoStrict:
/* Not used */
break;
case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break;
case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break;
case oEnableProgressFilter: opt.enable_progress_filter = 1; break;
case oMultifile: multifile=1; break;
case oKeyidFormat:
if(ascii_strcasecmp(pargs.r.ret_str,"short")==0)
opt.keyid_format=KF_SHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0)
opt.keyid_format=KF_LONG;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0)
opt.keyid_format=KF_0xSHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0)
opt.keyid_format=KF_0xLONG;
else if(ascii_strcasecmp(pargs.r.ret_str,"none")==0)
opt.keyid_format = KF_NONE;
else
log_error("unknown keyid-format '%s'\n",pargs.r.ret_str);
break;
case oExitOnStatusWriteError:
opt.exit_on_status_write_error = 1;
break;
case oLimitCardInsertTries:
opt.limit_card_insert_tries = pargs.r.ret_int;
break;
case oRequireCrossCert: opt.flags.require_cross_cert=1; break;
case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break;
case oAutoKeyLocate:
if(!parse_auto_key_locate(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid auto-key-locate list\n"),
configname,configlineno);
else
log_error(_("invalid auto-key-locate list\n"));
}
break;
case oNoAutoKeyLocate:
release_akl();
break;
case oEnableLargeRSA:
#if SECMEM_BUFFER_SIZE >= 65536
opt.flags.large_rsa=1;
#else
if (configname)
log_info("%s:%d: WARNING: gpg not built with large secure "
"memory buffer. Ignoring enable-large-rsa\n",
configname,configlineno);
else
log_info("WARNING: gpg not built with large secure "
"memory buffer. Ignoring --enable-large-rsa\n");
#endif /* SECMEM_BUFFER_SIZE >= 65536 */
break;
case oDisableLargeRSA: opt.flags.large_rsa=0;
break;
case oEnableDSA2: opt.flags.dsa2=1; break;
case oDisableDSA2: opt.flags.dsa2=0; break;
case oAllowMultisigVerification:
case oAllowMultipleMessages:
opt.flags.allow_multiple_messages=1;
break;
case oNoAllowMultipleMessages:
opt.flags.allow_multiple_messages=0;
break;
case oAllowWeakDigestAlgos:
opt.flags.allow_weak_digest_algos = 1;
break;
case oFakedSystemTime:
{
time_t faked_time = isotime2epoch (pargs.r.ret_str);
if (faked_time == (time_t)(-1))
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
gnupg_set_time (faked_time, 0);
}
break;
case oNoAutostart: opt.autostart = 0; break;
case oNoop: break;
default:
pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
break;
}
}
if (configfp)
{
fclose( configfp );
configfp = NULL;
/* Remember the first config file name. */
if (!save_configname)
save_configname = configname;
else
xfree(configname);
configname = NULL;
goto next_pass;
}
xfree(configname); configname = NULL;
if (log_get_errorcount (0))
g10_exit(2);
/* The command --gpgconf-list is pretty simple and may be called
directly after the option parsing. */
if (cmd == aGPGConfList)
{
gpgconf_list (save_configname ? save_configname : default_configname);
g10_exit (0);
}
xfree (save_configname);
xfree (default_configname);
if (print_dane_records)
log_error ("invalid option \"%s\"; use \"%s\" instead\n",
"--print-dane-records",
"--export-options export-dane");
if (print_pka_records)
log_error ("invalid option \"%s\"; use \"%s\" instead\n",
"--print-pks-records",
"--export-options export-pka");
if (log_get_errorcount (0))
g10_exit(2);
if( nogreeting )
greeting = 0;
if( greeting )
{
es_fprintf (es_stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
es_fprintf (es_stderr, "%s\n", strusage(15) );
}
#ifdef IS_DEVELOPMENT_VERSION
if (!opt.batch)
{
const char *s;
if((s=strusage(25)))
log_info("%s\n",s);
if((s=strusage(26)))
log_info("%s\n",s);
if((s=strusage(27)))
log_info("%s\n",s);
}
#endif
/* FIXME: We should use logging to a file only in server mode;
however we have not yet implemetyed that. Thus we try to get
away with --batch as indication for logging to file
required. */
if (logfile && opt.batch)
{
log_set_file (logfile);
log_set_prefix (NULL, 1|2|4);
}
if (opt.verbose > 2)
log_info ("using character set '%s'\n", get_native_charset ());
if( may_coredump && !opt.quiet )
log_info(_("WARNING: program may create a core file!\n"));
if (opt.flags.rfc4880bis)
log_info ("WARNING: using experimental features from RFC4880bis!\n");
else
{
opt.mimemode = 0; /* This will use text mode instead. */
}
if (eyes_only) {
if (opt.set_filename)
log_info(_("WARNING: %s overrides %s\n"),
"--for-your-eyes-only","--set-filename");
opt.set_filename="_CONSOLE";
}
if (opt.no_literal) {
log_info(_("Note: %s is not for normal use!\n"), "--no-literal");
if (opt.textmode)
log_error(_("%s not allowed with %s!\n"),
"--textmode", "--no-literal" );
if (opt.set_filename)
log_error(_("%s makes no sense with %s!\n"),
eyes_only?"--for-your-eyes-only":"--set-filename",
"--no-literal" );
}
if (opt.set_filesize)
log_info(_("Note: %s is not for normal use!\n"), "--set-filesize");
if( opt.batch )
tty_batchmode( 1 );
if (gnupg_faked_time_p ())
{
gnupg_isotime_t tbuf;
log_info (_("WARNING: running with faked system time: "));
gnupg_get_isotime (tbuf);
dump_isotime (tbuf);
log_printf ("\n");
}
/* Print a warning if an argument looks like an option. */
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
{
int i;
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
if(require_secmem && !got_secmem)
{
log_info(_("will not run with insecure memory due to %s\n"),
"--require-secmem");
g10_exit(2);
}
set_debug (debug_level);
if (DBG_CLOCK)
log_clock ("start");
/* Do these after the switch(), so they can override settings. */
if(PGP6)
{
/* That does not anymore work because we have no more support
for v3 signatures. */
opt.disable_mdc=1;
opt.escape_from=1;
opt.ask_sig_expire=0;
}
else if(PGP7)
{
/* That does not anymore work because we have no more support
for v3 signatures. */
opt.escape_from=1;
opt.ask_sig_expire=0;
}
else if(PGP8)
{
opt.escape_from=1;
}
if( def_cipher_string ) {
opt.def_cipher_algo = string_to_cipher_algo (def_cipher_string);
xfree(def_cipher_string); def_cipher_string = NULL;
if ( openpgp_cipher_test_algo (opt.def_cipher_algo) )
log_error(_("selected cipher algorithm is invalid\n"));
}
if( def_digest_string ) {
opt.def_digest_algo = string_to_digest_algo (def_digest_string);
xfree(def_digest_string); def_digest_string = NULL;
if ( openpgp_md_test_algo (opt.def_digest_algo) )
log_error(_("selected digest algorithm is invalid\n"));
}
if( compress_algo_string ) {
opt.compress_algo = string_to_compress_algo(compress_algo_string);
xfree(compress_algo_string); compress_algo_string = NULL;
if( check_compress_algo(opt.compress_algo) )
log_error(_("selected compression algorithm is invalid\n"));
}
if( cert_digest_string ) {
opt.cert_digest_algo = string_to_digest_algo (cert_digest_string);
xfree(cert_digest_string); cert_digest_string = NULL;
if (openpgp_md_test_algo(opt.cert_digest_algo))
log_error(_("selected certification digest algorithm is invalid\n"));
}
if( s2k_cipher_string ) {
opt.s2k_cipher_algo = string_to_cipher_algo (s2k_cipher_string);
xfree(s2k_cipher_string); s2k_cipher_string = NULL;
if (openpgp_cipher_test_algo (opt.s2k_cipher_algo))
log_error(_("selected cipher algorithm is invalid\n"));
}
if( s2k_digest_string ) {
opt.s2k_digest_algo = string_to_digest_algo (s2k_digest_string);
xfree(s2k_digest_string); s2k_digest_string = NULL;
if (openpgp_md_test_algo(opt.s2k_digest_algo))
log_error(_("selected digest algorithm is invalid\n"));
}
if( opt.completes_needed < 1 )
log_error(_("completes-needed must be greater than 0\n"));
if( opt.marginals_needed < 2 )
log_error(_("marginals-needed must be greater than 1\n"));
if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 )
log_error(_("max-cert-depth must be in the range from 1 to 255\n"));
if(opt.def_cert_level<0 || opt.def_cert_level>3)
log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n"));
if( opt.min_cert_level < 1 || opt.min_cert_level > 3 )
log_error(_("invalid min-cert-level; must be 1, 2, or 3\n"));
switch( opt.s2k_mode ) {
case 0:
log_info(_("Note: simple S2K mode (0) is strongly discouraged\n"));
break;
case 1: case 3: break;
default:
log_error(_("invalid S2K mode; must be 0, 1 or 3\n"));
}
/* This isn't actually needed, but does serve to error out if the
string is invalid. */
if(opt.def_preference_list &&
keygen_set_std_prefs(opt.def_preference_list,0))
log_error(_("invalid default preferences\n"));
if(pers_cipher_list &&
keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM))
log_error(_("invalid personal cipher preferences\n"));
if(pers_digest_list &&
keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH))
log_error(_("invalid personal digest preferences\n"));
if(pers_compress_list &&
keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP))
log_error(_("invalid personal compress preferences\n"));
/* We don't support all possible commands with multifile yet */
if(multifile)
{
char *cmdname;
switch(cmd)
{
case aSign:
cmdname="--sign";
break;
case aSignEncr:
cmdname="--sign --encrypt";
break;
case aClearsign:
cmdname="--clearsign";
break;
case aDetachedSign:
cmdname="--detach-sign";
break;
case aSym:
cmdname="--symmetric";
break;
case aEncrSym:
cmdname="--symmetric --encrypt";
break;
case aStore:
cmdname="--store";
break;
default:
cmdname=NULL;
break;
}
if(cmdname)
log_error(_("%s does not yet work with %s\n"),cmdname,"--multifile");
}
if( log_get_errorcount(0) )
g10_exit(2);
if(opt.compress_level==0)
opt.compress_algo=COMPRESS_ALGO_NONE;
/* Check our chosen algorithms against the list of legal
algorithms. */
if(!GNUPG)
{
const char *badalg=NULL;
preftype_t badtype=PREFTYPE_NONE;
if(opt.def_cipher_algo
&& !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL))
{
badalg = openpgp_cipher_algo_name (opt.def_cipher_algo);
badtype = PREFTYPE_SYM;
}
else if(opt.def_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.def_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.cert_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.cert_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.compress_algo!=-1
&& !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL))
{
badalg = compress_algo_to_string(opt.compress_algo);
badtype = PREFTYPE_ZIP;
}
if(badalg)
{
switch(badtype)
{
case PREFTYPE_SYM:
log_info(_("you may not use cipher algorithm '%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
case PREFTYPE_HASH:
log_info(_("you may not use digest algorithm '%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
case PREFTYPE_ZIP:
log_info(_("you may not use compression algorithm '%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
default:
BUG();
}
compliance_failure();
}
}
/* Set the random seed file. */
if( use_random_seed ) {
char *p = make_filename (gnupg_homedir (), "random_seed", NULL );
gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
if (!access (p, F_OK))
register_secured_file (p);
xfree(p);
}
/* If there is no command but the --fingerprint is given, default
to the --list-keys command. */
if (!cmd && fpr_maybe_cmd)
{
set_cmd (&cmd, aListKeys);
}
if( opt.verbose > 1 )
set_packet_list_mode(1);
/* Add the keyrings, but not for some special commands. We always
* need to add the keyrings if we are running under SELinux, this
* is so that the rings are added to the list of secured files.
* We do not add any keyring if --no-keyring has been used. */
if (default_keyring >= 0
&& (ALWAYS_ADD_KEYRINGS
|| (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest)))
{
if (!nrings || default_keyring > 0) /* Add default ring. */
keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG,
KEYDB_RESOURCE_FLAG_DEFAULT);
for (sl = nrings; sl; sl = sl->next )
keydb_add_resource (sl->d, sl->flags);
}
FREE_STRLIST(nrings);
if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
/* In loopback mode, never ask for the password multiple
times. */
{
opt.passphrase_repeat = 0;
}
if (cmd == aGPGConfTest)
g10_exit(0);
if( pwfd != -1 ) /* Read the passphrase now. */
read_passphrase_from_fd( pwfd );
fname = argc? *argv : NULL;
if(fname && utf8_strings)
opt.flags.utf8_filename=1;
ctrl = xcalloc (1, sizeof *ctrl);
gpg_init_default_ctrl (ctrl);
#ifndef NO_TRUST_MODELS
switch (cmd)
{
case aPrimegen:
case aPrintMD:
case aPrintMDs:
case aGenRandom:
case aDeArmor:
case aEnArmor:
break;
case aFixTrustDB:
case aExportOwnerTrust:
rc = setup_trustdb (0, trustdb_name);
break;
case aListTrustDB:
rc = setup_trustdb (argc? 1:0, trustdb_name);
break;
default:
/* If we are using TM_ALWAYS, we do not need to create the
trustdb. */
rc = setup_trustdb (opt.trust_model != TM_ALWAYS, trustdb_name);
break;
}
if (rc)
log_error (_("failed to initialize the TrustDB: %s\n"),
gpg_strerror (rc));
#endif /*!NO_TRUST_MODELS*/
switch (cmd)
{
case aStore:
case aSym:
case aSign:
case aSignSym:
case aClearsign:
if (!opt.quiet && any_explicit_recipient)
log_info (_("WARNING: recipients (-r) given "
"without using public key encryption\n"));
break;
default:
break;
}
/* Check for certain command whether we need to migrate a
secring.gpg to the gpg-agent. */
switch (cmd)
{
case aListSecretKeys:
case aSign:
case aSignEncr:
case aSignEncrSym:
case aSignSym:
case aClearsign:
case aDecrypt:
case aSignKey:
case aLSignKey:
case aEditKey:
case aPasswd:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
case aQuickKeygen:
case aQuickAddUid:
case aQuickAddKey:
case aQuickRevUid:
case aFullKeygen:
case aKeygen:
case aImport:
case aExportSecret:
case aExportSecretSub:
case aGenRevoke:
case aDesigRevoke:
case aCardEdit:
case aChangePIN:
migrate_secring (ctrl);
break;
case aListKeys:
if (opt.with_secret)
migrate_secring (ctrl);
break;
default:
break;
}
/* The command dispatcher. */
switch( cmd )
{
case aServer:
gpg_server (ctrl);
break;
case aStore: /* only store the file */
if( argc > 1 )
wrong_args(_("--store [filename]"));
if( (rc = encrypt_store(fname)) )
{
write_status_failure ("store", rc);
log_error ("storing '%s' failed: %s\n",
print_fname_stdin(fname),gpg_strerror (rc) );
}
break;
case aSym: /* encrypt the given file only with the symmetric cipher */
if( argc > 1 )
wrong_args(_("--symmetric [filename]"));
if( (rc = encrypt_symmetric(fname)) )
{
write_status_failure ("symencrypt", rc);
log_error (_("symmetric encryption of '%s' failed: %s\n"),
print_fname_stdin(fname),gpg_strerror (rc) );
}
break;
case aEncr: /* encrypt the given file */
if(multifile)
encrypt_crypt_files (ctrl, argc, argv, remusr);
else
{
if( argc > 1 )
wrong_args(_("--encrypt [filename]"));
if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) )
{
write_status_failure ("encrypt", rc);
log_error("%s: encryption failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
}
break;
case aEncrSym:
/* This works with PGP 8 in the sense that it acts just like a
symmetric message. It doesn't work at all with 2 or 6. It
might work with 7, but alas, I don't have a copy to test
with right now. */
if( argc > 1 )
wrong_args(_("--symmetric --encrypt [filename]"));
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --encrypt"
" with --s2k-mode 0\n"));
else if(PGP6 || PGP7)
log_error(_("you cannot use --symmetric --encrypt"
" while in %s mode\n"),compliance_option_string());
else
{
if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) )
{
write_status_failure ("encrypt", rc);
log_error ("%s: encryption failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
}
break;
case aSign: /* sign the given file */
sl = NULL;
if( detached_sig ) { /* sign all files */
for( ; argc; argc--, argv++ )
add_to_strlist( &sl, *argv );
}
else {
if( argc > 1 )
wrong_args(_("--sign [filename]"));
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
}
if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 0, NULL, NULL)))
{
write_status_failure ("sign", rc);
log_error ("signing failed: %s\n", gpg_strerror (rc) );
}
free_strlist(sl);
break;
case aSignEncr: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args(_("--sign --encrypt [filename]"));
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 1, remusr, NULL)))
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: sign+encrypt failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
free_strlist(sl);
break;
case aSignEncrSym: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args(_("--symmetric --sign --encrypt [filename]"));
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --sign --encrypt"
" with --s2k-mode 0\n"));
else if(PGP6 || PGP7)
log_error(_("you cannot use --symmetric --sign --encrypt"
" while in %s mode\n"),compliance_option_string());
else
{
if( argc )
{
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if ((rc = sign_file (ctrl, sl, detached_sig, locusr,
2, remusr, NULL)))
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: symmetric+sign+encrypt failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
free_strlist(sl);
}
break;
case aSignSym: /* sign and conventionally encrypt the given file */
if (argc > 1)
wrong_args(_("--sign --symmetric [filename]"));
rc = sign_symencrypt_file (ctrl, fname, locusr);
if (rc)
{
write_status_failure ("sign-symencrypt", rc);
log_error("%s: sign+symmetric failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
break;
case aClearsign: /* make a clearsig */
if( argc > 1 )
wrong_args(_("--clearsign [filename]"));
if( (rc = clearsign_file (ctrl, fname, locusr, NULL)) )
{
write_status_failure ("sign", rc);
log_error("%s: clearsign failed: %s\n",
print_fname_stdin(fname), gpg_strerror (rc) );
}
break;
case aVerify:
if (multifile)
{
if ((rc = verify_files (ctrl, argc, argv)))
log_error("verify files failed: %s\n", gpg_strerror (rc) );
}
else
{
if ((rc = verify_signatures (ctrl, argc, argv)))
log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
}
if (rc)
write_status_failure ("verify", rc);
break;
case aDecrypt:
if (multifile)
decrypt_messages (ctrl, argc, argv);
else
{
if( argc > 1 )
wrong_args(_("--decrypt [filename]"));
if( (rc = decrypt_message (ctrl, fname) ))
{
write_status_failure ("decrypt", rc);
log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );
}
}
break;
case aQuickSignKey:
case aQuickLSignKey:
{
const char *fpr;
if (argc < 1)
wrong_args ("--quick-[l]sign-key fingerprint [userids]");
fpr = *argv++; argc--;
sl = NULL;
for( ; argc; argc--, argv++)
append_to_strlist2 (&sl, *argv, utf8_strings);
keyedit_quick_sign (ctrl, fpr, sl, locusr, (cmd == aQuickLSignKey));
free_strlist (sl);
}
break;
case aSignKey:
if( argc != 1 )
wrong_args(_("--sign-key user-id"));
/* fall through */
case aLSignKey:
if( argc != 1 )
wrong_args(_("--lsign-key user-id"));
/* fall through */
sl=NULL;
if(cmd==aSignKey)
append_to_strlist(&sl,"sign");
else if(cmd==aLSignKey)
append_to_strlist(&sl,"lsign");
else
BUG();
append_to_strlist( &sl, "save" );
username = make_username( fname );
keyedit_menu (ctrl, username, locusr, sl, 0, 0 );
xfree(username);
free_strlist(sl);
break;
case aEditKey: /* Edit a key signature */
if( !argc )
wrong_args(_("--edit-key user-id [commands]"));
username = make_username( fname );
if( argc > 1 ) {
sl = NULL;
for( argc--, argv++ ; argc; argc--, argv++ )
append_to_strlist( &sl, *argv );
keyedit_menu (ctrl, username, locusr, sl, 0, 1 );
free_strlist(sl);
}
else
keyedit_menu (ctrl, username, locusr, NULL, 0, 1 );
xfree(username);
break;
case aPasswd:
if (argc != 1)
wrong_args (_("--passwd "));
else
{
username = make_username (fname);
keyedit_passwd (ctrl, username);
xfree (username);
}
break;
case aDeleteKeys:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
sl = NULL;
/* I'm adding these in reverse order as add_to_strlist2
reverses them again, and it's easier to understand in the
proper order :) */
for( ; argc; argc-- )
add_to_strlist2( &sl, argv[argc-1], utf8_strings );
delete_keys(sl,cmd==aDeleteSecretKeys,cmd==aDeleteSecretAndPublicKeys);
free_strlist(sl);
break;
case aCheckKeys:
opt.check_sigs = 1;
case aListSigs:
opt.list_sigs = 1;
case aListKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
public_key_list (ctrl, sl, 0);
free_strlist(sl);
break;
case aListSecretKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
secret_key_list (ctrl, sl);
free_strlist(sl);
break;
case aLocateKeys:
sl = NULL;
for (; argc; argc--, argv++)
add_to_strlist2( &sl, *argv, utf8_strings );
public_key_list (ctrl, sl, 1);
free_strlist (sl);
break;
case aQuickKeygen:
{
const char *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
wrong_args("--quick-gen-key USER-ID [ALGO [USAGE [EXPIRE]]]");
username = make_username (fname);
argv++, argc--;
x_algo = "";
x_usage = "";
x_expire = "";
if (argc)
{
x_algo = *argv++; argc--;
if (argc)
{
x_usage = *argv++; argc--;
if (argc)
{
x_expire = *argv++; argc--;
}
}
}
quick_generate_keypair (ctrl, username, x_algo, x_usage, x_expire);
xfree (username);
}
break;
case aKeygen: /* generate a key */
if( opt.batch ) {
if( argc > 1 )
wrong_args("--gen-key [parameterfile]");
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else {
if (opt.command_fd != -1 && argc)
{
if( argc > 1 )
wrong_args("--gen-key [parameterfile]");
opt.batch = 1;
generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
}
else if (argc)
wrong_args ("--gen-key");
else
generate_keypair (ctrl, 0, NULL, NULL, 0);
}
break;
case aFullKeygen: /* Generate a key with all options. */
if (opt.batch)
{
if (argc > 1)
wrong_args ("--full-gen-key [parameterfile]");
generate_keypair (ctrl, 1, argc? *argv : NULL, NULL, 0);
}
else
{
if (argc)
wrong_args("--full-gen-key");
generate_keypair (ctrl, 1, NULL, NULL, 0);
}
break;
case aQuickAddUid:
{
const char *uid, *newuid;
if (argc != 2)
wrong_args ("--quick-adduid USER-ID NEW-USER-ID");
uid = *argv++; argc--;
newuid = *argv++; argc--;
keyedit_quick_adduid (ctrl, uid, newuid);
}
break;
case aQuickAddKey:
{
const char *x_fpr, *x_algo, *x_usage, *x_expire;
if (argc < 1 || argc > 4)
wrong_args ("--quick-addkey FINGERPRINT [ALGO [USAGE [EXPIRE]]]");
x_fpr = *argv++; argc--;
x_algo = "";
x_usage = "";
x_expire = "";
if (argc)
{
x_algo = *argv++; argc--;
if (argc)
{
x_usage = *argv++; argc--;
if (argc)
{
x_expire = *argv++; argc--;
}
}
}
keyedit_quick_addkey (ctrl, x_fpr, x_algo, x_usage, x_expire);
}
break;
case aQuickRevUid:
{
const char *uid, *uidtorev;
if (argc != 2)
wrong_args ("--quick-revuid USER-ID USER-ID-TO-REVOKE");
uid = *argv++; argc--;
uidtorev = *argv++; argc--;
keyedit_quick_revuid (ctrl, uid, uidtorev);
}
break;
case aFastImport:
opt.import_options |= IMPORT_FAST;
case aImport:
import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options);
break;
/* TODO: There are a number of command that use this same
"make strlist, call function, report error, free strlist"
pattern. Join them together here and avoid all that
duplicated code. */
case aExport:
case aSendKeys:
case aRecvKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
if( cmd == aSendKeys )
rc = keyserver_export (ctrl, sl );
else if( cmd == aRecvKeys )
rc = keyserver_import (ctrl, sl );
else
{
export_stats_t stats = export_new_stats ();
rc = export_pubkeys (ctrl, sl, opt.export_options, stats);
export_print_stats (stats);
export_release_stats (stats);
}
if(rc)
{
if(cmd==aSendKeys)
{
write_status_failure ("send-keys", rc);
log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc));
}
else if(cmd==aRecvKeys)
{
write_status_failure ("recv-keys", rc);
log_error (_("keyserver receive failed: %s\n"),
gpg_strerror (rc));
}
else
{
write_status_failure ("export", rc);
log_error (_("key export failed: %s\n"), gpg_strerror (rc));
}
}
free_strlist(sl);
break;
case aExportSshKey:
if (argc != 1)
wrong_args ("--export-ssh-key ");
rc = export_ssh_key (ctrl, argv[0]);
if (rc)
{
write_status_failure ("export-ssh-key", rc);
log_error (_("export as ssh key failed: %s\n"), gpg_strerror (rc));
}
break;
case aSearchKeys:
sl = NULL;
for (; argc; argc--, argv++)
append_to_strlist2 (&sl, *argv, utf8_strings);
rc = keyserver_search (ctrl, sl);
if (rc)
{
write_status_failure ("search-keys", rc);
log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
}
free_strlist (sl);
break;
case aRefreshKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc = keyserver_refresh (ctrl, sl);
if(rc)
{
write_status_failure ("refresh-keys", rc);
log_error (_("keyserver refresh failed: %s\n"),gpg_strerror (rc));
}
free_strlist(sl);
break;
case aFetchKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc = keyserver_fetch (ctrl, sl);
if(rc)
{
write_status_failure ("fetch-keys", rc);
log_error ("key fetch failed: %s\n",gpg_strerror (rc));
}
free_strlist(sl);
break;
case aExportSecret:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
{
export_stats_t stats = export_new_stats ();
export_seckeys (ctrl, sl, stats);
export_print_stats (stats);
export_release_stats (stats);
}
free_strlist(sl);
break;
case aExportSecretSub:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
{
export_stats_t stats = export_new_stats ();
export_secsubkeys (ctrl, sl, stats);
export_print_stats (stats);
export_release_stats (stats);
}
free_strlist(sl);
break;
case aGenRevoke:
if( argc != 1 )
wrong_args("--gen-revoke user-id");
username = make_username(*argv);
gen_revoke( username );
xfree( username );
break;
case aDesigRevoke:
if (argc != 1)
wrong_args ("--desig-revoke user-id");
username = make_username (*argv);
gen_desig_revoke (ctrl, username, locusr);
xfree (username);
break;
case aDeArmor:
if( argc > 1 )
wrong_args("--dearmor [file]");
rc = dearmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("dearmor", rc);
log_error (_("dearmoring failed: %s\n"), gpg_strerror (rc));
}
break;
case aEnArmor:
if( argc > 1 )
wrong_args("--enarmor [file]");
rc = enarmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("enarmor", rc);
log_error (_("enarmoring failed: %s\n"), gpg_strerror (rc));
}
break;
case aPrimegen:
#if 0 /*FIXME*/
{ int mode = argc < 2 ? 0 : atoi(*argv);
if( mode == 1 && argc == 2 ) {
mpi_print (es_stdout,
generate_public_prime( atoi(argv[1]) ), 1);
}
else if( mode == 2 && argc == 3 ) {
mpi_print (es_stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), NULL,NULL ), 1);
}
else if( mode == 3 && argc == 3 ) {
MPI *factors;
mpi_print (es_stdout, generate_elg_prime(
1, atoi(argv[1]),
atoi(argv[2]), NULL,&factors ), 1);
es_putc ('\n', es_stdout);
mpi_print (es_stdout, factors[0], 1 ); /* print q */
}
else if( mode == 4 && argc == 3 ) {
MPI g = mpi_alloc(1);
mpi_print (es_stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), g, NULL ), 1);
es_putc ('\n', es_stdout);
mpi_print (es_stdout, g, 1 );
mpi_free (g);
}
else
wrong_args("--gen-prime mode bits [qbits] ");
es_putc ('\n', es_stdout);
}
#endif
wrong_args("--gen-prime not yet supported ");
break;
case aGenRandom:
{
int level = argc ? atoi(*argv):0;
int count = argc > 1 ? atoi(argv[1]): 0;
int endless = !count;
if( argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0 )
wrong_args("--gen-random 0|1|2 [count]");
while( endless || count ) {
byte *p;
/* Wee need a multiple of 3, so that in case of
armored output we get a correct string. No
linefolding is done, as it is best to levae this to
other tools */
size_t n = !endless && count < 99? count : 99;
p = gcry_random_bytes (n, level);
#ifdef HAVE_DOSISH_SYSTEM
setmode ( fileno(stdout), O_BINARY );
#endif
if (opt.armor) {
char *tmp = make_radix64_string (p, n);
es_fputs (tmp, es_stdout);
xfree (tmp);
if (n%3 == 1)
es_putc ('=', es_stdout);
if (n%3)
es_putc ('=', es_stdout);
} else {
es_fwrite( p, n, 1, es_stdout );
}
xfree(p);
if( !endless )
count -= n;
}
if (opt.armor)
es_putc ('\n', es_stdout);
}
break;
case aPrintMD:
if( argc < 1)
wrong_args("--print-md algo [files]");
{
int all_algos = (**argv=='*' && !(*argv)[1]);
int algo = all_algos? 0 : gcry_md_map_name (*argv);
if( !algo && !all_algos )
log_error(_("invalid hash algorithm '%s'\n"), *argv );
else {
argc--; argv++;
if( !argc )
print_mds(NULL, algo);
else {
for(; argc; argc--, argv++ )
print_mds(*argv, algo);
}
}
}
break;
case aPrintMDs: /* old option */
if( !argc )
print_mds(NULL,0);
else {
for(; argc; argc--, argv++ )
print_mds(*argv,0);
}
break;
#ifndef NO_TRUST_MODELS
case aListTrustDB:
if( !argc )
list_trustdb (es_stdout, NULL);
else {
for( ; argc; argc--, argv++ )
list_trustdb (es_stdout, *argv );
}
break;
case aUpdateTrustDB:
if( argc )
wrong_args("--update-trustdb");
update_trustdb (ctrl);
break;
case aCheckTrustDB:
/* Old versions allowed for arguments - ignore them */
check_trustdb (ctrl);
break;
case aFixTrustDB:
how_to_fix_the_trustdb ();
break;
case aListTrustPath:
if( !argc )
wrong_args("--list-trust-path ");
for( ; argc; argc--, argv++ ) {
username = make_username( *argv );
list_trust_path( username );
xfree(username);
}
break;
case aExportOwnerTrust:
if( argc )
wrong_args("--export-ownertrust");
export_ownertrust();
break;
case aImportOwnerTrust:
if( argc > 1 )
wrong_args("--import-ownertrust [file]");
import_ownertrust( argc? *argv:NULL );
break;
#endif /*!NO_TRUST_MODELS*/
case aRebuildKeydbCaches:
if (argc)
wrong_args ("--rebuild-keydb-caches");
keydb_rebuild_caches (1);
break;
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus:
if (argc)
wrong_args ("--card-status");
card_status (es_stdout, NULL, 0);
break;
case aCardEdit:
if (argc) {
sl = NULL;
for (argc--, argv++ ; argc; argc--, argv++)
append_to_strlist (&sl, *argv);
card_edit (ctrl, sl);
free_strlist (sl);
}
else
card_edit (ctrl, NULL);
break;
case aChangePIN:
if (!argc)
change_pin (0,1);
else if (argc == 1)
change_pin (atoi (*argv),1);
else
wrong_args ("--change-pin [no]");
break;
#endif /* ENABLE_CARD_SUPPORT*/
case aListConfig:
{
char *str=collapse_args(argc,argv);
list_config(str);
xfree(str);
}
break;
case aListGcryptConfig:
/* Fixme: It would be nice to integrate that with
--list-config but unfortunately there is no way yet to have
libgcrypt print it to an estream for further parsing. */
gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
break;
case aTOFUPolicy:
#ifdef USE_TOFU
{
int policy;
int i;
KEYDB_HANDLE hd;
if (argc < 2)
wrong_args ("--tofu-policy POLICY KEYID [KEYID...]");
policy = parse_tofu_policy (argv[0]);
hd = keydb_new ();
if (! hd)
g10_exit (1);
for (i = 1; i < argc; i ++)
{
KEYDB_SEARCH_DESC desc;
kbnode_t kb;
rc = classify_user_id (argv[i], &desc, 0);
if (rc)
{
log_error (_("error parsing key specification '%s': %s\n"),
argv[i], gpg_strerror (rc));
g10_exit (1);
}
if (! (desc.mode == KEYDB_SEARCH_MODE_SHORT_KID
|| desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20
|| desc.mode == KEYDB_SEARCH_MODE_FPR
|| desc.mode == KEYDB_SEARCH_MODE_KEYGRIP))
{
log_error (_("'%s' does not appear to be a valid"
" key ID, fingerprint or keygrip\n"),
argv[i]);
g10_exit (1);
}
rc = keydb_search_reset (hd);
if (rc)
{
/* This should not happen, thus no need to tranalate
the string. */
log_error ("keydb_search_reset failed: %s\n",
gpg_strerror (rc));
g10_exit (1);
}
rc = keydb_search (hd, &desc, 1, NULL);
if (rc)
{
log_error (_("key \"%s\" not found: %s\n"), argv[i],
gpg_strerror (rc));
g10_exit (1);
}
rc = keydb_get_keyblock (hd, &kb);
if (rc)
{
log_error (_("error reading keyblock: %s\n"),
gpg_strerror (rc));
g10_exit (1);
}
merge_keys_and_selfsig (kb);
if (tofu_set_policy (ctrl, kb, policy))
g10_exit (1);
}
keydb_release (hd);
}
#endif /*USE_TOFU*/
break;
case aListPackets:
default:
if( argc > 1 )
wrong_args(_("[filename]"));
/* Issue some output for the unix newbie */
if (!fname && !opt.outfile
&& gnupg_isatty (fileno (stdin))
&& gnupg_isatty (fileno (stdout))
&& gnupg_isatty (fileno (stderr)))
log_info(_("Go ahead and type your message ...\n"));
a = iobuf_open(fname);
if (a && is_secured_file (iobuf_get_fd (a)))
{
iobuf_close (a);
a = NULL;
gpg_err_set_errno (EPERM);
}
if( !a )
log_error(_("can't open '%s'\n"), print_fname_stdin(fname));
else {
if( !opt.no_armor ) {
if( use_armor_filter( a ) ) {
afx = new_armor_context ();
push_armor_filter (afx, a);
}
}
if( cmd == aListPackets ) {
opt.list_packets=1;
set_packet_list_mode(1);
}
rc = proc_packets (ctrl, NULL, a );
if( rc )
{
write_status_failure ("-", rc);
log_error ("processing message failed: %s\n",
gpg_strerror (rc));
}
iobuf_close(a);
}
break;
}
/* cleanup */
gpg_deinit_default_ctrl (ctrl);
xfree (ctrl);
release_armor_context (afx);
FREE_STRLIST(remusr);
FREE_STRLIST(locusr);
g10_exit(0);
return 8; /*NEVER REACHED*/
}
/* Note: This function is used by signal handlers!. */
static void
emergency_cleanup (void)
{
gcry_control (GCRYCTL_TERM_SECMEM );
}
void
g10_exit( int rc )
{
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
if (DBG_CLOCK)
log_clock ("stop");
if ( (opt.debug & DBG_MEMSTAT_VALUE) )
{
keydb_dump_stats ();
gcry_control (GCRYCTL_DUMP_MEMORY_STATS);
gcry_control (GCRYCTL_DUMP_RANDOM_STATS);
}
if (opt.debug)
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
emergency_cleanup ();
rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
exit (rc);
}
/* Pretty-print hex hashes. This assumes at least an 80-character
display, but there are a few other similar assumptions in the
display code. */
static void
print_hex (gcry_md_hd_t md, int algo, const char *fname)
{
int i,n,count,indent=0;
const byte *p;
if (fname)
indent = es_printf("%s: ",fname);
if (indent>40)
{
es_printf ("\n");
indent=0;
}
if (algo==DIGEST_ALGO_RMD160)
indent += es_printf("RMD160 = ");
else if (algo>0)
indent += es_printf("%6s = ", gcry_md_algo_name (algo));
else
algo = abs(algo);
count = indent;
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
count += es_printf ("%02X",*p++);
for(i=1;i79)
{
es_printf ("\n%*s",indent," ");
count = indent;
}
else
count += es_printf(" ");
if (!(i%8))
count += es_printf(" ");
}
else if (n==20)
{
if(!(i%2))
{
if(count+4>79)
{
es_printf ("\n%*s",indent," ");
count=indent;
}
else
count += es_printf(" ");
}
if (!(i%10))
count += es_printf(" ");
}
else
{
if(!(i%4))
{
if (count+8>79)
{
es_printf ("\n%*s",indent," ");
count=indent;
}
else
count += es_printf(" ");
}
}
count += es_printf("%02X",*p);
}
es_printf ("\n");
}
static void
print_hashline( gcry_md_hd_t md, int algo, const char *fname )
{
int i, n;
const byte *p;
if ( fname )
{
for (p = fname; *p; p++ )
{
if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' )
es_printf ("%%%02X", *p );
else
es_putc (*p, es_stdout);
}
}
es_putc (':', es_stdout);
es_printf ("%d:", algo);
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
for(i=0; i < n ; i++, p++ )
es_printf ("%02X", *p);
es_fputs (":\n", es_stdout);
}
static void
print_mds( const char *fname, int algo )
{
estream_t fp;
char buf[1024];
size_t n;
gcry_md_hd_t md;
if (!fname)
{
fp = es_stdin;
es_set_binary (fp);
}
else
{
fp = es_fopen (fname, "rb" );
if (fp && is_secured_file (es_fileno (fp)))
{
es_fclose (fp);
fp = NULL;
gpg_err_set_errno (EPERM);
}
}
if (!fp)
{
log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
return;
}
gcry_md_open (&md, 0, 0);
if (algo)
gcry_md_enable (md, algo);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
gcry_md_enable (md, GCRY_MD_MD5);
gcry_md_enable (md, GCRY_MD_SHA1);
if (!gcry_md_test_algo (GCRY_MD_RMD160))
gcry_md_enable (md, GCRY_MD_RMD160);
if (!gcry_md_test_algo (GCRY_MD_SHA224))
gcry_md_enable (md, GCRY_MD_SHA224);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
gcry_md_enable (md, GCRY_MD_SHA256);
if (!gcry_md_test_algo (GCRY_MD_SHA384))
gcry_md_enable (md, GCRY_MD_SHA384);
if (!gcry_md_test_algo (GCRY_MD_SHA512))
gcry_md_enable (md, GCRY_MD_SHA512);
}
while ((n=es_fread (buf, 1, DIM(buf), fp)))
gcry_md_write (md, buf, n);
if (es_ferror(fp))
log_error ("%s: %s\n", fname?fname:"[stdin]", strerror(errno));
else
{
gcry_md_final (md);
if (opt.with_colons)
{
if ( algo )
print_hashline (md, algo, fname);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
print_hashline( md, GCRY_MD_MD5, fname );
print_hashline( md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hashline( md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hashline (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hashline( md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hashline ( md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hashline ( md, GCRY_MD_SHA512, fname );
}
}
else
{
if (algo)
print_hex (md, -algo, fname);
else
{
if (!gcry_md_test_algo (GCRY_MD_MD5))
print_hex (md, GCRY_MD_MD5, fname);
print_hex (md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hex (md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hex (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hex (md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hex (md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hex (md, GCRY_MD_SHA512, fname );
}
}
}
gcry_md_close (md);
if (fp != es_stdin)
es_fclose (fp);
}
/****************
* Check the supplied name,value string and add it to the notation
* data to be used for signatures. which==0 for sig notations, and 1
* for cert notations.
*/
static void
add_notation_data( const char *string, int which )
{
struct notation *notation;
notation=string_to_notation(string,utf8_strings);
if(notation)
{
if(which)
{
notation->next=opt.cert_notations;
opt.cert_notations=notation;
}
else
{
notation->next=opt.sig_notations;
opt.sig_notations=notation;
}
}
}
static void
add_policy_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;iflags |= 1;
}
static void
add_keyserver_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;iflags |= 1;
}
diff --git a/g10/gpgv.c b/g10/gpgv.c
index 59edbb978..729fcf843 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -1,695 +1,690 @@
/* gpgv.c - The GnuPG signature verify utility
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2006,
* 2008, 2009, 2012 Free Software Foundation, Inc.
*
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_DOSISH_SYSTEM
#include /* for setmode() */
#endif
#ifdef HAVE_LIBREADLINE
#define GNUPG_LIBREADLINE_H_INCLUDED
#include
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include "util.h"
#include "packet.h"
#include "iobuf.h"
#include "main.h"
#include "options.h"
#include "keydb.h"
#include "trustdb.h"
#include "filter.h"
#include "ttyio.h"
#include "i18n.h"
#include "sysutils.h"
#include "status.h"
#include "call-agent.h"
#include "../common/init.h"
enum cmd_and_opt_values {
aNull = 0,
oQuiet = 'q',
oVerbose = 'v',
oBatch = 500,
oKeyring,
oIgnoreTimeConflict,
oStatusFD,
oLoggerFD,
oHomedir,
oWeakDigest,
aTest
};
static ARGPARSE_OPTS opts[] = {
ARGPARSE_group (300, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_s (oKeyring, "keyring",
N_("|FILE|take the keys from the keyring FILE")),
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict",
N_("make timestamp conflicts only a warning")),
ARGPARSE_s_i (oStatusFD, "status-fd",
N_("|FD|write status info to this FD")),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_s (oWeakDigest, "weak-digest",
N_("|ALGO|reject signatures made with ALGO")),
ARGPARSE_end ()
};
int g10_errors_seen = 0;
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
{
const char *s;
char *result;
s = getfnc (NULL);
result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
strcpy (stpcpy (stpcpy (result, libname), " "), s);
return result;
}
static const char *
my_strusage( int level )
{
static char *ver_gcry;
const char *p;
switch (level)
{
case 11: p = "@GPG@v (GnuPG)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 1:
case 40: p = _("Usage: gpgv [options] [files] (-h for help)");
break;
case 41: p = _("Syntax: gpgv [options] [files]\n"
"Check signatures against known trusted keys\n");
break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
default: p = NULL;
}
return p;
}
int
main( int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int rc=0;
strlist_t sl;
strlist_t nrings = NULL;
unsigned configlineno;
ctrl_t ctrl;
early_system_init ();
set_strusage (my_strusage);
log_set_prefix ("gpgv", 1);
/* Make sure that our subsystems are ready. */
i18n_init();
init_common_subsystems (&argc, &argv);
- if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
- {
- log_fatal ( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
- NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
- }
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
gnupg_init_signals (0, NULL);
opt.command_fd = -1; /* no command fd */
opt.keyserver_options.options |= KEYSERVER_AUTO_KEY_RETRIEVE;
opt.trust_model = TM_ALWAYS;
opt.no_sig_cache = 1;
opt.flags.require_cross_cert = 1;
opt.batch = 1;
opt.weak_digests = NULL;
tty_no_terminal(1);
tty_batchmode(1);
dotlock_disable ();
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
additional_weak_digest("MD5");
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
while (optfile_parse( NULL, NULL, &configlineno, &pargs, opts))
{
switch (pargs.r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oVerbose:
opt.verbose++;
opt.list_sigs=1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
break;
case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
case oStatusFD: set_status_fd( pargs.r.ret_int ); break;
case oLoggerFD:
log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oWeakDigest:
additional_weak_digest(pargs.r.ret_str);
break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
default : pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
if (log_get_errorcount (0))
g10_exit(2);
if (opt.verbose > 1)
set_packet_list_mode(1);
/* Note: We open all keyrings in read-only mode. */
if (!nrings) /* No keyring given: use default one. */
keydb_add_resource ("trustedkeys" EXTSEP_S "kbx",
(KEYDB_RESOURCE_FLAG_READONLY
|KEYDB_RESOURCE_FLAG_GPGVDEF));
for (sl = nrings; sl; sl = sl->next)
keydb_add_resource (sl->d, KEYDB_RESOURCE_FLAG_READONLY);
FREE_STRLIST (nrings);
ctrl = xcalloc (1, sizeof *ctrl);
if ((rc = verify_signatures (ctrl, argc, argv)))
log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
xfree (ctrl);
/* cleanup */
g10_exit (0);
return 8; /*NOTREACHED*/
}
void
g10_exit( int rc )
{
rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
exit(rc );
}
/* Stub:
* We have to override the trustcheck from pkclist.c because
* this utility assumes that all keys in the keyring are trustworthy
*/
int
check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
{
(void)ctrl;
(void)sig;
return 0;
}
void
read_trust_options(byte *trust_model, ulong *created, ulong *nextcheck,
byte *marginals, byte *completes, byte *cert_depth,
byte *min_cert_level)
{
(void)trust_model;
(void)created;
(void)nextcheck;
(void)marginals;
(void)completes;
(void)cert_depth;
(void)min_cert_level;
}
/* Stub:
* We don't have the trustdb , so we have to provide some stub functions
* instead
*/
int
cache_disabled_value(PKT_public_key *pk)
{
(void)pk;
return 0;
}
void
check_trustdb_stale (ctrl_t ctrl)
{
(void)ctrl;
}
int
get_validity_info (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
{
(void)ctrl;
(void)pk;
(void)uid;
return '?';
}
unsigned int
get_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask)
{
(void)ctrl;
(void)pk;
(void)uid;
(void)sig;
(void)may_ask;
return 0;
}
const char *
trust_value_to_string (unsigned int value)
{
(void)value;
return "err";
}
const char *
uid_trust_string_fixed (ctrl_t ctrl, PKT_public_key *key, PKT_user_id *uid)
{
(void)ctrl;
(void)key;
(void)uid;
return "err";
}
int
get_ownertrust_info (PKT_public_key *pk)
{
(void)pk;
return '?';
}
unsigned int
get_ownertrust (PKT_public_key *pk)
{
(void)pk;
return TRUST_UNKNOWN;
}
/* Stubs:
* Because we only work with trusted keys, it does not make sense to
* get them from a keyserver
*/
struct keyserver_spec *
keyserver_match (struct keyserver_spec *spec)
{
(void)spec;
return NULL;
}
int
keyserver_any_configured (ctrl_t ctrl)
{
(void)ctrl;
return 0;
}
int
keyserver_import_keyid (u32 *keyid, void *dummy)
{
(void)keyid;
(void)dummy;
return -1;
}
int
keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
struct keyserver_spec *keyserver)
{
(void)ctrl;
(void)fprint;
(void)fprint_len;
(void)keyserver;
return -1;
}
int
keyserver_import_cert (const char *name)
{
(void)name;
return -1;
}
int
keyserver_import_pka (const char *name,unsigned char *fpr)
{
(void)name;
(void)fpr;
return -1;
}
gpg_error_t
keyserver_import_wkd (ctrl_t ctrl, const char *name,
unsigned char **fpr, size_t *fpr_len)
{
(void)ctrl;
(void)name;
(void)fpr;
(void)fpr_len;
return GPG_ERR_BUG;
}
int
keyserver_import_name (const char *name,struct keyserver_spec *spec)
{
(void)name;
(void)spec;
return -1;
}
int
keyserver_import_ldap (const char *name)
{
(void)name;
return -1;
}
gpg_error_t
read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock)
{
(void)ctrl;
(void)fname;
(void)r_keyblock;
return -1;
}
/* Stub:
* No encryption here but mainproc links to these functions.
*/
gpg_error_t
get_session_key (ctrl_t ctrl, PKT_pubkey_enc *k, DEK *dek)
{
(void)ctrl;
(void)k;
(void)dek;
return GPG_ERR_GENERAL;
}
/* Stub: */
gpg_error_t
get_override_session_key (DEK *dek, const char *string)
{
(void)dek;
(void)string;
return GPG_ERR_GENERAL;
}
/* Stub: */
int
decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
{
(void)ctrl;
(void)procctx;
(void)ed;
(void)dek;
return GPG_ERR_GENERAL;
}
/* Stub:
* No interactive commands, so we don't need the helptexts
*/
void
display_online_help (const char *keyword)
{
(void)keyword;
}
/* Stub:
* We don't use secret keys, but getkey.c links to this
*/
int
check_secret_key (PKT_public_key *pk, int n)
{
(void)pk;
(void)n;
return GPG_ERR_GENERAL;
}
/* Stub:
* No secret key, so no passphrase needed
*/
DEK *
passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache,
const char *tmp, int *canceled)
{
(void)cipher_algo;
(void)s2k;
(void)create;
(void)nocache;
(void)tmp;
if (canceled)
*canceled = 0;
return NULL;
}
void
passphrase_clear_cache (const char *cacheid)
{
(void)cacheid;
}
struct keyserver_spec *
parse_preferred_keyserver(PKT_signature *sig)
{
(void)sig;
return NULL;
}
struct keyserver_spec *
parse_keyserver_uri (const char *uri, int require_scheme,
const char *configname, unsigned int configlineno)
{
(void)uri;
(void)require_scheme;
(void)configname;
(void)configlineno;
return NULL;
}
void
free_keyserver_spec (struct keyserver_spec *keyserver)
{
(void)keyserver;
}
/* Stubs to avoid linking to photoid.c */
void
show_photos (const struct user_attribute *attrs, int count, PKT_public_key *pk)
{
(void)attrs;
(void)count;
(void)pk;
}
int
parse_image_header (const struct user_attribute *attr, byte *type, u32 *len)
{
(void)attr;
(void)type;
(void)len;
return 0;
}
char *
image_type_to_string (byte type, int string)
{
(void)type;
(void)string;
return NULL;
}
#ifdef ENABLE_CARD_SUPPORT
int
agent_scd_getattr (const char *name, struct agent_card_info_s *info)
{
(void)name;
(void)info;
return 0;
}
#endif /* ENABLE_CARD_SUPPORT */
/* We do not do any locking, so use these stubs here */
void
dotlock_disable (void)
{
}
dotlock_t
dotlock_create (const char *file_to_lock, unsigned int flags)
{
(void)file_to_lock;
(void)flags;
return NULL;
}
void
dotlock_destroy (dotlock_t h)
{
(void)h;
}
int
dotlock_take (dotlock_t h, long timeout)
{
(void)h;
(void)timeout;
return 0;
}
int
dotlock_release (dotlock_t h)
{
(void)h;
return 0;
}
void
dotlock_remove_lockfiles (void)
{
}
gpg_error_t
agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk)
{
(void)ctrl;
(void)pk;
return gpg_error (GPG_ERR_NO_SECKEY);
}
gpg_error_t
agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock)
{
(void)ctrl;
(void)keyblock;
return gpg_error (GPG_ERR_NO_SECKEY);
}
gpg_error_t
agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
char **r_serialno, int *r_cleartext)
{
(void)ctrl;
(void)hexkeygrip;
(void)r_cleartext;
*r_serialno = NULL;
return gpg_error (GPG_ERR_NO_SECKEY);
}
gpg_error_t
gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid,
unsigned char **r_fpr, size_t *r_fprlen,
char **r_url)
{
(void)ctrl;
(void)userid;
if (r_fpr)
*r_fpr = NULL;
if (r_fprlen)
*r_fprlen = 0;
if (r_url)
*r_url = NULL;
return gpg_error (GPG_ERR_NOT_FOUND);
}
gpg_error_t
export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
export_stats_t stats,
kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
{
(void)ctrl;
(void)keyspec;
(void)options;
(void)stats;
*r_keyblock = NULL;
*r_data = NULL;
*r_datalen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
gpg_error_t
tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
(void)ctrl;
(void)pk;
(void)user_id;
(void)policy;
return gpg_error (GPG_ERR_GENERAL);
}
const char *
tofu_policy_str (enum tofu_policy policy)
{
(void)policy;
return "unknown";
}
void
tofu_begin_batch_update (void)
{
}
void
tofu_end_batch_update (void)
{
}
diff --git a/g13/g13-syshelp.c b/g13/g13-syshelp.c
index f3c20f59d..0bb34dacb 100644
--- a/g13/g13-syshelp.c
+++ b/g13/g13-syshelp.c
@@ -1,753 +1,748 @@
/* g13-syshelp.c - Helper for disk key management with GnuPG
* Copyright (C) 2015 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_PWD_H
# include
#endif
#include
#include "g13-syshelp.h"
#include
#include
#include "i18n.h"
#include "sysutils.h"
#include "asshelp.h"
#include "../common/init.h"
#include "keyblob.h"
enum cmd_and_opt_values {
aNull = 0,
oQuiet = 'q',
oVerbose = 'v',
oRecipient = 'r',
aGPGConfList = 500,
oDebug,
oDebugLevel,
oDebugAll,
oDebugNone,
oDebugWait,
oDebugAllowCoreDump,
oLogFile,
oNoLogFile,
oAuditLog,
oOutput,
oAgentProgram,
oGpgProgram,
oType,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oStatusFD,
oLoggerFD,
oNoVerbose,
oNoSecmemWarn,
oHomedir,
oDryRun,
oNoDetach,
oNoRandomSeedFile,
oFakedSystemTime
};
static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level",
N_("|LEVEL|set the debugging level to LEVEL")),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_n (oDebugNone, "debug-none", "@"),
ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
ARGPARSE_end ()
};
/* The list of supported debug flags. */
static struct debug_flags_s debug_flags [] =
{
{ DBG_MOUNT_VALUE , "mount" },
{ DBG_CRYPTO_VALUE , "crypto" },
{ DBG_MEMORY_VALUE , "memory" },
{ DBG_MEMSTAT_VALUE, "memstat" },
{ DBG_IPC_VALUE , "ipc" },
{ 0, NULL }
};
/* The timer tick interval used by the idle task. */
#define TIMERTICK_INTERVAL_SEC (1)
/* It is possible that we are currently running under setuid permissions. */
static int maybe_setuid = 1;
/* Helper to implement --debug-level and --debug. */
static const char *debug_level;
static unsigned int debug_value;
/* Local prototypes. */
static void g13_syshelp_deinit_default_ctrl (ctrl_t ctrl);
static void release_tab_items (tab_item_t tab);
static tab_item_t parse_g13tab (const char *username);
static const char *
my_strusage( int level )
{
const char *p;
switch (level)
{
case 11: p = "@G13@-syshelp (@GNUPG@)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
break;
case 1:
case 40: p = _("Usage: @G13@-syshelp [options] [files] (-h for help)");
break;
case 41:
p = _("Syntax: @G13@-syshelp [options] [files]\n"
"Helper to perform root-only tasks for g13\n");
break;
case 31: p = "\nHome: "; break;
case 32: p = gnupg_homedir (); break;
default: p = NULL; break;
}
return p;
}
/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active
debug flags are propagated to the subsystems. With DEBUG_LEVEL
set, a specific set of debug flags is set; and individual debugging
flags will be added on top. */
static void
set_debug (void)
{
int numok = (debug_level && digitp (debug_level));
int numlvl = numok? atoi (debug_level) : 0;
if (!debug_level)
;
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
opt.debug = ~0;
/* if (numok) */
/* opt.debug &= ~(DBG_HASHING_VALUE); */
}
else
{
log_error (_("invalid debug-level '%s' given\n"), debug_level);
g13_exit(2);
}
opt.debug |= debug_value;
if (opt.debug && !opt.verbose)
opt.verbose = 1;
if (opt.debug)
opt.quiet = 0;
if (opt.debug & DBG_CRYPTO_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
parse_debug_flag (NULL, &opt.debug, debug_flags);
}
int
main ( int argc, char **argv)
{
ARGPARSE_ARGS pargs;
int orig_argc;
char **orig_argv;
gpg_error_t err = 0;
/* const char *fname; */
int may_coredump;
FILE *configfp = NULL;
char *configname = NULL;
unsigned configlineno;
int parse_debug = 0;
int no_more_options = 0;
int default_config =1;
char *logfile = NULL;
/* int debug_wait = 0; */
int use_random_seed = 1;
/* int nodetach = 0; */
/* int nokeysetup = 0; */
struct server_control_s ctrl;
/*mtrace();*/
early_system_init ();
gnupg_reopen_std (G13_NAME "-syshelp");
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
log_set_prefix (G13_NAME "-syshelp", 1);
/* Make sure that our subsystems are ready. */
i18n_init ();
init_common_subsystems (&argc, &argv);
- /* Check that the Libgcrypt is suitable. */
- if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
- log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
- NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-
/* Take extra care of the random pool. */
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
may_coredump = disable_core_dumps ();
g13_init_signals ();
dotlock_create (NULL, 0); /* Register locking cleanup. */
opt.session_env = session_env_new ();
if (!opt.session_env)
log_fatal ("error allocating session environment block: %s\n",
strerror (errno));
/* Fixme: We enable verbose mode here because there is currently no
way to do this when starting g13-syshelp. To fix that we should
add a g13-syshelp.conf file in /etc/gnupg. */
opt.verbose = 1;
/* First check whether we have a debug option on the commandline. */
orig_argc = argc;
orig_argv = argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
while (arg_parse( &pargs, opts))
{
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
parse_debug++;
}
/* Initialize the secure memory. */
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
maybe_setuid = 0;
/*
Now we are now working under our real uid
*/
/* Setup malloc hooks. */
{
struct assuan_malloc_hooks malloc_hooks;
malloc_hooks.malloc = gcry_malloc;
malloc_hooks.realloc = gcry_realloc;
malloc_hooks.free = gcry_free;
assuan_set_malloc_hooks (&malloc_hooks);
}
/* Prepare libassuan. */
assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
/*assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);*/
setup_libassuan_logging (&opt.debug);
/* Setup a default control structure for command line mode. */
memset (&ctrl, 0, sizeof ctrl);
g13_syshelp_init_default_ctrl (&ctrl);
ctrl.no_server = 1;
ctrl.status_fd = -1; /* No status output. */
if (default_config )
configname = make_filename (gnupg_sysconfdir (),
G13_NAME"-syshelp.conf", NULL);
argc = orig_argc;
argv = orig_argv;
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags = 1; /* Do not remove the args. */
next_pass:
if (configname)
{
configlineno = 0;
configfp = fopen (configname, "r");
if (!configfp)
{
if (default_config)
{
if (parse_debug)
log_info (_("NOTE: no default option file '%s'\n"), configname);
}
else
{
log_error (_("option file '%s': %s\n"),
configname, strerror(errno));
g13_exit(2);
}
xfree (configname);
configname = NULL;
}
if (parse_debug && configname)
log_info (_("reading options from '%s'\n"), configname);
default_config = 0;
}
while (!no_more_options
&& optfile_parse (configfp, configname, &configlineno, &pargs, opts))
{
switch (pargs.r_opt)
{
case oQuiet: opt.quiet = 1; break;
case oDryRun: opt.dry_run = 1; break;
case oVerbose:
opt.verbose++;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
break;
case oNoVerbose:
opt.verbose = 0;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
break;
case oLogFile: logfile = pargs.r.ret_str; break;
case oNoLogFile: logfile = NULL; break;
case oNoDetach: /*nodetach = 1; */break;
case oDebug:
if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
{
pargs.r_opt = ARGPARSE_INVALID_ARG;
pargs.err = ARGPARSE_PRINT_ERROR;
}
break;
case oDebugAll: debug_value = ~0; break;
case oDebugNone: debug_value = 0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oDebugWait: /*debug_wait = pargs.r.ret_int; */break;
case oDebugAllowCoreDump:
may_coredump = enable_core_dumps ();
break;
case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
case oFakedSystemTime:
{
time_t faked_time = isotime2epoch (pargs.r.ret_str);
if (faked_time == (time_t)(-1))
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
gnupg_set_time (faked_time, 0);
}
break;
case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break;
case oNoRandomSeedFile: use_random_seed = 0; break;
default:
pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
break;
}
}
if (configfp)
{
fclose (configfp);
configfp = NULL;
/* Keep a copy of the config filename. */
opt.config_filename = configname;
configname = NULL;
goto next_pass;
}
xfree (configname);
configname = NULL;
if (!opt.config_filename)
opt.config_filename = make_filename (gnupg_homedir (),
G13_NAME".conf", NULL);
if (log_get_errorcount(0))
g13_exit(2);
/* Now that we have the options parsed we need to update the default
control structure. */
g13_syshelp_init_default_ctrl (&ctrl);
if (may_coredump && !opt.quiet)
log_info (_("WARNING: program may create a core file!\n"));
if (logfile)
{
log_set_file (logfile);
log_set_prefix (NULL, 1|2|4);
}
if (gnupg_faked_time_p ())
{
gnupg_isotime_t tbuf;
log_info (_("WARNING: running with faked system time: "));
gnupg_get_isotime (tbuf);
dump_isotime (tbuf);
log_printf ("\n");
}
/* Print any pending secure memory warnings. */
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
/* Setup the debug flags for all subsystems. */
set_debug ();
/* Install a regular exit handler to make real sure that the secure
memory gets wiped out. */
g13_install_emergency_cleanup ();
/* Terminate if we found any error until now. */
if (log_get_errorcount(0))
g13_exit (2);
/* Set the standard GnuPG random seed file. */
if (use_random_seed)
{
char *p = make_filename (gnupg_homedir (), "random_seed", NULL);
gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
xfree(p);
}
/* Get the UID of the caller. */
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
{
const char *uidstr;
struct passwd *pwd = NULL;
uidstr = getenv ("USERV_UID");
/* Print a quick note if we are not started via userv. */
if (!uidstr)
{
if (getuid ())
{
log_info ("WARNING: Not started via userv\n");
ctrl.fail_all_cmds = 1;
}
ctrl.client.uid = getuid ();
}
else
{
unsigned long myuid;
errno = 0;
myuid = strtoul (uidstr, NULL, 10);
if (myuid == ULONG_MAX && errno)
{
log_info ("WARNING: Started via broken userv: %s\n",
strerror (errno));
ctrl.fail_all_cmds = 1;
ctrl.client.uid = getuid ();
}
else
ctrl.client.uid = (uid_t)myuid;
}
pwd = getpwuid (ctrl.client.uid);
if (!pwd || !*pwd->pw_name)
{
log_info ("WARNING: Name for UID not found: %s\n", strerror (errno));
ctrl.fail_all_cmds = 1;
ctrl.client.uname = xstrdup ("?");
}
else
ctrl.client.uname = xstrdup (pwd->pw_name);
/* Check that the user name does not contain a directory
separator. */
if (strchr (ctrl.client.uname, '/'))
{
log_info ("WARNING: Invalid user name passed\n");
ctrl.fail_all_cmds = 1;
}
}
#else /*!HAVE_PWD_H || !HAVE_GETPWUID*/
log_info ("WARNING: System does not support required syscalls\n");
ctrl.fail_all_cmds = 1;
ctrl.client.uid = getuid ();
ctrl.client.uname = xstrdup ("?");
#endif /*!HAVE_PWD_H || !HAVE_GETPWUID*/
/* Read the table entries for this user. */
if (!ctrl.fail_all_cmds
&& !(ctrl.client.tab = parse_g13tab (ctrl.client.uname)))
ctrl.fail_all_cmds = 1;
/* Start the server. */
err = syshelp_server (&ctrl);
if (err)
log_error ("server exited with error: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
/* Cleanup. */
g13_syshelp_deinit_default_ctrl (&ctrl);
g13_exit (0);
return 8; /*NOTREACHED*/
}
/* Store defaults into the per-connection CTRL object. */
void
g13_syshelp_init_default_ctrl (ctrl_t ctrl)
{
ctrl->conttype = CONTTYPE_DM_CRYPT;
}
/* Release all resources allocated by default in the CTRl object. */
static void
g13_syshelp_deinit_default_ctrl (ctrl_t ctrl)
{
xfree (ctrl->client.uname);
release_tab_items (ctrl->client.tab);
}
/* Release the list of g13tab itejms at TAB. */
static void
release_tab_items (tab_item_t tab)
{
while (tab)
{
tab_item_t next = tab->next;
xfree (tab->mountpoint);
xfree (tab);
tab = next;
}
}
void
g13_syshelp_i_know_what_i_am_doing (void)
{
const char * const yesfile = "Yes-g13-I-know-what-I-am-doing";
char *fname;
fname = make_filename (gnupg_sysconfdir (), yesfile, NULL);
if (access (fname, F_OK))
{
log_info ("*******************************************************\n");
log_info ("* The G13 support for DM-Crypt is new and not matured.\n");
log_info ("* Bugs or improper use may delete all your disks!\n");
log_info ("* To confirm that you are ware of this risk, create\n");
log_info ("* the file '%s'.\n", fname);
log_info ("*******************************************************\n");
exit (1);
}
xfree (fname);
}
/* Parse the /etc/gnupg/g13tab for user USERNAME. Return a table for
the user on success. Return NULL on error and print
diagnostics. */
static tab_item_t
parse_g13tab (const char *username)
{
gpg_error_t err;
int c, n;
char line[512];
char *p;
char *fname;
estream_t fp;
int lnr;
char **words = NULL;
tab_item_t table = NULL;
tab_item_t *tabletail, ti;
fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL);
fp = es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err));
goto leave;
}
tabletail = &table;
err = 0;
lnr = 0;
while (es_fgets (line, DIM(line)-1, fp))
{
lnr++;
n = strlen (line);
if (!n || line[n-1] != '\n')
{
/* Eat until end of line. */
while ((c=es_getc (fp)) != EOF && c != '\n')
;
err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
: GPG_ERR_INCOMPLETE_LINE);
log_error (_("file '%s', line %d: %s\n"),
fname, lnr, gpg_strerror (err));
continue;
}
line[--n] = 0; /* Chop the LF. */
if (n && line[n-1] == '\r')
line[--n] = 0; /* Chop an optional CR. */
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
if (!*p || *p == '#')
continue;
/* Parse the line. The format is
* [