From 673c2a6f9aa6c69588f4c1be08589b8d3475a520 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 22 Jan 2013 01:20:07 -0800 Subject: [PATCH] newuidmap,newgidmap: New suid helpers for using subordinate uids and gids Signed-off-by: "Eric W. Biederman" Signed-off-by: Serge Hallyn --- libmisc/Makefile.am | 2 + libmisc/idmapping.c | 126 ++++++++++++++++++++++++++++++ libmisc/idmapping.h | 44 +++++++++++ man/Makefile.am | 4 + man/newgidmap.1.xml | 157 +++++++++++++++++++++++++++++++++++++ man/newuidmap.1.xml | 154 +++++++++++++++++++++++++++++++++++++ src/Makefile.am | 5 +- src/newgidmap.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ src/newuidmap.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 856 insertions(+), 2 deletions(-) create mode 100644 libmisc/idmapping.c create mode 100644 libmisc/idmapping.h create mode 100644 man/newgidmap.1.xml create mode 100644 man/newuidmap.1.xml create mode 100644 src/newgidmap.c create mode 100644 src/newuidmap.c diff --git a/libmisc/Makefile.am b/libmisc/Makefile.am index 5de91938..76f3c052 100644 --- a/libmisc/Makefile.am +++ b/libmisc/Makefile.am @@ -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 index 00000000..cb9e8985 --- /dev/null +++ b/libmisc/idmapping.c @@ -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 +#include +#include +#include +#include +#include +#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 index 00000000..88cf7616 --- /dev/null +++ b/libmisc/idmapping.h @@ -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_ */ + diff --git a/man/Makefile.am b/man/Makefile.am index fc617e98..b50ca41c 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -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 index 00000000..424a480c --- /dev/null +++ b/man/newgidmap.1.xml @@ -0,0 +1,157 @@ + + + +]> + + + + newgidmap + 1 + User Commands + shadow-utils + &SHADOW_UTILS_VERSION; + + + newgidmap + set the gid mapping of a user namespace + + + + + newgidmap + + pid + + + gid + + + lowergid + + + count + + + + pid + + + gid + + + lowergid + + + count + + + ... + + + + + + + DESCRIPTION + + The newgidmap sets /proc/[pid]/gid_map based on it's + command line arguments and the gids allowed in /etc/subgid. + + + + + + OPTIONS + + There currently are no options to the newgidmap command. + + + + + + + NOTE + + The only restriction placed on the login shell is that the command + name must be listed in /etc/shells, 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 /bin/rsh in + /etc/shells is discouraged since accidentally + changing to a restricted shell would prevent the user from ever + changing her login shell back to its original value. + + + + + + FILES + + + /etc/subgid + + List of users subordinate user IDs. + + + + /proc/[pid]/gid_map + + Mapping of gids from one between user namespaces. + + + + + + + SEE ALSO + + + login.defs5 + , + + useradd8 + , + + usermod8 + , + + newusers8 + , + + userdel8 + , + + subgid5 + . + + + diff --git a/man/newuidmap.1.xml b/man/newuidmap.1.xml new file mode 100644 index 00000000..c6687eaa --- /dev/null +++ b/man/newuidmap.1.xml @@ -0,0 +1,154 @@ + + + +]> + + + + newuidmap + 1 + User Commands + shadow-utils + &SHADOW_UTILS_VERSION; + + + newuidmap + set the uid mapping of a user namespace + + + + + newuidmap + + pid + + + uid + + + loweruid + + + count + + + + uid + + + loweruid + + + count + + + ... + + + + + + + DESCRIPTION + + The newuidmap sets /proc/[pid]/uid_map based on it's + command line arguments and the uids allowed in /etc/subuid. + + + + + + OPTIONS + + There currently are no options to the newuidmap command. + + + + + + + NOTE + + The only restriction placed on the login shell is that the command + name must be listed in /etc/shells, 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 /bin/rsh in + /etc/shells is discouraged since accidentally + changing to a restricted shell would prevent the user from ever + changing her login shell back to its original value. + + + + + + FILES + + + /etc/subuid + + List of users subordinate user IDs. + + + + /proc/[pid]/uid_map + + Mapping of uids from one between user namespaces. + + + + + + + SEE ALSO + + + login.defs5 + , + + useradd8 + , + + usermod8 + , + + newusers8 + , + + userdel8 + , + + subuid5 + . + + + diff --git a/src/Makefile.am b/src/Makefile.am index 072c3d7a..e71ad55e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..1527a615 --- /dev/null +++ b/src/newgidmap.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 [ ] ... \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 index 00000000..69c50940 --- /dev/null +++ b/src/newuidmap.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 [ ] ... \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; +}