#endif
#include <npth.h>
-#define JNLIB_NEED_LOG_LOGV
-#define JNLIB_NEED_AFLOCAL
+#define GNUPG_COMMON_NEED_AFLOCAL
#include "agent.h"
#include <assuan.h> /* Malloc hooks and socket wrappers. */
#include "i18n.h"
-#include "mkdtemp.h" /* Gnulib replacement. */
#include "sysutils.h"
#include "gc-opt-flags.h"
#include "exechelp.h"
oDebugLevel,
oDebugWait,
oDebugQuickRandom,
+ oDebugPinentry,
oNoGreeting,
oNoOptions,
oHomedir,
oPinentryProgram,
oPinentryTouchFile,
+ oPinentryInvisibleChar,
oDisplay,
oTTYname,
oTTYtype,
oEnablePassphraseHistory,
oUseStandardSocket,
oNoUseStandardSocket,
+ oExtraSocket,
+ oBrowserSocket,
oFakedSystemTime,
oIgnoreCacheForSigning,
oNoAllowMarkTrusted,
oAllowPresetPassphrase,
oAllowLoopbackPinentry,
+ oNoAllowExternalCache,
+ oAllowEmacsPinentry,
oKeepTTY,
oKeepDISPLAY,
oSSHSupport,
};
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
static ARGPARSE_OPTS opts[] = {
- { aGPGConfList, "gpgconf-list", 256, "@" },
- { aGPGConfTest, "gpgconf-test", 256, "@" },
- { aUseStandardSocketP, "use-standard-socket-p", 256, "@" },
-
- { 301, NULL, 0, N_("@Options:\n ") },
-
- { oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
- { oServer, "server", 0, N_("run in server mode (foreground)") },
- { oVerbose, "verbose", 0, N_("verbose") },
- { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
- { oSh, "sh", 0, N_("sh-style command output") },
- { oCsh, "csh", 0, N_("csh-style command output") },
- { oOptions, "options" , 2, N_("|FILE|read options from FILE")},
- { oDebug, "debug" ,4|16, "@"},
- { oDebugAll, "debug-all" ,0, "@"},
- { oDebugLevel, "debug-level" ,2, "@"},
- { oDebugWait,"debug-wait",1, "@"},
+ 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", "@"),
- { oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
- { oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")},
- { oLogFile, "log-file" ,2, N_("use a log file for the server")},
- { oUseStandardSocket, "use-standard-socket", 0, "@"}, /* dummy */
- { oNoUseStandardSocket, "no-use-standard-socket", 0, "@"}, /* dummy */
- { oPinentryProgram, "pinentry-program", 2 ,
- N_("|PGM|use PGM as the PIN-Entry program") },
- { oPinentryTouchFile, "pinentry-touch-file", 2 , "@" },
- { oScdaemonProgram, "scdaemon-program", 2 ,
- N_("|PGM|use PGM as the SCdaemon program") },
- { oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
- { oDisableCheckOwnSocket, "disable-check-own-socket", 0, "@" },
- { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
-
- { oBatch, "batch", 0, "@" },
- { oHomedir, "homedir", 2, "@"},
-
- { oDisplay, "display", 2, "@" },
- { oTTYname, "ttyname", 2, "@" },
- { oTTYtype, "ttytype", 2, "@" },
- { oLCctype, "lc-ctype", 2, "@" },
- { oLCmessages, "lc-messages", 2, "@" },
- { oXauthority, "xauthority", 2, "@" },
- { oKeepTTY, "keep-tty", 0, N_("ignore requests to change the TTY")},
- { oKeepDISPLAY, "keep-display",
- 0, N_("ignore requests to change the X display")},
-
- { oDefCacheTTL, "default-cache-ttl", 4,
- N_("|N|expire cached PINs after N seconds")},
- { oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
- { oMaxCacheTTL, "max-cache-ttl", 4, "@" },
- { oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
-
- { oEnforcePassphraseConstraints, "enforce-passphrase-constraints", 0, "@"},
- { oMinPassphraseLen, "min-passphrase-len", 4, "@" },
- { oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
- { oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
- { oMaxPassphraseDays, "max-passphrase-days", 4, "@" },
- { oEnablePassphraseHistory, "enable-passphrase-history", 0, "@" },
-
- { oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
- N_("do not use the PIN cache when signing")},
- { oNoAllowMarkTrusted, "no-allow-mark-trusted", 0,
- N_("disallow clients to mark keys as \"trusted\"")},
- { oAllowMarkTrusted, "allow-mark-trusted", 0, "@"},
- { oAllowPresetPassphrase, "allow-preset-passphrase", 0,
- N_("allow presetting passphrase")},
- { oAllowLoopbackPinentry, "allow-loopback-pinentry", 0,
- N_("allow presetting passphrase")},
- { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh support") },
- { oPuttySupport, "enable-putty-support", 0,
+ 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_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 (oAllowLoopbackPinentry, "allow-loopback-pinentry",
+ N_("allow caller to override the 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")
+ /* */ N_("enable putty support")
#else
- "@"
+ /* */ "@"
#endif
- },
- { oWriteEnvFile, "write-env-file", 2|8, "@" }, /* dummy */
- {0}
+ ),
+
+ /* 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 */
#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;
/* 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. */
+/* 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;
Local prototypes.
*/
-static char *create_socket_name (char *standard_name);
-static gnupg_fd_t create_server_socket (char *name, int is_ssh,
+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_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);
else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
- opt.debug = DBG_ASSUAN_VALUE;
+ opt.debug = DBG_IPC_VALUE;
else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
- opt.debug = DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE;
+ opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
- opt.debug = (DBG_ASSUAN_VALUE|DBG_COMMAND_VALUE
+ opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
|DBG_CACHE_VALUE);
else if (!strcmp (debug_level, "guru") || numok)
{
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
- log_info ("enabled debug flags:%s%s%s%s%s%s%s%s\n",
- (opt.debug & DBG_COMMAND_VALUE)? " command":"",
- (opt.debug & DBG_MPI_VALUE )? " mpi":"",
- (opt.debug & DBG_CRYPTO_VALUE )? " crypto":"",
- (opt.debug & DBG_MEMORY_VALUE )? " memory":"",
- (opt.debug & DBG_CACHE_VALUE )? " cache":"",
- (opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
- (opt.debug & DBG_HASHING_VALUE)? " hashing":"",
- (opt.debug & DBG_ASSUAN_VALUE )? " assuan":"");
+ parse_debug_flag (NULL, &opt.debug, debug_flags);
}
-/* Helper for cleanup to remove one socket with NAME. */
+/* 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)
+remove_socket (char *name, char *redir_name)
{
if (name && *name)
{
char *p;
+ if (redir_name)
+ name = redir_name;
+
gnupg_remove (name);
p = strrchr (name, '/');
if (p)
return;
done = 1;
deinitialize_module_cache ();
- remove_socket (socket_name);
- remove_socket (socket_name_ssh);
+ 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);
}
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.scdaemon_program = NULL;
opt.def_cache_ttl = DEFAULT_CACHE_TTL;
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
opt.enable_passhrase_history = 0;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 1;
+ opt.allow_external_cache = 1;
+ opt.allow_emacs_pinentry = 0;
opt.disable_scdaemon = 0;
disable_check_own_socket = 0;
return 1;
case oQuiet: opt.quiet = 1; break;
case oVerbose: opt.verbose++; break;
- case oDebug: opt.debug |= pargs->r.ret_ulong; 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)
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 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 oAllowLoopbackPinentry: opt.allow_loopback_pinentry = 1; break;
+ case oNoAllowExternalCache: opt.allow_external_cache = 0;
+ break;
+
+ case oAllowEmacsPinentry: opt.allow_emacs_pinentry = 1;
+ break;
+
default:
return 0; /* not 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 )
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. */
/* 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, JNLIB_LOG_WITH_PREFIX|JNLIB_LOG_WITH_PID);
+ log_set_prefix (GPG_AGENT_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_WITH_PID);
/* Make sure that our subsystems are ready. */
i18n_init ();
case oXauthority: default_xauthority = xstrdup (pargs.r.ret_str);
break;
- case oUseStandardSocket: /* dummy */ break;
- case oNoUseStandardSocket: /* dummy */ break;
+ case oUseStandardSocket:
+ case oNoUseStandardSocket:
+ obsolete_option (configname, configlineno, "use-standard-socket");
+ break;
case oFakedSystemTime:
{
case oKeepTTY: opt.keep_tty = 1; break;
case oKeepDISPLAY: opt.keep_display = 1; break;
- case oSSHSupport: opt.ssh_support = 1; break;
+ case oSSHSupport:
+ ssh_support = 1;
+ break;
case oPuttySupport:
# ifdef HAVE_W32_SYSTEM
putty_support = 1;
- opt.ssh_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: /* dummy */ break;
+ case oWriteEnvFile:
+ obsolete_option (configname, configlineno, "write-env-file");
+ break;
default : pargs.err = configfp? 1:2; break;
}
if (log_get_errorcount(0))
exit(2);
+ finalize_rereadable_options ();
+
/* Turn the homedir into an absolute one. */
opt.homedir = make_absfilename (opt.homedir, NULL);
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);
-#else
- es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
#endif
+ es_printf ("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);
agent_exit (0);
}
if (logfile)
{
log_set_file (logfile);
- log_set_prefix (NULL, (JNLIB_LOG_WITH_PREFIX
- |JNLIB_LOG_WITH_TIME
- |JNLIB_LOG_WITH_PID));
+ log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX
+ | GPGRT_LOG_WITH_TIME
+ | GPGRT_LOG_WITH_PID));
current_logfile = xstrdup (logfile);
}
else
{ /* Regular server mode */
gnupg_fd_t fd;
- gnupg_fd_t fd_ssh;
+ gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
+ gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
+ gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
pid_t pid;
/* Remove the DISPLAY variable so that a pinentry does not
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);
- fd = create_server_socket (socket_name, 0, &socket_nonce);
- if (opt.ssh_support)
+ 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_ssh = create_socket_name (GPG_AGENT_SSH_SOCK_NAME);
- fd_ssh = create_server_socket (socket_name_ssh, 1, &socket_nonce_ssh);
+ 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);
}
- else
- fd_ssh = GNUPG_INVALID_FD;
/* 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
#endif /*HAVE_SIGPROCMASK*/
/* Create the SSH info string if enabled. */
- if (opt.ssh_support)
+ if (ssh_support)
{
if (asprintf (&infostr_ssh_sock, "SSH_AUTH_SOCK=%s",
socket_name_ssh) < 0)
*socket_name = 0; /* Don't let cleanup() remove the socket -
the child should do this from now on */
- if (opt.ssh_support)
+ 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 (opt.ssh_support && (putenv (infostr_ssh_sock)
- || putenv (infostr_ssh_valid)))
+ if (ssh_support && (putenv (infostr_ssh_sock)
+ || putenv (infostr_ssh_valid)))
{
log_error ("failed to set environment: %s\n",
strerror (errno) );
shell's eval to set it */
if (csh_style)
{
- if (opt.ssh_support)
+ if (ssh_support)
{
*strchr (infostr_ssh_sock, '=') = ' ';
es_printf ("setenv %s;\n", infostr_ssh_sock);
}
else
{
- if (opt.ssh_support)
+ if (ssh_support)
{
es_printf ("%s; export SSH_AUTH_SOCK;\n",
infostr_ssh_sock);
}
}
- if (opt.ssh_support)
+ if (ssh_support)
{
xfree (infostr_ssh_sock);
xfree (infostr_ssh_valid);
}
log_get_prefix (&oldflags);
- log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED);
+ log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
opt.running_detached = 1;
}
#endif /*!HAVE_W32_SYSTEM*/
log_info ("%s %s started\n", strusage(11), strusage(13) );
- handle_connections (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD);
+ handle_connections (fd, fd_extra, fd_browser, fd_ssh);
assuan_sock_close (fd);
}
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. */
+ structure. Note that some values may have already been set. */
static void
agent_init_default_ctrl (ctrl_t ctrl)
{
}
+/* 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 explictly 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.
parse_rereadable_options (&pargs, 1);
}
fclose (fp);
+ finalize_rereadable_options ();
set_debug ();
}
Pointer to an allocated string with the absolute name of the socket
used. */
static char *
-create_socket_name (char *standard_name)
+create_socket_name (char *standard_name, int with_homedir)
{
char *name;
- name = make_filename (opt.homedir, standard_name, NULL);
+ if (with_homedir)
+ name = make_filename (opt.homedir, 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);
}
- if (strlen (name) + 1 >= DIMof (struct sockaddr_un, sun_path) )
- {
- log_error (_("name of socket too long\n"));
- 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. Not that this
- function needs to be used for the regular socket first and only
- then for the ssh socket. */
+ 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 is_ssh, assuan_sock_nonce_t *nonce)
+create_server_socket (char *name, int primary, int cygwin,
+ char **r_redir_name, assuan_sock_nonce_t *nonce)
{
- struct sockaddr_un *serv_addr;
+ 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);
}
- serv_addr = xmalloc (sizeof (*serv_addr));
- memset (serv_addr, 0, sizeof *serv_addr);
- serv_addr->sun_family = AF_UNIX;
- if (strlen (name) + 1 >= sizeof (serv_addr->sun_path))
+#if ASSUAN_VERSION_NUMBER >= 0x020300 /* >= 2.3.0 */
+ if (cygwin)
+ assuan_sock_set_flag (fd, "cygwin", 1);
+#else
+ (void)cygwin;
+#endif
+
+ unaddr = xmalloc (sizeof *unaddr);
+ addr = (struct sockaddr*)unaddr;
+
+#if ASSUAN_VERSION_NUMBER >= 0x020104 /* >= 2.1.4 */
+ {
+ 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);
+ }
+ }
+#else /* Assuan < 2.1.4 */
+ memset (unaddr, 0, sizeof *unaddr);
+ unaddr->sun_family = AF_UNIX;
+ if (strlen (name) + 1 >= sizeof (unaddr->sun_path))
{
log_error (_("socket name '%s' is too long\n"), name);
+ *name = 0; /* Inhibit removal of the socket by cleanup(). */
agent_exit (2);
}
- strcpy (serv_addr->sun_path, name);
- len = SUN_LEN (serv_addr);
- rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+ strcpy (unaddr->sun_path, name);
+#endif /* Assuan < 2.1.4 */
+
+ 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. */
#endif
))
{
- /* Check whether a gpg-agent is already running.
- We do this test only if this is not the ssh socket.
- For ssh we assume that a test for gpg-agent has already been
- done and reuse the requested ssh 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 (!is_ssh && !check_for_running_agent (1))
+ /* 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, JNLIB_LOG_WITH_PREFIX);
+ 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"));
assuan_sock_close (fd);
agent_exit (2);
}
- gnupg_remove (name);
- rc = assuan_sock_bind (fd, (struct sockaddr*) serv_addr, len);
+ gnupg_remove (unaddr->sun_path);
+ rc = assuan_sock_bind (fd, addr, len);
}
- if (rc != -1
- && (rc=assuan_sock_get_nonce ((struct sockaddr*)serv_addr, len, nonce)))
+ 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"),
- serv_addr->sun_path,
- gpg_strerror (gpg_error_from_errno (errno)));
+ unaddr->sun_path,
+ gpg_strerror (gpg_error_from_syserror ()));
assuan_sock_close (fd);
*name = 0; /* Inhibit removal of the socket by cleanup(). */
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"), serv_addr->sun_path);
+ log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
return fd;
}
{
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 ();
}
if (!cds->cbData || mapfile[cds->cbData - 1])
return 0; /* Ignore empty and non-properly terminated strings. */
- if (DBG_ASSUAN)
+ if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map file '%s'", mapfile);
}
maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
- if (DBG_ASSUAN)
+ if (DBG_IPC)
{
npth_protect ();
log_debug ("ssh map handle %p\n", maphd);
goto leave;
}
- if (DBG_ASSUAN)
+ if (DBG_IPC)
{
char *sidstr;
}
data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0);
- if (DBG_ASSUAN)
+ if (DBG_IPC)
log_debug ("ssh IPC buffer at %p\n", data);
if (!data)
goto leave;
#endif /*HAVE_W32_SYSTEM*/
-/* This is the standard connection thread's main function. */
static void *
-start_connection_thread (void *arg)
+do_start_connection_thread (ctrl_t ctrl)
{
- 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;
- }
-
agent_init_default_ctrl (ctrl);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d started\n"),
}
+/* 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)
/* 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_ssh)
+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;
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)
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);
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;
next timeout. */
continue;
- if (!shutdown_pending && FD_ISSET (FD2INT (listen_fd), &read_fdset))
- {
+ if (!shutdown_pending)
+ {
+ int idx;
ctrl_t ctrl;
+ npth_t thread;
- 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 if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
- {
- log_error ("error allocating connection control data: %s\n",
- strerror (errno) );
- assuan_sock_close (fd);
- }
- else if ( !(ctrl->session_env = session_env_new ()) )
- {
- log_error ("error allocating session environment block: %s\n",
- strerror (errno) );
- xfree (ctrl);
- assuan_sock_close (fd);
- }
- else
+ for (idx=0; idx < DIM(listentbl); idx++)
{
- npth_t thread;
-
- ctrl->thread_startup.fd = fd;
- ret = npth_create (&thread, &tattr,
- start_connection_thread, ctrl);
- if (ret)
+ 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 ("error spawning connection handler: %s\n",
- strerror (ret));
- assuan_sock_close (fd);
- xfree (ctrl);
+ log_error ("accept failed for %s: %s\n",
+ listentbl[idx].name, strerror (errno));
}
-
- }
- fd = GNUPG_INVALID_FD;
- }
-
- if (!shutdown_pending && listen_fd_ssh != GNUPG_INVALID_FD
- && FD_ISSET ( FD2INT (listen_fd_ssh), &read_fdset))
- {
- ctrl_t ctrl;
-
- plen = sizeof paddr;
- fd = INT2FD(npth_accept (FD2INT(listen_fd_ssh),
- (struct sockaddr *)&paddr, &plen));
- if (fd == GNUPG_INVALID_FD)
- {
- log_error ("accept failed for ssh: %s\n", strerror (errno));
- }
- else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
- {
- log_error ("error allocating connection control data: %s\n",
- strerror (errno) );
- assuan_sock_close (fd);
- }
- else if ( !(ctrl->session_env = session_env_new ()) )
- {
- log_error ("error allocating session environment block: %s\n",
- strerror (errno) );
- xfree (ctrl);
- assuan_sock_close (fd);
- }
- else
- {
- npth_t thread;
-
- agent_init_default_ctrl (ctrl);
- ctrl->thread_startup.fd = fd;
- ret = npth_create (&thread, &tattr,
- start_connection_thread_ssh, ctrl);
- if (ret)
+ else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)))
{
- log_error ("error spawning ssh connection handler: %s\n",
- strerror (ret));
+ 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;
}
- fd = GNUPG_INVALID_FD;
- }
+ }
}
cleanup ();