have_euid:1, /* effective uid */
have_rgid:1, /* real gid */
have_egid:1, /* effective gid */
+ have_passwd:1, /* passwd entry */
have_groups:1, /* add groups */
keep_groups:1, /* keep groups */
clear_groups:1, /* remove groups */
+ init_groups:1, /* initialize groups */
have_securebits:1; /* remove groups */
/* uids and gids */
uid_t ruid, euid;
gid_t rgid, egid;
+ /* real user passwd entry */
+ struct passwd passwd;
+
/* supplementary groups */
size_t num_groups;
gid_t *groups;
const char *apparmor_profile;
};
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
fputs(USAGE_HEADER, out);
- fprintf(out, _(" %s [options] <program> [args...]\n"), program_invocation_short_name);
+ fprintf(out, _(" %s [options] <program> [<argument>...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Run a program with different privilege settings.\n"), out);
+
fputs(USAGE_OPTIONS, out);
fputs(_(" -d, --dump show current state (and do not exec anything)\n"), out);
fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out);
fputs(_(" --regid <gid> set real and effective gid\n"), out);
fputs(_(" --clear-groups clear supplementary groups\n"), out);
fputs(_(" --keep-groups keep supplementary groups\n"), out);
+ fputs(_(" --init-groups initialize supplementary groups\n"), out);
fputs(_(" --groups <group,...> set supplementary groups\n"), out);
fputs(_(" --securebits <bits> set securebits\n"), out);
fputs(_(" --selinux-label <label> set SELinux label\n"), out);
fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
+
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out);
fprintf(out, USAGE_MAN_TAIL("setpriv(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ exit(EXIT_SUCCESS);
}
static int real_cap_last_cap(void)
static void dump_one_secbit(int *first, int *bits, int bit, const char *name)
{
if (*bits & bit) {
- if (!*first)
- printf(",");
- else
+ if (*first)
*first = 0;
+ else
+ printf(",");
fputs(name, stdout);
*bits &= ~bit;
}
dump_one_secbit(&first, &bits, SECBIT_KEEP_CAPS_LOCKED,
"keep_caps_locked");
if (bits) {
- if (!first)
- printf(",");
- else
+ if (first)
first = 0;
+ else
+ printf(",");
printf("0x%x", (unsigned)bits);
}
static void dump_groups(void)
{
- int n = getgroups(0, 0);
+ int n = getgroups(0, NULL);
gid_t *groups;
if (n < 0) {
return;
}
- groups = alloca(n * sizeof(gid_t));
+ groups = xmalloc(n * sizeof(gid_t));
n = getgroups(n, groups);
if (n < 0) {
+ free(groups);
warn("getgroups failed");
return;
}
}
}
printf("\n");
+ free(groups);
}
static void dump(int dumplevel)
err(SETPRIV_EXIT_PRIVERR,
_("write failed: %s"), _PATH_PROC_ATTR_EXEC);
- if (close_fd(fd) != 0)
+ if (close(fd) != 0)
err(SETPRIV_EXIT_PRIVERR,
- _("write failed: %s"), _PATH_PROC_ATTR_EXEC);
+ _("close failed: %s"), _PATH_PROC_ATTR_EXEC);
}
static void do_apparmor_profile(const char *label)
if (access(_PATH_SYS_APPARMOR, F_OK) != 0)
errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running"));
- f = fopen(_PATH_PROC_ATTR_EXEC, "wx");
+ f = fopen(_PATH_PROC_ATTR_EXEC, "r+");
if (!f)
err(SETPRIV_EXIT_PRIVERR,
_("cannot open %s"), _PATH_PROC_ATTR_EXEC);
- fprintf(f, "changeprofile %s", label);
+ fprintf(f, "exec %s", label);
if (close_stream(f) != 0)
err(SETPRIV_EXIT_PRIVERR,
return tmp;
}
+static struct passwd *get_passwd(const char *s, uid_t *uid, const char *err)
+{
+ struct passwd *pw;
+ long tmp;
+ pw = getpwnam(s);
+ if (pw) {
+ *uid = pw->pw_uid;
+ } else {
+ tmp = strtol_or_err(s, err);
+ *uid = tmp;
+ pw = getpwuid(*uid);
+ }
+ return pw;
+}
+
+static struct passwd *passwd_copy(struct passwd *dst, const struct passwd *src)
+{
+ struct passwd *rv;
+ rv = memcpy(dst, src, sizeof(*dst));
+ rv->pw_name = xstrdup(rv->pw_name);
+ rv->pw_passwd = xstrdup(rv->pw_passwd);
+ rv->pw_gecos = xstrdup(rv->pw_gecos);
+ rv->pw_dir = xstrdup(rv->pw_dir);
+ rv->pw_shell = xstrdup(rv->pw_shell);
+ return rv;
+}
+
int main(int argc, char **argv)
{
enum {
REGID,
CLEAR_GROUPS,
KEEP_GROUPS,
+ INIT_GROUPS,
GROUPS,
INHCAPS,
LISTCAPS,
};
static const struct option longopts[] = {
- {"dump", no_argument, 0, 'd'},
- {"nnp", no_argument, 0, NNP},
- {"no-new-privs", no_argument, 0, NNP},
- {"inh-caps", required_argument, 0, INHCAPS},
- {"list-caps", no_argument, 0, LISTCAPS},
- {"ruid", required_argument, 0, RUID},
- {"euid", required_argument, 0, EUID},
- {"rgid", required_argument, 0, RGID},
- {"egid", required_argument, 0, EGID},
- {"reuid", required_argument, 0, REUID},
- {"regid", required_argument, 0, REGID},
- {"clear-groups", no_argument, 0, CLEAR_GROUPS},
- {"keep-groups", no_argument, 0, KEEP_GROUPS},
- {"groups", required_argument, 0, GROUPS},
- {"bounding-set", required_argument, 0, CAPBSET},
- {"securebits", required_argument, 0, SECUREBITS},
- {"selinux-label", required_argument, 0, SELINUX_LABEL},
- {"apparmor-profile", required_argument, 0, APPARMOR_PROFILE},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0}
+ { "dump", no_argument, NULL, 'd' },
+ { "nnp", no_argument, NULL, NNP },
+ { "no-new-privs", no_argument, NULL, NNP },
+ { "inh-caps", required_argument, NULL, INHCAPS },
+ { "list-caps", no_argument, NULL, LISTCAPS },
+ { "ruid", required_argument, NULL, RUID },
+ { "euid", required_argument, NULL, EUID },
+ { "rgid", required_argument, NULL, RGID },
+ { "egid", required_argument, NULL, EGID },
+ { "reuid", required_argument, NULL, REUID },
+ { "regid", required_argument, NULL, REGID },
+ { "clear-groups", no_argument, NULL, CLEAR_GROUPS },
+ { "keep-groups", no_argument, NULL, KEEP_GROUPS },
+ { "init-groups", no_argument, NULL, INIT_GROUPS },
+ { "groups", required_argument, NULL, GROUPS },
+ { "bounding-set", required_argument, NULL, CAPBSET },
+ { "securebits", required_argument, NULL, SECUREBITS },
+ { "selinux-label", required_argument, NULL, SELINUX_LABEL },
+ { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
};
static const ul_excl_t excl[] = {
/* keep in same order with enum definitions */
- {CLEAR_GROUPS, KEEP_GROUPS, GROUPS},
+ {CLEAR_GROUPS, KEEP_GROUPS, INIT_GROUPS, GROUPS},
{0}
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
int c;
struct privctx opts;
+ struct passwd *pw = NULL;
int dumplevel = 0;
int total_opts = 0;
int list_caps = 0;
if (opts.have_ruid)
errx(EXIT_FAILURE, _("duplicate ruid"));
opts.have_ruid = 1;
- opts.ruid = get_user(optarg, _("failed to parse ruid"));
+ pw = get_passwd(optarg, &opts.ruid, _("failed to parse ruid"));
+ if (pw) {
+ passwd_copy(&opts.passwd, pw);
+ opts.have_passwd = 1;
+ }
break;
case EUID:
if (opts.have_euid)
if (opts.have_ruid || opts.have_euid)
errx(EXIT_FAILURE, _("duplicate ruid or euid"));
opts.have_ruid = opts.have_euid = 1;
- opts.ruid = opts.euid = get_user(optarg, _("failed to parse reuid"));
+ pw = get_passwd(optarg, &opts.ruid, _("failed to parse reuid"));
+ opts.euid = opts.ruid;
+ if (pw) {
+ passwd_copy(&opts.passwd, pw);
+ opts.have_passwd = 1;
+ }
break;
case RGID:
if (opts.have_rgid)
_("duplicate --keep-groups option"));
opts.keep_groups = 1;
break;
+ case INIT_GROUPS:
+ if (opts.init_groups)
+ errx(EXIT_FAILURE,
+ _("duplicate --init-groups option"));
+ opts.init_groups = 1;
+ break;
case GROUPS:
if (opts.have_groups)
errx(EXIT_FAILURE,
opts.apparmor_profile = optarg;
break;
case 'h':
- usage(stdout);
+ usage();
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
- case '?':
- usage(stderr);
default:
- errx(EXIT_FAILURE, _("unrecognized option '%c'"), c);
+ errtryhelp(EXIT_FAILURE);
}
}
errx(EXIT_FAILURE, _("No program specified"));
if ((opts.have_rgid || opts.have_egid)
- && !opts.keep_groups && !opts.clear_groups && !opts.have_groups)
+ && !opts.keep_groups && !opts.clear_groups && !opts.init_groups
+ && !opts.have_groups)
+ errx(EXIT_FAILURE,
+ _("--[re]gid requires --keep-groups, --clear-groups, --init-groups, or --groups"));
+
+ if (opts.init_groups && !opts.have_ruid)
+ errx(EXIT_FAILURE,
+ _("--init-groups requires --ruid or --reuid"));
+
+ if (opts.init_groups && !opts.have_passwd)
errx(EXIT_FAILURE,
- _("--[re]gid requires --keep-groups, --clear-groups, or --groups"));
+ _("uid %ld not found, --init-groups requires an user that "
+ "can be found on the system"),
+ (long) opts.ruid);
- if (opts.nnp)
- if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
- err(EXIT_FAILURE, _("disallow granting new privileges failed"));
+ if (opts.nnp && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+ err(EXIT_FAILURE, _("disallow granting new privileges failed"));
if (opts.selinux_label)
do_selinux_label(opts.selinux_label);
if (opts.have_groups) {
if (setgroups(opts.num_groups, opts.groups) != 0)
err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
+ } else if (opts.init_groups) {
+ if (initgroups(opts.passwd.pw_name, opts.passwd.pw_gid) != 0)
+ err(SETPRIV_EXIT_PRIVERR, _("initgroups failed"));
} else if (opts.clear_groups) {
gid_t x = 0;
if (setgroups(0, &x) != 0)
err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
}
- if (opts.have_securebits)
- if (prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0)
- err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed"));
+ if (opts.have_securebits && prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0)
+ err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed"));
if (opts.bounding_set) {
do_caps(CAPNG_BOUNDING_SET, opts.bounding_set);