diff options
author | Balint Reczey <balint@balintreczey.hu> | 2021-11-07 15:18:49 +0100 |
---|---|---|
committer | Balint Reczey <balint@balintreczey.hu> | 2021-11-07 15:18:49 +0100 |
commit | 749c1780621163ca5108f164861324bafa9e0ae8 (patch) | |
tree | 51001872624a692018c45bf39276df94b603fb19 /libmisc/idmapping.c | |
parent | d906ecd3b652d95af6ffb974a2f6669501bb9496 (diff) | |
download | shadow-749c1780621163ca5108f164861324bafa9e0ae8.tar.gz |
New upstream version 4.9upstream/4.9
Diffstat (limited to 'libmisc/idmapping.c')
-rw-r--r-- | libmisc/idmapping.c | 60 |
1 files changed, 43 insertions, 17 deletions
diff --git a/libmisc/idmapping.c b/libmisc/idmapping.c index aea94936..3324f671 100644 --- a/libmisc/idmapping.c +++ b/libmisc/idmapping.c @@ -36,8 +36,8 @@ #include <stdio.h> #include "prototypes.h" #include "idmapping.h" -#include <sys/prctl.h> #if HAVE_SYS_CAPABILITY_H +#include <sys/prctl.h> #include <sys/capability.h> #endif @@ -47,19 +47,19 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv) int idx, argidx; if (ranges < 0 || argc < 0) { - fprintf(stderr, "%s: error calculating number of arguments\n", Prog); + fprintf(shadow_logfd, "%s: error calculating number of arguments\n", Prog); return NULL; } if (ranges != ((argc + 2) / 3)) { - fprintf(stderr, "%s: ranges: %u is wrong for argc: %d\n", Prog, ranges, argc); + fprintf(shadow_logfd, "%s: ranges: %u is wrong for argc: %d\n", Prog, ranges, argc); return NULL; } if ((ranges * 3) > argc) { - fprintf(stderr, "ranges: %u argc: %d\n", + fprintf(shadow_logfd, "ranges: %u argc: %d\n", ranges, argc); - fprintf(stderr, + fprintf(shadow_logfd, _( "%s: Not enough arguments to form %u mappings\n"), Prog, ranges); return NULL; @@ -67,7 +67,7 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv) mappings = calloc(ranges, sizeof(*mappings)); if (!mappings) { - fprintf(stderr, _( "%s: Memory allocation failure\n"), + fprintf(shadow_logfd, _( "%s: Memory allocation failure\n"), Prog); exit(EXIT_FAILURE); } @@ -88,24 +88,24 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv) return NULL; } if (ULONG_MAX - mapping->upper <= mapping->count || ULONG_MAX - mapping->lower <= mapping->count) { - fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog); + fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog); exit(EXIT_FAILURE); } if (mapping->upper > UINT_MAX || mapping->lower > UINT_MAX || mapping->count > UINT_MAX) { - fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog); + fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog); exit(EXIT_FAILURE); } if (mapping->lower + mapping->count > UINT_MAX || mapping->upper + mapping->count > UINT_MAX) { - fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog); + fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog); exit(EXIT_FAILURE); } if (mapping->lower + mapping->count < mapping->lower || mapping->upper + mapping->count < mapping->upper) { /* this one really shouldn't be possible given previous checks */ - fprintf(stderr, _( "%s: subuid overflow detected.\n"), Prog); + fprintf(shadow_logfd, _( "%s: subuid overflow detected.\n"), Prog); exit(EXIT_FAILURE); } } @@ -123,6 +123,25 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv) */ #define ULONG_DIGITS ((((sizeof(unsigned long) * CHAR_BIT) + 9)/10)*3) +#if HAVE_SYS_CAPABILITY_H +static inline bool maps_lower_root(int cap, int ranges, struct map_range *mappings) +{ + int idx; + struct map_range *mapping; + + if (cap != CAP_SETUID) + return false; + + mapping = mappings; + for (idx = 0; idx < ranges; idx++, mapping++) { + if (mapping->lower == 0) + return true; + } + + return false; +} +#endif + /* * The ruid refers to the caller's uid and is used to reset the effective uid * back to the callers real uid. @@ -157,19 +176,19 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings, } else if (strcmp(map_file, "gid_map") == 0) { cap = CAP_SETGID; } else { - fprintf(stderr, _("%s: Invalid map file %s specified\n"), Prog, map_file); + fprintf(shadow_logfd, _("%s: Invalid map file %s specified\n"), Prog, map_file); exit(EXIT_FAILURE); } /* Align setuid- and fscaps-based new{g,u}idmap behavior. */ if (geteuid() == 0 && geteuid() != ruid) { if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { - fprintf(stderr, _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), Prog); + fprintf(shadow_logfd, _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), Prog); exit(EXIT_FAILURE); } if (seteuid(ruid) < 0) { - fprintf(stderr, _("%s: Could not seteuid to %d\n"), Prog, ruid); + fprintf(shadow_logfd, _("%s: Could not seteuid to %d\n"), Prog, ruid); exit(EXIT_FAILURE); } } @@ -177,9 +196,15 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings, /* Lockdown new{g,u}idmap by dropping all unneeded capabilities. */ memset(data, 0, sizeof(data)); data[0].effective = CAP_TO_MASK(cap); + /* + * When uid 0 from the ancestor userns is supposed to be mapped into + * the child userns we need to retain CAP_SETFCAP. + */ + if (maps_lower_root(cap, ranges, mappings)) + data[0].effective |= CAP_TO_MASK(CAP_SETFCAP); data[0].permitted = data[0].effective; if (capset(&hdr, data) < 0) { - fprintf(stderr, _("%s: Could not set caps\n"), Prog); + fprintf(shadow_logfd, _("%s: Could not set caps\n"), Prog); exit(EXIT_FAILURE); } #endif @@ -197,7 +222,7 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings, mapping->lower, mapping->count); if ((written <= 0) || (written >= (bufsize - (pos - buf)))) { - fprintf(stderr, _("%s: snprintf failed!\n"), Prog); + fprintf(shadow_logfd, _("%s: snprintf failed!\n"), Prog); exit(EXIT_FAILURE); } pos += written; @@ -206,14 +231,15 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings, /* Write the mapping to the mapping file */ fd = openat(proc_dir_fd, map_file, O_WRONLY); if (fd < 0) { - fprintf(stderr, _("%s: open of %s failed: %s\n"), + fprintf(shadow_logfd, _("%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"), + fprintf(shadow_logfd, _("%s: write to %s failed: %s\n"), Prog, map_file, strerror(errno)); exit(EXIT_FAILURE); } close(fd); + free(buf); } |