1 /* g13-syshelp.c - Helper for disk key management with GnuPG
2 * Copyright (C) 2015 Werner Koch
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
34 #include "g13-syshelp.h"
42 #include "../common/init.h"
46 enum cmd_and_opt_values {
91 static ARGPARSE_OPTS opts[] = {
93 ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
95 ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
96 ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
98 ARGPARSE_s_s (oDebug, "debug", "@"),
99 ARGPARSE_s_s (oDebugLevel, "debug-level",
100 N_("|LEVEL|set the debugging level to LEVEL")),
101 ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
102 ARGPARSE_s_n (oDebugNone, "debug-none", "@"),
103 ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
104 ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
110 /* The list of supported debug flags. */
111 static struct debug_flags_s debug_flags [] =
113 { DBG_MOUNT_VALUE , "mount" },
114 { DBG_CRYPTO_VALUE , "crypto" },
115 { DBG_MEMORY_VALUE , "memory" },
116 { DBG_MEMSTAT_VALUE, "memstat" },
117 { DBG_IPC_VALUE , "ipc" },
122 /* The timer tick interval used by the idle task. */
123 #define TIMERTICK_INTERVAL_SEC (1)
125 /* It is possible that we are currently running under setuid permissions. */
126 static int maybe_setuid = 1;
128 /* Helper to implement --debug-level and --debug. */
129 static const char *debug_level;
130 static unsigned int debug_value;
133 /* Local prototypes. */
134 static void g13_syshelp_deinit_default_ctrl (ctrl_t ctrl);
135 static void release_tab_items (tab_item_t tab);
136 static tab_item_t parse_g13tab (const char *username);
141 my_strusage( int level )
147 case 11: p = "@G13@-syshelp (@GNUPG@)";
149 case 13: p = VERSION; break;
150 case 17: p = PRINTABLE_OS_NAME; break;
151 case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
154 case 40: p = _("Usage: @G13@-syshelp [options] [files] (-h for help)");
157 p = _("Syntax: @G13@-syshelp [options] [files]\n"
158 "Helper to perform root-only tasks for g13\n");
161 case 31: p = "\nHome: "; break;
162 case 32: p = gnupg_homedir (); break;
164 default: p = NULL; break;
170 /* Setup the debugging. With a DEBUG_LEVEL of NULL only the active
171 debug flags are propagated to the subsystems. With DEBUG_LEVEL
172 set, a specific set of debug flags is set; and individual debugging
173 flags will be added on top. */
177 int numok = (debug_level && digitp (debug_level));
178 int numlvl = numok? atoi (debug_level) : 0;
182 else if (!strcmp (debug_level, "none") || (numok && numlvl < 1))
184 else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2))
185 opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
186 else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5))
187 opt.debug = DBG_IPC_VALUE|DBG_MOUNT_VALUE;
188 else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8))
189 opt.debug = (DBG_IPC_VALUE|DBG_MOUNT_VALUE|DBG_CRYPTO_VALUE);
190 else if (!strcmp (debug_level, "guru") || numok)
194 /* opt.debug &= ~(DBG_HASHING_VALUE); */
198 log_error (_("invalid debug-level '%s' given\n"), debug_level);
202 opt.debug |= debug_value;
204 if (opt.debug && !opt.verbose)
209 if (opt.debug & DBG_CRYPTO_VALUE )
210 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
211 gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
214 parse_debug_flag (NULL, &opt.debug, debug_flags);
219 main ( int argc, char **argv)
225 /* const char *fname; */
227 FILE *configfp = NULL;
228 char *configname = NULL;
229 unsigned configlineno;
231 int no_more_options = 0;
232 int default_config =1;
233 char *logfile = NULL;
234 /* int debug_wait = 0; */
235 int use_random_seed = 1;
236 /* int nodetach = 0; */
237 /* int nokeysetup = 0; */
238 struct server_control_s ctrl;
242 early_system_init ();
243 gnupg_reopen_std (G13_NAME "-syshelp");
244 set_strusage (my_strusage);
245 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
247 log_set_prefix (G13_NAME "-syshelp", GPGRT_LOG_WITH_PREFIX);
249 /* Make sure that our subsystems are ready. */
251 init_common_subsystems (&argc, &argv);
253 /* Take extra care of the random pool. */
254 gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
256 may_coredump = disable_core_dumps ();
260 dotlock_create (NULL, 0); /* Register locking cleanup. */
262 opt.session_env = session_env_new ();
263 if (!opt.session_env)
264 log_fatal ("error allocating session environment block: %s\n",
267 /* Fixme: We enable verbose mode here because there is currently no
268 way to do this when starting g13-syshelp. To fix that we should
269 add a g13-syshelp.conf file in /etc/gnupg. */
272 /* First check whether we have a debug option on the commandline. */
277 pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
278 while (arg_parse( &pargs, opts))
280 if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
284 /* Initialize the secure memory. */
285 gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
289 Now we are now working under our real uid
292 /* Setup malloc hooks. */
294 struct assuan_malloc_hooks malloc_hooks;
296 malloc_hooks.malloc = gcry_malloc;
297 malloc_hooks.realloc = gcry_realloc;
298 malloc_hooks.free = gcry_free;
299 assuan_set_malloc_hooks (&malloc_hooks);
302 /* Prepare libassuan. */
303 assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
304 /*assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);*/
305 setup_libassuan_logging (&opt.debug, NULL);
307 /* Setup a default control structure for command line mode. */
308 memset (&ctrl, 0, sizeof ctrl);
309 g13_syshelp_init_default_ctrl (&ctrl);
311 ctrl.status_fd = -1; /* No status output. */
314 configname = make_filename (gnupg_sysconfdir (),
315 G13_NAME"-syshelp.conf", NULL);
321 pargs.flags = 1; /* Do not remove the args. */
327 configfp = fopen (configname, "r");
333 log_info (_("NOTE: no default option file '%s'\n"), configname);
337 log_error (_("option file '%s': %s\n"),
338 configname, strerror(errno));
344 if (parse_debug && configname)
345 log_info (_("reading options from '%s'\n"), configname);
349 while (!no_more_options
350 && optfile_parse (configfp, configname, &configlineno, &pargs, opts))
354 case oQuiet: opt.quiet = 1; break;
356 case oDryRun: opt.dry_run = 1; break;
360 gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
364 gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
367 case oLogFile: logfile = pargs.r.ret_str; break;
368 case oNoLogFile: logfile = NULL; break;
370 case oNoDetach: /*nodetach = 1; */break;
373 if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
375 pargs.r_opt = ARGPARSE_INVALID_ARG;
376 pargs.err = ARGPARSE_PRINT_ERROR;
379 case oDebugAll: debug_value = ~0; break;
380 case oDebugNone: debug_value = 0; break;
381 case oDebugLevel: debug_level = pargs.r.ret_str; break;
382 case oDebugWait: /*debug_wait = pargs.r.ret_int; */break;
383 case oDebugAllowCoreDump:
384 may_coredump = enable_core_dumps ();
387 case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break;
388 case oLoggerFD: log_set_fd (pargs.r.ret_int ); break;
390 case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
392 case oFakedSystemTime:
394 time_t faked_time = isotime2epoch (pargs.r.ret_str);
395 if (faked_time == (time_t)(-1))
396 faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
397 gnupg_set_time (faked_time, 0);
401 case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break;
403 case oNoRandomSeedFile: use_random_seed = 0; break;
406 pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
415 /* Keep a copy of the config filename. */
416 opt.config_filename = configname;
423 if (!opt.config_filename)
424 opt.config_filename = make_filename (gnupg_homedir (),
425 G13_NAME".conf", NULL);
427 if (log_get_errorcount(0))
430 /* Now that we have the options parsed we need to update the default
431 control structure. */
432 g13_syshelp_init_default_ctrl (&ctrl);
434 if (may_coredump && !opt.quiet)
435 log_info (_("WARNING: program may create a core file!\n"));
439 log_set_file (logfile);
440 log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
443 if (gnupg_faked_time_p ())
445 gnupg_isotime_t tbuf;
447 log_info (_("WARNING: running with faked system time: "));
448 gnupg_get_isotime (tbuf);
453 /* Print any pending secure memory warnings. */
454 gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
456 /* Setup the debug flags for all subsystems. */
459 /* Install a regular exit handler to make real sure that the secure
460 memory gets wiped out. */
461 g13_install_emergency_cleanup ();
463 /* Terminate if we found any error until now. */
464 if (log_get_errorcount(0))
467 /* Set the standard GnuPG random seed file. */
470 char *p = make_filename (gnupg_homedir (), "random_seed", NULL);
471 gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
475 /* Get the UID of the caller. */
476 #if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
479 struct passwd *pwd = NULL;
481 uidstr = getenv ("USERV_UID");
483 /* Print a quick note if we are not started via userv. */
488 log_info ("WARNING: Not started via userv\n");
489 ctrl.fail_all_cmds = 1;
491 ctrl.client.uid = getuid ();
498 myuid = strtoul (uidstr, NULL, 10);
499 if (myuid == ULONG_MAX && errno)
501 log_info ("WARNING: Started via broken userv: %s\n",
503 ctrl.fail_all_cmds = 1;
504 ctrl.client.uid = getuid ();
507 ctrl.client.uid = (uid_t)myuid;
510 pwd = getpwuid (ctrl.client.uid);
511 if (!pwd || !*pwd->pw_name)
513 log_info ("WARNING: Name for UID not found: %s\n", strerror (errno));
514 ctrl.fail_all_cmds = 1;
515 ctrl.client.uname = xstrdup ("?");
518 ctrl.client.uname = xstrdup (pwd->pw_name);
520 /* Check that the user name does not contain a directory
522 if (strchr (ctrl.client.uname, '/'))
524 log_info ("WARNING: Invalid user name passed\n");
525 ctrl.fail_all_cmds = 1;
528 #else /*!HAVE_PWD_H || !HAVE_GETPWUID*/
529 log_info ("WARNING: System does not support required syscalls\n");
530 ctrl.fail_all_cmds = 1;
531 ctrl.client.uid = getuid ();
532 ctrl.client.uname = xstrdup ("?");
533 #endif /*!HAVE_PWD_H || !HAVE_GETPWUID*/
535 /* Read the table entries for this user. */
536 if (!ctrl.fail_all_cmds
537 && !(ctrl.client.tab = parse_g13tab (ctrl.client.uname)))
538 ctrl.fail_all_cmds = 1;
540 /* Start the server. */
541 err = syshelp_server (&ctrl);
543 log_error ("server exited with error: %s <%s>\n",
544 gpg_strerror (err), gpg_strsource (err));
547 g13_syshelp_deinit_default_ctrl (&ctrl);
549 return 8; /*NOTREACHED*/
553 /* Store defaults into the per-connection CTRL object. */
555 g13_syshelp_init_default_ctrl (ctrl_t ctrl)
557 ctrl->conttype = CONTTYPE_DM_CRYPT;
560 /* Release all resources allocated by default in the CTRl object. */
562 g13_syshelp_deinit_default_ctrl (ctrl_t ctrl)
564 xfree (ctrl->client.uname);
565 release_tab_items (ctrl->client.tab);
569 /* Release the list of g13tab itejms at TAB. */
571 release_tab_items (tab_item_t tab)
575 tab_item_t next = tab->next;
576 xfree (tab->mountpoint);
584 g13_syshelp_i_know_what_i_am_doing (void)
586 const char * const yesfile = "Yes-g13-I-know-what-I-am-doing";
589 fname = make_filename (gnupg_sysconfdir (), yesfile, NULL);
590 if (access (fname, F_OK))
592 log_info ("*******************************************************\n");
593 log_info ("* The G13 support for DM-Crypt is new and not matured.\n");
594 log_info ("* Bugs or improper use may delete all your disks!\n");
595 log_info ("* To confirm that you are ware of this risk, create\n");
596 log_info ("* the file '%s'.\n", fname);
597 log_info ("*******************************************************\n");
604 /* Parse the /etc/gnupg/g13tab for user USERNAME. Return a table for
605 the user on success. Return NULL on error and print
608 parse_g13tab (const char *username)
618 tab_item_t table = NULL;
619 tab_item_t *tabletail, ti;
621 fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL);
622 fp = es_fopen (fname, "r");
625 err = gpg_error_from_syserror ();
626 log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err));
633 while (es_fgets (line, DIM(line)-1, fp))
637 if (!n || line[n-1] != '\n')
639 /* Eat until end of line. */
640 while ((c=es_getc (fp)) != EOF && c != '\n')
642 err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
643 : GPG_ERR_INCOMPLETE_LINE);
644 log_error (_("file '%s', line %d: %s\n"),
645 fname, lnr, gpg_strerror (err));
648 line[--n] = 0; /* Chop the LF. */
649 if (n && line[n-1] == '\r')
650 line[--n] = 0; /* Chop an optional CR. */
652 /* Allow for empty lines and spaces */
653 for (p=line; spacep (p); p++)
655 if (!*p || *p == '#')
658 /* Parse the line. The format is
659 * <username> <blockdev> [<label>|"-" [<mountpoint>]]
662 words = strtokenize (p, " \t");
665 err = gpg_error_from_syserror ();
668 if (!words[0] || !words[1])
670 log_error (_("file '%s', line %d: %s\n"),
671 fname, lnr, gpg_strerror (GPG_ERR_SYNTAX));
674 if (!(*words[1] == '/'
675 || !strncmp (words[1], "PARTUUID=", 9)
676 || !strncmp (words[1], "partuuid=", 9)))
678 log_error (_("file '%s', line %d: %s\n"),
679 fname, lnr, "Invalid block device syntax");
684 if (strlen (words[2]) > 16 || strchr (words[2], '/'))
686 log_error (_("file '%s', line %d: %s\n"),
687 fname, lnr, "Label too long or invalid syntax");
691 if (words[3] && *words[3] != '/')
693 log_error (_("file '%s', line %d: %s\n"),
694 fname, lnr, "Invalid mountpoint syntax");
698 if (strcmp (words[0], username))
699 continue; /* Skip entries for other usernames! */
701 ti = xtrymalloc (sizeof *ti + strlen (words[1]));
704 err = gpg_error_from_syserror ();
709 ti->mountpoint = NULL;
710 strcpy (ti->blockdev, *words[1]=='/'? words[1] : words[1]+9);
713 if (strcmp (words[2], "-")
714 && !(ti->label = xtrystrdup (words[2])))
716 err = gpg_error_from_syserror ();
720 if (words[3] && !(ti->mountpoint = xtrystrdup (words[3])))
722 err = gpg_error_from_syserror ();
729 tabletail = &ti->next;
732 if (!err && !es_feof (fp))
733 err = gpg_error_from_syserror ();
735 log_error (_("error reading '%s', line %d: %s\n"),
736 fname, lnr, gpg_strerror (err));
744 release_tab_items (table);