1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/types.h>
33 #include <sys/procctl.h>
34
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <grp.h>
39 #include <limits.h>
40 #include <paths.h>
41 #include <pwd.h>
42 #include <stdbool.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 static void usage(void) __dead2;
49
50 static gid_t
resolve_group(const char * group)51 resolve_group(const char *group)
52 {
53 char *endp;
54 struct group *gp;
55 unsigned long gid;
56
57 gp = getgrnam(group);
58 if (gp != NULL)
59 return (gp->gr_gid);
60
61 /*
62 * Numeric IDs don't need a trip through the database to check them,
63 * POSIX seems to think we should generally accept a numeric ID as long
64 * as it's within the valid range.
65 */
66 errno = 0;
67 gid = strtoul(group, &endp, 0);
68 if (errno == 0 && *endp == '\0' && gid <= GID_MAX)
69 return (gid);
70
71 errx(1, "no such group '%s'", group);
72 }
73
74 static uid_t
resolve_user(const char * user)75 resolve_user(const char *user)
76 {
77 char *endp;
78 struct passwd *pw;
79 unsigned long uid;
80
81 pw = getpwnam(user);
82 if (pw != NULL)
83 return (pw->pw_uid);
84
85 errno = 0;
86 uid = strtoul(user, &endp, 0);
87 if (errno == 0 && *endp == '\0' && uid <= UID_MAX)
88 return (uid);
89
90 errx(1, "no such user '%s'", user);
91 }
92
93 int
main(int argc,char * argv[])94 main(int argc, char *argv[])
95 {
96 const char *group, *p, *shell, *user;
97 char *grouplist;
98 long ngroups_max;
99 gid_t gid, *gidlist;
100 uid_t uid;
101 int arg, ch, error, gids;
102 bool nonprivileged;
103
104 gid = 0;
105 uid = 0;
106 gids = 0;
107 user = group = grouplist = NULL;
108 gidlist = NULL;
109 nonprivileged = false;
110 while ((ch = getopt(argc, argv, "G:g:u:n")) != -1) {
111 switch(ch) {
112 case 'u':
113 user = optarg;
114 if (*user == '\0')
115 usage();
116 break;
117 case 'g':
118 group = optarg;
119 if (*group == '\0')
120 usage();
121 break;
122 case 'G':
123 grouplist = optarg;
124
125 /*
126 * XXX Why not allow us to drop all of our supplementary
127 * groups?
128 */
129 if (*grouplist == '\0')
130 usage();
131 break;
132 case 'n':
133 nonprivileged = true;
134 break;
135 case '?':
136 default:
137 usage();
138 }
139 }
140 argc -= optind;
141 argv += optind;
142
143 if (argc < 1)
144 usage();
145
146 if (group != NULL)
147 gid = resolve_group(group);
148
149 if (grouplist != NULL) {
150 ngroups_max = sysconf(_SC_NGROUPS_MAX);
151 if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
152 err(1, "malloc");
153 for (gids = 0; (p = strsep(&grouplist, ",")) != NULL &&
154 gids < ngroups_max; ) {
155 if (*p == '\0')
156 continue;
157
158 gidlist[gids++] = resolve_group(p);
159 }
160 if (p != NULL && gids == ngroups_max)
161 errx(1, "too many supplementary groups provided");
162 }
163
164 if (user != NULL)
165 uid = resolve_user(user);
166
167 if (nonprivileged) {
168 arg = PROC_NO_NEW_PRIVS_ENABLE;
169 error = procctl(P_PID, getpid(), PROC_NO_NEW_PRIVS_CTL, &arg);
170 if (error != 0)
171 err(1, "procctl");
172 }
173
174 if (chdir(argv[0]) == -1)
175 err(1, "%s", argv[0]);
176 if (chroot(".") == -1) {
177 if (errno == EPERM && !nonprivileged && geteuid() != 0)
178 errx(1, "unprivileged use requires -n");
179 err(1, "%s", argv[0]);
180 }
181
182 if (gidlist != NULL && setgroups(gids, gidlist) == -1)
183 err(1, "setgroups");
184 if (group && setgid(gid) == -1)
185 err(1, "setgid");
186 if (user && setuid(uid) == -1)
187 err(1, "setuid");
188
189 if (argv[1]) {
190 execvp(argv[1], &argv[1]);
191 err(1, "%s", argv[1]);
192 }
193
194 if (!(shell = getenv("SHELL")))
195 shell = _PATH_BSHELL;
196 execlp(shell, shell, "-i", (char *)NULL);
197 err(1, "%s", shell);
198 /* NOTREACHED */
199 }
200
201 static void
usage(void)202 usage(void)
203 {
204 (void)fprintf(stderr, "usage: chroot [-g group] [-G group,group,...] "
205 "[-u user] [-n] newroot [command]\n");
206 exit(1);
207 }
208