]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/setpriv.c
misc: cosmetics, remove argument from usage(FILE*)
[thirdparty/util-linux.git] / sys-utils / setpriv.c
index a17f925091c86230cc9960dd0a10297ba7266425..310e6938d233f2142fb55baf452cc8aec3b67d39 100644 (file)
@@ -62,15 +62,20 @@ struct privctx {
                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;
@@ -87,10 +92,16 @@ struct privctx {
        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);
@@ -104,10 +115,12 @@ static void __attribute__((__noreturn__)) usage(FILE *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);
@@ -115,7 +128,7 @@ static void __attribute__((__noreturn__)) usage(FILE *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)
@@ -169,10 +182,10 @@ static int print_caps(FILE *f, capng_type_t which)
 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;
        }
@@ -200,10 +213,10 @@ static void dump_securebits(void)
        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);
        }
 
@@ -246,7 +259,7 @@ static void dump_label(const char *name)
 
 static void dump_groups(void)
 {
-       int n = getgroups(0, 0);
+       int n = getgroups(0, NULL);
        gid_t *groups;
 
        if (n < 0) {
@@ -254,9 +267,10 @@ static void dump_groups(void)
                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;
        }
@@ -273,6 +287,7 @@ static void dump_groups(void)
                }
        }
        printf("\n");
+       free(groups);
 }
 
 static void dump(int dumplevel)
@@ -526,9 +541,9 @@ static void do_selinux_label(const char *label)
                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)
@@ -538,12 +553,12 @@ 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,
@@ -572,6 +587,33 @@ static gid_t get_group(const char *s, const char *err)
        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 {
@@ -584,6 +626,7 @@ int main(int argc, char **argv)
                REGID,
                CLEAR_GROUPS,
                KEEP_GROUPS,
+               INIT_GROUPS,
                GROUPS,
                INHCAPS,
                LISTCAPS,
@@ -594,38 +637,40 @@ int main(int argc, char **argv)
        };
 
        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;
@@ -654,7 +699,11 @@ int main(int argc, char **argv)
                        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)
@@ -666,7 +715,12 @@ int main(int argc, char **argv)
                        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)
@@ -698,6 +752,12 @@ int main(int argc, char **argv)
                                     _("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,
@@ -738,14 +798,12 @@ int main(int argc, char **argv)
                        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);
                }
        }
 
@@ -769,13 +827,23 @@ int main(int argc, char **argv)
                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);
@@ -806,15 +874,17 @@ int main(int argc, char **argv)
        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);