From 3b870aa9f9865754119e32cf3ff6beb154b4d9df Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Thu, 28 Oct 2010 15:17:21 +0100 Subject: [PATCH] Add -u and -g options to febootstrap-supermin-helper Bash automatically resets euid to uid when it executes. This means that the effective user id of a program at the point it calls febootstrap-supermin-helper will be lost if any part of execution chain involved bash. This in turn can result in: * the generation of an incorrect checksum, which contains the uid. * the generation of supermin files with a mixture of owners The -u and -g options allow the caller to pass in an explicit user and group to run as. febootstrap-supermin-helper will set(u|g)id as appropriate. --- helper/febootstrap-supermin-helper.pod | 13 ++++ helper/main.c | 116 ++++++++++++++++++++++++++++++++- lib/.gitignore | 10 +++ m4/.gitignore | 4 ++ m4/gnulib-cache.m4 | 3 +- 5 files changed, 142 insertions(+), 4 deletions(-) diff --git a/helper/febootstrap-supermin-helper.pod b/helper/febootstrap-supermin-helper.pod index c6c551c..1ef960f 100644 --- a/helper/febootstrap-supermin-helper.pod +++ b/helper/febootstrap-supermin-helper.pod @@ -94,6 +94,19 @@ If this option is not specified, then every kernel module from the host will be included. This is safer, but can produce rather large appliances which need a lot more memory to boot. +=item B<-u user> | B<--user user> | B<-g group> | B<--group group> + +Run febootstrap-supermin-helper as an alternate user and/or group. +C and C can be specified as either a name, which will +be resolved using the system name service, or a uid/gid. Use of these +options requires root privileges. + +Use of these options is required if running febootstrap-supermin-helper +as root with the effective uid/gid set to non-root. Bash will reset +the effective uid/gid to the real uid/gid when invoked. As +febootstrap-supermin-helper uses bash in parts, this will result in the +creation of an appliance with a mixture of ownerships. + =back =head1 SPEED diff --git a/helper/main.c b/helper/main.c index f194008..b769bc7 100644 --- a/helper/main.c +++ b/helper/main.c @@ -28,23 +28,26 @@ #include #include #include +#include +#include #include "error.h" +#include "xstrtol.h" #include "helper.h" struct timeval start_t; int verbose = 0; -static const char *format = "cpio"; - enum { HELP_OPTION = CHAR_MAX + 1 }; -static const char *options = "f:k:vV"; +static const char *options = "f:g:k:u:vV"; static const struct option long_options[] = { { "help", 0, 0, HELP_OPTION }, { "format", required_argument, 0, 'f' }, + { "group", 0, 0, 'g' }, { "kmods", required_argument, 0, 'k' }, + { "user", 0, 0, 'u' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } @@ -76,6 +79,12 @@ usage (FILE *f, const char *progname) " Display this help text and exit.\n" " -f cpio|ext2|checksum | --format cpio|ext2|checksum\n" " Specify output format (default: cpio).\n" + " -u user\n" + " The user name or uid the appliance will run as. Use of this\n" + " option requires root privileges.\n" + " -g group\n" + " The group name or gid the appliance will run as. Use of\n" + " this option requires root privileges.\n" " -k file | --kmods file\n" " Specify kernel module whitelist.\n" " --verbose | -v\n" @@ -85,14 +94,76 @@ usage (FILE *f, const char *progname) progname, progname, progname, progname, progname, progname); } +static uid_t +parseuser (const char *id, const char *progname) +{ + + struct passwd *pwd; + + errno = 0; + pwd = getpwnam (id); + + if (NULL == pwd) { + if (errno != 0) { + fprintf (stderr, "Error looking up user: %m\n"); + exit (EXIT_FAILURE); + } + + long val; + int err = xstrtol (id, NULL, 10, &val, ""); + if (err != LONGINT_OK) { + fprintf (stderr, "%s is not a valid user name or uid\n", id); + usage (stderr, progname); + exit (EXIT_FAILURE); + } + + return (uid_t) val; + } + + return pwd->pw_uid; +} + +static gid_t +parsegroup (const char *id, const char *progname) +{ + + struct group *grp; + + errno = 0; + grp = getgrnam (id); + + if (NULL == grp) { + if (errno != 0) { + fprintf (stderr, "Error looking up group: %m\n"); + exit (EXIT_FAILURE); + } + + long val; + int err = xstrtol (id, NULL, 10, &val, ""); + if (err != LONGINT_OK) { + fprintf (stderr, "%s is not a valid group name or gid\n", id); + usage (stderr, progname); + exit (EXIT_FAILURE); + } + + return (gid_t) val; + } + + return grp->gr_gid; +} + int main (int argc, char *argv[]) { /* First thing: start the clock. */ gettimeofday (&start_t, NULL); + const char *format = "cpio"; const char *whitelist = NULL; + uid_t euid = geteuid (); + gid_t egid = getegid (); + /* Command line arguments. */ for (;;) { int c = getopt_long (argc, argv, options, long_options, NULL); @@ -107,6 +178,14 @@ main (int argc, char *argv[]) format = optarg; break; + case 'u': + euid = parseuser (optarg, argv[0]); + break; + + case 'g': + egid = parsegroup (optarg, argv[0]); + break; + case 'k': whitelist = optarg; break; @@ -125,6 +204,37 @@ main (int argc, char *argv[]) } } + /* We need to set the real, not effective, uid here to work round a + * misfeature in bash. bash will automatically reset euid to uid when + * invoked. As shell is used in places by febootstrap-supermin-helper, this + * results in code running with varying privilege. */ + uid_t uid = getuid (); + gid_t gid = getgid (); + + if (uid != euid || gid != egid) { + if (uid != 0) { + fprintf (stderr, "The -u and -g options require root privileges.\n"); + usage (stderr, argv[0]); + exit (EXIT_FAILURE); + } + + /* Need to become root first because setgid and setuid require it */ + if (seteuid (0) == -1) { + perror ("seteuid"); + exit (EXIT_FAILURE); + } + + /* Set gid and uid to command-line parameters */ + if (setgid (egid) == -1) { + perror ("setgid"); + exit (EXIT_FAILURE); + } + if (setuid (euid) == -1) { + perror ("setuid"); + exit (EXIT_FAILURE); + } + } + /* Select the correct writer module. */ struct writer *writer; int nr_outputs; diff --git a/lib/.gitignore b/lib/.gitignore index 3dcbcf7..c76f105 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -132,3 +132,13 @@ /hash-pjw.c /hash-pjw.h /sys_wait.in.h +/getopt.c +/getopt.in.h +/getopt1.c +/getopt_int.h +/inttypes.h +/inttypes.in.h +/xstrtol-error.c +/xstrtol.c +/xstrtol.h +/xstrtoul.c diff --git a/m4/.gitignore b/m4/.gitignore index 97d2c1b..bbef2cd 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -90,3 +90,7 @@ /xvasprintf.m4 /asm-underscore.m4 /sys_wait_h.m4 +/getopt.m4 +/inttypes-pri.m4 +/inttypes.m4 +/xstrtol.m4 diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index ebb8cc4..3210f5c 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -15,7 +15,7 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --no-libtool --macro-prefix=gl error filevercmp fts full-write hash hash-pjw xalloc xvasprintf +# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --no-libtool --macro-prefix=gl error filevercmp fts full-write hash hash-pjw xalloc xstrtol xvasprintf # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([]) @@ -27,6 +27,7 @@ gl_MODULES([ hash hash-pjw xalloc + xstrtol xvasprintf ]) gl_AVOID([]) -- 1.8.3.1