summaryrefslogtreecommitdiff
path: root/libmisc/idmapping.c
diff options
context:
space:
mode:
authorBalint Reczey <balint@balintreczey.hu>2021-11-07 15:18:49 +0100
committerBalint Reczey <balint@balintreczey.hu>2021-11-07 15:18:49 +0100
commit749c1780621163ca5108f164861324bafa9e0ae8 (patch)
tree51001872624a692018c45bf39276df94b603fb19 /libmisc/idmapping.c
parentd906ecd3b652d95af6ffb974a2f6669501bb9496 (diff)
downloadshadow-749c1780621163ca5108f164861324bafa9e0ae8.tar.gz
New upstream version 4.9upstream/4.9
Diffstat (limited to 'libmisc/idmapping.c')
-rw-r--r--libmisc/idmapping.c60
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);
}