1 /*-
2 * Copyright(c) 2024 Baptiste Daroussin <bapt@FreeBSD.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/limits.h>
8 #include <sys/ucred.h>
9
10 #include <err.h>
11 #include <paths.h>
12 #include <pwd.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18
19 static void
usage(void)20 usage(void)
21 {
22 fprintf(stderr, "usage: mdo [-u username] [-i] [--] [command [args]]\n");
23 exit(EXIT_FAILURE);
24 }
25
26 int
main(int argc,char ** argv)27 main(int argc, char **argv)
28 {
29 struct passwd *pw;
30 const char *username = "root";
31 struct setcred wcred = SETCRED_INITIALIZER;
32 u_int setcred_flags = 0;
33 bool uidonly = false;
34 int ch;
35
36 while ((ch = getopt(argc, argv, "u:i")) != -1) {
37 switch (ch) {
38 case 'u':
39 username = optarg;
40 break;
41 case 'i':
42 uidonly = true;
43 break;
44 default:
45 usage();
46 }
47 }
48 argc -= optind;
49 argv += optind;
50
51 if ((pw = getpwnam(username)) == NULL) {
52 if (strspn(username, "0123456789") == strlen(username)) {
53 const char *errp = NULL;
54 uid_t uid = strtonum(username, 0, UID_MAX, &errp);
55 if (errp != NULL)
56 err(EXIT_FAILURE, "invalid user ID '%s'",
57 username);
58 pw = getpwuid(uid);
59 }
60 if (pw == NULL)
61 err(EXIT_FAILURE, "invalid username '%s'", username);
62 }
63
64 wcred.sc_uid = wcred.sc_ruid = wcred.sc_svuid = pw->pw_uid;
65 setcred_flags |= SETCREDF_UID | SETCREDF_RUID | SETCREDF_SVUID;
66
67 if (!uidonly) {
68 /*
69 * If there are too many groups specified for some UID, setting
70 * the groups will fail. We preserve this condition by
71 * allocating one more group slot than allowed, as
72 * getgrouplist() itself is just some getter function and thus
73 * doesn't (and shouldn't) check the limit, and to allow
74 * setcred() to actually check for overflow.
75 */
76 const long ngroups_alloc = sysconf(_SC_NGROUPS_MAX) + 2;
77 gid_t *const groups = malloc(sizeof(*groups) * ngroups_alloc);
78 int ngroups = ngroups_alloc;
79
80 if (groups == NULL)
81 err(EXIT_FAILURE, "cannot allocate memory for groups");
82
83 getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
84
85 wcred.sc_gid = wcred.sc_rgid = wcred.sc_svgid = pw->pw_gid;
86 wcred.sc_supp_groups = groups + 1;
87 wcred.sc_supp_groups_nb = ngroups - 1;
88 setcred_flags |= SETCREDF_GID | SETCREDF_RGID | SETCREDF_SVGID |
89 SETCREDF_SUPP_GROUPS;
90 }
91
92 if (setcred(setcred_flags, &wcred, sizeof(wcred)) != 0)
93 err(EXIT_FAILURE, "calling setcred() failed");
94
95 if (*argv == NULL) {
96 const char *sh = getenv("SHELL");
97 if (sh == NULL)
98 sh = _PATH_BSHELL;
99 execlp(sh, sh, "-i", NULL);
100 } else {
101 execvp(argv[0], argv);
102 }
103 err(EXIT_FAILURE, "exec failed");
104 }
105