mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-21 10:09:57 +01:00
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 <wk@gnupg.org>
This commit is contained in:
parent
b2b079cb2f
commit
286a89da7c
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
124
g13/call-syshelp.c
Normal file
124
g13/call-syshelp.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <npth.h>
|
||||
|
||||
#include "g13.h"
|
||||
#include <assuan.h>
|
||||
#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;
|
||||
}
|
26
g13/call-syshelp.h
Normal file
26
g13/call-syshelp.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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*/
|
720
g13/g13-syshelp.c
Normal file
720
g13/g13-syshelp.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#ifdef HAVE_PWD_H
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "g13-syshelp.h"
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include <assuan.h>
|
||||
|
||||
#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
|
||||
* <username> <blockdev> [<label>|"-" [<mountpoint>]]
|
||||
*/
|
||||
xfree (words);
|
||||
words = strtokenize (p, " \t");
|
||||
if (!words)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
break;
|
||||
}
|
||||
if (!words[0] || !words[1])
|
||||
{
|
||||
log_error (_("file '%s', line %d: %s\n"),
|
||||
fname, lnr, gpg_strerror (GPG_ERR_SYNTAX));
|
||||
continue;
|
||||
}
|
||||
if (!(*words[1] == '/'
|
||||
|| !strncmp (words[1], "PARTUUID=", 9)
|
||||
|| !strncmp (words[1], "partuuid=", 9)))
|
||||
{
|
||||
log_error (_("file '%s', line %d: %s\n"),
|
||||
fname, lnr, "Invalid block device syntax");
|
||||
continue;
|
||||
}
|
||||
if (words[2])
|
||||
{
|
||||
if (strlen (words[2]) > 16 || strchr (words[2], '/'))
|
||||
{
|
||||
log_error (_("file '%s', line %d: %s\n"),
|
||||
fname, lnr, "Label too long or invalid syntax");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (words[3] && *words[3] != '/')
|
||||
{
|
||||
log_error (_("file '%s', line %d: %s\n"),
|
||||
fname, lnr, "Invalid mountpoint syntax");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (strcmp (words[0], username))
|
||||
continue; /* Skip entries for other usernames! */
|
||||
|
||||
ti = xtrymalloc (sizeof *ti + strlen (words[1]));
|
||||
if (!ti)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
break;
|
||||
}
|
||||
ti->next = NULL;
|
||||
ti->label = NULL;
|
||||
ti->mountpoint = NULL;
|
||||
strcpy (ti->blockdev, *words[1]=='/'? words[1] : words[1]+9);
|
||||
if (words[2])
|
||||
{
|
||||
if (strcmp (words[2], "-")
|
||||
&& !(ti->label = xtrystrdup (words[2])))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
xfree (ti);
|
||||
break;
|
||||
}
|
||||
if (words[3] && !(ti->mountpoint = xtrystrdup (words[3])))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
xfree (ti->label);
|
||||
xfree (ti);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*tabletail = ti;
|
||||
tabletail = &ti->next;
|
||||
}
|
||||
|
||||
if (!err && !es_feof (fp))
|
||||
err = gpg_error_from_syserror ();
|
||||
if (err)
|
||||
log_error (_("error reading '%s', line %d: %s\n"),
|
||||
fname, lnr, gpg_strerror (err));
|
||||
|
||||
leave:
|
||||
xfree (words);
|
||||
es_fclose (fp);
|
||||
xfree (fname);
|
||||
if (err)
|
||||
{
|
||||
release_tab_items (table);
|
||||
return NULL;
|
||||
}
|
||||
return table;
|
||||
}
|
93
g13/g13-syshelp.h
Normal file
93
g13/g13-syshelp.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* g130syshelp.h - Global definitions for 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef G13_SYSHELP_H
|
||||
#define G13_SYSHELP_H
|
||||
|
||||
#include "g13-common.h"
|
||||
|
||||
|
||||
struct tab_item_s;
|
||||
typedef struct tab_item_s *tab_item_t;
|
||||
|
||||
struct tab_item_s
|
||||
{
|
||||
tab_item_t next;
|
||||
char *label; /* Optional malloced label for that entry. */
|
||||
char *mountpoint; /* NULL or a malloced mountpoint. */
|
||||
char blockdev[1]; /* String with the name of the block device. If
|
||||
it starts with a slash is is a regular device
|
||||
name, otherwise it is a PARTUUID. */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Forward declaration for an object defined in g13-sh-cmd.c. */
|
||||
struct server_local_s;
|
||||
|
||||
/* Session control object. This object is passed down to most
|
||||
functions. The default values for it are set by
|
||||
g13_syshelp_init_default_ctrl(). */
|
||||
struct server_control_s
|
||||
{
|
||||
int no_server; /* We are not running under server control */
|
||||
int status_fd; /* Only for non-server mode */
|
||||
struct server_local_s *server_local;
|
||||
|
||||
struct {
|
||||
uid_t uid; /* UID of the client calling use. */
|
||||
char *uname;
|
||||
tab_item_t tab;/* Linked list with the g13tab items for this user. */
|
||||
} client;
|
||||
|
||||
/* Flag indicating that we should fail all commands. */
|
||||
int fail_all_cmds;
|
||||
|
||||
/* Type of the current container. See the CONTTYPE_ constants. */
|
||||
int conttype;
|
||||
|
||||
/* A pointer into client.tab with the selected tab line or NULL. */
|
||||
tab_item_t devti;
|
||||
};
|
||||
|
||||
|
||||
/*-- g13-syshelp.c --*/
|
||||
void g13_syshelp_init_default_ctrl (struct server_control_s *ctrl);
|
||||
|
||||
/*-- sh-cmd.c --*/
|
||||
gpg_error_t syshelp_server (ctrl_t ctrl);
|
||||
gpg_error_t sh_encrypt_keyblob (ctrl_t ctrl,
|
||||
const void *keyblob, size_t keybloblen,
|
||||
char **r_enckeyblob, size_t *r_enckeybloblen);
|
||||
|
||||
/*-- sh-exectool.c --*/
|
||||
gpg_error_t sh_exec_tool (const char *pgmname, const char *argv[],
|
||||
const char *input_string,
|
||||
char **result, size_t *resultlen);
|
||||
|
||||
/*-- sh-blockdev.c --*/
|
||||
gpg_error_t sh_blockdev_getsz (const char *name, unsigned long long *r_nblocks);
|
||||
gpg_error_t sh_is_empty_partition (const char *name);
|
||||
|
||||
/*-- sh-dmcrypt.c --*/
|
||||
gpg_error_t sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname,
|
||||
estream_t devfp);
|
||||
|
||||
|
||||
#endif /*G13_SYSHELP_H*/
|
29
g13/g13.c
29
g13/g13.c
@ -43,6 +43,8 @@
|
||||
#include "create.h"
|
||||
#include "mount.h"
|
||||
#include "mountinfo.h"
|
||||
#include "backend.h"
|
||||
#include "call-syshelp.h"
|
||||
|
||||
|
||||
enum cmd_and_opt_values {
|
||||
@ -73,6 +75,7 @@ enum cmd_and_opt_values {
|
||||
|
||||
oAgentProgram,
|
||||
oGpgProgram,
|
||||
oType,
|
||||
|
||||
oDisplay,
|
||||
oTTYname,
|
||||
@ -114,6 +117,7 @@ static ARGPARSE_OPTS opts[] = {
|
||||
ARGPARSE_group (301, N_("@\nOptions:\n ")),
|
||||
|
||||
ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
|
||||
ARGPARSE_s_s (oType, "type", N_("|NAME|use container format NAME")),
|
||||
|
||||
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
|
||||
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
|
||||
@ -570,6 +574,19 @@ main ( int argc, char **argv)
|
||||
add_to_strlist (&recipients, pargs.r.ret_str);
|
||||
break;
|
||||
|
||||
case oType:
|
||||
if (!strcmp (pargs.r.ret_str, "help"))
|
||||
{
|
||||
be_parse_conttype_name (NULL);
|
||||
g13_exit (0);
|
||||
}
|
||||
cmdline_conttype = be_parse_conttype_name (pargs.r.ret_str);
|
||||
if (!cmdline_conttype)
|
||||
{
|
||||
pargs.r_opt = ARGPARSE_INVALID_ARG;
|
||||
pargs.err = ARGPARSE_PRINT_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
|
||||
@ -756,6 +773,8 @@ main ( int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
g13_deinit_default_ctrl (&ctrl);
|
||||
|
||||
if (!err)
|
||||
join_idle_task ();
|
||||
|
||||
@ -767,12 +786,20 @@ main ( int argc, char **argv)
|
||||
|
||||
/* Store defaults into the per-connection CTRL object. */
|
||||
void
|
||||
g13_init_default_ctrl (struct server_control_s *ctrl)
|
||||
g13_init_default_ctrl (ctrl_t ctrl)
|
||||
{
|
||||
ctrl->conttype = cmdline_conttype? cmdline_conttype : CONTTYPE_ENCFS;
|
||||
}
|
||||
|
||||
|
||||
/* Release remaining resources allocated in the CTRL object. */
|
||||
void
|
||||
g13_deinit_default_ctrl (ctrl_t ctrl)
|
||||
{
|
||||
call_syshelp_release (ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* This function is called for each signal we catch. It is run in the
|
||||
main context or the one of a NPth thread and thus it is not
|
||||
restricted in what it may do. */
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
/* Forward declaration for an object defined in server.c. */
|
||||
struct server_local_s;
|
||||
/* Forward declaration for an object defined in call-syshelp.c. */
|
||||
struct call_syshelp_s;
|
||||
|
||||
|
||||
/* Session control object. This object is passed down to most
|
||||
functions. The default values for it are set by
|
||||
@ -34,6 +37,7 @@ struct server_control_s
|
||||
int no_server; /* We are not running under server control */
|
||||
int status_fd; /* Only for non-server mode */
|
||||
struct server_local_s *server_local;
|
||||
struct call_syshelp_s *syshelp_local;
|
||||
|
||||
int agent_seen; /* Flag indicating that the gpg-agent has been
|
||||
accessed. */
|
||||
@ -47,6 +51,7 @@ struct server_control_s
|
||||
|
||||
|
||||
/*-- g13.c --*/
|
||||
void g13_init_default_ctrl (struct server_control_s *ctrl);
|
||||
void g13_init_default_ctrl (ctrl_t ctrl);
|
||||
void g13_deinit_default_ctrl (ctrl_t ctrl);
|
||||
|
||||
#endif /*G13_H*/
|
||||
|
@ -20,7 +20,8 @@
|
||||
#ifndef G13_KEYBLOB_H
|
||||
#define G13_KEYBLOB_H
|
||||
|
||||
/* The header block is the actual core of G13. Here is the format:
|
||||
/* The setup area (header block) is the actual core of G13. Here is
|
||||
the format:
|
||||
|
||||
u8 Packet type. Value is 61 (0x3d).
|
||||
u8 Constant value 255 (0xff).
|
||||
@ -29,7 +30,7 @@
|
||||
u8 Version. Value is 1.
|
||||
u8 reserved
|
||||
u8 reserved
|
||||
u8 OS Flag: reserved, should be 0.
|
||||
u8 OS Flag: 0 = unspecified, 1 = Linux
|
||||
u32 Length of the entire header. This includes all bytes
|
||||
starting at the packet type and ending with the last
|
||||
padding byte of the header.
|
||||
@ -37,9 +38,9 @@
|
||||
u8 Number of copies of this header at the end of the
|
||||
container (usually 0).
|
||||
b6 reserved
|
||||
n bytes: OpenPGP encrypted and optionally signed message.
|
||||
n bytes: CMS encrypted and optionally signed packet. Such a CMS
|
||||
packet will be enclosed in a a private flagged OpenPGP
|
||||
n bytes: OpenPGP encrypted and optionally signed keyblob.
|
||||
n bytes: CMS encrypted and optionally signed keyblob. Such a CMS
|
||||
packet will be enclosed in a private flagged OpenPGP
|
||||
packet. Either the OpenPGP encrypted packet as described
|
||||
above, the CMS encrypted or both packets must exist. The
|
||||
encapsulation packet has this structure:
|
||||
@ -54,6 +55,8 @@
|
||||
u32 Length of the following structure
|
||||
b10 Value: "GnuPG/PAD\x00".
|
||||
b(n) Padding stuff.
|
||||
(repeat the above value
|
||||
or if the remaining N < 10, all 0x00).
|
||||
Given this structure the minimum padding is 16 bytes.
|
||||
|
||||
n bytes: File system container.
|
||||
@ -77,6 +80,14 @@
|
||||
keyblob. If a value is given it is expected to be the GUID of the
|
||||
partition. */
|
||||
|
||||
#define KEYBLOB_TAG_CREATED 3
|
||||
/* This is an ISO 8601 time string with the date the container was
|
||||
created. */
|
||||
|
||||
#define KEYBLOB_TAG_ALGOSTR 10
|
||||
/* For a dm-crypt container this is the used algorithm string. For
|
||||
example: "aes-cbc-essiv:sha256". */
|
||||
|
||||
#define KEYBLOB_TAG_KEYNO 16
|
||||
/* This tag indicates a new key. The value is a 4 byte big endian
|
||||
integer giving the key number. If the container type does only
|
||||
@ -105,8 +116,14 @@
|
||||
The value is the key used for MACing. */
|
||||
|
||||
|
||||
#define KEYBLOB_TAG_HDRCOPY 21
|
||||
/* The value of this tag is a copy of the setup area prefix header
|
||||
block (packet 61 with marker "GnuPG/G13\x00". We use it to allow
|
||||
signing of that cleartext data. */
|
||||
|
||||
|
||||
#define KEYBLOB_TAG_FILLER 0xffff
|
||||
/* This tag may be used for alignment and padding porposes. The value
|
||||
/* This tag may be used for alignment and padding purposes. The value
|
||||
has no meaning. */
|
||||
|
||||
|
||||
|
151
g13/sh-blockdev.c
Normal file
151
g13/sh-blockdev.c
Normal file
@ -0,0 +1,151 @@
|
||||
/* sh-blockdev.c - Block device functions for 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "g13-syshelp.h"
|
||||
#include <assuan.h>
|
||||
#include "i18n.h"
|
||||
#include "keyblob.h"
|
||||
|
||||
#ifndef HAVE_STRTOULL
|
||||
# error building this tool requires strtoull(3)
|
||||
#endif
|
||||
#ifndef ULLONG_MAX
|
||||
# error ULLONG_MAX missing
|
||||
#endif
|
||||
|
||||
|
||||
/* Return the size measured in the number of 512 byte sectors for the
|
||||
block device NAME. */
|
||||
gpg_error_t
|
||||
sh_blockdev_getsz (const char *name, unsigned long long *r_nblocks)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *argv[3];
|
||||
char *result;
|
||||
|
||||
*r_nblocks = 0;
|
||||
argv[0] = "--getsz";
|
||||
argv[1] = name;
|
||||
argv[2] = NULL;
|
||||
err = sh_exec_tool ("/sbin/blockdev", argv, NULL, &result, NULL);
|
||||
if (!err)
|
||||
{
|
||||
gpg_err_set_errno (0);
|
||||
*r_nblocks = strtoull (result, NULL, 10);
|
||||
if (*r_nblocks == ULLONG_MAX && errno)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
*r_nblocks = 0;
|
||||
}
|
||||
xfree (result);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Return 0 if the device NAME looks like an empty partition. */
|
||||
gpg_error_t
|
||||
sh_is_empty_partition (const char *name)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *argv[6];
|
||||
char *buffer;
|
||||
estream_t fp;
|
||||
char *p;
|
||||
size_t nread;
|
||||
|
||||
argv[0] = "-o";
|
||||
argv[1] = "value";
|
||||
argv[2] = "-s";
|
||||
argv[3] = "UUID";
|
||||
argv[4] = name;
|
||||
argv[5] = NULL;
|
||||
err = sh_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
|
||||
if (err)
|
||||
return gpg_error (GPG_ERR_FALSE);
|
||||
if (*buffer)
|
||||
{
|
||||
/* There seems to be an UUID - thus we have a file system. */
|
||||
xfree (buffer);
|
||||
return gpg_error (GPG_ERR_FALSE);
|
||||
}
|
||||
xfree (buffer);
|
||||
|
||||
argv[0] = "-o";
|
||||
argv[1] = "value";
|
||||
argv[2] = "-s";
|
||||
argv[3] = "PARTUUID";
|
||||
argv[4] = name;
|
||||
argv[5] = NULL;
|
||||
err = sh_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
|
||||
if (err)
|
||||
return gpg_error (GPG_ERR_FALSE);
|
||||
if (!*buffer)
|
||||
{
|
||||
/* If there is no PARTUUID we assume that name has already a
|
||||
mapped partition. */
|
||||
xfree (buffer);
|
||||
return gpg_error (GPG_ERR_FALSE);
|
||||
}
|
||||
xfree (buffer);
|
||||
|
||||
/* As a safeguard we require that the first 32k of a partition are
|
||||
all zero before we assume the partition is empty. */
|
||||
buffer = xtrymalloc (32 * 1024);
|
||||
if (!buffer)
|
||||
return gpg_error_from_syserror ();
|
||||
fp = es_fopen (name, "rb,samethread");
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error opening '%s': %s\n", name, gpg_strerror (err));
|
||||
xfree (buffer);
|
||||
return gpg_error (GPG_ERR_FALSE);
|
||||
}
|
||||
if (es_read (fp, buffer, 32 * 1024, &nread))
|
||||
err = gpg_error_from_syserror ();
|
||||
else if (nread != 32 *1024)
|
||||
err = gpg_error (GPG_ERR_TOO_SHORT);
|
||||
else
|
||||
err = 0;
|
||||
es_fclose (fp);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error reading the first 32 KiB from '%s': %s\n",
|
||||
name, gpg_strerror (err));
|
||||
xfree (buffer);
|
||||
return err;
|
||||
}
|
||||
for (p=buffer; nread && !*p; nread--, p++)
|
||||
;
|
||||
xfree (buffer);
|
||||
if (nread)
|
||||
return gpg_error (GPG_ERR_FALSE); /* No all zeroes. */
|
||||
|
||||
return 0;
|
||||
}
|
555
g13/sh-cmd.c
Normal file
555
g13/sh-cmd.c
Normal file
@ -0,0 +1,555 @@
|
||||
/* sh-cmd.c - The Assuan server for 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "g13-syshelp.h"
|
||||
#include <assuan.h>
|
||||
#include "i18n.h"
|
||||
#include "keyblob.h"
|
||||
|
||||
|
||||
/* Local data for this server module. A pointer to this is stored in
|
||||
the CTRL object of each connection. */
|
||||
struct server_local_s
|
||||
{
|
||||
/* The Assuan contect we are working on. */
|
||||
assuan_context_t assuan_ctx;
|
||||
|
||||
/* The malloced name of the device. */
|
||||
char *devicename;
|
||||
|
||||
/* A stream open for read of the device set by the DEVICE command or
|
||||
NULL if no DEVICE command has been used. */
|
||||
estream_t devicefp;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* Local prototypes. */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Helper functions.
|
||||
*/
|
||||
|
||||
/* Set an error and a description. */
|
||||
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
|
||||
#define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
|
||||
"not called via userv or unknown user")
|
||||
|
||||
|
||||
/* Skip over options. Blanks after the options are also removed. */
|
||||
static char *
|
||||
skip_options (const char *line)
|
||||
{
|
||||
while (spacep (line))
|
||||
line++;
|
||||
while ( *line == '-' && line[1] == '-' )
|
||||
{
|
||||
while (*line && !spacep (line))
|
||||
line++;
|
||||
while (spacep (line))
|
||||
line++;
|
||||
}
|
||||
return (char*)line;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether the option NAME appears in LINE. */
|
||||
/* static int */
|
||||
/* has_option (const char *line, const char *name) */
|
||||
/* { */
|
||||
/* const char *s; */
|
||||
/* int n = strlen (name); */
|
||||
|
||||
/* s = strstr (line, name); */
|
||||
/* if (s && s >= skip_options (line)) */
|
||||
/* return 0; */
|
||||
/* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
|
||||
/* } */
|
||||
|
||||
|
||||
/* Helper to print a message while leaving a command. */
|
||||
static gpg_error_t
|
||||
leave_cmd (assuan_context_t ctx, gpg_error_t err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
const char *name = assuan_get_command_name (ctx);
|
||||
if (!name)
|
||||
name = "?";
|
||||
if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
|
||||
log_error ("command '%s' failed: %s\n", name,
|
||||
gpg_strerror (err));
|
||||
else
|
||||
log_error ("command '%s' failed: %s <%s>\n", name,
|
||||
gpg_strerror (err), gpg_strsource (err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* The handler for Assuan OPTION commands. */
|
||||
static gpg_error_t
|
||||
option_handler (assuan_context_t ctx, const char *key, const char *value)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err = 0;
|
||||
|
||||
(void)ctrl;
|
||||
(void)key;
|
||||
(void)value;
|
||||
|
||||
if (ctrl->fail_all_cmds)
|
||||
err = set_error_fail_cmd ();
|
||||
else
|
||||
err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* The handler for an Assuan RESET command. */
|
||||
static gpg_error_t
|
||||
reset_notify (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
(void)line;
|
||||
|
||||
xfree (ctrl->server_local->devicename);
|
||||
ctrl->server_local->devicename = NULL;
|
||||
es_fclose (ctrl->server_local->devicefp);
|
||||
ctrl->server_local->devicefp = NULL;
|
||||
ctrl->devti = NULL;
|
||||
|
||||
assuan_close_input_fd (ctx);
|
||||
assuan_close_output_fd (ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char hlp_device[] =
|
||||
"DEVICE <name>\n"
|
||||
"\n"
|
||||
"Set the device used by further commands.\n"
|
||||
"A device name or a PARTUUID string may be used.";
|
||||
static gpg_error_t
|
||||
cmd_device (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err = 0;
|
||||
tab_item_t ti;
|
||||
estream_t fp = NULL;
|
||||
|
||||
/* strcpy (line, "/dev/sdb1"); /\* FIXME *\/ */
|
||||
line = skip_options (line);
|
||||
|
||||
/* Always close an open device stream of this session. */
|
||||
xfree (ctrl->server_local->devicename);
|
||||
ctrl->server_local->devicename = NULL;
|
||||
es_fclose (ctrl->server_local->devicefp);
|
||||
ctrl->server_local->devicefp = NULL;
|
||||
|
||||
/* Are we allowed to use the given device? */
|
||||
for (ti=ctrl->client.tab; ti; ti = ti->next)
|
||||
if (!strcmp (line, ti->blockdev))
|
||||
break;
|
||||
if (!ti)
|
||||
{
|
||||
set_error (GPG_ERR_EACCES, "device not configured for user");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
ctrl->server_local->devicename = xtrystrdup (line);
|
||||
if (!ctrl->server_local->devicename)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether we have permissions to open the device and keep an
|
||||
FD open. */
|
||||
fp = es_fopen (ctrl->server_local->devicename, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error opening '%s': %s\n",
|
||||
ctrl->server_local->devicename, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
es_fclose (ctrl->server_local->devicefp);
|
||||
ctrl->server_local->devicefp = fp;
|
||||
fp = NULL;
|
||||
ctrl->devti = ti;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
if (err)
|
||||
{
|
||||
xfree (ctrl->server_local->devicename);
|
||||
ctrl->server_local->devicename = NULL;
|
||||
ctrl->devti = NULL;
|
||||
}
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
static const char hlp_create[] =
|
||||
"CREATE <type>\n"
|
||||
"\n"
|
||||
"Create a new encrypted partition on the current device.\n"
|
||||
"<type> must be \"dm-crypt\" for now.";
|
||||
static gpg_error_t
|
||||
cmd_create (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err = 0;
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
if (strcmp (line, "dm-crypt"))
|
||||
{
|
||||
err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!ctrl->server_local->devicename
|
||||
|| !ctrl->server_local->devicefp
|
||||
|| !ctrl->devti)
|
||||
{
|
||||
err = set_error (GPG_ERR_ENOENT, "No device has been set");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = sh_is_empty_partition (ctrl->server_local->devicename);
|
||||
if (err)
|
||||
{
|
||||
assuan_set_error (ctx, err, "Partition is not empty");
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = sh_dmcrypt_create_container (ctrl,
|
||||
ctrl->server_local->devicename,
|
||||
ctrl->server_local->devicefp);
|
||||
|
||||
|
||||
|
||||
|
||||
leave:
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_getinfo[] =
|
||||
"GETINFO <what>\n"
|
||||
"\n"
|
||||
"Multipurpose function to return a variety of information.\n"
|
||||
"Supported values for WHAT are:\n"
|
||||
"\n"
|
||||
" version - Return the version of the program.\n"
|
||||
" pid - Return the process id of the server.\n"
|
||||
" showtab - Show the table for the user.";
|
||||
static gpg_error_t
|
||||
cmd_getinfo (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err = 0;
|
||||
char *buf;
|
||||
|
||||
if (!strcmp (line, "version"))
|
||||
{
|
||||
const char *s = PACKAGE_VERSION;
|
||||
err = assuan_send_data (ctx, s, strlen (s));
|
||||
}
|
||||
else if (!strcmp (line, "pid"))
|
||||
{
|
||||
char numbuf[50];
|
||||
|
||||
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
|
||||
err = assuan_send_data (ctx, numbuf, strlen (numbuf));
|
||||
}
|
||||
else if (!strncmp (line, "getsz", 5))
|
||||
{
|
||||
unsigned long long nblocks;
|
||||
err = sh_blockdev_getsz (line+6, &nblocks);
|
||||
if (!err)
|
||||
log_debug ("getsz=%llu\n", nblocks);
|
||||
}
|
||||
else if (!strcmp (line, "showtab"))
|
||||
{
|
||||
tab_item_t ti;
|
||||
|
||||
for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
|
||||
{
|
||||
buf = es_bsprintf ("%s %s%s %s %s%s\n",
|
||||
ctrl->client.uname,
|
||||
*ti->blockdev=='/'? "":"partuuid=",
|
||||
ti->blockdev,
|
||||
ti->label? ti->label : "-",
|
||||
ti->mountpoint? " ":"",
|
||||
ti->mountpoint? ti->mountpoint:"");
|
||||
if (!buf)
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
{
|
||||
err = assuan_send_data (ctx, buf, strlen (buf));
|
||||
if (!err)
|
||||
err = assuan_send_data (ctx, NULL, 0); /* Flush */
|
||||
}
|
||||
xfree (buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
|
||||
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
/* This command handler is used for all commands if this process has
|
||||
not been started as expected. */
|
||||
static gpg_error_t
|
||||
fail_command (assuan_context_t ctx, char *line)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const char *name = assuan_get_command_name (ctx);
|
||||
|
||||
(void)line;
|
||||
|
||||
if (!name)
|
||||
name = "?";
|
||||
|
||||
err = set_error_fail_cmd ();
|
||||
log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Tell the Assuan library about our commands. */
|
||||
static int
|
||||
register_commands (assuan_context_t ctx, int fail_all)
|
||||
{
|
||||
static struct {
|
||||
const char *name;
|
||||
assuan_handler_t handler;
|
||||
const char * const help;
|
||||
} table[] = {
|
||||
{ "DEVICE", cmd_device, hlp_device },
|
||||
{ "CREATE", cmd_create, hlp_create },
|
||||
{ "INPUT", NULL },
|
||||
{ "OUTPUT", NULL },
|
||||
{ "GETINFO", cmd_getinfo, hlp_getinfo },
|
||||
{ NULL }
|
||||
};
|
||||
gpg_error_t err;
|
||||
int i;
|
||||
|
||||
for (i=0; table[i].name; i++)
|
||||
{
|
||||
err = assuan_register_command (ctx, table[i].name,
|
||||
fail_all ? fail_command : table[i].handler,
|
||||
table[i].help);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Startup the server. */
|
||||
gpg_error_t
|
||||
syshelp_server (ctrl_t ctrl)
|
||||
{
|
||||
gpg_error_t err;
|
||||
assuan_fd_t filedes[2];
|
||||
assuan_context_t ctx = NULL;
|
||||
|
||||
/* We use a pipe based server so that we can work from scripts.
|
||||
assuan_init_pipe_server will automagically detect when we are
|
||||
called with a socketpair and ignore FILEDES in this case. */
|
||||
filedes[0] = assuan_fdopen (0);
|
||||
filedes[1] = assuan_fdopen (1);
|
||||
err = assuan_new (&ctx);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to allocate an Assuan context: %s\n",
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = assuan_init_pipe_server (ctx, filedes);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
|
||||
if (err)
|
||||
{
|
||||
log_error ("failed to the register commands with Assuan: %s\n",
|
||||
gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
assuan_set_pointer (ctx, ctrl);
|
||||
|
||||
{
|
||||
char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
|
||||
"from %lu(%s)",
|
||||
PACKAGE_VERSION,
|
||||
(unsigned long)ctrl->client.uid,
|
||||
ctrl->client.uname);
|
||||
if (tmp)
|
||||
{
|
||||
assuan_set_hello_line (ctx, tmp);
|
||||
xfree (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
assuan_register_reset_notify (ctx, reset_notify);
|
||||
assuan_register_option_handler (ctx, option_handler);
|
||||
|
||||
ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
|
||||
if (!ctrl->server_local)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
ctrl->server_local->assuan_ctx = ctx;
|
||||
|
||||
while ( !(err = assuan_accept (ctx)) )
|
||||
{
|
||||
err = assuan_process (ctx);
|
||||
if (err)
|
||||
log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
|
||||
}
|
||||
if (err == -1)
|
||||
err = 0;
|
||||
else
|
||||
log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
|
||||
|
||||
leave:
|
||||
reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
|
||||
if (ctrl->server_local)
|
||||
{
|
||||
xfree (ctrl->server_local);
|
||||
ctrl->server_local = NULL;
|
||||
}
|
||||
|
||||
assuan_release (ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
|
||||
char **r_enckeyblob, size_t *r_enckeybloblen)
|
||||
{
|
||||
assuan_context_t ctx = ctrl->server_local->assuan_ctx;
|
||||
gpg_error_t err;
|
||||
unsigned char *enckeyblob;
|
||||
size_t enckeybloblen;
|
||||
|
||||
*r_enckeyblob = NULL;
|
||||
|
||||
/* Send the plaintext. */
|
||||
err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
assuan_begin_confidential (ctx);
|
||||
err = assuan_send_data (ctx, keyblob, keybloblen);
|
||||
if (!err)
|
||||
err = assuan_send_data (ctx, NULL, 0);
|
||||
assuan_end_confidential (ctx);
|
||||
if (!err)
|
||||
err = assuan_write_line (ctx, "END");
|
||||
if (err)
|
||||
{
|
||||
log_error (_("error sending data: %s\n"), gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Inquire the ciphertext. */
|
||||
err = assuan_inquire (ctx, "ENCKEYBLOB",
|
||||
&enckeyblob, &enckeybloblen, 16 * 1024);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
*r_enckeyblob = enckeyblob;
|
||||
*r_enckeybloblen = enckeybloblen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Send a status line with status ID NO. The arguments are a list of
|
||||
strings terminated by a NULL argument. */
|
||||
gpg_error_t
|
||||
g13_status (ctrl_t ctrl, int no, ...)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
va_list arg_ptr;
|
||||
const char *text;
|
||||
|
||||
va_start (arg_ptr, no);
|
||||
|
||||
if (1)
|
||||
{
|
||||
assuan_context_t ctx = ctrl->server_local->assuan_ctx;
|
||||
char buf[950], *p;
|
||||
size_t n;
|
||||
|
||||
p = buf;
|
||||
n = 0;
|
||||
while ( (text = va_arg (arg_ptr, const char *)) )
|
||||
{
|
||||
if (n)
|
||||
{
|
||||
*p++ = ' ';
|
||||
n++;
|
||||
}
|
||||
for ( ; *text && n < DIM (buf)-2; n++)
|
||||
*p++ = *text++;
|
||||
}
|
||||
*p = 0;
|
||||
err = assuan_write_status (ctx, get_status_string (no), buf);
|
||||
}
|
||||
|
||||
va_end (arg_ptr);
|
||||
return err;
|
||||
}
|
406
g13/sh-dmcrypt.c
Normal file
406
g13/sh-dmcrypt.c
Normal file
@ -0,0 +1,406 @@
|
||||
/* sh-dmcrypt.c - The DM-Crypt part for 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_STAT
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "g13-syshelp.h"
|
||||
#include <assuan.h>
|
||||
#include "i18n.h"
|
||||
#include "utils.h"
|
||||
#include "keyblob.h"
|
||||
|
||||
/* The standard disk block size (logical). */
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
/* The physical block size used by modern devices. */
|
||||
#define PHY_SECTOR_SIZE (SECTOR_SIZE*8) /* 4 KiB */
|
||||
|
||||
/* The length of the crypto setup area in sectors. 16 KiB is a nice
|
||||
multiple of a modern block size and should be sufficient for all
|
||||
kind of extra public key encryption packet. */
|
||||
#define SETUP_AREA_SECTORS 32 /* 16 KiB */
|
||||
|
||||
/* The number of header block copies stored at the begin and end of
|
||||
the device. */
|
||||
#define HEADER_SETUP_AREA_COPIES 2
|
||||
#define FOOTER_SETUP_AREA_COPIES 2
|
||||
|
||||
/* The length in blocks of the space we put at the start and at the
|
||||
end of the device. This space is used to store N copies of the
|
||||
setup area for the actual encrypted container inbetween. */
|
||||
#define HEADER_SECTORS (SETUP_AREA_SECTORS * HEADER_SETUP_AREA_COPIES)
|
||||
#define FOOTER_SECTORS (SETUP_AREA_SECTORS * FOOTER_SETUP_AREA_COPIES)
|
||||
|
||||
/* Minimim size of the encrypted space in blocks. This is more or
|
||||
less an arbitrary value. */
|
||||
#define MIN_ENCRYPTED_SPACE 32
|
||||
|
||||
/* Some consistency checks for the above constants. */
|
||||
#if (PHY_SECTOR_SIZE % SECTOR_SIZE)
|
||||
# error the physical secotor size should be a multiple of 512
|
||||
#endif
|
||||
#if ((SETUP_AREA_SECTORS*SECTOR_SIZE) % PHY_SECTOR_SIZE)
|
||||
# error The setup area size should be a multiple of the phy. sector size.
|
||||
#endif
|
||||
|
||||
|
||||
/* Check whether the block device DEVNAME is used by device mapper.
|
||||
Returns: 0 if the device is good and not yet used by DM. */
|
||||
static gpg_error_t
|
||||
check_blockdev (const char *devname)
|
||||
{
|
||||
gpg_error_t err;
|
||||
struct stat sb;
|
||||
unsigned int devmajor, devminor;
|
||||
char *result = NULL;
|
||||
char **lines = NULL;
|
||||
char **fields = NULL;
|
||||
int lno, count;
|
||||
|
||||
if (stat (devname, &sb))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error stating '%s': %s\n", devname, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
if (!S_ISBLK (sb.st_mode))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_ENOTBLK);
|
||||
log_error ("can't use '%s': %s\n", devname, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
devmajor = major (sb.st_rdev);
|
||||
devminor = minor (sb.st_rdev);
|
||||
|
||||
{
|
||||
const char *argv[2];
|
||||
|
||||
argv[0] = "deps";
|
||||
argv[1] = NULL;
|
||||
err = sh_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
log_error ("error running '%s' to search for '%s': %s\n",
|
||||
"dmsetup deps", devname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
lines = strsplit (result, '\n', 0, NULL);
|
||||
if (!lines)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
if (lines[0] && !strcmp (lines[0], "No devices found"))
|
||||
;
|
||||
else
|
||||
{
|
||||
for (lno=0; lines[lno]; lno++)
|
||||
{
|
||||
unsigned int xmajor, xminor;
|
||||
|
||||
if (!*lines[lno])
|
||||
continue;
|
||||
xfree (fields);
|
||||
fields = strsplit (lines[lno], ':', 0, &count);
|
||||
if (!fields)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
if (count < 3
|
||||
|| sscanf (fields[2], " (%u,%u)", &xmajor, &xminor) != 2)
|
||||
{
|
||||
log_error ("error running '%s' to search for '%s': %s\n",
|
||||
"dmsetup deps", devname, "unexpected output");
|
||||
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (xmajor == devmajor && xminor == devminor)
|
||||
{
|
||||
log_error ("device '%s' (%u:%u) already used by device mapper\n",
|
||||
devname, devmajor, devminor);
|
||||
err = gpg_error (GPG_ERR_EBUSY);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
leave:
|
||||
xfree (fields);
|
||||
xfree (lines);
|
||||
xfree (result);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Return a malloced buffer with the prefix of the setup area. This
|
||||
is the data written right before the encrypted keyblob. Return NULL
|
||||
on error and sets ERRNO. */
|
||||
static void *
|
||||
mk_setup_area_prefix (size_t *r_length)
|
||||
{
|
||||
unsigned char *packet;
|
||||
size_t setuparealen;
|
||||
|
||||
packet = xtrymalloc (32);
|
||||
if (!packet)
|
||||
return NULL;
|
||||
*r_length = 32;
|
||||
|
||||
setuparealen = SETUP_AREA_SECTORS * SECTOR_SIZE;
|
||||
|
||||
packet[0] = (0xc0|61); /* CTB for the private packet type 0x61. */
|
||||
packet[1] = 0xff; /* 5 byte length packet, value 20. */
|
||||
packet[2] = 0;
|
||||
packet[3] = 0;
|
||||
packet[4] = 0;
|
||||
packet[5] = 26;
|
||||
memcpy (packet+6, "GnuPG/G13", 10); /* Packet subtype. */
|
||||
packet[16] = 1; /* G13 packet format version. */
|
||||
packet[17] = 0; /* Reserved. */
|
||||
packet[18] = 0; /* Reserved. */
|
||||
packet[19] = 1; /* OS Flag = Linux */
|
||||
packet[20] = (setuparealen >> 24); /* Total length of header. */
|
||||
packet[21] = (setuparealen >> 16);
|
||||
packet[22] = (setuparealen >> 8);
|
||||
packet[23] = (setuparealen);
|
||||
packet[24] = HEADER_SETUP_AREA_COPIES;
|
||||
packet[25] = FOOTER_SETUP_AREA_COPIES;
|
||||
packet[26] = 0; /* Reserved. */
|
||||
packet[27] = 0; /* Reserved. */
|
||||
packet[28] = 0; /* Reserved. */
|
||||
packet[29] = 0; /* Reserved. */
|
||||
packet[30] = 0; /* Reserved. */
|
||||
packet[31] = 0; /* Reserved. */
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *header_space;
|
||||
char *targetname = NULL;
|
||||
size_t nread;
|
||||
char *p;
|
||||
char hexkey[16*2+1];
|
||||
char *table = NULL;
|
||||
unsigned long long nblocks;
|
||||
char *result = NULL;
|
||||
unsigned char twobyte[2];
|
||||
membuf_t keyblob;
|
||||
void *keyblob_buf = NULL;
|
||||
size_t keyblob_len;
|
||||
size_t n;
|
||||
const char *s;
|
||||
|
||||
if (!ctrl->devti)
|
||||
return gpg_error (GPG_ERR_INV_ARG);
|
||||
|
||||
header_space = xtrymalloc (HEADER_SECTORS * SECTOR_SIZE);
|
||||
if (!header_space)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
/* Start building the keyblob. */
|
||||
init_membuf (&keyblob, 512);
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_BLOBVERSION, "\x01", 1);
|
||||
n = CONTTYPE_DM_CRYPT;
|
||||
twobyte[0] = (n >> 8);
|
||||
twobyte[1] = n;
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_CONTTYPE, twobyte, 2);
|
||||
{
|
||||
gnupg_isotime_t tbuf;
|
||||
|
||||
gnupg_get_isotime (tbuf);
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_CREATED, tbuf, strlen (tbuf));
|
||||
}
|
||||
|
||||
/* Rewind out stream. */
|
||||
if (es_fseeko (devfp, 0, SEEK_SET))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error seeking to begin of '%s': %s\n",
|
||||
devname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
es_clearerr (devfp);
|
||||
|
||||
/* Extra check that the device is empty. */
|
||||
if (es_read (devfp, header_space, HEADER_SECTORS * SECTOR_SIZE, &nread))
|
||||
err = gpg_error_from_syserror ();
|
||||
else if (nread != HEADER_SECTORS * SECTOR_SIZE)
|
||||
err = gpg_error (GPG_ERR_TOO_SHORT);
|
||||
else
|
||||
err = 0;
|
||||
if (err)
|
||||
{
|
||||
log_error ("error reading header space of '%s': %s\n",
|
||||
devname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
for (p=header_space; nread && !*p; nread--, p++)
|
||||
;
|
||||
if (nread)
|
||||
{
|
||||
log_error ("header space of '%s' already used - use %s to override\n",
|
||||
devname, "--force");
|
||||
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Check that the device is not used by device mapper. */
|
||||
err = check_blockdev (devname);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Compute the number of blocks. */
|
||||
err = sh_blockdev_getsz (devname, &nblocks);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error getting size of '%s': %s\n",
|
||||
devname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (nblocks <= HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS)
|
||||
{
|
||||
log_error ("device '%s' is too small (min=%d blocks)\n",
|
||||
devname,
|
||||
HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS);
|
||||
err = gpg_error (GPG_ERR_TOO_SHORT);
|
||||
goto leave;
|
||||
}
|
||||
nblocks -= HEADER_SECTORS + FOOTER_SECTORS;
|
||||
|
||||
/* Device mapper needs a name for the device: Take it from the label
|
||||
or use "0". */
|
||||
targetname = strconcat ("g13-", ctrl->client.uname, "-",
|
||||
ctrl->devti->label? ctrl->devti->label : "0",
|
||||
NULL);
|
||||
if (!targetname)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Create the key. */
|
||||
{
|
||||
char key[16];
|
||||
gcry_randomize (key, sizeof key, GCRY_STRONG_RANDOM);
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_ENCKEY, key, sizeof key);
|
||||
bin2hex (key, 16, hexkey);
|
||||
wipememory (key, 16);
|
||||
/* Add a 2*(4+16) byte filler to conceal the fact that we use
|
||||
AES-128. If we ever want to switch to 256 bit we can resize
|
||||
that filler to keep the keyblob at the same size. */
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_FILLER, key, sizeof key);
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_FILLER, key, sizeof key);
|
||||
}
|
||||
|
||||
/* Build dmcrypt table. */
|
||||
s = "aes-cbc-essiv:sha256";
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_ALGOSTR, s, strlen (s));
|
||||
table = es_bsprintf ("0 %llu crypt %s %s 0 %s %d",
|
||||
nblocks, s, hexkey, devname, HEADER_SECTORS);
|
||||
if (!table)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
wipememory (hexkey, sizeof hexkey);
|
||||
|
||||
/* Add a copy of the setup area prefix to the keyblob. */
|
||||
p = mk_setup_area_prefix (&n);
|
||||
if (!p)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
append_tuple (&keyblob, KEYBLOB_TAG_HDRCOPY, p, n);
|
||||
|
||||
/* Turn the keyblob into a buffer and callback to encrypt it. */
|
||||
keyblob_buf = get_membuf (&keyblob, &keyblob_len);
|
||||
if (!keyblob_buf)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
err = sh_encrypt_keyblob (ctrl, keyblob_buf, keyblob_len, &p, &n);
|
||||
if (err)
|
||||
{
|
||||
log_error ("encrypting the keyblob failed: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
wipememory (keyblob_buf, keyblob_len);
|
||||
xfree (keyblob_buf);
|
||||
keyblob_buf = NULL;
|
||||
|
||||
/* Create the container. */
|
||||
/* { */
|
||||
/* const char *argv[3]; */
|
||||
|
||||
/* argv[0] = "create"; */
|
||||
/* argv[1] = targetname; */
|
||||
/* argv[2] = NULL; */
|
||||
/* err = sh_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL); */
|
||||
/* } */
|
||||
/* if (err) */
|
||||
/* { */
|
||||
/* log_error ("error running dmsetup for '%s': %s\n", */
|
||||
/* devname, gpg_strerror (err)); */
|
||||
/* goto leave; */
|
||||
/* } */
|
||||
/* log_debug ("dmsetup result: %s\n", result); */
|
||||
|
||||
/* Write the setup area. */
|
||||
|
||||
|
||||
leave:
|
||||
wipememory (hexkey, sizeof hexkey);
|
||||
if (table)
|
||||
{
|
||||
wipememory (table, strlen (table));
|
||||
xfree (table);
|
||||
}
|
||||
if (keyblob_buf)
|
||||
{
|
||||
wipememory (keyblob_buf, keyblob_len);
|
||||
xfree (keyblob_buf);
|
||||
}
|
||||
xfree (get_membuf (&keyblob, NULL));
|
||||
xfree (targetname);
|
||||
xfree (result);
|
||||
xfree (header_space);
|
||||
return err;
|
||||
}
|
303
g13/sh-exectool.c
Normal file
303
g13/sh-exectool.c
Normal file
@ -0,0 +1,303 @@
|
||||
/* sh-exectool.c - Utility functions to execute a helper tool
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "g13-syshelp.h"
|
||||
#include <assuan.h>
|
||||
#include "i18n.h"
|
||||
#include "membuf.h"
|
||||
#include "exechelp.h"
|
||||
#include "sysutils.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *pgmname;
|
||||
int cont;
|
||||
int used;
|
||||
char buffer[256];
|
||||
} read_and_log_buffer_t;
|
||||
|
||||
|
||||
static void
|
||||
read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int c;
|
||||
|
||||
if (!fderr)
|
||||
{
|
||||
/* Flush internal buffer. */
|
||||
if (state->used)
|
||||
{
|
||||
const char *pname;
|
||||
int len;
|
||||
|
||||
state->buffer[state->used] = 0;
|
||||
state->used = 0;
|
||||
|
||||
pname = strrchr (state->pgmname, '/');
|
||||
if (pname && pname != state->pgmname && pname[1])
|
||||
pname++;
|
||||
else
|
||||
pname = state->pgmname;
|
||||
/* If our pgmname plus colon is identical to the start of
|
||||
the output, print only the output. */
|
||||
len = strlen (pname);
|
||||
if (!state->cont
|
||||
&& !strncmp (state->buffer, pname, len)
|
||||
&& strlen (state->buffer) > strlen (pname)
|
||||
&& state->buffer[len] == ':' )
|
||||
log_info ("%s\n", state->buffer);
|
||||
else
|
||||
log_info ("%s%c %s\n",
|
||||
pname, state->cont? '+':':', state->buffer);
|
||||
}
|
||||
state->cont = 0;
|
||||
return;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
c = es_fgetc (fderr->stream);
|
||||
if (c == EOF)
|
||||
{
|
||||
if (es_feof (fderr->stream))
|
||||
{
|
||||
fderr->ignore = 1; /* Not anymore needed. */
|
||||
}
|
||||
else if (es_ferror (fderr->stream))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error reading stderr of '%s': %s\n",
|
||||
state->pgmname, gpg_strerror (err));
|
||||
fderr->ignore = 1; /* Disable. */
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
read_and_log_stderr (state, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state->used >= sizeof state->buffer - 1)
|
||||
{
|
||||
read_and_log_stderr (state, NULL);
|
||||
state->cont = 1;
|
||||
}
|
||||
state->buffer[state->used++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
read_stdout (membuf_t *mb, es_poll_t *fdout, const char *pgmname)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
int c;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
c = es_fgetc (fdout->stream);
|
||||
if (c == EOF)
|
||||
{
|
||||
if (es_feof (fdout->stream))
|
||||
{
|
||||
fdout->ignore = 1; /* Ready. */
|
||||
}
|
||||
else if (es_ferror (fdout->stream))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error reading stdout of '%s': %s\n",
|
||||
pgmname, gpg_strerror (err));
|
||||
fdout->ignore = 1; /* Disable. */
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[1];
|
||||
*buf = c;
|
||||
put_membuf (mb, buf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Run the program PGMNAME with the command line arguments given in
|
||||
the NULL terminates array ARGV. If INPUT_STRING is not NULL it
|
||||
will be fed to stdin of the process. stderr is logged using
|
||||
log_info and the process' stdout is returned in a newly malloced
|
||||
buffer RESULT with the length stored at RESULTLEN if not given as
|
||||
NULL. A hidden Nul is appended to the output. On error NULL is
|
||||
stored at RESULT, a diagnostic is printed, and an error code
|
||||
returned. */
|
||||
gpg_error_t
|
||||
sh_exec_tool (const char *pgmname, const char *argv[],
|
||||
const char *input_string,
|
||||
char **result, size_t *resultlen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
pid_t pid;
|
||||
estream_t infp = NULL;
|
||||
estream_t outfp, errfp;
|
||||
es_poll_t fds[3];
|
||||
int count;
|
||||
read_and_log_buffer_t fderrstate;
|
||||
membuf_t fdout_mb;
|
||||
size_t len, nwritten;
|
||||
|
||||
*result = NULL;
|
||||
if (resultlen)
|
||||
*resultlen = 0;
|
||||
memset (fds, 0, sizeof fds);
|
||||
memset (&fderrstate, 0, sizeof fderrstate);
|
||||
init_membuf (&fdout_mb, 4096);
|
||||
|
||||
err = gnupg_spawn_process (pgmname, argv, GPG_ERR_SOURCE_DEFAULT,
|
||||
NULL, GNUPG_SPAWN_NONBLOCK,
|
||||
input_string? &infp : NULL,
|
||||
&outfp, &errfp, &pid);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
fderrstate.pgmname = pgmname;
|
||||
|
||||
fds[0].stream = infp;
|
||||
fds[0].want_write = 1;
|
||||
if (!input_string)
|
||||
fds[0].ignore = 1;
|
||||
fds[1].stream = outfp;
|
||||
fds[1].want_read = 1;
|
||||
fds[2].stream = errfp;
|
||||
fds[2].want_read = 1;
|
||||
/* Now read as long as we have something to poll. We continue
|
||||
reading even after EOF or error on stdout so that we get the
|
||||
other error messages or remaining outout. */
|
||||
while (!fds[1].ignore && !fds[2].ignore)
|
||||
{
|
||||
count = es_poll (fds, DIM(fds), -1);
|
||||
if (count == -1)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
if (!count)
|
||||
{
|
||||
log_debug ("unexpected timeout while polling '%s'\n", pgmname);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds[0].got_write)
|
||||
{
|
||||
len = strlen (input_string);
|
||||
log_debug ("writing '%s'\n", input_string);
|
||||
if (es_write (fds[0].stream, input_string, len, &nwritten))
|
||||
{
|
||||
if (errno != EAGAIN)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error writing '%s': %s\n",
|
||||
pgmname, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
else
|
||||
log_debug (" .. EAGAIN\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (nwritten <= len);
|
||||
input_string += nwritten;
|
||||
}
|
||||
|
||||
if (es_fflush (fds[0].stream) && errno != EAGAIN)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error writing '%s' (flush): %s\n",
|
||||
pgmname, gpg_strerror (err));
|
||||
if (gpg_err_code (err) == GPG_ERR_EPIPE && !*input_string)
|
||||
{
|
||||
/* fixme: How can we tell whether estream has
|
||||
pending bytes after a HUP - which is an
|
||||
error? */
|
||||
}
|
||||
else
|
||||
goto leave;
|
||||
}
|
||||
if (!*input_string)
|
||||
{
|
||||
fds[0].ignore = 1; /* ready. */
|
||||
es_fclose (infp); infp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fds[1].got_read)
|
||||
read_stdout (&fdout_mb, fds + 1, pgmname); /* FIXME: Add error
|
||||
handling. */
|
||||
if (fds[2].got_read)
|
||||
read_and_log_stderr (&fderrstate, fds + 2);
|
||||
|
||||
}
|
||||
|
||||
read_and_log_stderr (&fderrstate, NULL); /* Flush. */
|
||||
es_fclose (infp); infp = NULL;
|
||||
es_fclose (outfp); outfp = NULL;
|
||||
es_fclose (errfp); errfp = NULL;
|
||||
|
||||
err = gnupg_wait_process (pgmname, pid, 1, NULL);
|
||||
pid = (pid_t)(-1);
|
||||
|
||||
leave:
|
||||
if (err)
|
||||
{
|
||||
gnupg_kill_process (pid);
|
||||
xfree (get_membuf (&fdout_mb, NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
put_membuf (&fdout_mb, "", 1); /* Make sure it is a string. */
|
||||
*result = get_membuf (&fdout_mb, resultlen);
|
||||
if (!*result)
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
es_fclose (infp);
|
||||
es_fclose (outfp);
|
||||
es_fclose (errfp);
|
||||
if (pid != (pid_t)(-1))
|
||||
gnupg_wait_process (pgmname, pid, 1, NULL);
|
||||
gnupg_release_process (pid);
|
||||
|
||||
return err;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user