From 81494fd30d3815502247a721f50d9eadf86a73fa Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 21 Oct 2015 08:38:10 +0200 Subject: [PATCH] g13: First chunk of code to support dm-crypt. * g13/call-syshelp.c, g13/call-syshelp.h: New. * g13/g13-syshelp.c, g13/g13-syshelp.h: New. * g13/sh-cmd.c: New. * g13/sh-blockdev.c: New. * g13/sh-exectool.c: New. * g13/sh-dmcrypt.c: New. * g13/Makefile.am (sbin_PROGRAMS): Add g13-syshelp.c (g13_syshelp_SOURCES): New. (g13_syshelp_LDADD): New. * g13/g13.c (opts): Add option --type. (g13_deinit_default_ctrl): New. (main): Implement that option. Call g13_deinit_default_ctrl. * g13/g13.h (struct call_syshelp_s): New declaration. (server_control_s): Add field syshelp_local. * g13/keyblob.h (KEYBLOB_TAG_CREATED): New. (KEYBLOB_TAG_ALGOSTR): New. (KEYBLOB_TAG_HDRCOPY): New. * g13/backend.c (be_parse_conttype_name): New. (be_get_detached_name): Add CONTTYPE_DM_CRYPT. Signed-off-by: Werner Koch --- g13/Makefile.am | 17 ++ g13/backend.c | 35 +++ g13/backend.h | 3 +- g13/call-syshelp.c | 124 ++++++++ g13/call-syshelp.h | 26 ++ g13/g13-syshelp.c | 720 +++++++++++++++++++++++++++++++++++++++++++++ g13/g13-syshelp.h | 93 ++++++ g13/g13.c | 29 +- g13/g13.h | 7 +- g13/keyblob.h | 29 +- g13/sh-blockdev.c | 151 ++++++++++ g13/sh-cmd.c | 555 ++++++++++++++++++++++++++++++++++ g13/sh-dmcrypt.c | 406 +++++++++++++++++++++++++ g13/sh-exectool.c | 303 +++++++++++++++++++ 14 files changed, 2489 insertions(+), 9 deletions(-) create mode 100644 g13/call-syshelp.c create mode 100644 g13/call-syshelp.h create mode 100644 g13/g13-syshelp.c create mode 100644 g13/g13-syshelp.h create mode 100644 g13/sh-blockdev.c create mode 100644 g13/sh-cmd.c create mode 100644 g13/sh-dmcrypt.c create mode 100644 g13/sh-exectool.c diff --git a/g13/Makefile.am b/g13/Makefile.am index e17d099c3..a3cd1333d 100644 --- a/g13/Makefile.am +++ b/g13/Makefile.am @@ -21,6 +21,7 @@ EXTRA_DIST = ChangeLog-2011 bin_PROGRAMS = g13 +sbin_PROGRAMS = g13-syshelp AM_CPPFLAGS = -I$(top_srcdir)/common @@ -37,6 +38,7 @@ g13_SOURCES = \ create.c create.h \ mount.c mount.h \ mountinfo.c mountinfo.h \ + call-syshelp.c call-syshelp.h \ runner.c runner.h \ backend.c backend.h \ be-encfs.c be-encfs.h \ @@ -45,3 +47,18 @@ g13_SOURCES = \ g13_LDADD = $(libcommonpth) \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) + + +g13_syshelp_SOURCES = \ + g13-syshelp.c g13-syshelp.h \ + g13-common.c g13-common.h \ + keyblob.h \ + utils.c utils.h \ + sh-cmd.c \ + sh-exectool.c \ + sh-blockdev.c \ + sh-dmcrypt.c + +g13_syshelp_LDADD = $(libcommon) \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) diff --git a/g13/backend.c b/g13/backend.c index 7b08cd52a..52e1e0a34 100644 --- a/g13/backend.c +++ b/g13/backend.c @@ -41,6 +41,38 @@ no_such_backend (int conttype) } +/* Parse NAME and return the corresponding content type. If the name + is not known, a error message is printed and zero returned. If + NAME is NULL the supported backend types are listed and 0 is + returned. */ +int +be_parse_conttype_name (const char *name) +{ + static struct { const char *name; int conttype; } names[] = { + { "encfs", CONTTYPE_ENCFS }, + { "dm-crypt", CONTTYPE_DM_CRYPT } + }; + int i; + + if (!name) + { + log_info ("Known backend types:\n"); + for (i=0; i < DIM (names); i++) + log_info (" %s\n", names[i].name); + return 0; + } + + for (i=0; i < DIM (names); i++) + { + if (!strcmp (names[i].name, name)) + return names[i].conttype; + } + + log_error ("invalid backend type '%s' given\n", name); + return 0; +} + + /* Return true if CONTTYPE is supported by us. */ int be_is_supported_conttype (int conttype) @@ -75,6 +107,9 @@ be_get_detached_name (int conttype, const char *fname, case CONTTYPE_ENCFS: return be_encfs_get_detached_name (fname, r_name, r_isdir); + case CONTTYPE_DM_CRYPT: + return 0; + default: return no_such_backend (conttype); } diff --git a/g13/backend.h b/g13/backend.h index 20d296606..e570fc5a1 100644 --- a/g13/backend.h +++ b/g13/backend.h @@ -23,7 +23,8 @@ #include "../common/membuf.h" #include "utils.h" /* For tupledesc_t */ -int be_is_supported_conttype (int conttype); +int be_parse_conttype_name (const char *name); +int be_is_supported_conttype (int conttype); gpg_error_t be_get_detached_name (int conttype, const char *fname, char **r_name, int *r_isdir); gpg_error_t be_create_new_keys (int conttype, membuf_t *mb); diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c new file mode 100644 index 000000000..2086dd1db --- /dev/null +++ b/g13/call-syshelp.c @@ -0,0 +1,124 @@ +/* call-syshelp.c - Communication with g13-syshelp + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "g13.h" +#include +#include "i18n.h" +#include "utils.h" + +/* Local data for this module. A pointer to this is stored in the + CTRL object of each connection. */ +struct call_syshelp_s +{ + assuan_context_t assctx; /* The Assuan context for the current + g13-syshep connection. */ +}; + + +/* Fork off the syshelp tool if this has not already been done. */ +static gpg_error_t +start_syshelp (ctrl_t ctrl) +{ + gpg_error_t err; + assuan_context_t ctx; + assuan_fd_t no_close_list[3]; + int i; + + if (ctrl->syshelp_local->assctx) + return 0; /* Already set. */ + + if (opt.verbose) + log_info ("starting a new syshelp\n"); + + if (es_fflush (NULL)) + { + err = gpg_error_from_syserror (); + log_error ("error flushing pending output: %s\n", gpg_strerror (err)); + return err; + } + + i = 0; + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr)); + no_close_list[i] = ASSUAN_INVALID_FD; + + err = assuan_new (&ctx); + if (err) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (err)); + return err; + } + + /* Call userv to start g13-syshelp. This userv script needs tpo be + installed under the name "gnupg-g13-syshelp": + + if ( glob service-user root + ) + reset + suppress-args + execute /home/wk/b/gnupg/g13/g13-syshelp -v + else + error Nothing to do for this service-user + fi + quit + */ + { + const char *argv[3]; + + argv[0] = "userv"; + argv[1] = "gnupg-g13-syshelp"; + argv[2] = NULL; + + err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv, + no_close_list, NULL, NULL, 0); + } + if (err) + { + log_error ("can't connect to '%s' - : %s\n", + "g13-syshelp", gpg_strerror (err)); + log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n"); + assuan_release (ctx); + return err; + } + ctrl->syshelp_local->assctx = ctx; + + if (DBG_IPC) + log_debug ("connection to g13-syshelp established\n"); + + return 0; +} + + +/* Release local resources associated with CTRL. */ +void +call_syshelp_release (ctrl_t ctrl) +{ + assuan_release (ctrl->syshelp_local->assctx); + ctrl->syshelp_local->assctx = NULL; +} diff --git a/g13/call-syshelp.h b/g13/call-syshelp.h new file mode 100644 index 000000000..c78d53b5c --- /dev/null +++ b/g13/call-syshelp.h @@ -0,0 +1,26 @@ +/* call-syshelp.h - Communication with g13-syshelp + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef GNUPG_G13_CALL_SYSHELP_H +#define GNUPG_G13_CALL_SYSHELP_H + +void call_syshelp_release (ctrl_t ctrl); + + +#endif /*GNUPG_G13_CALL_SYSHELP_H*/ diff --git a/g13/g13-syshelp.c b/g13/g13-syshelp.c new file mode 100644 index 000000000..c09a5e917 --- /dev/null +++ b/g13/g13-syshelp.c @@ -0,0 +1,720 @@ +/* g13-syshelp.c - Helper for disk key management with GnuPG + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PWD_H +# include +#endif +#include + +#include "g13-syshelp.h" + +#include +#include + +#include "i18n.h" +#include "sysutils.h" +#include "asshelp.h" +#include "../common/init.h" +#include "keyblob.h" + + +enum cmd_and_opt_values { + aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + oRecipient = 'r', + + aGPGConfList = 500, + + oDebug, + oDebugLevel, + oDebugAll, + oDebugNone, + oDebugWait, + oDebugAllowCoreDump, + oLogFile, + oNoLogFile, + oAuditLog, + + oOutput, + + oAgentProgram, + oGpgProgram, + oType, + + oDisplay, + oTTYname, + oTTYtype, + oLCctype, + oLCmessages, + oXauthority, + + oStatusFD, + oLoggerFD, + + oNoVerbose, + oNoSecmemWarn, + oHomedir, + oDryRun, + oNoDetach, + + oNoRandomSeedFile, + oFakedSystemTime + }; + + +static ARGPARSE_OPTS opts[] = { + + ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), + + ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), + ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), + + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_s (oDebugLevel, "debug-level", + N_("|LEVEL|set the debugging level to LEVEL")), + ARGPARSE_s_n (oDebugAll, "debug-all", "@"), + ARGPARSE_s_n (oDebugNone, "debug-none", "@"), + ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), + ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), + + ARGPARSE_end () +}; + + +/* The list of supported debug flags. */ +static struct debug_flags_s debug_flags [] = + { + { DBG_MOUNT_VALUE , "mount" }, + { DBG_CRYPTO_VALUE , "crypto" }, + { DBG_MEMORY_VALUE , "memory" }, + { DBG_MEMSTAT_VALUE, "memstat" }, + { DBG_IPC_VALUE , "ipc" }, + { 0, NULL } + }; + + +/* The timer tick interval used by the idle task. */ +#define TIMERTICK_INTERVAL_SEC (1) + +/* It is possible that we are currently running under setuid permissions. */ +static int maybe_setuid = 1; + +/* Helper to implement --debug-level and --debug. */ +static const char *debug_level; +static unsigned int debug_value; + + +/* Local prototypes. */ +static void g13_syshelp_deinit_default_ctrl (ctrl_t ctrl); +static void release_tab_items (tab_item_t tab); +static tab_item_t parse_g13tab (const char *username); + + + +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 11: p = "@G13@-syshelp (@GNUPG@)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); + break; + case 1: + case 40: p = _("Usage: @G13@-syshelp [options] [files] (-h for help)"); + break; + case 41: + p = _("Syntax: @G13@-syshelp [options] [files]\n" + "Helper to perform root-only tasks for g13\n"); + break; + + case 31: p = "\nHome: "; break; + case 32: p = opt.homedir; break; + + default: p = NULL; break; + } + return p; +} + + +/* Setup the debugging. With a DEBUG_LEVEL of NULL only the active + debug flags are propagated to the subsystems. With DEBUG_LEVEL + set, a specific set of debug flags is set; and individual debugging + flags will be added on top. */ +static void +set_debug (void) +{ + int numok = (debug_level && digitp (debug_level)); + int numlvl = numok? atoi (debug_level) : 0; + + if (!debug_level) + ; + else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) + opt.debug = 0; + else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) + opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE; + else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) + opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE; + else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) + opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE); + else if (!strcmp (debug_level, "guru") || numok) + { + opt.debug = ~0; + /* if (numok) */ + /* opt.debug &= ~(DBG_HASHING_VALUE); */ + } + else + { + log_error (_("invalid debug-level '%s' given\n"), debug_level); + g13_exit(2); + } + + opt.debug |= debug_value; + + if (opt.debug && !opt.verbose) + opt.verbose = 1; + if (opt.debug) + opt.quiet = 0; + + if (opt.debug & DBG_CRYPTO_VALUE ) + gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + + if (opt.debug) + parse_debug_flag (NULL, &opt.debug, debug_flags); +} + + +int +main ( int argc, char **argv) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + gpg_error_t err = 0; + /* const char *fname; */ + int may_coredump; + FILE *configfp = NULL; + char *configname = NULL; + unsigned configlineno; + int parse_debug = 0; + int no_more_options = 0; + int default_config =1; + char *logfile = NULL; + /* int debug_wait = 0; */ + int use_random_seed = 1; + /* int nodetach = 0; */ + /* int nokeysetup = 0; */ + struct server_control_s ctrl; + + /*mtrace();*/ + + early_system_init (); + gnupg_reopen_std (G13_NAME "-syshelp"); + set_strusage (my_strusage); + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + + log_set_prefix (G13_NAME "-syshelp", 1); + + /* Make sure that our subsystems are ready. */ + i18n_init (); + init_common_subsystems (&argc, &argv); + + /* Check that the Libgcrypt is suitable. */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) ) + log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt", + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) ); + + /* Take extra care of the random pool. */ + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + + may_coredump = disable_core_dumps (); + + g13_init_signals (); + + dotlock_create (NULL, 0); /* Register locking cleanup. */ + + opt.session_env = session_env_new (); + if (!opt.session_env) + log_fatal ("error allocating session environment block: %s\n", + strerror (errno)); + + opt.homedir = default_homedir (); + + /* First check whether we have a debug option on the commandline. */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); + while (arg_parse( &pargs, opts)) + { + if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) + parse_debug++; + } + + /* Initialize the secure memory. */ + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + maybe_setuid = 0; + + /* + Now we are now working under our real uid + */ + + /* Setup malloc hooks. */ + { + struct assuan_malloc_hooks malloc_hooks; + + malloc_hooks.malloc = gcry_malloc; + malloc_hooks.realloc = gcry_realloc; + malloc_hooks.free = gcry_free; + assuan_set_malloc_hooks (&malloc_hooks); + } + + /* Prepare libassuan. */ + assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); + /*assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);*/ + setup_libassuan_logging (&opt.debug); + + /* Setup a default control structure for command line mode. */ + memset (&ctrl, 0, sizeof ctrl); + g13_syshelp_init_default_ctrl (&ctrl); + ctrl.no_server = 1; + ctrl.status_fd = -1; /* No status output. */ + + if (default_config ) + configname = make_filename (gnupg_sysconfdir (), + G13_NAME"-syshelp.conf", NULL); + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags = 1; /* Do not remove the args. */ + + next_pass: + if (configname) + { + configlineno = 0; + configfp = fopen (configname, "r"); + if (!configfp) + { + if (default_config) + { + if (parse_debug) + log_info (_("NOTE: no default option file '%s'\n"), configname); + } + else + { + log_error (_("option file '%s': %s\n"), + configname, strerror(errno)); + g13_exit(2); + } + xfree (configname); + configname = NULL; + } + if (parse_debug && configname) + log_info (_("reading options from '%s'\n"), configname); + default_config = 0; + } + + while (!no_more_options + && optfile_parse (configfp, configname, &configlineno, &pargs, opts)) + { + switch (pargs.r_opt) + { + case oQuiet: opt.quiet = 1; break; + + case oDryRun: opt.dry_run = 1; break; + + case oVerbose: + opt.verbose++; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + case oNoVerbose: + opt.verbose = 0; + gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); + break; + + case oLogFile: logfile = pargs.r.ret_str; break; + case oNoLogFile: logfile = NULL; break; + + case oNoDetach: /*nodetach = 1; */break; + + case oDebug: + if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags)) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + case oDebugAll: debug_value = ~0; break; + case oDebugNone: debug_value = 0; break; + case oDebugLevel: debug_level = pargs.r.ret_str; break; + case oDebugWait: /*debug_wait = pargs.r.ret_int; */break; + case oDebugAllowCoreDump: + may_coredump = enable_core_dumps (); + break; + + case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; + case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; + + case oHomedir: opt.homedir = pargs.r.ret_str; break; + + case oFakedSystemTime: + { + time_t faked_time = isotime2epoch (pargs.r.ret_str); + if (faked_time == (time_t)(-1)) + faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); + gnupg_set_time (faked_time, 0); + } + break; + + case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break; + + case oNoRandomSeedFile: use_random_seed = 0; break; + + default: + pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR; + break; + } + } + + if (configfp) + { + fclose (configfp); + configfp = NULL; + /* Keep a copy of the config filename. */ + opt.config_filename = configname; + configname = NULL; + goto next_pass; + } + xfree (configname); + configname = NULL; + + if (!opt.config_filename) + opt.config_filename = make_filename (opt.homedir, G13_NAME".conf", NULL); + + if (log_get_errorcount(0)) + g13_exit(2); + + /* Now that we have the options parsed we need to update the default + control structure. */ + g13_syshelp_init_default_ctrl (&ctrl); + + if (may_coredump && !opt.quiet) + log_info (_("WARNING: program may create a core file!\n")); + + if (logfile) + { + log_set_file (logfile); + log_set_prefix (NULL, 1|2|4); + } + + if (gnupg_faked_time_p ()) + { + gnupg_isotime_t tbuf; + + log_info (_("WARNING: running with faked system time: ")); + gnupg_get_isotime (tbuf); + dump_isotime (tbuf); + log_printf ("\n"); + } + + /* Print any pending secure memory warnings. */ + gcry_control (GCRYCTL_RESUME_SECMEM_WARN); + + /* Setup the debug flags for all subsystems. */ + set_debug (); + + /* Install a regular exit handler to make real sure that the secure + memory gets wiped out. */ + g13_install_emergency_cleanup (); + + /* Terminate if we found any error until now. */ + if (log_get_errorcount(0)) + g13_exit (2); + + /* Set the standard GnuPG random seed file. */ + if (use_random_seed) + { + char *p = make_filename (opt.homedir, "random_seed", NULL); + gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); + xfree(p); + } + + /* Get the UID of the caller. */ +#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) + { + const char *uidstr; + struct passwd *pwd = NULL; + + uidstr = getenv ("USERV_UID"); + + /* Print a quick note if we are not started via userv. */ + if (!uidstr) + { + if (getuid ()) + { + log_info ("WARNING: Not started via userv\n"); + ctrl.fail_all_cmds = 1; + } + ctrl.client.uid = getuid (); + } + else + { + unsigned long myuid; + + errno = 0; + myuid = strtoul (uidstr, NULL, 10); + if (myuid == ULONG_MAX && errno) + { + log_info ("WARNING: Started via broken userv: %s\n", + strerror (errno)); + ctrl.fail_all_cmds = 1; + ctrl.client.uid = getuid (); + } + else + ctrl.client.uid = (uid_t)myuid; + } + + pwd = getpwuid (ctrl.client.uid); + if (!pwd || !*pwd->pw_name) + { + log_info ("WARNING: Name for UID not found: %s\n", strerror (errno)); + ctrl.fail_all_cmds = 1; + ctrl.client.uname = xstrdup ("?"); + } + else + ctrl.client.uname = xstrdup (pwd->pw_name); + } +#else /*!HAVE_PWD_H || !HAVE_GETPWUID*/ + log_info ("WARNING: System does not support required syscalls\n"); + ctrl.fail_all_cmds = 1; + ctrl.client.uid = getuid (); + ctrl.client.uname = xstrdup ("?"); +#endif /*!HAVE_PWD_H || !HAVE_GETPWUID*/ + + /* Read the table entries for this user. */ + if (!ctrl.fail_all_cmds + && !(ctrl.client.tab = parse_g13tab (ctrl.client.uname))) + ctrl.fail_all_cmds = 1; + + /* Start the server. */ + err = syshelp_server (&ctrl); + if (err) + log_error ("server exited with error: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + + /* Cleanup. */ + g13_syshelp_deinit_default_ctrl (&ctrl); + g13_exit (0); + return 8; /*NOTREACHED*/ +} + + +/* Store defaults into the per-connection CTRL object. */ +void +g13_syshelp_init_default_ctrl (ctrl_t ctrl) +{ + ctrl->conttype = CONTTYPE_DM_CRYPT; +} + +/* Release all resources allocated by default in the CTRl object. */ +static void +g13_syshelp_deinit_default_ctrl (ctrl_t ctrl) +{ + xfree (ctrl->client.uname); + release_tab_items (ctrl->client.tab); +} + + +/* Release the list of g13tab itejms at TAB. */ +static void +release_tab_items (tab_item_t tab) +{ + while (tab) + { + tab_item_t next = tab->next; + xfree (tab->mountpoint); + xfree (tab); + tab = next; + } +} + + +/* Parse the /etc/gnupg/g13tab for user USERNAME. Return a table for + the user on success. Return NULL on error and print + diagnostics. */ +static tab_item_t +parse_g13tab (const char *username) +{ + gpg_error_t err; + int c, n; + char line[512]; + char *p; + char *fname; + estream_t fp; + int lnr; + char **words = NULL; + tab_item_t table = NULL; + tab_item_t *tabletail, ti; + + fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL); + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err)); + goto leave; + } + + tabletail = &table; + err = 0; + lnr = 0; + while (es_fgets (line, DIM(line)-1, fp)) + { + lnr++; + n = strlen (line); + if (!n || line[n-1] != '\n') + { + /* Eat until end of line. */ + while ((c=es_getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + log_error (_("file '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + continue; + } + line[--n] = 0; /* Chop the LF. */ + if (n && line[n-1] == '\r') + line[--n] = 0; /* Chop an optional CR. */ + + /* Allow for empty lines and spaces */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + + /* Parse the line. The format is + * [