]> granicus.if.org Git - shadow/commitdiff
newuidmap,newgidmap: New suid helpers for using subordinate uids and gids
authorEric W. Biederman <ebiederm@xmission.com>
Tue, 22 Jan 2013 09:20:07 +0000 (01:20 -0800)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Mon, 5 Aug 2013 15:08:46 +0000 (10:08 -0500)
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
libmisc/Makefile.am
libmisc/idmapping.c [new file with mode: 0644]
libmisc/idmapping.h [new file with mode: 0644]
man/Makefile.am
man/newgidmap.1.xml [new file with mode: 0644]
man/newuidmap.1.xml [new file with mode: 0644]
src/Makefile.am
src/newgidmap.c [new file with mode: 0644]
src/newuidmap.c [new file with mode: 0644]

index 5de91938ff83d488a84904055aa3467575143c6d..76f3c052580d46c460300564fba633649230cc46 100644 (file)
@@ -32,6 +32,8 @@ libmisc_a_SOURCES = \
        getgr_nam_gid.c \
        getrange.c \
        hushed.c \
+       idmapping.h \
+       idmapping.c \
        isexpired.c \
        limits.c \
        list.c log.c \
diff --git a/libmisc/idmapping.c b/libmisc/idmapping.c
new file mode 100644 (file)
index 0000000..cb9e898
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2013 Eric Biederman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "prototypes.h"
+#include "idmapping.h"
+
+struct map_range *get_map_ranges(int ranges, int argc, char **argv)
+{
+       struct map_range *mappings, *mapping;
+       int idx, argidx;
+
+       if ((ranges * 3) > argc) {
+               fprintf(stderr, "ranges: %u argc: %d\n",
+                       ranges, argc);
+               fprintf(stderr,
+                       _( "%s: Not enough arguments to form %u mappings\n"),
+                       Prog, ranges);
+               return NULL;
+       }
+
+       mappings = calloc(ranges, sizeof(*mappings));
+       if (!mappings) {
+               fprintf(stderr, _( "%s: Memory allocation failure\n"),
+                       Prog);
+               exit(EXIT_FAILURE);
+       }
+
+       /* Gather up the ranges from the command line */
+       mapping = mappings;
+       for (idx = 0; idx < ranges; idx++, argidx += 3, mapping++) {
+               if (!getulong(argv[argidx + 0], &mapping->upper))
+                       return NULL;
+               if (!getulong(argv[argidx + 1], &mapping->lower))
+                       return NULL;
+               if (!getulong(argv[argidx + 2], &mapping->count))
+                       return NULL;
+       }
+       return mappings;
+}
+
+/* Number of ascii digits needed to print any unsigned long in decimal.
+ * There are approximately 10 bits for every 3 decimal digits.
+ * So from bits to digits the formula is roundup((Number of bits)/10) * 3.
+ * For common sizes of integers this works out to:
+ *  2bytes -->  6 ascii estimate  -> 65536  (5 real)
+ *  4bytes --> 12 ascii estimated -> 4294967296 (10 real)
+ *  8bytes --> 21 ascii estimated -> 18446744073709551616 (20 real)
+ * 16bytes --> 39 ascii estimated -> 340282366920938463463374607431768211456 (39 real)
+ */
+#define ULONG_DIGITS ((((sizeof(unsigned long) * CHAR_BIT) + 9)/10)*3)
+
+
+void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings,
+       const char *map_file)
+{
+       int idx;
+       struct map_range *mapping;
+       size_t bufsize;
+       char *buf, *pos;
+       int fd;
+
+       bufsize = ranges * ((ULONG_DIGITS  + 1) * 3);
+       pos = buf = xmalloc(bufsize);
+
+       /* Build the mapping command */
+       mapping = mappings;
+       for (idx = 0; idx < ranges; idx++, mapping++) {
+               /* Append this range to the string that will be written */
+               int written = snprintf(pos, bufsize - (pos - buf),
+                       "%lu %lu %lu\n",
+                       mapping->upper,
+                       mapping->lower,
+                       mapping->count);
+               if ((written <= 0) || (written >= (bufsize - (pos - buf)))) {
+                       fprintf(stderr, _("%s: snprintf failed!\n"), Prog);
+                       exit(EXIT_FAILURE);
+               }
+               pos += written;
+       }
+
+       /* Write the mapping to the maping file */
+       fd = openat(proc_dir_fd, map_file, O_WRONLY);
+       if (fd < 0) {
+               fprintf(stderr, _("%s: open of %s failed: %s\n"),
+                       Prog, map_file, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       if (write(fd, buf, pos - buf) != (pos - buf)) {
+               fprintf(stderr, _("%s: write to %s failed: %s\n"),
+                       Prog, map_file, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       close(fd);
+}
diff --git a/libmisc/idmapping.h b/libmisc/idmapping.h
new file mode 100644 (file)
index 0000000..88cf761
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Eric Biederman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IDMAPPING_H_
+#define _IDMAPPING_H_
+
+struct map_range {
+       unsigned long upper;
+       unsigned long lower;
+       unsigned long count;
+};
+
+extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
+extern void write_mapping(int proc_dir_fd, int ranges,
+       struct map_range *mappings, const char *map_file);
+
+#endif /* _ID_MAPPING_H_ */
+
index fc617e9856782a3ff18e9ddb69430436f1587db0..b50ca41c19408f2061b033c8674565b5fdc91c5b 100644 (file)
@@ -30,7 +30,9 @@ man_MANS = \
        man1/login.1 \
        man5/login.defs.5 \
        man8/logoutd.8 \
+       man1/newgidmap.1 \
        man1/newgrp.1 \
+       man1/newuidmap.1 \
        man8/newusers.8 \
        man8/nologin.8 \
        man1/passwd.1 \
@@ -83,7 +85,9 @@ man_XMANS = \
        login.access.5.xml \
        login.defs.5.xml \
        logoutd.8.xml \
+       newgidmap.1.xml \
        newgrp.1.xml \
+       newuidmap.1.xml \
        newusers.8.xml \
        nologin.8.xml \
        passwd.1.xml \
diff --git a/man/newgidmap.1.xml b/man/newgidmap.1.xml
new file mode 100644 (file)
index 0000000..424a480
--- /dev/null
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (c) 2013 Eric W. Biederman
+   All rights reserved.
+  
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. The name of the copyright holders or contributors may not be used to
+      endorse or promote products derived from this software without
+      specific prior written permission.
+  
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+   HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!-- SHADOW-CONFIG-HERE -->
+]>
+
+<refentry id='newgidmap.1'>
+  <refmeta>
+    <refentrytitle>newgidmap</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">User Commands</refmiscinfo>
+    <refmiscinfo class="source">shadow-utils</refmiscinfo>
+    <refmiscinfo class="version">&SHADOW_UTILS_VERSION;</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>newgidmap</refname>
+    <refpurpose>set the gid mapping of a user namespace</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id='synopsis'>
+    <cmdsynopsis>
+      <command>newgidmap</command>
+      <arg choice='plain'>
+       <replaceable>pid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>gid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>lowergid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>count</replaceable>
+      </arg>
+      <arg choice='opt'>
+       <arg choice='plain'>
+         <replaceable>pid</replaceable>
+       </arg>
+       <arg choice='plain'>
+         <replaceable>gid</replaceable>
+       </arg>
+       <arg choice='plain'>
+         <replaceable>lowergid</replaceable>
+       </arg>
+       <arg choice='plain'>
+         <replaceable>count</replaceable>
+       </arg>
+       <arg choice='opt'>
+         <replaceable>...</replaceable>
+       </arg>
+      </arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+      The <command>newgidmap</command> sets <filename>/proc/[pid]/gid_map</filename> based on it's
+      command line arguments and the gids allowed in <filename>/etc/subgid</filename>.
+    </para>
+
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      There currently are no options to the <command>newgidmap</command> command.
+    </para>
+    <variablelist remap='IP'>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='note'>
+    <title>NOTE</title>
+    <para>
+      The only restriction placed on the login shell is that the command
+      name must be listed in <filename>/etc/shells</filename>, unless the
+      invoker is the superuser, and then any value may be added. An
+      account with a restricted login shell may not change her login shell.
+      For this reason, placing <filename>/bin/rsh</filename> in
+      <filename>/etc/shells</filename> is discouraged since accidentally
+      changing to a restricted shell would prevent the user from ever
+      changing her login shell back to its original value.
+    </para>
+  </refsect1>
+
+
+  <refsect1 id='files'>
+    <title>FILES</title>
+    <variablelist>
+      <varlistentry>
+       <term><filename>/etc/subgid</filename></term>
+       <listitem>
+         <para>List of users subordinate user IDs.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><filename>/proc/[pid]/gid_map</filename></term>
+       <listitem>
+         <para>Mapping of gids from one between user namespaces.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='see_also'>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>usermod</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>newusers</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>userdel</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>subgid</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/newuidmap.1.xml b/man/newuidmap.1.xml
new file mode 100644 (file)
index 0000000..c6687ea
--- /dev/null
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright (c) 2013 Eric W. Biederman
+   All rights reserved.
+  
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. The name of the copyright holders or contributors may not be used to
+      endorse or promote products derived from this software without
+      specific prior written permission.
+  
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+   HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!-- SHADOW-CONFIG-HERE -->
+]>
+
+<refentry id='newuidmap.1'>
+  <refmeta>
+    <refentrytitle>newuidmap</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">User Commands</refmiscinfo>
+    <refmiscinfo class="source">shadow-utils</refmiscinfo>
+    <refmiscinfo class="version">&SHADOW_UTILS_VERSION;</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>newuidmap</refname>
+    <refpurpose>set the uid mapping of a user namespace</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv id='synopsis'>
+    <cmdsynopsis>
+      <command>newuidmap</command>
+      <arg choice='plain'>
+       <replaceable>pid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>uid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>loweruid</replaceable>
+      </arg>
+      <arg choice='plain'>
+       <replaceable>count</replaceable>
+      </arg>
+      <arg choice='opt'>
+       <arg choice='plain'>
+         <replaceable>uid</replaceable>
+       </arg>
+       <arg choice='plain'>
+         <replaceable>loweruid</replaceable>
+       </arg>
+       <arg choice='plain'>
+         <replaceable>count</replaceable>
+       </arg>
+       <arg choice='opt'>
+         <replaceable>...</replaceable>
+       </arg>
+      </arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+      The <command>newuidmap</command> sets <filename>/proc/[pid]/uid_map</filename> based on it's
+      command line arguments and the uids allowed in <filename>/etc/subuid</filename>.
+    </para>
+
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      There currently are no options to the <command>newuidmap</command> command.
+    </para>
+    <variablelist remap='IP'>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='note'>
+    <title>NOTE</title>
+    <para>
+      The only restriction placed on the login shell is that the command
+      name must be listed in <filename>/etc/shells</filename>, unless the
+      invoker is the superuser, and then any value may be added. An
+      account with a restricted login shell may not change her login shell.
+      For this reason, placing <filename>/bin/rsh</filename> in
+      <filename>/etc/shells</filename> is discouraged since accidentally
+      changing to a restricted shell would prevent the user from ever
+      changing her login shell back to its original value.
+    </para>
+  </refsect1>
+
+
+  <refsect1 id='files'>
+    <title>FILES</title>
+    <variablelist>
+      <varlistentry>
+       <term><filename>/etc/subuid</filename></term>
+       <listitem>
+         <para>List of users subordinate user IDs.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><filename>/proc/[pid]/uid_map</filename></term>
+       <listitem>
+         <para>Mapping of uids from one between user namespaces.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='see_also'>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>login.defs</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>usermod</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>newusers</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>userdel</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>subuid</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+</refentry>
index 072c3d7a88e321dbcd4cfef042e980bd58caf7a7..e71ad55e1d7c895a95fbe13ffb3ccc8f5b379181 100644 (file)
@@ -24,7 +24,8 @@ INCLUDES = \
 
 bin_PROGRAMS   = groups login su
 sbin_PROGRAMS  = nologin
-ubin_PROGRAMS  = faillog lastlog chage chfn chsh expiry gpasswd newgrp passwd
+ubin_PROGRAMS  = faillog lastlog chage chfn chsh expiry gpasswd newgrp passwd \
+       newgidmap newuidmap
 usbin_PROGRAMS = \
        chgpasswd \
        chpasswd \
@@ -49,7 +50,7 @@ usbin_PROGRAMS = \
 noinst_PROGRAMS = id sulogin
 
 suidbins       = su
-suidubins      = chage chfn chsh expiry gpasswd newgrp passwd
+suidubins      = chage chfn chsh expiry gpasswd newgrp passwd newuidmap newgidmap
 if ACCT_TOOLS_SETUID
        suidubins += chage chgpasswd chpasswd groupadd groupdel groupmod newusers useradd userdel usermod
 endif
diff --git a/src/newgidmap.c b/src/newgidmap.c
new file mode 100644 (file)
index 0000000..1527a61
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2013 Eric Biederman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "prototypes.h"
+#include "subordinateio.h"
+#include "idmapping.h"
+
+/*
+ * Global variables
+ */
+const char *Prog;
+
+static bool verify_range(struct passwd *pw, struct map_range *range)
+{
+       /* An empty range is invalid */
+       if (range->count == 0)
+               return false;
+
+       /* Test /etc/subgid */
+       if (have_sub_gids(pw->pw_name, range->lower, range->count))
+               return true;
+
+       /* Allow a process to map it's own gid */
+       if ((range->count == 1) && (pw->pw_gid == range->lower))
+               return true;
+
+       return false;
+}
+
+static void verify_ranges(struct passwd *pw, int ranges,
+       struct map_range *mappings)
+{
+       struct map_range *mapping;
+       int idx;
+
+       mapping = mappings;
+       for (idx = 0; idx < ranges; idx++, mapping++) {
+               if (!verify_range(pw, mapping)) {
+                       fprintf(stderr, _( "%s: gid range [%lu-%lu) -> [%lu-%lu) not allowed\n"),
+                               Prog,
+                               mapping->upper,
+                               mapping->upper + mapping->count,
+                               mapping->lower,
+                               mapping->lower + mapping->count);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+static void usage(void)
+{
+       fprintf(stderr, _("usage: %s <pid> <gid> <lowergid> <count> [ <gid> <lowergid> <count> ] ... \n"), Prog);
+       exit(EXIT_FAILURE);
+}
+
+/*
+ * newgidmap - Set the gid_map for the specified process
+ */
+int main(int argc, char **argv)
+{
+       char proc_dir_name[PATH_MAX];
+       char *target_str;
+       pid_t target, parent;
+       int proc_dir_fd;
+       int ranges;
+       struct map_range *mappings;
+       struct stat st;
+       struct passwd *pw;
+       int written;
+
+       Prog = Basename (argv[0]);
+
+       /*
+        * The valid syntax are
+        * newgidmap target_pid
+        */
+       if (argc < 2)
+               usage();
+
+       /* Find the process that needs it's user namespace
+        * gid mapping set.
+        */
+       target_str = argv[1];
+       if (!get_pid(target_str, &target))
+               usage();
+
+       written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/",
+               target);
+       if ((written <= 0) || (written >= sizeof(proc_dir_name))) {
+               fprintf(stderr, "%s: snprintf of proc path failed: %s\n",
+                       Prog, strerror(errno));
+       }
+
+       proc_dir_fd = open(proc_dir_name, O_DIRECTORY);
+       if (proc_dir_fd < 0) {
+               fprintf(stderr, _("%s: Could not open proc directory for target %u\n"),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       /* Who am i? */
+       pw = get_my_pwent ();
+       if (NULL == pw) {
+               fprintf (stderr,
+                       _("%s: Cannot determine your user name.\n"),
+                       Prog);
+               SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
+                               (unsigned long) getuid ()));
+               return EXIT_FAILURE;
+       }
+       
+       /* Get the effective uid and effective gid of the target process */
+       if (fstat(proc_dir_fd, &st) < 0) {
+               fprintf(stderr, _("%s: Could not stat directory for target %u\n"),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       /* Verify real user and real group matches the password entry
+        * and the effective user and group of the program whose
+        * mappings we have been asked to set.
+        */
+       if ((getuid() != pw->pw_uid) ||
+           (getgid() != pw->pw_gid) ||
+           (pw->pw_uid != st.st_uid) ||
+           (pw->pw_gid != st.st_gid)) {
+               fprintf(stderr, _( "%s: Target %u is owned by a different user\n" ),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       if (!sub_gid_open(O_RDONLY)) {
+               return EXIT_FAILURE;
+       }
+
+       ranges = ((argc - 2) + 2) / 3;
+       mappings = get_map_ranges(ranges, argc - 2, argv + 2);
+       if (!mappings)
+               usage();
+
+       verify_ranges(pw, ranges, mappings);
+
+       write_mapping(proc_dir_fd, ranges, mappings, "gid_map");
+       sub_gid_close();
+
+       return EXIT_SUCCESS;
+}
diff --git a/src/newuidmap.c b/src/newuidmap.c
new file mode 100644 (file)
index 0000000..69c5094
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2013 Eric Biederman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "prototypes.h"
+#include "subordinateio.h"
+#include "idmapping.h"
+
+/*
+ * Global variables
+ */
+const char *Prog;
+
+static bool verify_range(struct passwd *pw, struct map_range *range)
+{
+       /* An empty range is invalid */
+       if (range->count == 0)
+               return false;
+
+       /* Test /etc/subuid */
+       if (have_sub_uids(pw->pw_name, range->lower, range->count))
+               return true;
+
+       /* Allow a process to map it's own uid */
+       if ((range->count == 1) && (pw->pw_uid == range->lower))
+               return true;
+
+       return false;
+}
+
+static void verify_ranges(struct passwd *pw, int ranges,
+       struct map_range *mappings)
+{
+       struct map_range *mapping;
+       int idx;
+
+       mapping = mappings;
+       for (idx = 0; idx < ranges; idx++, mapping++) {
+               if (!verify_range(pw, mapping)) {
+                       fprintf(stderr, _( "%s: uid range [%lu-%lu) -> [%lu-%lu) not allowed\n"),
+                               Prog,
+                               mapping->upper,
+                               mapping->upper + mapping->count,
+                               mapping->lower,
+                               mapping->lower + mapping->count);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+void usage(void)
+{
+       fprintf(stderr, _("usage: %s <pid> <uid> <loweruid> <count> [ <uid> <loweruid> <count> ] ... \n"), Prog);
+       exit(EXIT_FAILURE);
+}
+
+/*
+ * newuidmap - Set the uid_map for the specified process
+ */
+int main(int argc, char **argv)
+{
+       char proc_dir_name[PATH_MAX];
+       char *target_str;
+       pid_t target, parent;
+       int proc_dir_fd;
+       int ranges;
+       struct map_range *mappings;
+       struct stat st;
+       struct passwd *pw;
+       int written;
+
+       Prog = Basename (argv[0]);
+
+       /*
+        * The valid syntax are
+        * newuidmap target_pid
+        */
+       if (argc < 2)
+               usage();
+
+       /* Find the process that needs it's user namespace
+        * uid mapping set.
+        */
+       target_str = argv[1];
+       if (!get_pid(target_str, &target))
+               usage();
+
+       written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/",
+               target);
+       if ((written <= 0) || (written >= sizeof(proc_dir_name))) {
+               fprintf(stderr, "%s: snprintf of proc path failed: %s\n",
+                       Prog, strerror(errno));
+       }
+
+       proc_dir_fd = open(proc_dir_name, O_DIRECTORY);
+       if (proc_dir_fd < 0) {
+               fprintf(stderr, _("%s: Could not open proc directory for target %u\n"),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       /* Who am i? */
+       pw = get_my_pwent ();
+       if (NULL == pw) {
+               fprintf (stderr,
+                       _("%s: Cannot determine your user name.\n"),
+                       Prog);
+               SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
+                               (unsigned long) getuid ()));
+               return EXIT_FAILURE;
+       }
+       
+       /* Get the effective uid and effective gid of the target process */
+       if (fstat(proc_dir_fd, &st) < 0) {
+               fprintf(stderr, _("%s: Could not stat directory for target %u\n"),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       /* Verify real user and real group matches the password entry
+        * and the effective user and group of the program whose
+        * mappings we have been asked to set.
+        */
+       if ((getuid() != pw->pw_uid) ||
+           (getgid() != pw->pw_gid) ||
+           (pw->pw_uid != st.st_uid) ||
+           (pw->pw_gid != st.st_gid)) {
+               fprintf(stderr, _( "%s: Target %u is owned by a different user\n" ),
+                       Prog, target);
+               return EXIT_FAILURE;
+       }
+
+       if (!sub_uid_open(O_RDONLY)) {
+               return EXIT_FAILURE;
+       }
+
+       ranges = ((argc - 2) + 2) / 3;
+       mappings = get_map_ranges(ranges, argc - 2, argv + 2);
+       if (!mappings)
+               usage();
+
+       verify_ranges(pw, ranges, mappings);
+
+       write_mapping(proc_dir_fd, ranges, mappings, "uid_map");
+       sub_uid_close();
+
+       return EXIT_SUCCESS;
+}