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 <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"
|
2021-01-07 16:45:25 +05:30
|
|
|
#include "getdef.h"
|
2013-01-22 14:50:07 +05:30
|
|
|
#include "idmapping.h"
|
2021-11-29 05:07:53 +05:30
|
|
|
#include "shadowlog.h"
|
2013-01-22 14:50:07 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* Global variables
|
|
|
|
*/
|
|
|
|
const char *Prog;
|
|
|
|
|
2018-02-15 18:19:40 +05:30
|
|
|
|
|
|
|
static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow_setgroups)
|
2013-01-22 14:50:07 +05:30
|
|
|
{
|
|
|
|
/* An empty range is invalid */
|
|
|
|
if (range->count == 0)
|
|
|
|
return false;
|
|
|
|
|
2018-02-15 18:19:40 +05:30
|
|
|
/* Test /etc/subgid. If the mapping is valid then we allow setgroups. */
|
|
|
|
if (have_sub_gids(pw->pw_name, range->lower, range->count)) {
|
|
|
|
*allow_setgroups = true;
|
2013-01-22 14:50:07 +05:30
|
|
|
return true;
|
2018-02-15 18:19:40 +05:30
|
|
|
}
|
2013-01-22 14:50:07 +05:30
|
|
|
|
2018-02-15 18:19:40 +05:30
|
|
|
/* Allow a process to map its own gid. */
|
2021-01-07 16:45:25 +05:30
|
|
|
if ((range->count == 1) && (getgid() == range->lower)) {
|
2018-02-15 18:19:40 +05:30
|
|
|
/* noop -- if setgroups is enabled already we won't disable it. */
|
2013-01-22 14:50:07 +05:30
|
|
|
return true;
|
2018-02-15 18:19:40 +05:30
|
|
|
}
|
2013-01-22 14:50:07 +05:30
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verify_ranges(struct passwd *pw, int ranges,
|
2018-02-15 18:19:40 +05:30
|
|
|
struct map_range *mappings, bool *allow_setgroups)
|
2013-01-22 14:50:07 +05:30
|
|
|
{
|
|
|
|
struct map_range *mapping;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
mapping = mappings;
|
|
|
|
for (idx = 0; idx < ranges; idx++, mapping++) {
|
2018-02-15 18:19:40 +05:30
|
|
|
if (!verify_range(pw, mapping, allow_setgroups)) {
|
2013-01-22 14:50:07 +05:30
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-01-03 17:11:13 +05:30
|
|
|
static void write_setgroups(int proc_dir_fd, bool allow_setgroups)
|
2018-02-15 18:19:40 +05:30
|
|
|
{
|
|
|
|
int setgroups_fd;
|
2022-01-03 16:49:00 +05:30
|
|
|
const char *policy;
|
|
|
|
char policy_buffer[4096];
|
2018-02-15 18:19:40 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* Default is "deny", and any "allow" will out-rank a "deny". We don't
|
|
|
|
* forcefully write an "allow" here because the process we are writing
|
|
|
|
* mappings for may have already set themselves to "deny" (and "allow"
|
|
|
|
* is the default anyway). So allow_setgroups == true is a noop.
|
|
|
|
*/
|
|
|
|
policy = "deny\n";
|
|
|
|
if (allow_setgroups)
|
|
|
|
return;
|
|
|
|
|
|
|
|
setgroups_fd = openat(proc_dir_fd, "setgroups", O_RDWR|O_CLOEXEC);
|
|
|
|
if (setgroups_fd < 0) {
|
|
|
|
/*
|
|
|
|
* If it's an ENOENT then we are on too old a kernel for the setgroups
|
|
|
|
* code to exist. Emit a warning and bail on this.
|
|
|
|
*/
|
|
|
|
if (ENOENT == errno) {
|
|
|
|
fprintf(stderr, _("%s: kernel doesn't support setgroups restrictions\n"), Prog);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fprintf(stderr, _("%s: couldn't open process setgroups: %s\n"),
|
|
|
|
Prog,
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether the policy is already what we want. /proc/self/setgroups
|
|
|
|
* is write-once, so attempting to write after it's already written to will
|
|
|
|
* fail.
|
|
|
|
*/
|
|
|
|
if (read(setgroups_fd, policy_buffer, sizeof(policy_buffer)) < 0) {
|
|
|
|
fprintf(stderr, _("%s: failed to read setgroups: %s\n"),
|
|
|
|
Prog,
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (!strncmp(policy_buffer, policy, strlen(policy)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Write the policy. */
|
|
|
|
if (lseek(setgroups_fd, 0, SEEK_SET) < 0) {
|
|
|
|
fprintf(stderr, _("%s: failed to seek setgroups: %s\n"),
|
|
|
|
Prog,
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (dprintf(setgroups_fd, "%s", policy) < 0) {
|
|
|
|
fprintf(stderr, _("%s: failed to setgroups %s policy: %s\n"),
|
|
|
|
Prog,
|
|
|
|
policy,
|
|
|
|
strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
close(setgroups_fd);
|
|
|
|
}
|
|
|
|
|
2013-01-22 14:50:07 +05:30
|
|
|
/*
|
|
|
|
* newgidmap - Set the gid_map for the specified process
|
|
|
|
*/
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2014-06-27 03:18:56 +05:30
|
|
|
char proc_dir_name[32];
|
2013-01-22 14:50:07 +05:30
|
|
|
char *target_str;
|
2019-10-13 06:27:12 +05:30
|
|
|
pid_t target;
|
2013-01-22 14:50:07 +05:30
|
|
|
int proc_dir_fd;
|
|
|
|
int ranges;
|
|
|
|
struct map_range *mappings;
|
|
|
|
struct stat st;
|
|
|
|
struct passwd *pw;
|
|
|
|
int written;
|
2018-02-15 18:19:40 +05:30
|
|
|
bool allow_setgroups = false;
|
2013-01-22 14:50:07 +05:30
|
|
|
|
|
|
|
Prog = Basename (argv[0]);
|
2021-11-29 05:07:53 +05:30
|
|
|
log_set_progname(Prog);
|
|
|
|
log_set_logfd(stderr);
|
2013-01-22 14:50:07 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* The valid syntax are
|
|
|
|
* newgidmap target_pid
|
|
|
|
*/
|
|
|
|
if (argc < 2)
|
|
|
|
usage();
|
|
|
|
|
2016-07-02 20:09:18 +05:30
|
|
|
/* Find the process that needs its user namespace
|
2013-01-22 14:50:07 +05:30
|
|
|
* gid mapping set.
|
|
|
|
*/
|
|
|
|
target_str = argv[1];
|
|
|
|
if (!get_pid(target_str, &target))
|
|
|
|
usage();
|
|
|
|
|
2014-06-27 03:18:56 +05:30
|
|
|
/* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */
|
2013-01-22 14:50:07 +05:30
|
|
|
written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/",
|
|
|
|
target);
|
2023-01-24 20:35:20 +05:30
|
|
|
if ((written <= 0) || ((size_t)written >= sizeof(proc_dir_name))) {
|
2013-01-22 14:50:07 +05:30
|
|
|
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;
|
|
|
|
}
|
2018-02-15 18:19:40 +05:30
|
|
|
|
2013-01-22 14:50:07 +05:30
|
|
|
/* 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) ||
|
2021-01-07 16:45:25 +05:30
|
|
|
(!getdef_bool("GRANT_AUX_GROUP_SUBIDS") && (getgid() != pw->pw_gid)) ||
|
2013-01-22 14:50:07 +05:30
|
|
|
(pw->pw_uid != st.st_uid) ||
|
2021-01-07 16:45:25 +05:30
|
|
|
(getgid() != st.st_gid)) {
|
2015-04-06 18:52:48 +05:30
|
|
|
fprintf(stderr, _( "%s: Target %u is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ),
|
|
|
|
Prog, target,
|
|
|
|
(unsigned long int)getuid(), (unsigned long int)pw->pw_uid, (unsigned long int)st.st_uid,
|
|
|
|
(unsigned long int)getgid(), (unsigned long int)pw->pw_gid, (unsigned long int)st.st_gid);
|
2013-01-22 14:50:07 +05:30
|
|
|
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();
|
|
|
|
|
2018-02-15 18:19:40 +05:30
|
|
|
verify_ranges(pw, ranges, mappings, &allow_setgroups);
|
2013-01-22 14:50:07 +05:30
|
|
|
|
2018-02-15 18:19:40 +05:30
|
|
|
write_setgroups(proc_dir_fd, allow_setgroups);
|
2018-10-08 21:48:18 +05:30
|
|
|
write_mapping(proc_dir_fd, ranges, mappings, "gid_map", pw->pw_uid);
|
2013-01-22 14:50:07 +05:30
|
|
|
sub_gid_close();
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|