[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH 1/2] Add -u and -g options to febootstrap-supermin-helper



Bash automatically resets euid to uid when it executes. This can mean that the
effective user id of a program at the point it calls febootstrap-supermin-helper
can 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 differing owners

The -u and -g options allow the caller to pass in an explicit user and group to
run as. These will be used when generating a checksum. Additionally,
febootstrap-supermin-helper will set(u|g)id as appropriate if they are given for
non-checksum output. This ensures all generated files will have the correct
ownership, regardless of how they are created.
---
 helper/checksum.c |    2 +-
 helper/helper.h   |    2 +
 helper/main.c     |  145 ++++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 130 insertions(+), 19 deletions(-)

diff --git a/helper/checksum.c b/helper/checksum.c
index 337134c..95db65f 100644
--- a/helper/checksum.c
+++ b/helper/checksum.c
@@ -51,7 +51,7 @@ checksum_start (const char *hostcpu, const char *appliance,
     error (EXIT_FAILURE, errno, "popen: command failed: %s", shacmd);
 
   fprintf (pp, "%s %s %s %d\n",
-           PACKAGE_STRING, hostcpu, modpath, geteuid ());
+           PACKAGE_STRING, hostcpu, modpath, run_uid);
 }
 
 static void
diff --git a/helper/helper.h b/helper/helper.h
index e0d1fbb..e5f47df 100644
--- a/helper/helper.h
+++ b/helper/helper.h
@@ -50,6 +50,8 @@ struct writer {
 /* main.c */
 extern struct timeval start_t;
 extern int verbose;
+extern uid_t run_uid;
+extern uid_t run_gid;
 
 /* appliance.c */
 extern void create_appliance (const char *hostcpu, char **inputs, int nr_inputs, const char *whitelist, const char *modpath, const char *initrd, const char *appliance, struct writer *writer);
diff --git a/helper/main.c b/helper/main.c
index 4afcb24..d258381 100644
--- a/helper/main.c
+++ b/helper/main.c
@@ -28,6 +28,8 @@
 #include <sys/types.h>
 #include <sys/time.h>
 #include <assert.h>
+#include <grp.h>
+#include <pwd.h>
 
 #include "error.h"
 
@@ -35,15 +37,17 @@
 
 struct timeval start_t;
 int verbose = 0;
-
-static const char *format = "cpio";
+uid_t run_uid;
+uid_t run_gid;
 
 enum { HELP_OPTION = CHAR_MAX + 1 };
 
-static const char *options = "f:k:vV";
+static const char *options = "f:u:g:k:vV";
 static const struct option long_options[] = {
   { "help", 0, 0, HELP_OPTION },
   { "format", required_argument, 0, 'f' },
+  { "user", 0, 0, 'u' },
+  { "group", 0, 0, 'g' },
   { "kmods", required_argument, 0, 'k' },
   { "verbose", 0, 0, 'v' },
   { "version", 0, 0, 'V' },
@@ -75,6 +79,14 @@ usage (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, except to generate a\n"
+          "       checksum.\n"
+          "  -g group\n"
+          "       The group name or gid the appliance will run as. Use of\n"
+          "       this option requires root privileges, except to generate a\n"
+          "       checksum.\n"
           "  -k file | --kmods file\n"
           "       Specify kernel module whitelist.\n"
           "  --verbose | -v\n"
@@ -84,12 +96,70 @@ usage (const char *progname)
           progname, progname, progname, progname, progname, progname);
 }
 
+static int
+parseint (const char *id, const char *progname)
+{
+  char *invalid;
+  long int val = strtol (id, &invalid, 10);
+  if (*invalid != '\0') {
+    fprintf (stderr, "%s is not a valid uid\n", id);
+    usage (progname);
+    exit (EXIT_FAILURE);
+  }
+  return val;
+}
+
+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);
+    }
+
+    return parseint (id, progname);
+  }
+
+  return pwd->pw_uid;
+}
+
+static uid_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);
+    }
+
+    return parseint (id, progname);
+  }
+
+  return grp->gr_gid;
+}
+
 int
 main (int argc, char *argv[])
 {
   /* First thing: start the clock. */
   gettimeofday (&start_t, NULL);
 
+  const char *format = "cpio";
+  run_uid = geteuid ();
+  run_gid = getegid ();
   const char *whitelist = NULL;
 
   /* Command line arguments. */
@@ -106,6 +176,14 @@ main (int argc, char *argv[])
       format = optarg;
       break;
 
+    case 'u':
+      run_uid = parseuser (optarg, argv[0]);
+      break;
+
+    case 'g':
+      run_gid = parsegroup (optarg, argv[0]);
+      break;
+
     case 'k':
       whitelist = optarg;
       break;
@@ -128,23 +206,54 @@ main (int argc, char *argv[])
   struct writer *writer;
   int nr_outputs;
 
-  if (strcmp (format, "cpio") == 0) {
-    writer = &cpio_writer;
-    nr_outputs = 2;             /* kernel and appliance (== initrd) */
-  }
-  else if (strcmp (format, "ext2") == 0) {
-    writer = &ext2_writer;
-    nr_outputs = 3;             /* kernel, initrd, appliance */
-  }
-  else if (strcmp (format, "checksum") == 0) {
+  if (strcmp (format, "checksum") == 0) {
     writer = &checksum_writer;
     nr_outputs = 0;             /* (none) */
-  }
-  else {
-    fprintf (stderr,
-             "%s: incorrect output format (-f): must be cpio|ext2|checksum\n",
-             argv[0]);
-    exit (EXIT_FAILURE);
+  } else {
+    /* 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 != run_uid || gid != run_gid) {
+      if (uid != 0) {
+        fprintf (stderr, "The -u and -g options require root privileges.\n");
+        usage (stderr, argv[0]);
+        exit (EXIT_FAILURE);
+      }
+
+      if (seteuid (0) == -1) {
+          perror ("seteuid");
+          exit (EXIT_FAILURE);
+      }
+
+      /* Set gid and uid to command-line parameters */
+      if (setgid (run_gid) == -1) {
+        perror ("setgid");
+        exit (EXIT_FAILURE);
+      }
+      if (setuid (run_uid) == -1) {
+        perror ("setuid");
+        exit (EXIT_FAILURE);
+      }
+    }
+
+    if (strcmp (format, "cpio") == 0) {
+      writer = &cpio_writer;
+      nr_outputs = 2;             /* kernel and appliance (== initrd) */
+    }
+    else if (strcmp (format, "ext2") == 0) {
+      writer = &ext2_writer;
+      nr_outputs = 3;             /* kernel, initrd, appliance */
+    }
+    else {
+      fprintf (stderr,
+               "%s: incorrect output format (-f): must be cpio|ext2|checksum\n",
+               argv[0]);
+      exit (EXIT_FAILURE);
+    }
   }
 
   /* [optind .. optind+nr_inputs-1] hostcpu [argc-nr_outputs-1 .. argc-1]

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]