2013-01-22 14:50:07 +05:30
|
|
|
/*
|
2021-12-05 21:05:27 +05:30
|
|
|
* SPDX-FileCopyrightText: 2013 Eric Biederman
|
2013-01-22 14:50:07 +05:30
|
|
|
*
|
2021-12-05 21:05:27 +05:30
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2013-01-22 14:50:07 +05:30
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
2013-08-16 04:43:20 +05:30
|
|
|
#include <stdio.h>
|
2013-01-22 14:50:07 +05:30
|
|
|
#include "prototypes.h"
|
2023-01-30 05:24:07 +05:30
|
|
|
#include "stpeprintf.h"
|
2013-01-22 14:50:07 +05:30
|
|
|
#include "idmapping.h"
|
2018-10-08 21:48:18 +05:30
|
|
|
#if HAVE_SYS_CAPABILITY_H
|
2020-04-18 01:20:48 +05:30
|
|
|
#include <sys/prctl.h>
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
#include <sys/capability.h>
|
2018-10-08 21:48:18 +05:30
|
|
|
#endif
|
2021-11-29 05:07:53 +05:30
|
|
|
#include "shadowlog.h"
|
2013-01-22 14:50:07 +05:30
|
|
|
|
|
|
|
struct map_range *get_map_ranges(int ranges, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct map_range *mappings, *mapping;
|
|
|
|
int idx, argidx;
|
|
|
|
|
2013-06-21 22:17:36 +05:30
|
|
|
if (ranges < 0 || argc < 0) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), "%s: error calculating number of arguments\n", log_get_progname());
|
2013-06-21 22:17:36 +05:30
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-09-08 15:19:52 +05:30
|
|
|
if (ranges != ((argc + 2) / 3)) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), "%s: ranges: %u is wrong for argc: %d\n", log_get_progname(), ranges, argc);
|
2013-06-21 22:17:36 +05:30
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-22 14:50:07 +05:30
|
|
|
if ((ranges * 3) > argc) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), "ranges: %u argc: %d\n",
|
2013-01-22 14:50:07 +05:30
|
|
|
ranges, argc);
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(),
|
2013-01-22 14:50:07 +05:30
|
|
|
_( "%s: Not enough arguments to form %u mappings\n"),
|
2021-11-29 05:07:53 +05:30
|
|
|
log_get_progname(), ranges);
|
2013-01-22 14:50:07 +05:30
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
mappings = calloc(ranges, sizeof(*mappings));
|
|
|
|
if (!mappings) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _( "%s: Memory allocation failure\n"),
|
|
|
|
log_get_progname());
|
2013-01-22 14:50:07 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gather up the ranges from the command line */
|
|
|
|
mapping = mappings;
|
2015-08-06 11:04:17 +05:30
|
|
|
for (idx = 0, argidx = 0; idx < ranges; idx++, argidx += 3, mapping++) {
|
2015-08-06 10:40:13 +05:30
|
|
|
if (!getulong(argv[argidx + 0], &mapping->upper)) {
|
|
|
|
free(mappings);
|
2013-01-22 14:50:07 +05:30
|
|
|
return NULL;
|
2015-08-06 10:40:13 +05:30
|
|
|
}
|
|
|
|
if (!getulong(argv[argidx + 1], &mapping->lower)) {
|
|
|
|
free(mappings);
|
2013-01-22 14:50:07 +05:30
|
|
|
return NULL;
|
2015-08-06 10:40:13 +05:30
|
|
|
}
|
|
|
|
if (!getulong(argv[argidx + 2], &mapping->count)) {
|
|
|
|
free(mappings);
|
2013-01-22 14:50:07 +05:30
|
|
|
return NULL;
|
2015-08-06 10:40:13 +05:30
|
|
|
}
|
2016-08-15 04:35:00 +05:30
|
|
|
if (ULONG_MAX - mapping->upper <= mapping->count || ULONG_MAX - mapping->lower <= mapping->count) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
|
2016-08-15 04:35:00 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2016-07-31 23:25:44 +05:30
|
|
|
if (mapping->upper > UINT_MAX ||
|
|
|
|
mapping->lower > UINT_MAX ||
|
|
|
|
mapping->count > UINT_MAX) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
|
2016-08-15 04:35:00 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (mapping->lower + mapping->count > UINT_MAX ||
|
|
|
|
mapping->upper + mapping->count > UINT_MAX) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
|
2016-08-15 04:35:00 +05:30
|
|
|
exit(EXIT_FAILURE);
|
2016-07-31 23:25:44 +05:30
|
|
|
}
|
2016-08-06 03:46:48 +05:30
|
|
|
if (mapping->lower + mapping->count < mapping->lower ||
|
|
|
|
mapping->upper + mapping->count < mapping->upper) {
|
2016-08-15 04:35:00 +05:30
|
|
|
/* this one really shouldn't be possible given previous checks */
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _( "%s: subuid overflow detected.\n"), log_get_progname());
|
2016-08-15 04:35:00 +05:30
|
|
|
exit(EXIT_FAILURE);
|
2016-07-31 23:25:44 +05:30
|
|
|
}
|
2013-01-22 14:50:07 +05:30
|
|
|
}
|
|
|
|
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)
|
|
|
|
*/
|
2023-01-18 17:10:01 +05:30
|
|
|
#define ULONG_DIGITS (((WIDTHOF(unsigned long) + 9)/10)*3)
|
2013-01-22 14:50:07 +05:30
|
|
|
|
2021-05-06 22:29:28 +05:30
|
|
|
#if HAVE_SYS_CAPABILITY_H
|
2022-08-05 21:10:26 +05:30
|
|
|
static inline bool maps_lower_root(int cap, int ranges, const struct map_range *mappings)
|
2021-05-06 22:29:28 +05:30
|
|
|
{
|
|
|
|
int idx;
|
2022-08-05 21:10:26 +05:30
|
|
|
const struct map_range *mapping;
|
2021-05-06 22:29:28 +05:30
|
|
|
|
|
|
|
if (cap != CAP_SETUID)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mapping = mappings;
|
|
|
|
for (idx = 0; idx < ranges; idx++, mapping++) {
|
|
|
|
if (mapping->lower == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
/*
|
|
|
|
* The ruid refers to the caller's uid and is used to reset the effective uid
|
|
|
|
* back to the callers real uid.
|
|
|
|
* This clutch mainly exists for setuid-based new{g,u}idmap binaries that are
|
|
|
|
* called in contexts where all capabilities other than the necessary
|
|
|
|
* CAP_SET{G,U}ID capabilities are dropped. Since the kernel will require
|
|
|
|
* assurance that the caller holds CAP_SYS_ADMIN over the target user namespace
|
|
|
|
* the only way it can confirm is in this case is if the effective uid is
|
|
|
|
* equivalent to the uid owning the target user namespace.
|
|
|
|
* Note, we only support this when a) new{g,u}idmap is not called by root and
|
|
|
|
* b) if the caller's uid and the uid retrieved via system appropriate means
|
|
|
|
* (shadow file or other) are identical. Specifically, this does not support
|
|
|
|
* when the root user calls the new{g,u}idmap binary for an unprivileged user.
|
|
|
|
* If this is wanted: use file capabilities!
|
|
|
|
*/
|
2022-08-05 21:10:26 +05:30
|
|
|
void write_mapping(int proc_dir_fd, int ranges, const struct map_range *mappings,
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
const char *map_file, uid_t ruid)
|
2013-01-22 14:50:07 +05:30
|
|
|
{
|
|
|
|
int idx;
|
2022-08-05 21:10:26 +05:30
|
|
|
const struct map_range *mapping;
|
2013-01-22 14:50:07 +05:30
|
|
|
size_t bufsize;
|
2023-01-30 05:24:07 +05:30
|
|
|
char *buf, *pos, *end;
|
2013-01-22 14:50:07 +05:30
|
|
|
int fd;
|
|
|
|
|
2018-10-08 21:48:18 +05:30
|
|
|
#if HAVE_SYS_CAPABILITY_H
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
int cap;
|
|
|
|
struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0};
|
|
|
|
struct __user_cap_data_struct data[2] = {{0}};
|
|
|
|
|
|
|
|
if (strcmp(map_file, "uid_map") == 0) {
|
|
|
|
cap = CAP_SETUID;
|
|
|
|
} else if (strcmp(map_file, "gid_map") == 0) {
|
|
|
|
cap = CAP_SETGID;
|
|
|
|
} else {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: Invalid map file %s specified\n"), log_get_progname(), map_file);
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Align setuid- and fscaps-based new{g,u}idmap behavior. */
|
2018-11-21 01:13:43 +05:30
|
|
|
if (geteuid() == 0 && geteuid() != ruid) {
|
2018-10-08 21:48:18 +05:30
|
|
|
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), log_get_progname());
|
2018-10-08 21:48:18 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
if (seteuid(ruid) < 0) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: Could not seteuid to %d\n"), log_get_progname(), ruid);
|
2018-10-08 21:48:18 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
|
|
|
|
/* Lockdown new{g,u}idmap by dropping all unneeded capabilities. */
|
|
|
|
memset(data, 0, sizeof(data));
|
|
|
|
data[0].effective = CAP_TO_MASK(cap);
|
2021-05-06 22:29:28 +05:30
|
|
|
/*
|
|
|
|
* 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);
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
data[0].permitted = data[0].effective;
|
|
|
|
if (capset(&hdr, data) < 0) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: Could not set caps\n"), log_get_progname());
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2018-10-08 21:48:18 +05:30
|
|
|
#endif
|
|
|
|
|
2021-08-18 23:36:02 +05:30
|
|
|
bufsize = ranges * ((ULONG_DIGITS + 1) * 3);
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
pos = buf = xmalloc(bufsize);
|
2023-01-30 05:24:07 +05:30
|
|
|
end = buf + bufsize;
|
new{g,u}idmap: align setuid and fscaps behavior
Commit 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
does contain a wrong commit message, is lacking an explanation of the
issue, misses some simplifications and hardening features. This commit
tries to rectify this.
In (crazy) environment where all capabilities are dropped from the
capability bounding set apart from CAP_SET{G,U}ID setuid- and
fscaps-based new{g,u}idmap binaries behave differently when writing
complex mappings for an unprivileged user:
1. newuidmap is setuid
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
First file_ns_capable(file, ns, CAP_SYS_ADMIN) is hit. This calls into
cap_capable() and hits the loop
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
if (ns->level <= cred->user_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
The first check fails and falls through to the end of the loop and
retrieves the parent user namespace and checks whether CAP_SYS_ADMIN is
available there which isn't.
2. newuidmap has CAP_SETUID as fscaps set
unshare -U sleep infinity &
newuidmap $? 0 100000 65536
The first file_ns_capable() check for CAP_SYS_ADMIN is passed since the
euid has not been changed:
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
Now new_idmap_permitted() is hit which calls ns_capable(ns->parent,
CAP_SET{G,U}ID). This check passes since CAP_SET{G,U}ID is available in
the parent user namespace.
Now file_ns_capable(file, ns->parent, CAP_SETUID) is hit and the
cap_capable() loop (see above) is entered again. This passes
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
since CAP_SET{G,U}ID is available in the parent user namespace. Now the
mapping can be written.
There is no need for this descrepancy between setuid and fscaps based
new{g,u}idmap binaries. The solution is to do a
seteuid() back to the unprivileged uid and PR_SET_KEEPCAPS to keep
CAP_SET{G,U}ID. The seteuid() will cause the
file_ns_capable(file, ns, CAP_SYS_ADMIN) check to pass and the
PR_SET_KEEPCAPS for CAP_SET{G,U}ID will cause the CAP_SET{G,U}ID to
pass.
Fixes: 1ecca8439d5 ("new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS")
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
2018-10-27 21:53:50 +05:30
|
|
|
|
2013-01-22 14:50:07 +05:30
|
|
|
/* Build the mapping command */
|
|
|
|
mapping = mappings;
|
|
|
|
for (idx = 0; idx < ranges; idx++, mapping++) {
|
|
|
|
/* Append this range to the string that will be written */
|
2023-01-30 05:24:07 +05:30
|
|
|
pos = stpeprintf(pos, end, "%lu %lu %lu\n",
|
|
|
|
mapping->upper,
|
|
|
|
mapping->lower,
|
|
|
|
mapping->count);
|
|
|
|
}
|
|
|
|
if (pos == end || pos == NULL) {
|
|
|
|
fprintf(log_get_logfd(), _("%s: stpeprintf failed!\n"), log_get_progname());
|
|
|
|
exit(EXIT_FAILURE);
|
2013-01-22 14:50:07 +05:30
|
|
|
}
|
|
|
|
|
2017-10-23 02:02:45 +05:30
|
|
|
/* Write the mapping to the mapping file */
|
2013-01-22 14:50:07 +05:30
|
|
|
fd = openat(proc_dir_fd, map_file, O_WRONLY);
|
|
|
|
if (fd < 0) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: open of %s failed: %s\n"),
|
|
|
|
log_get_progname(), map_file, strerror(errno));
|
2013-01-22 14:50:07 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (write(fd, buf, pos - buf) != (pos - buf)) {
|
2021-11-29 05:07:53 +05:30
|
|
|
fprintf(log_get_logfd(), _("%s: write to %s failed: %s\n"),
|
|
|
|
log_get_progname(), map_file, strerror(errno));
|
2013-01-22 14:50:07 +05:30
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
close(fd);
|
Fix covscan RESOURCE_LEAK
Error: RESOURCE_LEAK (CWE-772): [#def1]
shadow-4.8.1/lib/commonio.c:320: alloc_fn: Storage is returned from allocation function "fopen_set_perms".
shadow-4.8.1/lib/commonio.c:320: var_assign: Assigning: "bkfp" = storage returned from "fopen_set_perms(backup, "w", &sb)".
shadow-4.8.1/lib/commonio.c:329: noescape: Resource "bkfp" is not freed or pointed-to in "putc".
shadow-4.8.1/lib/commonio.c:334: noescape: Resource "bkfp" is not freed or pointed-to in "fflush".
shadow-4.8.1/lib/commonio.c:339: noescape: Resource "bkfp" is not freed or pointed-to in "fileno".
shadow-4.8.1/lib/commonio.c:342: leaked_storage: Variable "bkfp" going out of scope leaks the storage it points to.
340| || (fclose (bkfp) != 0)) {
341| /* FIXME: unlink the backup file? */
342|-> return -1;
343| }
344|
Error: RESOURCE_LEAK (CWE-772): [#def2]
shadow-4.8.1/libmisc/addgrps.c:69: alloc_fn: Storage is returned from allocation function "malloc".
shadow-4.8.1/libmisc/addgrps.c:69: var_assign: Assigning: "grouplist" = storage returned from "malloc(i * 4UL)".
shadow-4.8.1/libmisc/addgrps.c:73: noescape: Resource "grouplist" is not freed or pointed-to in "getgroups". [Note: The source code implementation of the function has been overridden by a builtin model.]
shadow-4.8.1/libmisc/addgrps.c:126: leaked_storage: Variable "grouplist" going out of scope leaks the storage it points to.
124| }
125|
126|-> return 0;
127| }
128| #else /* HAVE_SETGROUPS && !USE_PAM */
Error: RESOURCE_LEAK (CWE-772): [#def3]
shadow-4.8.1/libmisc/chowntty.c:62: alloc_fn: Storage is returned from allocation function "getgr_nam_gid".
shadow-4.8.1/libmisc/chowntty.c:62: var_assign: Assigning: "grent" = storage returned from "getgr_nam_gid(getdef_str("TTYGROUP"))".
shadow-4.8.1/libmisc/chowntty.c:98: leaked_storage: Variable "grent" going out of scope leaks the storage it points to.
96| */
97| #endif
98|-> }
99|
Error: RESOURCE_LEAK (CWE-772): [#def4]
shadow-4.8.1/libmisc/copydir.c:742: open_fn: Returning handle opened by "open". [Note: The source code implementation of the function has been overridden by a user model.]
shadow-4.8.1/libmisc/copydir.c:742: var_assign: Assigning: "ifd" = handle returned from "open(src, 0)".
shadow-4.8.1/libmisc/copydir.c:748: leaked_handle: Handle variable "ifd" going out of scope leaks the handle.
746| #ifdef WITH_SELINUX
747| if (set_selinux_file_context (dst, NULL) != 0) {
748|-> return -1;
749| }
750| #endif /* WITH_SELINUX */
Error: RESOURCE_LEAK (CWE-772): [#def5]
shadow-4.8.1/libmisc/copydir.c:751: open_fn: Returning handle opened by "open". [Note: The source code implementation of the function has been overridden by a user model.]
shadow-4.8.1/libmisc/copydir.c:751: var_assign: Assigning: "ofd" = handle returned from "open(dst, 577, statp->st_mode & 0xfffU)".
shadow-4.8.1/libmisc/copydir.c:752: noescape: Resource "ofd" is not freed or pointed-to in "fchown_if_needed".
shadow-4.8.1/libmisc/copydir.c:775: leaked_handle: Handle variable "ofd" going out of scope leaks the handle.
773| ) {
774| (void) close (ifd);
775|-> return -1;
776| }
777|
Error: RESOURCE_LEAK (CWE-772): [#def7]
shadow-4.8.1/libmisc/idmapping.c:188: alloc_fn: Storage is returned from allocation function "xmalloc".
shadow-4.8.1/libmisc/idmapping.c:188: var_assign: Assigning: "buf" = storage returned from "xmalloc(bufsize)".
shadow-4.8.1/libmisc/idmapping.c:188: var_assign: Assigning: "pos" = "buf".
shadow-4.8.1/libmisc/idmapping.c:213: noescape: Resource "buf" is not freed or pointed-to in "write".
shadow-4.8.1/libmisc/idmapping.c:219: leaked_storage: Variable "pos" going out of scope leaks the storage it points to.
shadow-4.8.1/libmisc/idmapping.c:219: leaked_storage: Variable "buf" going out of scope leaks the storage it points to.
217| }
218| close(fd);
219|-> }
Error: RESOURCE_LEAK (CWE-772): [#def8]
shadow-4.8.1/libmisc/list.c:211: alloc_fn: Storage is returned from allocation function "xstrdup".
shadow-4.8.1/libmisc/list.c:211: var_assign: Assigning: "members" = storage returned from "xstrdup(comma)".
shadow-4.8.1/libmisc/list.c:217: var_assign: Assigning: "cp" = "members".
shadow-4.8.1/libmisc/list.c:218: noescape: Resource "cp" is not freed or pointed-to in "strchr".
shadow-4.8.1/libmisc/list.c:244: leaked_storage: Variable "cp" going out of scope leaks the storage it points to.
shadow-4.8.1/libmisc/list.c:244: leaked_storage: Variable "members" going out of scope leaks the storage it points to.
242| if ('\0' == *members) {
243| *array = (char *) 0;
244|-> return array;
245| }
246|
Error: RESOURCE_LEAK (CWE-772): [#def11]
shadow-4.8.1/libmisc/myname.c:61: alloc_fn: Storage is returned from allocation function "xgetpwnam".
shadow-4.8.1/libmisc/myname.c:61: var_assign: Assigning: "pw" = storage returned from "xgetpwnam(cp)".
shadow-4.8.1/libmisc/myname.c:67: leaked_storage: Variable "pw" going out of scope leaks the storage it points to.
65| }
66|
67|-> return xgetpwuid (ruid);
68| }
69|
Error: RESOURCE_LEAK (CWE-772): [#def12]
shadow-4.8.1/libmisc/user_busy.c:260: alloc_fn: Storage is returned from allocation function "opendir".
shadow-4.8.1/libmisc/user_busy.c:260: var_assign: Assigning: "task_dir" = storage returned from "opendir(task_path)".
shadow-4.8.1/libmisc/user_busy.c:262: noescape: Resource "task_dir" is not freed or pointed-to in "readdir".
shadow-4.8.1/libmisc/user_busy.c:278: leaked_storage: Variable "task_dir" going out of scope leaks the storage it points to.
276| _("%s: user %s is currently used by process %d\n"),
277| Prog, name, pid);
278|-> return 1;
279| }
280| }
Error: RESOURCE_LEAK (CWE-772): [#def20]
shadow-4.8.1/src/newgrp.c:162: alloc_fn: Storage is returned from allocation function "xgetspnam".
shadow-4.8.1/src/newgrp.c:162: var_assign: Assigning: "spwd" = storage returned from "xgetspnam(pwd->pw_name)".
shadow-4.8.1/src/newgrp.c:234: leaked_storage: Variable "spwd" going out of scope leaks the storage it points to.
232| }
233|
234|-> return;
235|
236| failure:
Error: RESOURCE_LEAK (CWE-772): [#def21]
shadow-4.8.1/src/passwd.c:530: alloc_fn: Storage is returned from allocation function "xstrdup".
shadow-4.8.1/src/passwd.c:530: var_assign: Assigning: "cp" = storage returned from "xstrdup(crypt_passwd)".
shadow-4.8.1/src/passwd.c:551: noescape: Resource "cp" is not freed or pointed-to in "strlen".
shadow-4.8.1/src/passwd.c:554: noescape: Resource "cp" is not freed or pointed-to in "strcat". [Note: The source code implementation of the function has been overridden by a builtin model.]
shadow-4.8.1/src/passwd.c:555: overwrite_var: Overwriting "cp" in "cp = newpw" leaks the storage that "cp" points to.
553| strcpy (newpw, "!");
554| strcat (newpw, cp);
555|-> cp = newpw;
556| }
557| return cp;
2021-06-14 16:09:48 +05:30
|
|
|
free(buf);
|
2013-01-22 14:50:07 +05:30
|
|
|
}
|