diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index efaebfd94..ef351749f 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1,3170 +1,3171 @@ /* 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 "../common/i18n.h" #include "../common/sysutils.h" #include "../common/gc-opt-flags.h" #include "../common/exechelp.h" #include "../common/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, oSupervised, oBatch, oPinentryProgram, oPinentryTouchFile, oPinentryInvisibleChar, oPinentryTimeout, oDisplay, oTTYname, oTTYtype, oLCctype, oLCmessages, oXauthority, oScdaemonProgram, oDefCacheTTL, oDefCacheTTLSSH, oMaxCacheTTL, oMaxCacheTTLSSH, oEnforcePassphraseConstraints, oMinPassphraseLen, oMinPassphraseNonalpha, oCheckPassphrasePattern, oMaxPassphraseDays, oEnablePassphraseHistory, oEnableExtendedKeyFormat, oUseStandardSocket, oNoUseStandardSocket, oExtraSocket, oBrowserSocket, oFakedSystemTime, oIgnoreCacheForSigning, oAllowMarkTrusted, oNoAllowMarkTrusted, oAllowPresetPassphrase, oAllowLoopbackPinentry, oNoAllowLoopbackPinentry, oNoAllowExternalCache, oAllowEmacsPinentry, oKeepTTY, oKeepDISPLAY, oSSHSupport, oSSHFingerprintDigest, 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)")), #ifndef HAVE_W32_SYSTEM ARGPARSE_s_n (oSupervised, "supervised", N_("run in supervised mode")), #endif 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_s (oSSHFingerprintDigest, "ssh-fingerprint-digest", N_("digest to use when communicating ssh fingerprints")), ARGPARSE_s_n (oPuttySupport, "enable-putty-support", #ifdef HAVE_W32_SYSTEM /* */ N_("enable putty support") #else /* */ "@" #endif ), ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"), /* 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", "@"), ARGPARSE_end () /* End of list */ }; /* The list of supported debug flags. */ static struct debug_flags_s debug_flags [] = { { 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. */ #ifndef HAVE_W32_SYSTEM static int *startup_fd_list; #endif /* 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; /* Flag indicating that we are in supervised mode. */ static int is_supervised; /* Flag to inhibit socket removal in cleanup. */ static int inhibit_socket_removal; /* 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 protocol. */ 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 will have at max only a few dozen * connections at a 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; else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) opt.debug = (DBG_IPC_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; } } /* Discover which inherited file descriptors correspond to which * services/sockets offered by gpg-agent, using the LISTEN_FDS and * LISTEN_FDNAMES convention. The understood labels are "ssh", * "extra", and "browser". "std" or other labels will be interpreted * as the standard socket. * * This function is designed to log errors when the expected file * descriptors don't make sense, but to do its best to continue to * work even in the face of minor misconfigurations. * * For more information on the LISTEN_FDS convention, see * sd_listen_fds(3) on certain Linux distributions. */ #ifndef HAVE_W32_SYSTEM static void map_supervised_sockets (gnupg_fd_t *r_fd, gnupg_fd_t *r_fd_extra, gnupg_fd_t *r_fd_browser, gnupg_fd_t *r_fd_ssh) { struct { const char *label; int **fdaddr; char **nameaddr; } tbl[] = { { "ssh", &r_fd_ssh, &socket_name_ssh }, { "browser", &r_fd_browser, &socket_name_browser }, { "extra", &r_fd_extra, &socket_name_extra }, { "std", &r_fd, &socket_name } /* (Must be the last item.) */ }; const char *envvar; char **fdnames; int nfdnames; int fd_count; *r_fd = *r_fd_extra = *r_fd_browser = *r_fd_ssh = -1; /* Print a warning if LISTEN_PID does not match outr pid. */ envvar = getenv ("LISTEN_PID"); if (!envvar) log_error ("no LISTEN_PID environment variable found in " "--supervised mode (ignoring)\n"); else if (strtoul (envvar, NULL, 10) != (unsigned long)getpid ()) log_error ("environment variable LISTEN_PID (%lu) does not match" " our pid (%lu) in --supervised mode (ignoring)\n", (unsigned long)strtoul (envvar, NULL, 10), (unsigned long)getpid ()); /* Parse LISTEN_FDNAMES into the array FDNAMES. */ envvar = getenv ("LISTEN_FDNAMES"); if (envvar) { fdnames = strtokenize (envvar, ":"); if (!fdnames) { log_error ("strtokenize failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); agent_exit (1); } for (nfdnames=0; fdnames[nfdnames]; nfdnames++) ; } else { fdnames = NULL; nfdnames = 0; } /* Parse LISTEN_FDS into fd_count or provide a replacement. */ envvar = getenv ("LISTEN_FDS"); if (envvar) fd_count = atoi (envvar); else if (fdnames) { log_error ("no LISTEN_FDS environment variable found in --supervised" " mode (relying on LISTEN_FDNAMES instead)\n"); fd_count = nfdnames; } else { log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables " "found in --supervised mode" " (assuming 1 active descriptor)\n"); fd_count = 1; } if (fd_count < 1) { log_error ("--supervised mode expects at least one file descriptor" " (was told %d, carrying on as though it were 1)\n", fd_count); fd_count = 1; } /* Assign the descriptors to the return values. */ if (!fdnames) { struct stat statbuf; if (fd_count != 1) log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1" " in --supervised mode." " (ignoring all sockets but the first one)\n", fd_count); if (fstat (3, &statbuf) == -1 && errno ==EBADF) log_fatal ("file descriptor 3 must be valid in --supervised mode" " if LISTEN_FDNAMES is not set\n"); *r_fd = 3; socket_name = gnupg_get_socket_name (3); } else if (fd_count != nfdnames) { log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match " "LISTEN_FDS (%d) in --supervised mode\n", nfdnames, fd_count); } else { int i, j, fd; char *name; for (i = 0; i < nfdnames; i++) { for (j = 0; j < DIM (tbl); j++) { if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1) { fd = 3 + i; if (**tbl[j].fdaddr == -1) { name = gnupg_get_socket_name (fd); if (name) { **tbl[j].fdaddr = fd; *tbl[j].nameaddr = name; log_info ("using fd %d for %s socket (%s)\n", fd, tbl[j].label, name); } else { log_error ("cannot listen on fd %d for %s socket\n", fd, tbl[j].label); close (fd); } } else { log_error ("cannot listen on more than one %s socket\n", tbl[j].label); close (fd); } break; } } } } xfree (fdnames); } #endif /*!HAVE_W32_SYSTEM*/ /* 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 (); if (!is_supervised && !inhibit_socket_removal) { 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_passphrase_history = 0; opt.enable_extended_key_format = 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; opt.ssh_fingerprint_digest = GCRY_MD_MD5; 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_passphrase_history = 1; break; case oEnableExtendedKeyFormat: opt.enable_extended_key_format = 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) { } static void thread_init_once (void) { static int npth_initialized = 0; if (!npth_initialized) { npth_initialized++; npth_init (); } gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* Now that we have set the syscall clamp we need to tell Libgcrypt * that it should get them from libgpg-error. Note that Libgcrypt * has already been initialized but at that point nPth was not * initialized and thus Libgcrypt could not set its system call * clamp. */ #if GCRYPT_VERSION_NUMBER >= 0x010800 /* 1.8.0 */ gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0); #endif } static void initialize_modules (void) { thread_init_once (); assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); initialize_module_cache (); initialize_module_call_pinentry (); initialize_module_call_scd (); initialize_module_trustlist (); } /* 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. We don't need it on Windows. */ #ifndef HAVE_W32_SYSTEM startup_fd_list = get_all_open_fds (); #endif /*!HAVE_W32_SYSTEM*/ #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); 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_sock_init (); setup_libassuan_logging (&opt.debug, NULL); 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, SECMEM_BUFFER_SIZE, 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 oSupervised: is_supervised = 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 oSSHFingerprintDigest: opt.ssh_fingerprint_digest = gcry_md_map_name (pargs.r.ret_str); if (opt.ssh_fingerprint_digest == 0) log_error ("Unknown digest algorithm: %s\n", pargs.r.ret_str); 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 && !is_supervised) { /* We have been called without any command 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); } if (is_supervised) ; else if (!opt.extra_socket) opt.extra_socket = 1; else if (socket_name_extra && (!strcmp (socket_name_extra, "none") || !strcmp (socket_name_extra, "/dev/null"))) { /* User requested not to create this socket. */ opt.extra_socket = 0; socket_name_extra = NULL; } if (is_supervised) ; else if (!opt.browser_socket) opt.browser_socket = 1; else if (socket_name_browser && (!strcmp (socket_name_browser, "none") || !strcmp (socket_name_browser, "/dev/null"))) { /* User requested not to create this socket. */ opt.browser_socket = 0; socket_name_browser = NULL; } set_debug (); if (atexit (cleanup)) { log_error ("atexit failed\n"); cleanup (); exit (1); } /* Try to create missing directories. */ create_directories (); if (debug_wait && pipe_server) { thread_init_once (); 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); es_printf ("enable-extended-key-format:%lu:\n", GC_OPT_FLAG_NONE|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; initialize_modules (); 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_supervised) { #ifndef HAVE_W32_SYSTEM gnupg_fd_t fd, fd_extra, fd_browser, fd_ssh; initialize_modules (); /* when supervised and sending logs to stderr, the process supervisor should handle log entry metadata (pid, name, timestamp) */ if (!logfile) log_set_prefix (NULL, 0); log_info ("%s %s starting in supervised mode.\n", strusage(11), strusage(13) ); /* See below in "regular server mode" on why we remove certain * envvars. */ if (!opt.keep_display) gnupg_unsetenv ("DISPLAY"); gnupg_unsetenv ("INSIDE_EMACS"); /* Virtually create the sockets. Note that we use -1 here * because the whole thing works only on Unix. */ map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh); if (fd == -1) log_fatal ("no standard socket provided\n"); #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*/ log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n", fd, fd_extra, fd_browser, fd_ssh); handle_connections (fd, fd_extra, fd_browser, fd_ssh); #endif /*!HAVE_W32_SYSTEM*/ } 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) { if (socket_name_extra) socket_name_extra = create_socket_name (socket_name_extra, 0); else socket_name_extra = create_socket_name /**/ (GPG_AGENT_EXTRA_SOCK_NAME, 1); 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) { if (socket_name_browser) socket_name_browser = create_socket_name (socket_name_browser, 0); else socket_name_browser= create_socket_name /**/ (GPG_AGENT_BROWSER_SOCK_NAME, 1); 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); } 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; initialize_modules (); #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; *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 */ initialize_modules (); /* 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("/")) + if (gnupg_chdir (gnupg_daemon_rootdir ())) { - log_error ("chdir to / failed: %s\n", strerror (errno)); + log_error ("chdir to '%s' failed: %s\n", + gnupg_daemon_rootdir (), 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); /* Libgcrypt < 1.8 does not know about nPth and thus when it reads * from /dev/random this will block the process. To mitigate this * problem we yield the thread when Libgcrypt tells us that it needs * more entropy. This way other threads have chance to run. */ #if GCRYPT_VERSION_NUMBER < 0x010800 /* 1.8.0 */ if (what && !strcmp (what, "need_entropy")) { #if GPGRT_VERSION_NUMBER < 0x011900 /* 1.25 */ /* In older gpg-error versions gpgrt_yield is buggy for use with * nPth and thus we need to resort to a sleep call. */ npth_usleep (1000); /* 1ms */ #else gpgrt_yield (); #endif } #endif } /* 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(). */ xfree (unaddr); 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); xfree (unaddr); 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(). */ xfree (unaddr); 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); xfree (unaddr); agent_exit (2); } if (opt.verbose) log_info (_("listening on socket '%s'\n"), unaddr->sun_path); xfree (unaddr); 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 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 && !DBG_IPC) 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 && !DBG_IPC) 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) { gpg_error_t err; 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 int sock_inotify_fd = -1; int home_inotify_fd = -1; 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 if (disable_check_own_socket) sock_inotify_fd = -1; else if ((err = gnupg_inotify_watch_socket (&sock_inotify_fd, socket_name))) { if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED) log_info ("error enabling daemon termination by socket removal: %s\n", gpg_strerror (err)); } if (disable_check_own_socket) home_inotify_fd = -1; else if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd, gnupg_homedir ()))) { if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED) log_info ("error enabling daemon termination bu homedir removal: %s\n", gpg_strerror (err)); } /* 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); } if (sock_inotify_fd != -1) { FD_SET (sock_inotify_fd, &fdset); if (sock_inotify_fd > nfd) nfd = sock_inotify_fd; } if (home_inotify_fd != -1) { FD_SET (home_inotify_fd, &fdset); if (home_inotify_fd > nfd) nfd = home_inotify_fd; } 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. * * Note that we do not close the listening socket because a * client trying to connect to that socket would instead * restart a new dirmngr instance - which is unlikely the * intention of a shutdown. */ FD_ZERO (&fdset); nfd = -1; if (sock_inotify_fd != -1) { FD_SET (sock_inotify_fd, &fdset); nfd = sock_inotify_fd; } if (home_inotify_fd != -1) { FD_SET (home_inotify_fd, &fdset); if (home_inotify_fd > nfd) nfd = home_inotify_fd; } } /* 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; if (sock_inotify_fd != -1 && FD_ISSET (sock_inotify_fd, &read_fdset) && gnupg_inotify_has_name (sock_inotify_fd, GPG_AGENT_SOCK_NAME)) { shutdown_pending = 1; log_info ("socket file has been removed - shutting down\n"); } if (home_inotify_fd != -1 && FD_ISSET (home_inotify_fd, &read_fdset)) { shutdown_pending = 1; log_info ("homedir has been removed - shutting down\n"); } 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); } } } } } if (sock_inotify_fd != -1) close (sock_inotify_fd); if (home_inotify_fd != -1) close (home_inotify_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; } assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1); 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. */ inhibit_socket_removal = 1; 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/common/homedir.c b/common/homedir.c index fce6d44be..a30e8dc76 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,1042 +1,1070 @@ /* homedir.c - Setup the home directory. * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. * Copyright (C) 2013, 2016 Werner Koch * * 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 #include #include #include #include #ifdef HAVE_W32_SYSTEM #include /* Due to the stupid mingw64 requirement to include this header before windows.h which is often implicitly included. */ #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_COMMON_APPDATA #define CSIDL_COMMON_APPDATA 0x0023 #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_STAT #include /* for stat() */ #endif #include "util.h" #include "sysutils.h" #include "zb32.h" /* The GnuPG homedir. This is only accessed by the functions * gnupg_homedir and gnupg_set_homedir. Malloced. */ static char *the_gnupg_homedir; /* Flag indicating that home directory is not the default one. */ static byte non_default_homedir; #ifdef HAVE_W32_SYSTEM /* A flag used to indicate that a control file for gpgconf has been detected. Under Windows the presence of this file indicates a portable installations and triggers several changes: - The GNUGHOME directory is fixed relative to installation directory. All other means to set the home directory are ignore. - All registry variables will be ignored. This flag is not used on Unix systems. */ static byte w32_portable_app; #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32_SYSTEM /* This flag is true if this process' binary has been installed under bin and not in the root directory as often used before GnuPG 2.1. */ static byte w32_bin_is_bin; #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32_SYSTEM static const char *w32_rootdir (void); #endif #ifdef HAVE_W32_SYSTEM static void w32_try_mkdir (const char *dir) { #ifdef HAVE_W32CE_SYSTEM wchar_t *wdir = utf8_to_wchar (dir); if (wdir) { CreateDirectory (wdir, NULL); xfree (wdir); } #else CreateDirectory (dir, NULL); #endif } #endif /* This is a helper function to load a Windows function from either of one DLLs. */ #ifdef HAVE_W32_SYSTEM static HRESULT w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e) { static int initialized; static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR); if (!initialized) { static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; void *handle; int i; initialized = 1; for (i=0, handle = NULL; !handle && dllnames[i]; i++) { handle = dlopen (dllnames[i], RTLD_LAZY); if (handle) { func = dlsym (handle, "SHGetFolderPathA"); if (!func) { dlclose (handle); handle = NULL; } } } } if (func) return func (a,b,c,d,e); else return -1; } #endif /*HAVE_W32_SYSTEM*/ /* Check whether DIR is the default homedir. */ static int is_gnupg_default_homedir (const char *dir) { int result; char *a = make_absfilename (dir, NULL); char *b = make_absfilename (GNUPG_DEFAULT_HOMEDIR, NULL); result = !compare_filenames (a, b); xfree (b); xfree (a); return result; } /* Get the standard home directory. In general this function should not be used as it does not consider a registry value (under W32) or the GNUPGHOME environment variable. It is better to use default_homedir(). */ const char * standard_homedir (void) { #ifdef HAVE_W32_SYSTEM static const char *dir; if (!dir) { const char *rdir; rdir = w32_rootdir (); if (w32_portable_app) { dir = xstrconcat (rdir, DIRSEP_S "home", NULL); } else { char path[MAX_PATH]; /* It might be better to use LOCAL_APPDATA because this is defined as "non roaming" and thus more likely to be kept locally. For private keys this is desired. However, given that many users copy private keys anyway forth and back, using a system roaming services might be better than to let them do it manually. A security conscious user will anyway use the registry entry to have better control. */ if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + 6 +1); strcpy (stpcpy (tmp, path), "\\gnupg"); dir = tmp; /* Try to create the directory if it does not yet exists. */ if (access (dir, F_OK)) w32_try_mkdir (dir); } else dir = GNUPG_DEFAULT_HOMEDIR; } } return dir; #else/*!HAVE_W32_SYSTEM*/ return GNUPG_DEFAULT_HOMEDIR; #endif /*!HAVE_W32_SYSTEM*/ } /* Set up the default home directory. The usual --homedir option should be parsed later. */ const char * default_homedir (void) { const char *dir; #ifdef HAVE_W32_SYSTEM /* For a portable application we only use the standard homedir. */ w32_rootdir (); if (w32_portable_app) return standard_homedir (); #endif /*HAVE_W32_SYSTEM*/ dir = getenv ("GNUPGHOME"); #ifdef HAVE_W32_SYSTEM if (!dir || !*dir) { static const char *saved_dir; if (!saved_dir) { if (!dir || !*dir) { char *tmp; tmp = read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, "HomeDir"); if (tmp && !*tmp) { xfree (tmp); tmp = NULL; } if (tmp) saved_dir = tmp; } if (!saved_dir) saved_dir = standard_homedir (); } dir = saved_dir; } #endif /*HAVE_W32_SYSTEM*/ if (!dir || !*dir) dir = GNUPG_DEFAULT_HOMEDIR; else if (!is_gnupg_default_homedir (dir)) non_default_homedir = 1; return dir; } #ifdef HAVE_W32_SYSTEM /* Check whether gpgconf is installed and if so read the gpgconf.ctl file. */ static void check_portable_app (const char *dir) { char *fname; fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); if (!access (fname, F_OK)) { strcpy (fname + strlen (fname) - 3, "ctl"); if (!access (fname, F_OK)) { /* gpgconf.ctl file found. Record this fact. */ w32_portable_app = 1; { unsigned int flags; log_get_prefix (&flags); log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); } /* FIXME: We should read the file to detect special flags and print a warning if we don't understand them */ } } xfree (fname); } /* Determine the root directory of the gnupg installation on Windows. */ static const char * w32_rootdir (void) { static int got_dir; static char dir[MAX_PATH+5]; if (!got_dir) { char *p; int rc; wchar_t wdir [MAX_PATH+5]; rc = GetModuleFileNameW (NULL, wdir, MAX_PATH); if (rc && WideCharToMultiByte (CP_UTF8, 0, wdir, -1, dir, MAX_PATH-4, NULL, NULL) < 0) rc = 0; if (!rc) { log_debug ("GetModuleFileName failed: %s\n", w32_strerror (-1)); *dir = 0; } got_dir = 1; p = strrchr (dir, DIRSEP_C); if (p) { *p = 0; check_portable_app (dir); /* If we are installed below "bin" we strip that and use the top directory instead. */ p = strrchr (dir, DIRSEP_C); if (p && !strcmp (p+1, "bin")) { *p = 0; w32_bin_is_bin = 1; } } if (!p) { log_debug ("bad filename '%s' returned for this process\n", dir); *dir = 0; } } if (*dir) return dir; /* Fallback to the hardwired value. */ return GNUPG_LIBEXECDIR; } static const char * w32_commondir (void) { static char *dir; if (!dir) { const char *rdir; char path[MAX_PATH]; /* Make sure that w32_rootdir has been called so that we are able to check the portable application flag. The common dir is the identical to the rootdir. In that case there is also no need to strdup its value. */ rdir = w32_rootdir (); if (w32_portable_app) return rdir; if (w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + 4 +1); strcpy (stpcpy (tmp, path), "\\GNU"); dir = tmp; /* No auto create of the directory. Either the installer or the admin has to create these directories. */ } else { /* Ooops: Not defined - probably an old Windows version. Use the installation directory instead. */ dir = xstrdup (rdir); } } return dir; } #endif /*HAVE_W32_SYSTEM*/ /* Change the homedir. Some care must be taken to set this early * enough because previous calls to gnupg_homedir may else return a * different string. */ void gnupg_set_homedir (const char *newdir) { if (!newdir || !*newdir) newdir = default_homedir (); else if (!is_gnupg_default_homedir (newdir)) non_default_homedir = 1; xfree (the_gnupg_homedir); the_gnupg_homedir = make_absfilename (newdir, NULL);; } /* Return the homedir. The returned string is valid until another * gnupg-set-homedir call. This is always an absolute directory name. * The function replaces the former global var opt.homedir. */ const char * gnupg_homedir (void) { /* If a homedir has not been set, set it to the default. */ if (!the_gnupg_homedir) the_gnupg_homedir = make_absfilename (default_homedir (), NULL); return the_gnupg_homedir; } /* Return whether the home dir is the default one. */ int gnupg_default_homedir_p (void) { return !non_default_homedir; } +/* Return the directory name used by daemons for their current working + * directory. */ +const char * +gnupg_daemon_rootdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + { + char path[MAX_PATH]; + size_t n; + + n = GetSystemDirectoryA (path, sizeof path); + if (!n || n >= sizeof path) + name = xstrdup ("/"); /* Error - use the curret top dir instead. */ + else + name = xstrdup (path); + } + + return name; + +#else /*!HAVE_W32_SYSTEM*/ + return "/"; +#endif /*!HAVE_W32_SYSTEM*/ +} + + /* Helper for gnupg-socketdir. This is a global function, so that * gpgconf can use it for its --create-socketdir command. If * SKIP_CHECKS is set permission checks etc. are not done. The * function always returns a malloced directory name and stores these * bit flags at R_INFO: * * 1 := Internal error, stat failed, out of core, etc. * 2 := No /run/user directory. * 4 := Directory not owned by the user, not a directory * or wrong permissions. * 8 := Same as 4 but for the subdir. * 16 := mkdir failed * 32 := Non default homedir; checking subdir. * 64 := Subdir does not exist. * 128 := Using homedir as fallback. */ char * _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) { #if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT) char *name; (void)skip_checks; *r_info = 0; name = xstrdup (gnupg_homedir ()); #else /* Unix and stat(2) available. */ static const char * const bases[] = { "/run", "/var/run", NULL}; int i; struct stat sb; char prefix[13 + 1 + 20 + 6 + 1]; const char *s; char *name = NULL; *r_info = 0; /* First make sure that non_default_homedir can be set. */ gnupg_homedir (); /* It has been suggested to first check XDG_RUNTIME_DIR envvar. * However, the specs state that the lifetime of the directory MUST * be bound to the user being logged in. Now GnuPG may also be run * as a background process with no (desktop) user logged in. Thus * we better don't do that. */ /* Check whether we have a /run/user dir. */ for (i=0; bases[i]; i++) { snprintf (prefix, sizeof prefix, "%s/user/%u", bases[i], (unsigned int)getuid ()); if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode)) break; } if (!bases[i]) { *r_info |= 2; /* No /run/user directory. */ goto leave; } if (sb.st_uid != getuid ()) { *r_info |= 4; /* Not owned by the user. */ if (!skip_checks) goto leave; } if (strlen (prefix) + 7 >= sizeof prefix) { *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ goto leave; } strcat (prefix, "/gnupg"); /* Check whether the gnupg sub directory has proper permissions. */ if (stat (prefix, &sb)) { if (errno != ENOENT) { *r_info |= 1; /* stat failed. */ goto leave; } /* Try to create the directory and check again. */ if (gnupg_mkdir (prefix, "-rwx")) { *r_info |= 16; /* mkdir failed. */ goto leave; } if (stat (prefix, &sb)) { *r_info |= 1; /* stat failed. */ goto leave; } } /* Check that it is a directory, owned by the user, and only the * user has permissions to use it. */ if (!S_ISDIR(sb.st_mode) || sb.st_uid != getuid () || (sb.st_mode & (S_IRWXG|S_IRWXO))) { *r_info |= 4; /* Bad permissions or not a directory. */ if (!skip_checks) goto leave; } /* If a non default homedir is used, we check whether an * corresponding sub directory below the socket dir is available * and use that. We hash the non default homedir to keep the new * subdir short enough. */ if (non_default_homedir) { char sha1buf[20]; char *suffix; *r_info |= 32; /* Testing subdir. */ s = gnupg_homedir (); gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, s, strlen (s)); suffix = zb32_encode (sha1buf, 8*15); if (!suffix) { *r_info |= 1; /* Out of core etc. */ goto leave; } name = strconcat (prefix, "/d.", suffix, NULL); xfree (suffix); if (!name) { *r_info |= 1; /* Out of core etc. */ goto leave; } /* Stat that directory and check constraints. * The command * gpgconf --remove-socketdir * can be used to remove that directory. */ if (stat (name, &sb)) { if (errno != ENOENT) *r_info |= 1; /* stat failed. */ else if (!skip_checks) { /* Try to create the directory and check again. */ if (gnupg_mkdir (name, "-rwx")) *r_info |= 16; /* mkdir failed. */ else if (stat (prefix, &sb)) { if (errno != ENOENT) *r_info |= 1; /* stat failed. */ else *r_info |= 64; /* Subdir does not exist. */ } else goto leave; /* Success! */ } else *r_info |= 64; /* Subdir does not exist. */ if (!skip_checks) { xfree (name); name = NULL; goto leave; } } else if (!S_ISDIR(sb.st_mode) || sb.st_uid != getuid () || (sb.st_mode & (S_IRWXG|S_IRWXO))) { *r_info |= 8; /* Bad permissions or subdir is not a directory. */ if (!skip_checks) { xfree (name); name = NULL; goto leave; } } } else name = xstrdup (prefix); leave: /* If nothing works fall back to the homedir. */ if (!name) { *r_info |= 128; /* Fallback. */ name = xstrdup (gnupg_homedir ()); } #endif /* Unix */ return name; } /* * Return the name of the socket dir. That is the directory used for * the IPC local sockets. This is an absolute directory name. */ const char * gnupg_socketdir (void) { static char *name; if (!name) { unsigned int dummy; name = _gnupg_socketdir_internal (0, &dummy); } return name; } /* Return the name of the sysconfdir. This is a static string. This function is required because under Windows we can't simply compile it in. */ const char * gnupg_sysconfdir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) { const char *s1, *s2; s1 = w32_commondir (); s2 = DIRSEP_S "etc" DIRSEP_S "gnupg"; name = xmalloc (strlen (s1) + strlen (s2) + 1); strcpy (stpcpy (name, s1), s2); } return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_SYSCONFDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_bindir (void) { #if defined (HAVE_W32CE_SYSTEM) static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "bin", NULL); return name; #elif defined(HAVE_W32_SYSTEM) const char *rdir; rdir = w32_rootdir (); if (w32_bin_is_bin) { static char *name; if (!name) name = xstrconcat (rdir, DIRSEP_S "bin", NULL); return name; } else return rdir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_BINDIR; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the name of the libexec directory. The name is allocated in a static area on the first use. This function won't fail. */ const char * gnupg_libexecdir (void) { #ifdef HAVE_W32_SYSTEM return gnupg_bindir (); #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LIBEXECDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_libdir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "lib" DIRSEP_S "gnupg", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LIBDIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_datadir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "gnupg", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_DATADIR; #endif /*!HAVE_W32_SYSTEM*/ } const char * gnupg_localedir (void) { #ifdef HAVE_W32_SYSTEM static char *name; if (!name) name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "locale", NULL); return name; #else /*!HAVE_W32_SYSTEM*/ return LOCALEDIR; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the name of the cache directory. The name is allocated in a static area on the first use. Windows only: If the directory does not exist it is created. */ const char * gnupg_cachedir (void) { #ifdef HAVE_W32_SYSTEM static const char *dir; if (!dir) { const char *rdir; rdir = w32_rootdir (); if (w32_portable_app) { dir = xstrconcat (rdir, DIRSEP_S, "var", DIRSEP_S, "cache", DIRSEP_S, "gnupg", NULL); } else { char path[MAX_PATH]; const char *s1[] = { "GNU", "cache", "gnupg", NULL }; int s1_len; const char **comp; s1_len = 0; for (comp = s1; *comp; comp++) s1_len += 1 + strlen (*comp); if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, path) >= 0) { char *tmp = xmalloc (strlen (path) + s1_len + 1); char *p; p = stpcpy (tmp, path); for (comp = s1; *comp; comp++) { p = stpcpy (p, "\\"); p = stpcpy (p, *comp); if (access (tmp, F_OK)) w32_try_mkdir (tmp); } dir = tmp; } else { dir = "c:\\temp\\cache\\gnupg"; #ifdef HAVE_W32CE_SYSTEM dir += 2; w32_try_mkdir ("\\temp\\cache"); w32_try_mkdir ("\\temp\\cache\\gnupg"); #endif } } } return dir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_LOCALSTATEDIR "/cache/" PACKAGE_NAME; #endif /*!HAVE_W32_SYSTEM*/ } /* Return the user socket name used by DirMngr. */ const char * dirmngr_socket_name (void) { static char *name; if (!name) name = make_filename (gnupg_socketdir (), DIRMNGR_SOCK_NAME, NULL); return name; } /* Return the default pinentry name. If RESET is true the internal cache is first flushed. */ static const char * get_default_pinentry_name (int reset) { static struct { const char *(*rfnc)(void); const char *name; } names[] = { /* The first entry is what we return in case we found no other pinentry. */ { gnupg_bindir, DIRSEP_S "pinentry" EXEEXT_S }, #ifdef HAVE_W32_SYSTEM /* Try Gpg4win directory (with bin and without.) */ { w32_rootdir, "\\..\\Gpg4win\\bin\\pinentry.exe" }, { w32_rootdir, "\\..\\Gpg4win\\pinentry.exe" }, /* Try old Gpgwin directory. */ { w32_rootdir, "\\..\\GNU\\GnuPG\\pinentry.exe" }, /* Try a Pinentry from the common GNU dir. */ { w32_rootdir, "\\..\\GNU\\bin\\pinentry.exe" }, #endif /* Last chance is a pinentry-basic (which comes with the GnuPG 2.1 Windows installer). */ { gnupg_bindir, DIRSEP_S "pinentry-basic" EXEEXT_S } }; static char *name; if (reset) { xfree (name); name = NULL; } if (!name) { int i; for (i=0; i < DIM(names); i++) { char *name2; name2 = xstrconcat (names[i].rfnc (), names[i].name, NULL); if (!access (name2, F_OK)) { /* Use that pinentry. */ xfree (name); name = name2; break; } if (!i) /* Store the first as fallback return. */ name = name2; else xfree (name2); } } return name; } /* If set, 'gnupg_module_name' returns modules from that build * directory. */ static char *gnupg_build_directory; /* For sanity checks. */ static int gnupg_module_name_called; /* Set NEWDIR as the new build directory. This will make * 'gnupg_module_name' return modules from that build directory. Must * be called before any invocation of 'gnupg_module_name', and must * not be called twice. It can be used by test suites to make sure * the components from the build directory are used instead of * potentially outdated installed ones. */ void gnupg_set_builddir (const char *newdir) { log_assert (! gnupg_module_name_called); log_assert (! gnupg_build_directory); gnupg_build_directory = xtrystrdup (newdir); } /* If no build directory has been configured, try to set it from the * environment. We only do this in development builds to avoid * increasing the set of influential environment variables and hence * the attack surface of production builds. */ static void gnupg_set_builddir_from_env (void) { #if defined(IS_DEVELOPMENT_VERSION) || defined(ENABLE_GNUPG_BUILDDIR_ENVVAR) if (gnupg_build_directory) return; gnupg_build_directory = getenv ("GNUPG_BUILDDIR"); #endif } /* Return the file name of a helper tool. WHICH is one of the GNUPG_MODULE_NAME_foo constants. */ const char * gnupg_module_name (int which) { gnupg_set_builddir_from_env (); gnupg_module_name_called = 1; #define X(a,b,c) do { \ static char *name; \ if (!name) \ name = gnupg_build_directory \ ? xstrconcat (gnupg_build_directory, \ DIRSEP_S b DIRSEP_S c EXEEXT_S, NULL) \ : xstrconcat (gnupg_ ## a (), DIRSEP_S c EXEEXT_S, NULL); \ return name; \ } while (0) switch (which) { case GNUPG_MODULE_NAME_AGENT: #ifdef GNUPG_DEFAULT_AGENT return GNUPG_DEFAULT_AGENT; #else X(bindir, "agent", "gpg-agent"); #endif case GNUPG_MODULE_NAME_PINENTRY: #ifdef GNUPG_DEFAULT_PINENTRY return GNUPG_DEFAULT_PINENTRY; /* (Set by a configure option) */ #else return get_default_pinentry_name (0); #endif case GNUPG_MODULE_NAME_SCDAEMON: #ifdef GNUPG_DEFAULT_SCDAEMON return GNUPG_DEFAULT_SCDAEMON; #else X(libexecdir, "scd", "scdaemon"); #endif case GNUPG_MODULE_NAME_DIRMNGR: #ifdef GNUPG_DEFAULT_DIRMNGR return GNUPG_DEFAULT_DIRMNGR; #else X(bindir, "dirmngr", DIRMNGR_NAME); #endif case GNUPG_MODULE_NAME_PROTECT_TOOL: #ifdef GNUPG_DEFAULT_PROTECT_TOOL return GNUPG_DEFAULT_PROTECT_TOOL; #else X(libexecdir, "agent", "gpg-protect-tool"); #endif case GNUPG_MODULE_NAME_DIRMNGR_LDAP: #ifdef GNUPG_DEFAULT_DIRMNGR_LDAP return GNUPG_DEFAULT_DIRMNGR_LDAP; #else X(libexecdir, "dirmngr", "dirmngr_ldap"); #endif case GNUPG_MODULE_NAME_CHECK_PATTERN: X(libexecdir, "tools", "gpg-check-pattern"); case GNUPG_MODULE_NAME_GPGSM: X(bindir, "sm", "gpgsm"); case GNUPG_MODULE_NAME_GPG: #if USE_GPG2_HACK if (! gnupg_build_directory) X(bindir, "g10", GPG_NAME "2"); else #endif X(bindir, "g10", GPG_NAME); case GNUPG_MODULE_NAME_GPGV: #if USE_GPG2_HACK if (! gnupg_build_directory) X(bindir, "g10", GPG_NAME "v2"); else #endif X(bindir, "g10", GPG_NAME "v"); case GNUPG_MODULE_NAME_CONNECT_AGENT: X(bindir, "tools", "gpg-connect-agent"); case GNUPG_MODULE_NAME_GPGCONF: X(bindir, "tools", "gpgconf"); default: BUG (); } #undef X } /* Flush some of the cached module names. This is for example used by gpg-agent to allow configuring a different pinentry. */ void gnupg_module_name_flush_some (void) { (void)get_default_pinentry_name (1); } diff --git a/common/sysutils.c b/common/sysutils.c index 1aa2e5314..e90010c44 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -1,1331 +1,1340 @@ /* sysutils.c - system helpers * Copyright (C) 1991-2001, 2003-2004, * 2006-2008 Free Software Foundation, Inc. * Copyright (C) 2013-2016 Werner Koch * * 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 #include #include #include #include #include #include #ifdef HAVE_STAT # include #endif #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 # include # include #endif #include #ifdef HAVE_SETRLIMIT # include # include #endif #ifdef HAVE_W32_SYSTEM # if WINVER < 0x0500 # define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ # endif # ifdef HAVE_WINSOCK2_H # include # endif # include #else /*!HAVE_W32_SYSTEM*/ # include # include #endif #ifdef HAVE_INOTIFY_INIT # include #endif /*HAVE_INOTIFY_INIT*/ #ifdef HAVE_NPTH # include #endif #include #include #include "util.h" #include "i18n.h" #include "sysutils.h" #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) /* Flag to tell whether special file names are enabled. See gpg.c for * an explanation of these file names. */ static int allow_special_filenames; static GPGRT_INLINE gpg_error_t my_error_from_syserror (void) { return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); } static GPGRT_INLINE gpg_error_t my_error (int e) { return gpg_err_make (default_errsource, (e)); } #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #warning using trap_unaligned static int setsysinfo(unsigned long op, void *buffer, unsigned long size, int *start, void *arg, unsigned long flag) { return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); } void trap_unaligned(void) { unsigned int buf[2]; buf[0] = SSIN_UACPROC; buf[1] = UAC_SIGBUS | UAC_NOPRINT; setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); } #else void trap_unaligned(void) { /* dummy */ } #endif int disable_core_dumps (void) { #ifdef HAVE_DOSISH_SYSTEM return 0; #else # ifdef HAVE_SETRLIMIT struct rlimit limit; /* We only set the current limit unless we were not able to retrieve the old value. */ if (getrlimit (RLIMIT_CORE, &limit)) limit.rlim_max = 0; limit.rlim_cur = 0; if( !setrlimit (RLIMIT_CORE, &limit) ) return 0; if( errno != EINVAL && errno != ENOSYS ) log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) ); #endif return 1; #endif } int enable_core_dumps (void) { #ifdef HAVE_DOSISH_SYSTEM return 0; #else # ifdef HAVE_SETRLIMIT struct rlimit limit; if (getrlimit (RLIMIT_CORE, &limit)) return 1; limit.rlim_cur = limit.rlim_max; setrlimit (RLIMIT_CORE, &limit); return 1; /* We always return true because this function is merely a debugging aid. */ # endif return 1; #endif } /* Allow the use of special "-&nnn" style file names. */ void enable_special_filenames (void) { allow_special_filenames = 1; } /* Return a string which is used as a kind of process ID. */ const byte * get_session_marker (size_t *rlen) { static byte marker[SIZEOF_UNSIGNED_LONG*2]; static int initialized; if (!initialized) { gcry_create_nonce (marker, sizeof marker); initialized = 1; } *rlen = sizeof (marker); return marker; } /* Return a random number in an unsigned int. */ unsigned int get_uint_nonce (void) { unsigned int value; gcry_create_nonce (&value, sizeof value); return value; } #if 0 /* not yet needed - Note that this will require inclusion of cmacros.am in Makefile.am */ int check_permissions(const char *path,int extension,int checkonly) { #if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) char *tmppath; struct stat statbuf; int ret=1; int isdir=0; if(opt.no_perm_warn) return 0; if(extension && path[0]!=DIRSEP_C) { if(strchr(path,DIRSEP_C)) tmppath=make_filename(path,NULL); else tmppath=make_filename(GNUPG_LIBDIR,path,NULL); } else tmppath=m_strdup(path); /* It's okay if the file doesn't exist */ if(stat(tmppath,&statbuf)!=0) { ret=0; goto end; } isdir=S_ISDIR(statbuf.st_mode); /* Per-user files must be owned by the user. Extensions must be owned by the user or root. */ if((!extension && statbuf.st_uid != getuid()) || (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) { if(!checkonly) log_info(_("Warning: unsafe ownership on %s \"%s\"\n"), isdir?"directory":extension?"extension":"file",path); goto end; } /* This works for both directories and files - basically, we don't care what the owner permissions are, so long as the group and other permissions are 0 for per-user files, and non-writable for extensions. */ if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) { char *dir; /* However, if the directory the directory/file is in is owned by the user and is 700, then this is not a problem. 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,&statbuf)==0 && statbuf.st_uid==getuid() && S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) { xfree (dir); ret=0; goto end; } m_free(dir); if(!checkonly) log_info(_("Warning: unsafe permissions on %s \"%s\"\n"), isdir?"directory":extension?"extension":"file",path); goto end; } ret=0; end: m_free(tmppath); return ret; #endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ return 0; } #endif /* Wrapper around the usual sleep function. This one won't wake up before the sleep time has really elapsed. When build with Pth it merely calls pth_sleep and thus suspends only the current thread. */ void gnupg_sleep (unsigned int seconds) { #ifdef USE_NPTH npth_sleep (seconds); #else /* Fixme: make sure that a sleep won't wake up to early. */ # ifdef HAVE_W32_SYSTEM Sleep (seconds*1000); # else sleep (seconds); # endif #endif } /* Wrapper around the platforms usleep function. This one won't wake * up before the sleep time has really elapsed. When build with nPth * it merely calls npth_usleep and thus suspends only the current * thread. */ void gnupg_usleep (unsigned int usecs) { #if defined(USE_NPTH) npth_usleep (usecs); #elif defined(HAVE_W32_SYSTEM) Sleep ((usecs + 999) / 1000); #elif defined(HAVE_NANOSLEEP) if (usecs) { struct timespec req; struct timespec rem; req.tv_sec = 0; req.tv_nsec = usecs * 1000; while (nanosleep (&req, &rem) < 0 && errno == EINTR) req = rem; } #else /*Standard Unix*/ if (usecs) { struct timeval tv; tv.tv_sec = usecs / 1000000; tv.tv_usec = usecs % 1000000; select (0, NULL, NULL, NULL, &tv); } #endif } /* This function is a NOP for POSIX systems but required under Windows as the file handles as returned by OS calls (like CreateFile) are different from the libc file descriptors (like open). This function translates system file handles to libc file handles. FOR_WRITE gives the direction of the handle. */ int translate_sys2libc_fd (gnupg_fd_t fd, int for_write) { #if defined(HAVE_W32CE_SYSTEM) (void)for_write; return (int) fd; #elif defined(HAVE_W32_SYSTEM) int x; if (fd == GNUPG_INVALID_FD) return -1; /* Note that _open_osfhandle is currently defined to take and return a long. */ x = _open_osfhandle ((long)fd, for_write ? 1 : 0); if (x == -1) log_error ("failed to translate osfhandle %p\n", (void *) fd); return x; #else /*!HAVE_W32_SYSTEM */ (void)for_write; return fd; #endif } /* This is the same as translate_sys2libc_fd but takes an integer which is assumed to be such an system handle. On WindowsCE the passed FD is a rendezvous ID and the function finishes the pipe creation. */ int translate_sys2libc_fd_int (int fd, int for_write) { #if HAVE_W32CE_SYSTEM fd = (int) _assuan_w32ce_finish_pipe (fd, for_write); return translate_sys2libc_fd ((void*)fd, for_write); #elif HAVE_W32_SYSTEM if (fd <= 2) return fd; /* Do not do this for error, stdin, stdout, stderr. */ return translate_sys2libc_fd ((void*)fd, for_write); #else (void)for_write; return fd; #endif } /* Check whether FNAME has the form "-&nnnn", where N is a non-zero * number. Returns this number or -1 if it is not the case. If the * caller wants to use the file descriptor for writing FOR_WRITE shall * be set to 1. If NOTRANSLATE is set the Windows specific mapping is * not done. */ int check_special_filename (const char *fname, int for_write, int notranslate) { if (allow_special_filenames && fname && *fname == '-' && fname[1] == '&') { int i; fname += 2; for (i=0; digitp (fname+i); i++ ) ; if (!fname[i]) return notranslate? atoi (fname) /**/ : translate_sys2libc_fd_int (atoi (fname), for_write); } return -1; } /* Replacement for tmpfile(). This is required because the tmpfile function of Windows' runtime library is broken, insecure, ignores TMPDIR and so on. In addition we create a file with an inheritable handle. */ FILE * gnupg_tmpfile (void) { #ifdef HAVE_W32_SYSTEM int attempts, n; #ifdef HAVE_W32CE_SYSTEM wchar_t buffer[MAX_PATH+7+12+1]; # define mystrlen(a) wcslen (a) wchar_t *name, *p; #else char buffer[MAX_PATH+7+12+1]; # define mystrlen(a) strlen (a) char *name, *p; #endif HANDLE file; int pid = GetCurrentProcessId (); unsigned int value; int i; SECURITY_ATTRIBUTES sec_attr; memset (&sec_attr, 0, sizeof sec_attr ); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = TRUE; n = GetTempPath (MAX_PATH+1, buffer); if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH) { gpg_err_set_errno (ENOENT); return NULL; } p = buffer + mystrlen (buffer); #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L"_gnupg"); p += 7; #else p = stpcpy (p, "_gnupg"); #endif /* We try to create the directory but don't care about an error as it may already exist and the CreateFile would throw an error anyway. */ CreateDirectory (buffer, NULL); *p++ = '\\'; name = p; for (attempts=0; attempts < 10; attempts++) { p = name; value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); for (i=0; i < 8; i++) { *p++ = tohex (((value >> 28) & 0x0f)); value <<= 4; } #ifdef HAVE_W32CE_SYSTEM wcscpy (p, L".tmp"); #else strcpy (p, ".tmp"); #endif file = CreateFile (buffer, GENERIC_READ | GENERIC_WRITE, 0, &sec_attr, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (file != INVALID_HANDLE_VALUE) { FILE *fp; #ifdef HAVE_W32CE_SYSTEM int fd = (int)file; fp = _wfdopen (fd, L"w+b"); #else int fd = _open_osfhandle ((long)file, 0); if (fd == -1) { CloseHandle (file); return NULL; } fp = fdopen (fd, "w+b"); #endif if (!fp) { int save = errno; close (fd); gpg_err_set_errno (save); return NULL; } return fp; } Sleep (1); /* One ms as this is the granularity of GetTickCount. */ } gpg_err_set_errno (ENOENT); return NULL; #undef mystrlen #else /*!HAVE_W32_SYSTEM*/ return tmpfile (); #endif /*!HAVE_W32_SYSTEM*/ } /* Make sure that the standard file descriptors are opened. Obviously some folks close them before an exec and the next file we open will get one of them assigned and thus any output (i.e. diagnostics) end up in that file (e.g. the trustdb). Not actually a gpg problem as this will happen with almost all utilities when called in a wrong way. However we try to minimize the damage here and raise awareness of the problem. Must be called before we open any files! */ void gnupg_reopen_std (const char *pgmname) { #if defined(HAVE_STAT) && !defined(HAVE_W32_SYSTEM) struct stat statbuf; int did_stdin = 0; int did_stdout = 0; int did_stderr = 0; FILE *complain; if (fstat (STDIN_FILENO, &statbuf) == -1 && errno ==EBADF) { if (open ("/dev/null",O_RDONLY) == STDIN_FILENO) did_stdin = 1; else did_stdin = 2; } if (fstat (STDOUT_FILENO, &statbuf) == -1 && errno == EBADF) { if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO) did_stdout = 1; else did_stdout = 2; } if (fstat (STDERR_FILENO, &statbuf)==-1 && errno==EBADF) { if (open ("/dev/null", O_WRONLY) == STDERR_FILENO) did_stderr = 1; else did_stderr = 2; } /* It's hard to log this sort of thing since the filehandle we would complain to may be closed... */ if (!did_stderr) complain = stderr; else if (!did_stdout) complain = stdout; else complain = NULL; if (complain) { if (did_stdin == 1) fprintf (complain, "%s: WARNING: standard input reopened\n", pgmname); if (did_stdout == 1) fprintf (complain, "%s: WARNING: standard output reopened\n", pgmname); if (did_stderr == 1) fprintf (complain, "%s: WARNING: standard error reopened\n", pgmname); if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) fprintf(complain,"%s: fatal: unable to reopen standard input," " output, or error\n", pgmname); } if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) exit (3); #else /* !(HAVE_STAT && !HAVE_W32_SYSTEM) */ (void)pgmname; #endif } /* Hack required for Windows. */ void gnupg_allow_set_foregound_window (pid_t pid) { if (!pid) log_info ("%s called with invalid pid %lu\n", "gnupg_allow_set_foregound_window", (unsigned long)pid); #if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid)) log_info ("AllowSetForegroundWindow(%lu) failed: %s\n", (unsigned long)pid, w32_strerror (-1)); #endif } int gnupg_remove (const char *fname) { #ifdef HAVE_W32CE_SYSTEM int rc; wchar_t *wfname; wfname = utf8_to_wchar (fname); if (!wfname) rc = 0; else { rc = DeleteFile (wfname); xfree (wfname); } if (!rc) return -1; /* ERRNO is automagically provided by gpg-error.h. */ return 0; #else return remove (fname); #endif } /* Wrapper for rename(2) to handle Windows peculiarities. If * BLOCK_SIGNALS is not NULL and points to a variable set to true, all * signals will be blocked by calling gnupg_block_all_signals; the * caller needs to call gnupg_unblock_all_signals if that variable is * still set to true on return. */ gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, int *block_signals) { gpg_error_t err = 0; if (block_signals && *block_signals) gnupg_block_all_signals (); #ifdef HAVE_DOSISH_SYSTEM { int wtime = 0; gnupg_remove (newname); again: if (rename (oldname, newname)) { if (GetLastError () == ERROR_SHARING_VIOLATION) { /* Another process has the file open. We do not use a * lock for read but instead we wait until the other * process has closed the file. This may take long but * that would also be the case with a dotlock approach for * read and write. Note that we don't need this on Unix * due to the inode concept. * * So let's wait until the rename has worked. The retry * intervals are 50, 100, 200, 400, 800, 50ms, ... */ if (!wtime || wtime >= 800) wtime = 50; else wtime *= 2; if (wtime >= 800) log_info (_("waiting for file '%s' to become accessible ...\n"), oldname); Sleep (wtime); goto again; } err = my_error_from_syserror (); } } #else /* Unix */ { #ifdef __riscos__ gnupg_remove (newname); #endif if (rename (oldname, newname) ) err = my_error_from_syserror (); } #endif /* Unix */ if (block_signals && *block_signals && err) { gnupg_unblock_all_signals (); *block_signals = 0; } if (err) log_error (_("renaming '%s' to '%s' failed: %s\n"), oldname, newname, gpg_strerror (err)); return err; } #ifndef HAVE_W32_SYSTEM static mode_t modestr_to_mode (const char *modestr) { mode_t mode = 0; if (modestr && *modestr) { modestr++; if (*modestr && *modestr++ == 'r') mode |= S_IRUSR; if (*modestr && *modestr++ == 'w') mode |= S_IWUSR; if (*modestr && *modestr++ == 'x') mode |= S_IXUSR; if (*modestr && *modestr++ == 'r') mode |= S_IRGRP; if (*modestr && *modestr++ == 'w') mode |= S_IWGRP; if (*modestr && *modestr++ == 'x') mode |= S_IXGRP; if (*modestr && *modestr++ == 'r') mode |= S_IROTH; if (*modestr && *modestr++ == 'w') mode |= S_IWOTH; if (*modestr && *modestr++ == 'x') mode |= S_IXOTH; } return mode; } #endif /* A wrapper around mkdir which takes a string for the mode argument. This makes it easier to handle the mode argument which is not defined on all systems. The format of the modestring is "-rwxrwxrwx" '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, write allowed, execution allowed with the first group for the user, the second for the group and the third for all others. If the string is shorter than above the missing mode characters are meant to be not set. */ int gnupg_mkdir (const char *name, const char *modestr) { #ifdef HAVE_W32CE_SYSTEM wchar_t *wname; (void)modestr; wname = utf8_to_wchar (name); if (!wname) return -1; if (!CreateDirectoryW (wname, NULL)) { xfree (wname); return -1; /* ERRNO is automagically provided by gpg-error.h. */ } xfree (wname); return 0; #elif MKDIR_TAKES_ONE_ARG (void)modestr; /* Note: In the case of W32 we better use CreateDirectory and try to set appropriate permissions. However using mkdir is easier because this sets ERRNO. */ return mkdir (name); #else return mkdir (name, modestr_to_mode (modestr)); #endif } +/* A simple wrapper around chdir. NAME is expected to be utf8 + * encoded. */ +int +gnupg_chdir (const char *name) +{ + return chdir (name); +} + + /* A wrapper around chmod which takes a string for the mode argument. This makes it easier to handle the mode argument which is not defined on all systems. The format of the modestring is the same as for gnupg_mkdir. */ int gnupg_chmod (const char *name, const char *modestr) { #ifdef HAVE_W32_SYSTEM (void)name; (void)modestr; return 0; #else return chmod (name, modestr_to_mode (modestr)); #endif } /* Our version of mkdtemp. The API is identical to POSIX.1-2008 version. We do not use a system provided mkdtemp because we have a good RNG instantly available and this way we don't have diverging versions. */ char * gnupg_mkdtemp (char *tmpl) { /* A lower bound on the number of temporary files to attempt to generate. The maximum total number of temporary file names that can exist for a given template is 62**6 (5*36**3 for Windows). It should never be necessary to try all these combinations. Instead if a reasonable number of names is tried (we define reasonable as 62**3 or 5*36**3) fail to give the system administrator the chance to remove the problems. */ #ifdef HAVE_W32_SYSTEM static const char letters[] = "abcdefghijklmnopqrstuvwxyz0123456789"; # define NUMBER_OF_LETTERS 36 # define ATTEMPTS_MIN (5 * 36 * 36 * 36) #else static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; # define NUMBER_OF_LETTERS 62 # define ATTEMPTS_MIN (62 * 62 * 62) #endif int len; char *XXXXXX; uint64_t value; unsigned int count; int save_errno = errno; /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX unsigned int attempts = TMP_MAX; #else unsigned int attempts = ATTEMPTS_MIN; #endif len = strlen (tmpl); if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) { gpg_err_set_errno (EINVAL); return NULL; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get a random start value. */ gcry_create_nonce (&value, sizeof value); /* Loop until a directory was created. */ for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[1] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[2] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[3] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[4] = letters[v % NUMBER_OF_LETTERS]; v /= NUMBER_OF_LETTERS; XXXXXX[5] = letters[v % NUMBER_OF_LETTERS]; if (!gnupg_mkdir (tmpl, "-rwx")) { gpg_err_set_errno (save_errno); return tmpl; } if (errno != EEXIST) return NULL; } /* We got out of the loop because we ran out of combinations to try. */ gpg_err_set_errno (EEXIST); return NULL; } int gnupg_setenv (const char *name, const char *value, int overwrite) { #ifdef HAVE_W32CE_SYSTEM (void)name; (void)value; (void)overwrite; return 0; #else /*!W32CE*/ # ifdef HAVE_W32_SYSTEM /* Windows maintains (at least) two sets of environment variables. One set can be accessed by GetEnvironmentVariable and SetEnvironmentVariable. This set is inherited by the children. The other set is maintained in the C runtime, and is accessed using getenv and putenv. We try to keep them in sync by modifying both sets. */ { int exists; char tmpbuf[10]; exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) { gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ return -1; } } # endif /*W32*/ # ifdef HAVE_SETENV return setenv (name, value, overwrite); # else /*!HAVE_SETENV*/ if (! getenv (name) || overwrite) { char *buf; (void)overwrite; if (!name || !value) { gpg_err_set_errno (EINVAL); return -1; } buf = strconcat (name, "=", value, NULL); if (!buf) return -1; # if __GNUC__ # warning no setenv - using putenv but leaking memory. # endif return putenv (buf); } return 0; # endif /*!HAVE_SETENV*/ #endif /*!W32CE*/ } int gnupg_unsetenv (const char *name) { #ifdef HAVE_W32CE_SYSTEM (void)name; return 0; #else /*!W32CE*/ # ifdef HAVE_W32_SYSTEM /* Windows maintains (at least) two sets of environment variables. One set can be accessed by GetEnvironmentVariable and SetEnvironmentVariable. This set is inherited by the children. The other set is maintained in the C runtime, and is accessed using getenv and putenv. We try to keep them in sync by modifying both sets. */ if (!SetEnvironmentVariable (name, NULL)) { gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ return -1; } # endif /*W32*/ # ifdef HAVE_UNSETENV return unsetenv (name); # else /*!HAVE_UNSETENV*/ { char *buf; if (!name) { gpg_err_set_errno (EINVAL); return -1; } buf = xtrystrdup (name); if (!buf) return -1; # if __GNUC__ # warning no unsetenv - trying putenv but leaking memory. # endif return putenv (buf); } # endif /*!HAVE_UNSETENV*/ #endif /*!W32CE*/ } /* Return the current working directory as a malloced string. Return NULL and sets ERRNo on error. */ char * gnupg_getcwd (void) { char *buffer; size_t size = 100; for (;;) { buffer = xtrymalloc (size+1); if (!buffer) return NULL; #ifdef HAVE_W32CE_SYSTEM strcpy (buffer, "/"); /* Always "/". */ return buffer; #else if (getcwd (buffer, size) == buffer) return buffer; xfree (buffer); if (errno != ERANGE) return NULL; size *= 2; #endif } } #ifdef HAVE_W32CE_SYSTEM /* There is a isatty function declaration in cegcc but it does not make sense, thus we redefine it. */ int _gnupg_isatty (int fd) { (void)fd; return 0; } #endif #ifdef HAVE_W32CE_SYSTEM /* Replacement for getenv which takes care of the our use of getenv. The code is not thread safe but we expect it to work in all cases because it is called for the first time early enough. */ char * _gnupg_getenv (const char *name) { static int initialized; static char *assuan_debug; if (!initialized) { assuan_debug = read_w32_registry_string (NULL, "\\Software\\GNU\\libassuan", "debug"); initialized = 1; } if (!strcmp (name, "ASSUAN_DEBUG")) return assuan_debug; else return NULL; } #endif /*HAVE_W32CE_SYSTEM*/ #ifdef HAVE_W32_SYSTEM /* Return the user's security identifier from the current process. */ PSID w32_get_user_sid (void) { int okay = 0; HANDLE proc = NULL; HANDLE token = NULL; TOKEN_USER *user = NULL; PSID sid = NULL; DWORD tokenlen, sidlen; proc = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); if (!proc) goto leave; if (!OpenProcessToken (proc, TOKEN_QUERY, &token)) goto leave; if (!GetTokenInformation (token, TokenUser, NULL, 0, &tokenlen) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto leave; user = xtrymalloc (tokenlen); if (!user) goto leave; if (!GetTokenInformation (token, TokenUser, user, tokenlen, &tokenlen)) goto leave; if (!IsValidSid (user->User.Sid)) goto leave; sidlen = GetLengthSid (user->User.Sid); sid = xtrymalloc (sidlen); if (!sid) goto leave; if (!CopySid (sidlen, sid, user->User.Sid)) goto leave; okay = 1; leave: xfree (user); if (token) CloseHandle (token); if (proc) CloseHandle (proc); if (!okay) { xfree (sid); sid = NULL; } return sid; } #endif /*HAVE_W32_SYSTEM*/ /* Support for inotify under Linux. */ /* Store a new inotify file handle for FNAME at R_FD or return an * error code. This file descriptor watch the removal of FNAME. */ gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname) { #if HAVE_INOTIFY_INIT gpg_error_t err; int fd; *r_fd = -1; if (!fname) return my_error (GPG_ERR_INV_VALUE); fd = inotify_init (); if (fd == -1) return my_error_from_syserror (); if (inotify_add_watch (fd, fname, IN_DELETE_SELF) == -1) { err = my_error_from_syserror (); close (fd); return err; } *r_fd = fd; return 0; #else /*!HAVE_INOTIFY_INIT*/ (void)fname; *r_fd = -1; return my_error (GPG_ERR_NOT_SUPPORTED); #endif /*!HAVE_INOTIFY_INIT*/ } /* Store a new inotify file handle for SOCKET_NAME at R_FD or return * an error code. */ gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name) { #if HAVE_INOTIFY_INIT gpg_error_t err; char *fname; int fd; char *p; *r_fd = -1; if (!socket_name) return my_error (GPG_ERR_INV_VALUE); fname = xtrystrdup (socket_name); if (!fname) return my_error_from_syserror (); fd = inotify_init (); if (fd == -1) { err = my_error_from_syserror (); xfree (fname); return err; } /* We need to watch the directory for the file because there won't * be an IN_DELETE_SELF for a socket file. To handle a removal of * the directory we also watch the directory itself. */ p = strrchr (fname, '/'); if (p) *p = 0; if (inotify_add_watch (fd, fname, (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1) { err = my_error_from_syserror (); close (fd); xfree (fname); return err; } xfree (fname); *r_fd = fd; return 0; #else /*!HAVE_INOTIFY_INIT*/ (void)socket_name; *r_fd = -1; return my_error (GPG_ERR_NOT_SUPPORTED); #endif /*!HAVE_INOTIFY_INIT*/ } /* Read an inotify event and return true if it matches NAME or if it * sees an IN_DELETE_SELF event for the directory of NAME. */ int gnupg_inotify_has_name (int fd, const char *name) { #if USE_NPTH && HAVE_INOTIFY_INIT #define BUFSIZE_FOR_INOTIFY (sizeof (struct inotify_event) + 255 + 1) union { struct inotify_event ev; char _buf[sizeof (struct inotify_event) + 255 + 1]; } buf; struct inotify_event *evp; int n; n = npth_read (fd, &buf, sizeof buf); /* log_debug ("notify read: n=%d\n", n); */ evp = &buf.ev; while (n >= sizeof (struct inotify_event)) { /* log_debug (" mask=%x len=%u name=(%s)\n", */ /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */ if ((evp->mask & IN_UNMOUNT)) { /* log_debug (" found (dir unmounted)\n"); */ return 3; /* Directory was unmounted. */ } if ((evp->mask & IN_DELETE_SELF)) { /* log_debug (" found (dir removed)\n"); */ return 2; /* Directory was removed. */ } if ((evp->mask & IN_DELETE)) { if (evp->len >= strlen (name) && !strcmp (evp->name, name)) { /* log_debug (" found (file removed)\n"); */ return 1; /* File was removed. */ } } n -= sizeof (*evp) + evp->len; evp = (struct inotify_event *)(void *) ((char *)evp + sizeof (*evp) + evp->len); } #else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ (void)fd; (void)name; #endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ return 0; /* Not found. */ } /* Return a malloc'ed string that is the path to the passed * unix-domain socket (or return NULL if this is not a valid * unix-domain socket). We use a plain int here because it is only * used on Linux. * * FIXME: This function needs to be moved to libassuan. */ #ifndef HAVE_W32_SYSTEM char * gnupg_get_socket_name (int fd) { struct sockaddr_un un; socklen_t len = sizeof(un); char *name = NULL; if (getsockname (fd, (struct sockaddr*)&un, &len) != 0) log_error ("could not getsockname(%d): %s\n", fd, gpg_strerror (my_error_from_syserror ())); else if (un.sun_family != AF_UNIX) log_error ("file descriptor %d is not a unix-domain socket\n", fd); else if (len <= offsetof (struct sockaddr_un, sun_path)) log_error ("socket name not present for file descriptor %d\n", fd); else if (len > sizeof(un)) log_error ("socket name for file descriptor %d was truncated " "(passed %zu bytes, wanted %u)\n", fd, sizeof(un), len); else { size_t namelen = len - offsetof (struct sockaddr_un, sun_path); /* log_debug ("file descriptor %d has path %s (%zu octets)\n", fd, */ /* un.sun_path, namelen); */ name = xtrymalloc (namelen + 1); if (!name) log_error ("failed to allocate memory for name of fd %d: %s\n", fd, gpg_strerror (my_error_from_syserror ())); else { memcpy (name, un.sun_path, namelen); name[namelen] = 0; } } return name; } #endif /*!HAVE_W32_SYSTEM*/ /* Check whether FD is valid. */ int gnupg_fd_valid (int fd) { int d = dup (fd); if (d < 0) return 0; close (d); return 1; } diff --git a/common/sysutils.h b/common/sysutils.h index e93ea2b1c..009b14b4a 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -1,89 +1,90 @@ /* sysutils.h - System utility functions for Gnupg * Copyright (C) 2002 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 . */ #ifndef GNUPG_COMMON_SYSUTILS_H #define GNUPG_COMMON_SYSUTILS_H /* Because we use system handles and not libc low level file descriptors on W32, we need to declare them as HANDLE (which actually is a plain pointer). This is required to eventually support 64 bits Windows systems. */ #ifdef HAVE_W32_SYSTEM typedef void *gnupg_fd_t; #define GNUPG_INVALID_FD ((void*)(-1)) #define INT2FD(s) ((void *)(s)) #define FD2INT(h) ((unsigned int)(h)) #else typedef int gnupg_fd_t; #define GNUPG_INVALID_FD (-1) #define INT2FD(s) (s) #define FD2INT(h) (h) #endif void trap_unaligned (void); int disable_core_dumps (void); int enable_core_dumps (void); void enable_special_filenames (void); const unsigned char *get_session_marker (size_t *rlen); unsigned int get_uint_nonce (void); /*int check_permissions (const char *path,int extension,int checkonly);*/ void gnupg_sleep (unsigned int seconds); void gnupg_usleep (unsigned int usecs); int translate_sys2libc_fd (gnupg_fd_t fd, int for_write); int translate_sys2libc_fd_int (int fd, int for_write); int check_special_filename (const char *fname, int for_write, int notranslate); FILE *gnupg_tmpfile (void); void gnupg_reopen_std (const char *pgmname); void gnupg_allow_set_foregound_window (pid_t pid); int gnupg_remove (const char *fname); gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, int *block_signals); -int gnupg_mkdir (const char *name, const char *modestr); +int gnupg_mkdir (const char *name, const char *modestr); +int gnupg_chdir (const char *name); int gnupg_chmod (const char *name, const char *modestr); char *gnupg_mkdtemp (char *template); int gnupg_setenv (const char *name, const char *value, int overwrite); int gnupg_unsetenv (const char *name); char *gnupg_getcwd (void); char *gnupg_get_socket_name (int fd); int gnupg_fd_valid (int fd); gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname); gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); int gnupg_inotify_has_name (int fd, const char *name); #ifdef HAVE_W32_SYSTEM void *w32_get_user_sid (void); #include "../common/w32help.h" #endif /*HAVE_W32_SYSTEM*/ #endif /*GNUPG_COMMON_SYSUTILS_H*/ diff --git a/common/util.h b/common/util.h index 5b712d3e9..c6d19c64b 100644 --- a/common/util.h +++ b/common/util.h @@ -1,379 +1,380 @@ /* util.h - Utility functions for GnuPG * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute and/or modify this * part of GnuPG 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. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef GNUPG_COMMON_UTIL_H #define GNUPG_COMMON_UTIL_H #include /* We need this for the memory function protos. */ #include /* We need errno. */ #include /* We need gpg_error_t and estream. */ /* These error codes are used but not defined in the required * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ #if GPG_ERROR_VERSION_NUMBER < 0x011a00 /* 1.26 */ # define GPG_ERR_UNKNOWN_FLAG 309 # define GPG_ERR_INV_ORDER 310 # define GPG_ERR_ALREADY_FETCHED 311 # define GPG_ERR_TRY_LATER 312 # define GPG_ERR_SYSTEM_BUG 666 # define GPG_ERR_DNS_UNKNOWN 711 # define GPG_ERR_DNS_SECTION 712 # define GPG_ERR_DNS_ADDRESS 713 # define GPG_ERR_DNS_NO_QUERY 714 # define GPG_ERR_DNS_NO_ANSWER 715 # define GPG_ERR_DNS_CLOSED 716 # define GPG_ERR_DNS_VERIFY 717 # define GPG_ERR_DNS_TIMEOUT 718 #endif /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) /* Get all the stuff from jnlib. */ #include "../common/logging.h" #include "../common/argparse.h" #include "../common/stringhelp.h" #include "../common/mischelp.h" #include "../common/strlist.h" #include "../common/dotlock.h" #include "../common/utf8conv.h" #include "../common/dynload.h" #include "../common/fwddecl.h" #include "../common/utilproto.h" #include "gettime.h" /* Redefine asprintf by our estream version which uses our own memory allocator.. */ #define asprintf gpgrt_asprintf #define vasprintf gpgrt_vasprintf /* Due to a bug in mingw32's snprintf related to the 'l' modifier and for increased portability we use our snprintf on all systems. */ #undef snprintf #define snprintf gpgrt_snprintf /* Replacements for macros not available with libgpg-error < 1.20. */ /* We need this type even if we are not using libreadline and or we did not include libreadline in the current file. */ #ifndef GNUPG_LIBREADLINE_H_INCLUDED typedef char **rl_completion_func_t (const char *, int, int); #endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ /* Handy malloc macros - please use only them. */ #define xtrymalloc(a) gcry_malloc ((a)) #define xtrymalloc_secure(a) gcry_malloc_secure ((a)) #define xtrycalloc(a,b) gcry_calloc ((a),(b)) #define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) #define xtryrealloc(a,b) gcry_realloc ((a),(b)) #define xtrystrdup(a) gcry_strdup ((a)) #define xfree(a) gcry_free ((a)) #define xfree_fnc gcry_free #define xmalloc(a) gcry_xmalloc ((a)) #define xmalloc_secure(a) gcry_xmalloc_secure ((a)) #define xcalloc(a,b) gcry_xcalloc ((a),(b)) #define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) #define xrealloc(a,b) gcry_xrealloc ((a),(b)) #define xstrdup(a) gcry_xstrdup ((a)) /* For compatibility with gpg 1.4 we also define these: */ #define xmalloc_clear(a) gcry_xcalloc (1, (a)) #define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) /* 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. Defined by init.c. */ extern gpg_err_source_t default_errsource; /* Convenience function to return a gpg-error code for memory allocation failures. This function makes sure that an error will be returned even if accidentally ERRNO is not set. */ static inline gpg_error_t out_of_core (void) { return gpg_error_from_syserror (); } /*-- yesno.c --*/ int answer_is_yes (const char *s); int answer_is_yes_no_default (const char *s, int def_answer); int answer_is_yes_no_quit (const char *s); int answer_is_okay_cancel (const char *s, int def_answer); /*-- xreadline.c --*/ ssize_t read_line (FILE *fp, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length); /*-- b64enc.c and b64dec.c --*/ struct b64state { unsigned int flags; int idx; int quad_count; FILE *fp; estream_t stream; char *title; unsigned char radbuf[4]; u32 crc; int stop_seen:1; int invalid_encoding:1; gpg_error_t lasterr; }; gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, const char *title); gpg_error_t b64enc_write (struct b64state *state, const void *buffer, size_t nbytes); gpg_error_t b64enc_finish (struct b64state *state); gpg_error_t b64dec_start (struct b64state *state, const char *title); gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, size_t *r_nbytes); gpg_error_t b64dec_finish (struct b64state *state); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); void log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen); void log_printsexp (const char *text, gcry_sexp_t sexp); gpg_error_t make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); int hash_algo_from_sigval (const unsigned char *sigval); unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, const void *e, size_t elen, size_t *r_len); gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_n, size_t *r_nlen, unsigned char const **r_e, size_t *r_elen); int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); size_t percent_plus_unescape_inplace (char *string, int nulrepl); size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); char *openpgp_oid_to_str (gcry_mpi_t a); int openpgp_oid_is_ed25519 (gcry_mpi_t a); int openpgp_oid_is_cv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); const char *openpgp_oid_to_curve (const char *oid, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits); /*-- homedir.c --*/ const char *standard_homedir (void); const char *default_homedir (void); void gnupg_set_homedir (const char *newdir); const char *gnupg_homedir (void); int gnupg_default_homedir_p (void); +const char *gnupg_daemon_rootdir (void); const char *gnupg_socketdir (void); const char *gnupg_sysconfdir (void); const char *gnupg_bindir (void); const char *gnupg_libexecdir (void); const char *gnupg_libdir (void); const char *gnupg_datadir (void); const char *gnupg_localedir (void); const char *gnupg_cachedir (void); const char *dirmngr_socket_name (void); char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info); /* All module names. We also include gpg and gpgsm for the sake for gpgconf. */ #define GNUPG_MODULE_NAME_AGENT 1 #define GNUPG_MODULE_NAME_PINENTRY 2 #define GNUPG_MODULE_NAME_SCDAEMON 3 #define GNUPG_MODULE_NAME_DIRMNGR 4 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5 #define GNUPG_MODULE_NAME_CHECK_PATTERN 6 #define GNUPG_MODULE_NAME_GPGSM 7 #define GNUPG_MODULE_NAME_GPG 8 #define GNUPG_MODULE_NAME_CONNECT_AGENT 9 #define GNUPG_MODULE_NAME_GPGCONF 10 #define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 #define GNUPG_MODULE_NAME_GPGV 12 const char *gnupg_module_name (int which); void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); /*-- gpgrlhelp.c --*/ void gnupg_rl_initialize (void); /*-- helpfile.c --*/ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); /*-- miscellaneous.c --*/ /* This function is called at startup to tell libgcrypt to use our own logging subsystem. */ void setup_libgcrypt_logging (void); /* Print an out of core message and die. */ void xoutofcore (void); /* Same as estream_asprintf but die on memory failure. */ char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* This is now an alias to estream_asprintf. */ char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* Replacement for gcry_cipher_algo_name. */ const char *gnupg_cipher_algo_name (int algo); void obsolete_option (const char *configname, unsigned int configlineno, const char *name); const char *print_fname_stdout (const char *s); const char *print_fname_stdin (const char *s); void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, const char *delim); void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); void print_utf8_buffer (estream_t fp, const void *p, size_t n); void print_utf8_string (estream_t stream, const char *p); void print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved); char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); int is_file_compressed (const char *s, int *ret_rc); int match_multistr (const char *multistr,const char *match); int gnupg_compare_version (const char *a, const char *b); struct debug_flags_s { unsigned int flag; const char *name; }; int parse_debug_flag (const char *string, unsigned int *debugvar, const struct debug_flags_s *flags); /*-- Simple replacement functions. */ /* We use the gnupg_ttyname macro to be safe not to run into conflicts which an extisting but broken ttyname. */ #if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) # define gnupg_ttyname(n) _gnupg_ttyname ((n)) /* Systems without ttyname (W32) will merely return NULL. */ static inline char * _gnupg_ttyname (int fd) { (void)fd; return NULL; } #else /*HAVE_TTYNAME*/ # define gnupg_ttyname(n) ttyname ((n)) #endif /*HAVE_TTYNAME */ #ifdef HAVE_W32CE_SYSTEM #define getpid() GetCurrentProcessId () char *_gnupg_getenv (const char *name); /* See sysutils.c */ #define getenv(a) _gnupg_getenv ((a)) char *_gnupg_setenv (const char *name); /* See sysutils.c */ #define setenv(a,b,c) _gnupg_setenv ((a),(b),(c)) int _gnupg_isatty (int fd); #define gnupg_isatty(a) _gnupg_isatty ((a)) #else #define gnupg_isatty(a) isatty ((a)) #endif /*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ || (*(p) >= 'a' && *(p) <= 'z')) #define alnump(p) (alphap (p) || digitp (p)) #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) /* Note this isn't identical to a C locale isspace() without \f and \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') /* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) #endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 6eabca9c3..436c8080b 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -1,2278 +1,2280 @@ /* 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 #ifdef HAVE_INOTIFY_INIT # include #endif /*HAVE_INOTIFY_INIT*/ #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 "../common/asshelp.h" #if USE_LDAP # include "ldap-wrapper.h" #endif #include "../common/init.h" #include "../common/gc-opt-flags.h" #include "dns-stuff.h" #include "http-common.h" #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, aSupervised, aListCRLs, aLoadCRL, aFetchCRL, aShutdown, aFlush, aGPGConfList, aGPGConfTest, oOptions, oDebug, oDebugAll, oDebugWait, oDebugLevel, oGnutlsDebug, oNoGreeting, oNoOptions, oHomedir, oNoDetach, oLogFile, oBatch, oDisableHTTP, oDisableLDAP, oDisableIPv4, oDisableIPv6, oIgnoreLDAPDP, oIgnoreHTTPDP, oIgnoreOCSPSvcUrl, oHonorHTTPProxy, oHTTPProxy, oLDAPProxy, oOnlyLDAPProxy, oLDAPFile, oLDAPTimeout, oLDAPAddServers, oOCSPResponder, oOCSPSigner, oOCSPMaxClockSkew, oOCSPMaxPeriod, oOCSPCurrentPeriod, oMaxReplies, oHkpCaCert, oFakedSystemTime, oForce, oAllowOCSP, oAllowVersionCheck, oSocketName, oLDAPWrapperProgram, oHTTPWrapperProgram, oIgnoreCertExtension, oUseTor, oNoUseTor, oKeyServer, oNameServer, oDisableCheckOwnSocket, oStandardResolver, oRecursiveResolver, oResolverTimeout, oConnectTimeout, oConnectQuickTimeout, 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)") ), #ifndef HAVE_W32_SYSTEM ARGPARSE_c (aSupervised, "supervised", N_("run in supervised mode")), #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 (oAllowVersionCheck, "allow-version-check", N_("allow online software version check")), 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_n (oNoUseTor, "no-use-tor", "@"), ARGPARSE_s_n (oDisableIPv4, "disable-ipv4", "@"), ARGPARSE_s_n (oDisableIPv6, "disable-ipv6", "@"), 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 (oDisableCheckOwnSocket, "disable-check-own-socket", "@"), 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_s_n (oStandardResolver, "standard-resolver", "@"), ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"), ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"), ARGPARSE_s_i (oConnectTimeout, "connect-timeout", "@"), ARGPARSE_s_i (oConnectQuickTimeout, "connect-quick-timeout", "@"), 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_DNS_VALUE , "dns" }, { DBG_NETWORK_VALUE, "network" }, { DBG_LOOKUP_VALUE , "lookup" }, { DBG_EXTPROG_VALUE, "extprog" }, { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; #define DEFAULT_MAX_REPLIES 10 #define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */ #define DEFAULT_CONNECT_TIMEOUT (15*1000) /* 15 seconds */ #define DEFAULT_CONNECT_QUICK_TIMEOUT ( 2*1000) /* 2 seconds */ /* 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; /* Flags to indicate that we shall not watch our own socket. */ static int disable_check_own_socket; /* Flag to control the Tor mode. */ static enum { TOR_MODE_AUTO = 0, /* Switch to NO or YES */ TOR_MODE_NEVER, /* Never use Tor. */ TOR_MODE_NO, /* Do not use Tor */ TOR_MODE_YES, /* Use Tor */ TOR_MODE_FORCE /* Force using Tor */ } tor_mode; /* Counter for the active connections. */ static int active_connections; /* This flag is set by any network access and used by the housekeeping * thread to run background network tasks. */ static int network_activity_seen; /* A list of filenames registred with --hkp-cacert. */ static strlist_t hkp_cacert_filenames; /* The timer tick used for housekeeping stuff. */ #define TIMERTICK_INTERVAL (60) /* How oft to run the housekeeping. */ #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 npth_key_t 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 netactivity_action (void); 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 (dirmngr_use_tor ()) { /* Enable Tor mode and when called again force a new curcuit * (e.g. on SIGHUP). */ enable_dns_tormode (1); 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"); } } else disable_dns_tormode (); } /* Return true if Tor shall be used. */ int dirmngr_use_tor (void) { if (tor_mode == TOR_MODE_AUTO) { /* FIXME: Figure out whether Tor is running. */ } if (tor_mode == TOR_MODE_FORCE) return 2; /* Use Tor (using 2 to indicate force mode) */ else if (tor_mode == TOR_MODE_YES) return 1; /* Use Tor */ else return 0; /* Do not use Tor. */ } 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.allow_version_check = 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 (hkp_cacert_filenames); FREE_STRLIST (opt.keyserver); /* Note: We do not allow resetting of TOR_MODE_FORCE at runtime. */ if (tor_mode != TOR_MODE_FORCE) tor_mode = TOR_MODE_AUTO; disable_check_own_socket = 0; enable_standard_resolver (0); set_dns_timeout (0); opt.connect_timeout = 0; opt.connect_quick_timeout = 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 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 oDisableCheckOwnSocket: disable_check_own_socket = 1; 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 oDisableIPv4: opt.disable_ipv4 = 1; break; case oDisableIPv6: opt.disable_ipv6 = 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 oAllowVersionCheck: opt.allow_version_check = 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: { /* We need to register the filenames with gnutls (http.c) and * also for our own cert cache. */ char *tmpname; /* Do tilde expansion and make path absolute. */ tmpname = make_absfilename (pargs->r.ret_str, NULL); http_register_tls_ca (tmpname); add_to_strlist (&hkp_cacert_filenames, pargs->r.ret_str); xfree (tmpname); } break; case oIgnoreCertExtension: add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); break; case oUseTor: tor_mode = TOR_MODE_FORCE; break; case oNoUseTor: if (tor_mode != TOR_MODE_FORCE) tor_mode = TOR_MODE_NEVER; break; case oStandardResolver: enable_standard_resolver (1); break; case oRecursiveResolver: enable_recursive_resolver (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; case oResolverTimeout: set_dns_timeout (pargs->r.ret_int); break; case oConnectTimeout: opt.connect_timeout = pargs->r.ret_ulong * 1000; break; case oConnectQuickTimeout: opt.connect_quick_timeout = pargs->r.ret_ulong * 1000; break; default: return 0; /* Not handled. */ } set_dns_verbose (opt.verbose, !!DBG_DNS); http_set_verbose (opt.verbose, !!DBG_NETWORK); set_dns_disable_ipv4 (opt.disable_ipv4); set_dns_disable_ipv6 (opt.disable_ipv6); return 1; /* Handled. */ } /* This fucntion is called after option parsing to adjust some values * and call option setup functions. */ static void post_option_parsing (void) { /* It would be too surpirsing if the quick timeout is larger than * the standard value. */ if (opt.connect_quick_timeout > opt.connect_timeout) opt.connect_quick_timeout = opt.connect_timeout; set_debug (); set_tor_mode (); } #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*/ #if HTTP_USE_NTBTLS static void my_ntbtls_log_handler (void *opaque, int level, const char *fmt, va_list argv) { (void)opaque; if (level == -1) log_logv_with_prefix (GPGRT_LOG_INFO, "ntbtls: ", fmt, argv); else { char prefix[10+20]; snprintf (prefix, sizeof prefix, "ntbtls(%d): ", level); log_logv_with_prefix (GPGRT_LOG_DEBUG, prefix, fmt, argv); } } #endif static void thread_init (void) { npth_init (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* 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*/ } int main (int argc, char **argv) { 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; struct assuan_malloc_hooks malloc_hooks; early_system_init (); set_strusage (my_strusage); log_set_prefix (DIRMNGR_NAME, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID); /* Make sure that our subsystems are ready. */ i18n_init (); init_common_subsystems (&argc, &argv); 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 (!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, dirmngr_assuan_log_monitor); setup_libgcrypt_logging (); #if HTTP_USE_NTBTLS ntbtls_set_log_handler (my_ntbtls_log_handler, NULL); #endif /* Setup defaults. */ shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; /* Reset rereadable options to default values. */ parse_rereadable_options (NULL, 0); /* Default TCP timeouts. */ opt.connect_timeout = DEFAULT_CONNECT_TIMEOUT; opt.connect_quick_timeout = DEFAULT_CONNECT_QUICK_TIMEOUT; /* 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); } } socket_name = dirmngr_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 aSupervised: 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 /* 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"); } post_option_parsing (); /* Get LDAP server list from file. */ #if USE_LDAP if (!ldapfile) { ldapfile = make_filename (gnupg_homedir (), "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) 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, GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); } if (debug_wait) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); gnupg_sleep (debug_wait); log_debug ("... okay\n"); } thread_init (); cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); start_command_handler (ASSUAN_INVALID_FD); shutdown_reaper (); } #ifndef HAVE_W32_SYSTEM else if (cmd == aSupervised) { /* In supervised mode, we expect file descriptor 3 to be an already opened, listening socket. We will also not detach from the controlling process or close stderr; the supervisor should handle all of that. */ struct stat statbuf; if (fstat (3, &statbuf) == -1 && errno == EBADF) { log_error ("file descriptor 3 must be validin --supervised mode\n"); dirmngr_exit (1); } socket_name = gnupg_get_socket_name (3); /* 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); } else log_set_prefix (NULL, 0); thread_init (); cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); handle_connections (3); shutdown_reaper (); } #endif /*HAVE_W32_SYSTEM*/ 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 ) { 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 (); dirmngr_exit (1); } } } 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("/")) + if (gnupg_chdir (gnupg_daemon_rootdir ())) { - log_error ("chdir to / failed: %s\n", strerror (errno)); + log_error ("chdir to '%s' failed: %s\n", + gnupg_daemon_rootdir (), strerror (errno)); dirmngr_exit (1); } + } #endif thread_init (); cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); handle_connections (fd); shutdown_reaper (); } else if (cmd == aListCRLs) { /* Just list the CRL cache and exit. */ if (argc) wrong_args ("--list-crls"); 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); thread_init (); cert_cache_init (hkp_cacert_filenames); 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); thread_init (); cert_cache_init (hkp_cacert_filenames); 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; /* 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 (), "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 ("allow-version-check:%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); filename_esc = percent_escape (get_default_keyserver (0), NULL); es_printf ("keyserver:%lu:\"%s:\n", flags | GC_OPT_FLAG_DEFAULT, filename_esc); xfree (filename_esc); es_printf ("nameserver:%lu:\n", flags | GC_OPT_FLAG_NONE); es_printf ("resolver-timeout:%lu:%u\n", flags | GC_OPT_FLAG_DEFAULT, 0); } cleanup (); return !!rc; } static void cleanup (void) { crl_cache_deinit (); cert_cache_deinit (1); reload_dns_stuff (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) { ctrl->magic = SERVER_CONTROL_MAGIC; if (opt.http_proxy) ctrl->http_proxy = xstrdup (opt.http_proxy); ctrl->http_no_crl = 1; ctrl->timeout = opt.connect_timeout; } void dirmngr_deinit_default_ctrl (ctrl_t ctrl) { if (!ctrl) return; ctrl->magic = 0xdeadbeef; 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); post_option_parsing (); } /* 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 (hkp_cacert_filenames); crl_cache_init (); reload_dns_stuff (0); ks_hkp_reload (); } /* This function is called if some network activity was done. At this * point we know the we have a network and we can decide whether to * run scheduled background tasks soon. The function should return * quickly and only trigger actions for another thread. */ static void netactivity_action (void) { network_activity_seen = 1; } /* 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; struct server_control_s ctrlbuf; (void)arg; curtime = gnupg_get_time (); if (sentinel) { log_info ("housekeeping is already going on\n"); return NULL; } sentinel++; if (opt.verbose > 1) log_info ("starting housekeeping\n"); memset (&ctrlbuf, 0, sizeof ctrlbuf); dirmngr_init_default_ctrl (&ctrlbuf); ks_hkp_housekeeping (curtime); if (network_activity_seen) { network_activity_seen = 0; if (opt.allow_version_check) dirmngr_load_swdb (&ctrlbuf, 0); } dirmngr_deinit_default_ctrl (&ctrlbuf); if (opt.verbose > 1) 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) { 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; } #ifdef HAVE_INOTIFY_INIT /* Read an inotify event and return true if it matches NAME. */ static int my_inotify_is_name (int fd, const char *name) { union { struct inotify_event ev; char _buf[sizeof (struct inotify_event) + 100 + 1]; } buf; int n; const char *s; s = strrchr (name, '/'); if (s && s[1]) name = s + 1; n = npth_read (fd, &buf, sizeof buf); if (n < sizeof (struct inotify_event)) return 0; if (buf.ev.len < strlen (name)+1) return 0; if (strcmp (buf.ev.name, name)) return 0; /* Not the desired file. */ return 1; /* Found. */ } #endif /*HAVE_INOTIFY_INIT*/ /* Main loop in daemon mode. Note that LISTEN_FD will be owned by * this function. */ 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 ); int nfd, ret; fd_set fdset, read_fdset; struct timespec abstime; struct timespec curtime; struct timespec timeout; int saved_errno; int my_inotify_fd = -1; 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 #ifdef HAVE_INOTIFY_INIT if (disable_check_own_socket) my_inotify_fd = -1; else if ((my_inotify_fd = inotify_init ()) == -1) log_info ("error enabling fast daemon termination: %s\n", strerror (errno)); else { /* We need to watch the directory for the file because there * won't be an IN_DELETE_SELF for a socket file. */ char *slash = strrchr (socket_name, '/'); log_assert (slash && slash[1]); *slash = 0; if (inotify_add_watch (my_inotify_fd, socket_name, IN_DELETE) == -1) { close (my_inotify_fd); my_inotify_fd = -1; } *slash = '/'; } #endif /*HAVE_INOTIFY_INIT*/ /* 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); if (my_inotify_fd != -1) { FD_SET (my_inotify_fd, &fdset); if (my_inotify_fd > nfd) nfd = my_inotify_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. * * Note that we do not close the listening socket because a * client trying to connect to that socket would instead * restart a new dirmngr instance - which is unlikely the * intention of a shutdown. */ /* assuan_sock_close (listen_fd); */ /* listen_fd = -1; */ FD_ZERO (&fdset); nfd = -1; if (my_inotify_fd != -1) { FD_SET (my_inotify_fd, &fdset); nfd = my_inotify_fd; } } /* 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) { /* Do not anymore accept connections. */ continue; } #ifdef HAVE_INOTIFY_INIT if (my_inotify_fd != -1 && FD_ISSET (my_inotify_fd, &read_fdset) && my_inotify_is_name (my_inotify_fd, socket_name)) { shutdown_pending = 1; log_info ("socket file has been removed - shutting down\n"); } #endif /*HAVE_INOTIFY_INIT*/ if (FD_ISSET (FD2INT (listen_fd), &read_fdset)) { gnupg_fd_t fd; 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, "conn fd=%d", FD2INT(fd)); 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); } } } #ifdef HAVE_INOTIFY_INIT if (my_inotify_fd != -1) close (my_inotify_fd); #endif /*HAVE_INOTIFY_INIT*/ npth_attr_destroy (&tattr); if (listen_fd != GNUPG_INVALID_FD) assuan_sock_close (listen_fd); cleanup (); log_info ("%s %s stopped\n", strusage(11), strusage(13)); } const char* dirmngr_get_current_socket_name (void) { if (socket_name) return socket_name; else return dirmngr_socket_name (); } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 26e89dd8d..bf3f42a0e 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -1,1395 +1,1396 @@ /* scdaemon.c - The GnuPG Smartcard Daemon * Copyright (C) 2001-2002, 2004-2005, 2007-2009 Free Software Foundation, Inc. * Copyright (C) 2001-2002, 2004-2005, 2007-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 /*HAVE_W32_SYSTEM*/ #include #include #include #define GNUPG_COMMON_NEED_AFLOCAL #include "scdaemon.h" #include #include #include /* malloc hooks */ #include "../common/i18n.h" #include "../common/sysutils.h" #include "app-common.h" #include "iso7816.h" #include "apdu.h" #include "ccid-driver.h" #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "../common/exechelp.h" #include "../common/init.h" #ifndef ENAMETOOLONG # define ENAMETOOLONG EINVAL #endif enum cmd_and_opt_values { aNull = 0, oCsh = 'c', oQuiet = 'q', oSh = 's', oVerbose = 'v', oNoVerbose = 500, aGPGConfList, aGPGConfTest, oOptions, oDebug, oDebugAll, oDebugLevel, oDebugWait, oDebugAllowCoreDump, oDebugCCIDDriver, oDebugLogTid, oDebugAssuanLogCats, oNoGreeting, oNoOptions, oHomedir, oNoDetach, oNoGrab, oLogFile, oServer, oMultiServer, oDaemon, oBatch, oReaderPort, oCardTimeout, octapiDriver, opcscDriver, oDisableCCID, oDisableOpenSC, oDisablePinpad, oAllowAdmin, oDenyAdmin, oDisableApplication, oEnablePinpadVarlen, }; static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), ARGPARSE_group (301, N_("@Options:\n ")), ARGPARSE_s_n (oServer,"server", N_("run in server mode (foreground)")), ARGPARSE_s_n (oMultiServer, "multi-server", N_("run in multi server mode (foreground)")), ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")), 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" , N_("|LEVEL|set the debugging level to LEVEL")), ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"), ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"), ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"), ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")), ARGPARSE_s_s (oReaderPort, "reader-port", N_("|N|connect to reader at port N")), ARGPARSE_s_s (octapiDriver, "ctapi-driver", N_("|NAME|use NAME as ct-API driver")), ARGPARSE_s_s (opcscDriver, "pcsc-driver", N_("|NAME|use NAME as PC/SC driver")), ARGPARSE_s_n (oDisableCCID, "disable-ccid", #ifdef HAVE_LIBUSB N_("do not use the internal CCID driver") #else "@" #endif /* end --disable-ccid */), ARGPARSE_s_u (oCardTimeout, "card-timeout", N_("|N|disconnect the card after N seconds of inactivity")), ARGPARSE_s_n (oDisablePinpad, "disable-pinpad", N_("do not use a reader's pinpad")), ARGPARSE_ignore (300, "disable-keypad"), ARGPARSE_s_n (oAllowAdmin, "allow-admin", "@"), ARGPARSE_s_n (oDenyAdmin, "deny-admin", N_("deny the use of admin card commands")), ARGPARSE_s_s (oDisableApplication, "disable-application", "@"), ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen", N_("use variable length input for pinpad")), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_end () }; /* The list of supported debug flags. */ static struct debug_flags_s debug_flags [] = { { 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" }, { DBG_CARD_IO_VALUE, "cardio" }, { DBG_READER_VALUE , "reader" }, { 0, NULL } }; /* The card driver we use by default for PC/SC. */ #if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__) #define DEFAULT_PCSC_DRIVER "winscard.dll" #elif defined(__APPLE__) #define DEFAULT_PCSC_DRIVER "/System/Library/Frameworks/PCSC.framework/PCSC" #elif defined(__GLIBC__) #define DEFAULT_PCSC_DRIVER "libpcsclite.so.1" #else #define DEFAULT_PCSC_DRIVER "libpcsclite.so" #endif /* The timer tick used to check card removal. We poll every 500ms to let the user immediately know a status change. For a card reader with an interrupt endpoint, this timer is not used with the internal CCID driver. This is not too good for power saving but given that there is no easy way to block on card status changes it is the best we can do. For PC/SC we could in theory use an extra thread to wait for status changes but that requires a native thread because there is no way to make the underlying PC/SC card change function block using a Npth mechanism. Given that a native thread could only be used under W32 we don't do that at all. */ #define TIMERTICK_INTERVAL_SEC (0) #define TIMERTICK_INTERVAL_USEC (500000) /* Flag to indicate that a shutdown was requested. */ static int shutdown_pending; /* It is possible that we are currently running under setuid permissions */ static int maybe_setuid = 1; /* Flag telling whether we are running as a pipe server. */ static int pipe_server; /* Name of the communication socket */ static char *socket_name; /* Name of the redirected socket or NULL. */ static 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; #ifdef HAVE_W32_SYSTEM static HANDLE the_event; #else /* PID to notify update of usb devices. */ static pid_t main_thread_pid; #endif static char *create_socket_name (char *standard_name); static gnupg_fd_t create_server_socket (const char *name, char **r_redir_name, assuan_sock_nonce_t *nonce); static void *start_connection_thread (void *arg); static void handle_connections (int listen_fd); /* Pth wrapper function definitions. */ ASSUAN_SYSTEM_NPTH_IMPL; static int active_connections; 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 const char * my_strusage (int level) { static char *ver_gcry, *ver_ksba; const char *p; switch (level) { case 11: p = "@SCDAEMON@ (@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; case 21: if (!ver_ksba) ver_ksba = make_libversion ("libksba", ksba_check_version); p = ver_ksba; break; case 1: case 40: p = _("Usage: @SCDAEMON@ [options] (-h for help)"); break; case 41: p = _("Syntax: scdaemon [options] [command [args]]\n" "Smartcard daemon for @GNUPG@\n"); break; default: p = NULL; } return p; } static int tid_log_callback (unsigned long *rvalue) { int len = sizeof (*rvalue); npth_t thread; thread = npth_self (); if (sizeof (thread) < len) len = sizeof (thread); memcpy (rvalue, &thread, len); return 2; /* Use use hex representation. */ } /* 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_IPC_VALUE; else if (!strcmp (level, "advanced") || (numok && numlvl <= 5)) opt.debug = DBG_IPC_VALUE; else if (!strcmp (level, "expert") || (numok && numlvl <= 8)) opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_CARD_IO_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); scd_exit(2); } 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); } static void cleanup (void) { if (socket_name && *socket_name) { char *name; name = redir_socket_name? redir_socket_name : socket_name; gnupg_remove (name); *socket_name = 0; } } 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 int configlineno; int parse_debug = 0; const char *debug_level = NULL; int default_config =1; int greeting = 0; int nogreeting = 0; int multi_server = 0; int is_daemon = 0; int nodetach = 0; int csh_style = 0; char *logfile = NULL; int debug_wait = 0; int gpgconf_list = 0; const char *config_filename = NULL; int allow_coredump = 0; struct assuan_malloc_hooks malloc_hooks; int res; npth_t pipecon_handler; early_system_init (); 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 ("scdaemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID); /* Make sure that our subsystems are ready. */ i18n_init (); init_common_subsystems (&argc, &argv); ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); 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, NULL); setup_libgcrypt_logging (); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); disable_core_dumps (); /* Set default options. */ opt.allow_admin = 1; opt.pcsc_driver = DEFAULT_PCSC_DRIVER; shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) csh_style = 1; /* 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); } /* initialize the secure memory. */ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); maybe_setuid = 0; /* Now we are working under our real uid */ if (default_config) configname = make_filename (gnupg_homedir (), SCDAEMON_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 ); } 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) ) { switch (pargs.r_opt) { case aGPGConfList: gpgconf_list = 1; break; case aGPGConfTest: gpgconf_list = 2; break; case oQuiet: opt.quiet = 1; break; case oVerbose: opt.verbose++; break; case oBatch: opt.batch=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: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugAllowCoreDump: enable_core_dumps (); allow_coredump = 1; break; case oDebugCCIDDriver: #ifdef HAVE_LIBUSB ccid_set_debug_level (ccid_set_debug_level (-1)+1); #endif /*HAVE_LIBUSB*/ break; case oDebugLogTid: log_set_pid_suffix_cb (tid_log_callback); break; case oDebugAssuanLogCats: set_libassuan_log_cats (pargs.r.ret_ulong); 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: 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 oMultiServer: pipe_server = 1; multi_server = 1; break; case oDaemon: is_daemon = 1; break; case oReaderPort: opt.reader_port = pargs.r.ret_str; break; case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break; case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break; case oDisableCCID: opt.disable_ccid = 1; break; case oDisableOpenSC: break; case oDisablePinpad: opt.disable_pinpad = 1; break; case oAllowAdmin: /* Dummy because allow is now the default. */ break; case oDenyAdmin: opt.allow_admin = 0; break; case oCardTimeout: opt.card_timeout = pargs.r.ret_ulong; break; case oDisableApplication: add_to_strlist (&opt.disabled_applications, pargs.r.ret_str); break; case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break; default: pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR; break; } } if (configfp) { fclose( configfp ); configfp = NULL; /* Keep a copy of the config name for use by --gpgconf-list. */ config_filename = configname; configname = NULL; goto next_pass; } xfree (configname); configname = NULL; if (log_get_errorcount(0)) 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 log_info ("NOTE: this is a development version!\n"); #endif /* 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 (atexit (cleanup)) { log_error ("atexit failed\n"); cleanup (); exit (1); } set_debug (debug_level); if (initialize_module_command ()) { log_error ("initialization failed\n"); cleanup (); exit (1); } if (gpgconf_list == 2) scd_exit (0); if (gpgconf_list) { /* List options and default values in the GPG Conf format. */ char *filename = NULL; char *filename_esc; if (config_filename) filename = xstrdup (config_filename); else filename = make_filename (gnupg_homedir (), SCDAEMON_NAME EXTSEP_S "conf", NULL); filename_esc = percent_escape (filename, NULL); es_printf ("%s-%s.conf:%lu:\"%s\n", GPGCONF_NAME, SCDAEMON_NAME, GC_OPT_FLAG_DEFAULT, filename_esc); xfree (filename_esc); xfree (filename); es_printf ("verbose:%lu:\n" "quiet:%lu:\n" "debug-level:%lu:\"none:\n" "log-file:%lu:\n", GC_OPT_FLAG_NONE, GC_OPT_FLAG_NONE, GC_OPT_FLAG_DEFAULT, GC_OPT_FLAG_NONE ); es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("pcsc-driver:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER ); #ifdef HAVE_LIBUSB es_printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); #endif es_printf ("deny-admin:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE ); es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0); es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE ); scd_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); } 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 (pipe_server) { /* This is the simple pipe based server */ ctrl_t ctrl; npth_attr_t tattr; int fd = -1; #ifndef HAVE_W32_SYSTEM { struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); } #endif npth_init (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* If --debug-allow-core-dump has been given we also need to switch the working directory to a place where we can actually write. */ if (allow_coredump) { if (chdir("/tmp")) log_debug ("chdir to '/tmp' failed: %s\n", strerror (errno)); else log_debug ("changed working directory to '/tmp'\n"); } /* In multi server mode we need to listen on an additional socket. Create that socket now before starting the handler for the pipe connection. This allows that handler to send back the name of that socket. */ if (multi_server) { socket_name = create_socket_name (SCDAEMON_SOCK_NAME); fd = FD2INT(create_server_socket (socket_name, &redir_socket_name, &socket_nonce)); } res = npth_attr_init (&tattr); if (res) { log_error ("error allocating thread attributes: %s\n", strerror (res)); scd_exit (2); } npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); ctrl = xtrycalloc (1, sizeof *ctrl); if ( !ctrl ) { log_error ("error allocating connection control data: %s\n", strerror (errno) ); scd_exit (2); } ctrl->thread_startup.fd = GNUPG_INVALID_FD; res = npth_create (&pipecon_handler, &tattr, start_connection_thread, ctrl); if (res) { log_error ("error spawning pipe connection handler: %s\n", strerror (res) ); xfree (ctrl); scd_exit (2); } npth_setname_np (pipecon_handler, "pipe-connection"); npth_attr_destroy (&tattr); /* We run handle_connection to wait for the shutdown signal and to run the ticker stuff. */ handle_connections (fd); if (fd != -1) close (fd); } else if (!is_daemon) { log_info (_("please use the option '--daemon'" " to run the program in the background\n")); } else { /* Regular server mode */ int fd; #ifndef HAVE_W32_SYSTEM pid_t pid; int i; #endif /* Create the socket. */ socket_name = create_socket_name (SCDAEMON_SOCK_NAME); fd = FD2INT (create_server_socket (socket_name, &redir_socket_name, &socket_nonce)); fflush (NULL); #ifdef HAVE_W32_SYSTEM (void)csh_style; (void)nodetach; #else 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; close (fd); /* create the info string: :: */ if (gpgrt_asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1", socket_name, (ulong) pid) < 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 (argc) { /* run the program given on the commandline */ if (putenv (infostr)) { log_error ("failed to set environment: %s\n", strerror (errno) ); kill (pid, SIGTERM ); exit (1); } 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) { *strchr (infostr, '=') = ' '; es_printf ( "setenv %s;\n", infostr); } else { es_printf ( "%s; export SCDAEMON_INFO;\n", infostr); } xfree (infostr); exit (0); } /* NOTREACHED */ } /* end parent */ /* This is the child. */ npth_init (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* Detach from tty and put process into a new session. */ if (!nodetach ) { /* 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); } } { struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); } - if (chdir("/")) + if (gnupg_chdir (gnupg_daemon_rootdir ())) { - log_error ("chdir to / failed: %s\n", strerror (errno)); + log_error ("chdir to '%s' failed: %s\n", + gnupg_daemon_rootdir (), strerror (errno)); exit (1); } #endif /*!HAVE_W32_SYSTEM*/ handle_connections (fd); close (fd); } return 0; } void scd_exit (int rc) { apdu_prepare_exit (); #if 0 #warning no update_random_seed_file update_random_seed_file(); #endif #if 0 /* 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); } static void scd_init_default_ctrl (ctrl_t ctrl) { (void)ctrl; } static void scd_deinit_default_ctrl (ctrl_t ctrl) { if (!ctrl) return; xfree (ctrl->in_data.value); ctrl->in_data.value = NULL; ctrl->in_data.valuelen = 0; } /* Return the name of the socket to be used to connect to this process. If no socket is available, return NULL. */ const char * scd_get_socket_name () { if (socket_name && *socket_name) return socket_name; return NULL; } #ifndef HAVE_W32_SYSTEM static void handle_signal (int signo) { switch (signo) { case SIGHUP: log_info ("SIGHUP received - " "re-reading configuration and resetting cards\n"); /* reread_configuration (); */ 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 ()); */ app_dump_state (); break; case SIGUSR2: log_info ("SIGUSR2 received - no action defined\n"); break; case SIGCONT: /* Nothing. */ break; case SIGTERM: if (!shutdown_pending) log_info ("SIGTERM received - shutting down ...\n"); else log_info ("SIGTERM received - still %i running threads\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 (); scd_exit (0); } break; case SIGINT: log_info ("SIGINT received - immediate shutdown\n"); log_info( "%s %s stopped\n", strusage(11), strusage(13)); cleanup (); scd_exit (0); break; default: log_info ("signal %d received - no action defined\n", signo); } } #endif /*!HAVE_W32_SYSTEM*/ /* Create a name for the socket. We 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. Retunrs: Pointer to an allcoated string with the absolute name of the socket used. */ static char * create_socket_name (char *standard_name) { char *name; name = make_filename (gnupg_socketdir (), standard_name, NULL); if (strchr (name, PATHSEP_C)) { log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S); scd_exit (2); } return name; } /* Create a Unix domain socket with NAME. Returns the file descriptor or terminates the process in case of an error. If the socket has been redirected the name of the real socket is stored as a malloced string at R_REDIR_NAME. */ static gnupg_fd_t create_server_socket (const char *name, 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 == GNUPG_INVALID_FD) { log_error (_("can't create socket: %s\n"), strerror (errno)); scd_exit (2); } 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 ())); scd_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); if (rc == -1 && errno == EADDRINUSE) { 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) { log_error (_("error binding socket to '%s': %s\n"), unaddr->sun_path, gpg_strerror (gpg_error_from_syserror ())); assuan_sock_close (fd); scd_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"), gpg_strerror (gpg_error_from_syserror ())); assuan_sock_close (fd); scd_exit (2); } if (opt.verbose) log_info (_("listening on socket '%s'\n"), unaddr->sun_path); return fd; } /* This is the standard connection thread's main function. */ static void * start_connection_thread (void *arg) { ctrl_t ctrl = arg; if (ctrl->thread_startup.fd != GNUPG_INVALID_FD && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_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 NULL; } active_connections++; scd_init_default_ctrl (ctrl); if (opt.verbose) log_info (_("handler for fd %d started\n"), FD2INT(ctrl->thread_startup.fd)); /* If this is a pipe server, we request a shutdown if the command handler asked for it. With the next ticker event and given that no other connections are running the shutdown will then happen. */ if (scd_command_handler (ctrl, FD2INT(ctrl->thread_startup.fd)) && pipe_server) shutdown_pending = 1; if (opt.verbose) log_info (_("handler for fd %d terminated\n"), FD2INT (ctrl->thread_startup.fd)); scd_deinit_default_ctrl (ctrl); xfree (ctrl); if (--active_connections == 0) scd_kick_the_loop (); return NULL; } void scd_kick_the_loop (void) { int ret; /* Kick the select loop. */ #ifdef HAVE_W32_SYSTEM ret = SetEvent (the_event); if (ret == 0) log_error ("SetEvent for scd_kick_the_loop failed: %s\n", w32_strerror (-1)); #else ret = kill (main_thread_pid, SIGCONT); if (ret < 0) log_error ("SetEvent for scd_kick_the_loop failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); #endif } /* Connection handler loop. Wait for connection requests and spawn a thread after accepting a connection. LISTEN_FD is allowed to be -1 in which case this code will only do regular timeouts and handle signals. */ static void handle_connections (int listen_fd) { npth_attr_t tattr; struct sockaddr_un paddr; socklen_t plen; fd_set fdset, read_fdset; int nfd; int ret; int fd; struct timespec timeout; struct timespec *t; int saved_errno; #ifdef HAVE_W32_SYSTEM HANDLE events[2]; unsigned int events_set; #else int signo; #endif ret = npth_attr_init(&tattr); if (ret) { log_error ("npth_attr_init failed: %s\n", strerror (ret)); return; } npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); #ifdef HAVE_W32_SYSTEM { HANDLE h, h2; SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; events[0] = the_event = INVALID_HANDLE_VALUE; events[1] = INVALID_HANDLE_VALUE; h = CreateEvent (&sa, TRUE, FALSE, NULL); if (!h) log_error ("can't create scd event: %s\n", w32_strerror (-1) ); else if (!DuplicateHandle (GetCurrentProcess(), h, GetCurrentProcess(), &h2, EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) { log_error ("setting synchronize for scd_kick_the_loop failed: %s\n", w32_strerror (-1) ); CloseHandle (h); } else { CloseHandle (h); events[0] = the_event = h2; } } #else npth_sigev_init (); npth_sigev_add (SIGHUP); npth_sigev_add (SIGUSR1); npth_sigev_add (SIGUSR2); npth_sigev_add (SIGINT); npth_sigev_add (SIGCONT); npth_sigev_add (SIGTERM); npth_sigev_fini (); main_thread_pid = getpid (); #endif FD_ZERO (&fdset); nfd = 0; if (listen_fd != -1) { FD_SET (listen_fd, &fdset); nfd = listen_fd; } for (;;) { int periodical_check; if (shutdown_pending) { if (active_connections == 0) break; /* ready */ /* Do not accept anymore connections but wait for existing connections to terminate. We do this by clearing out all file descriptors to wait for, so that the select will be used to just wait on a signal or timeout event. */ FD_ZERO (&fdset); listen_fd = -1; } periodical_check = scd_update_reader_status_file (); timeout.tv_sec = TIMERTICK_INTERVAL_SEC; timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000; if (shutdown_pending || periodical_check) t = &timeout; else t = NULL; /* 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; #ifndef HAVE_W32_SYSTEM ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t, 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, t, events, &events_set); saved_errno = errno; if (events_set & 1) continue; #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) /* Timeout. Will be handled when calculating the next timeout. */ continue; if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) { ctrl_t ctrl; plen = sizeof paddr; fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen); if (fd == -1) { 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) ); close (fd); } else { char threadname[50]; npth_t thread; snprintf (threadname, sizeof threadname, "conn fd=%d", fd); ctrl->thread_startup.fd = INT2FD (fd); ret = npth_create (&thread, &tattr, start_connection_thread, ctrl); if (ret) { log_error ("error spawning connection handler: %s\n", strerror (ret)); xfree (ctrl); close (fd); } else npth_setname_np (thread, threadname); } } } #ifdef HAVE_W32_SYSTEM if (the_event != INVALID_HANDLE_VALUE) CloseHandle (the_event); #endif cleanup (); log_info (_("%s %s stopped\n"), strusage(11), strusage(13)); npth_attr_destroy (&tattr); } /* Return the number of active connections. */ int get_active_connection_count (void) { return active_connections; }