xref: /freebsd/usr.sbin/jail/jail.c (revision 52267f7411adcc76ede961420e08c0e42f42d415)
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  */
9 
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
12 
13 #include <sys/param.h>
14 #include <sys/jail.h>
15 #include <sys/queue.h>
16 #include <sys/socket.h>
17 #include <sys/sysctl.h>
18 #include <sys/types.h>
19 
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <netdb.h>
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <grp.h>
27 #include <login_cap.h>
28 #include <paths.h>
29 #include <pwd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 static void		usage(void);
37 static int		add_addresses(struct addrinfo *);
38 static struct in_addr	*copy_addr4(void);
39 #ifdef INET6
40 static struct in6_addr	*copy_addr6(void);
41 #endif
42 
43 extern char	**environ;
44 
45 struct addr4entry {
46 	STAILQ_ENTRY(addr4entry)	addr4entries;
47 	struct in_addr			ip4;
48 	int				count;
49 };
50 struct addr6entry {
51 	STAILQ_ENTRY(addr6entry)	addr6entries;
52 #ifdef INET6
53 	struct in6_addr			ip6;
54 #endif
55 	int				count;
56 };
57 STAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4);
58 STAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6);
59 
60 #define GET_USER_INFO do {						\
61 	pwd = getpwnam(username);					\
62 	if (pwd == NULL) {						\
63 		if (errno)						\
64 			err(1, "getpwnam: %s", username);		\
65 		else							\
66 			errx(1, "%s: no such user", username);		\
67 	}								\
68 	lcap = login_getpwclass(pwd);					\
69 	if (lcap == NULL)						\
70 		err(1, "getpwclass: %s", username);			\
71 	ngroups = NGROUPS;						\
72 	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
73 		err(1, "getgrouplist: %s", username);			\
74 } while (0)
75 
76 int
77 main(int argc, char **argv)
78 {
79 	login_cap_t *lcap = NULL;
80 	struct jail j;
81 	struct passwd *pwd = NULL;
82 	gid_t groups[NGROUPS];
83 	int ch, error, i, ngroups, securelevel;
84 	int hflag, iflag, Jflag, lflag, uflag, Uflag;
85 	char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip;
86 	static char *cleanenv;
87 	const char *shell, *p = NULL;
88 	long ltmp;
89 	FILE *fp;
90 	struct addrinfo hints, *res0;
91 
92 	hflag = iflag = Jflag = lflag = uflag = Uflag = 0;
93 	securelevel = -1;
94 	jailname = username = JidFile = cleanenv = NULL;
95 	fp = NULL;
96 
97 	while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) {
98 		switch (ch) {
99 		case 'h':
100 			hflag = 1;
101 			break;
102 		case 'i':
103 			iflag = 1;
104 			break;
105 		case 'J':
106 			JidFile = optarg;
107 			Jflag = 1;
108 			break;
109 		case 'n':
110 			jailname = optarg;
111 			break;
112 		case 's':
113 			ltmp = strtol(optarg, &ep, 0);
114 			if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp)
115 				errx(1, "invalid securelevel: `%s'", optarg);
116 			securelevel = ltmp;
117 			break;
118 		case 'u':
119 			username = optarg;
120 			uflag = 1;
121 			break;
122 		case 'U':
123 			username = optarg;
124 			Uflag = 1;
125 			break;
126 		case 'l':
127 			lflag = 1;
128 			break;
129 		default:
130 			usage();
131 		}
132 	}
133 	argc -= optind;
134 	argv += optind;
135 	if (argc < 4)
136 		usage();
137 	if (uflag && Uflag)
138 		usage();
139 	if (lflag && username == NULL)
140 		usage();
141 	if (uflag)
142 		GET_USER_INFO;
143 	if (realpath(argv[0], path) == NULL)
144 		err(1, "realpath: %s", argv[0]);
145 	if (chdir(path) != 0)
146 		err(1, "chdir: %s", path);
147 	/* Initialize struct jail. */
148 	memset(&j, 0, sizeof(j));
149 	j.version = JAIL_API_VERSION;
150 	j.path = path;
151 	j.hostname = argv[1];
152 	if (jailname != NULL)
153 		j.jailname = jailname;
154 
155 	/* Handle IP addresses. If requested resolve hostname too. */
156 	bzero(&hints, sizeof(struct addrinfo));
157 	hints.ai_protocol = IPPROTO_TCP;
158 	hints.ai_socktype = SOCK_STREAM;
159 	if (JAIL_API_VERSION < 2)
160 		hints.ai_family = PF_INET;
161 	else
162 		hints.ai_family = PF_UNSPEC;
163 	/* Handle hostname. */
164 	if (hflag != 0) {
165 		error = getaddrinfo(j.hostname, NULL, &hints, &res0);
166 		if (error != 0)
167 			errx(1, "failed to handle hostname: %s",
168 			    gai_strerror(error));
169 		error = add_addresses(res0);
170 		freeaddrinfo(res0);
171 		if (error != 0)
172 			errx(1, "failed to add addresses.");
173 	}
174 	/* Handle IP addresses. */
175 	hints.ai_flags = AI_NUMERICHOST;
176 	ip = strtok(argv[2], ",");
177 	while (ip != NULL) {
178 		error = getaddrinfo(ip, NULL, &hints, &res0);
179 		if (error != 0)
180 			errx(1, "failed to handle ip: %s", gai_strerror(error));
181 		error = add_addresses(res0);
182 		freeaddrinfo(res0);
183 		if (error != 0)
184 			errx(1, "failed to add addresses.");
185 		ip = strtok(NULL, ",");
186 	}
187 	/* Count IP addresses and add them to struct jail. */
188 	if (!STAILQ_EMPTY(&addr4)) {
189 		j.ip4s = STAILQ_FIRST(&addr4)->count;
190 		j.ip4 = copy_addr4();
191 		if (j.ip4s > 0 && j.ip4 == NULL)
192 			errx(1, "copy_addr4()");
193 	}
194 #ifdef INET6
195 	if (!STAILQ_EMPTY(&addr6)) {
196 		j.ip6s = STAILQ_FIRST(&addr6)->count;
197 		j.ip6 = copy_addr6();
198 		if (j.ip6s > 0 && j.ip6 == NULL)
199 			errx(1, "copy_addr6()");
200 	}
201 #endif
202 
203 	if (Jflag) {
204 		fp = fopen(JidFile, "w");
205 		if (fp == NULL)
206 			errx(1, "Could not create JidFile: %s", JidFile);
207 	}
208 	i = jail(&j);
209 	if (i == -1)
210 		err(1, "syscall failed with");
211 	if (iflag) {
212 		printf("%d\n", i);
213 		fflush(stdout);
214 	}
215 	if (Jflag) {
216 		if (fp != NULL) {
217 			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
218 			    i, j.path, j.hostname, argv[2], argv[3]);
219 			(void)fclose(fp);
220 		} else {
221 			errx(1, "Could not write JidFile: %s", JidFile);
222 		}
223 	}
224 	if (securelevel > 0) {
225 		if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel,
226 		    sizeof(securelevel)))
227 			err(1, "Can not set securelevel to %d", securelevel);
228 	}
229 	if (username != NULL) {
230 		if (Uflag)
231 			GET_USER_INFO;
232 		if (lflag) {
233 			p = getenv("TERM");
234 			environ = &cleanenv;
235 		}
236 		if (setgroups(ngroups, groups) != 0)
237 			err(1, "setgroups");
238 		if (setgid(pwd->pw_gid) != 0)
239 			err(1, "setgid");
240 		if (setusercontext(lcap, pwd, pwd->pw_uid,
241 		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
242 			err(1, "setusercontext");
243 		login_close(lcap);
244 	}
245 	if (lflag) {
246 		if (*pwd->pw_shell)
247 			shell = pwd->pw_shell;
248 		else
249 			shell = _PATH_BSHELL;
250 		if (chdir(pwd->pw_dir) < 0)
251 			errx(1, "no home directory");
252 		setenv("HOME", pwd->pw_dir, 1);
253 		setenv("SHELL", shell, 1);
254 		setenv("USER", pwd->pw_name, 1);
255 		if (p)
256 			setenv("TERM", p, 1);
257 	}
258 	if (execv(argv[3], argv + 3) != 0)
259 		err(1, "execv: %s", argv[3]);
260 	exit(0);
261 }
262 
263 static void
264 usage(void)
265 {
266 
267 	(void)fprintf(stderr, "%s%s%s\n",
268 	     "usage: jail [-hi] [-n jailname] [-J jid_file] ",
269 	     "[-s securelevel] [-l -u username | -U username] ",
270 	     "path hostname [ip[,..]] command ...");
271 	exit(1);
272 }
273 
274 static int
275 add_addresses(struct addrinfo *res0)
276 {
277 	int error;
278 	struct addrinfo *res;
279 	struct addr4entry *a4p;
280 	struct sockaddr_in *sai;
281 #ifdef INET6
282 	struct addr6entry *a6p;
283 	struct sockaddr_in6 *sai6;
284 #endif
285 	int count;
286 
287 	error = 0;
288 	for (res = res0; res && error == 0; res = res->ai_next) {
289 		switch (res->ai_family) {
290 		case AF_INET:
291 			sai = (struct sockaddr_in *)(void *)res->ai_addr;
292 			STAILQ_FOREACH(a4p, &addr4, addr4entries) {
293 			    if (bcmp(&sai->sin_addr, &a4p->ip4,
294 				sizeof(struct in_addr)) == 0) {
295 				    err(1, "Ignoring duplicate IPv4 address.");
296 				    break;
297 			    }
298 			}
299 			a4p = (struct addr4entry *) malloc(
300 			    sizeof(struct addr4entry));
301 			if (a4p == NULL) {
302 				error = 1;
303 				break;
304 			}
305 			bzero(a4p, sizeof(struct addr4entry));
306 			bcopy(&sai->sin_addr, &a4p->ip4,
307 			    sizeof(struct in_addr));
308 			if (!STAILQ_EMPTY(&addr4))
309 				count = STAILQ_FIRST(&addr4)->count;
310 			else
311 				count = 0;
312 			STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries);
313 			STAILQ_FIRST(&addr4)->count = count + 1;
314 			break;
315 #ifdef INET6
316 		case AF_INET6:
317 			sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
318 			STAILQ_FOREACH(a6p, &addr6, addr6entries) {
319 			    if (bcmp(&sai6->sin6_addr, &a6p->ip6,
320 				sizeof(struct in6_addr)) == 0) {
321 				    err(1, "Ignoring duplicate IPv6 address.");
322 				    break;
323 			    }
324 			}
325 			a6p = (struct addr6entry *) malloc(
326 			    sizeof(struct addr6entry));
327 			if (a6p == NULL) {
328 				error = 1;
329 				break;
330 			}
331 			bzero(a6p, sizeof(struct addr6entry));
332 			bcopy(&sai6->sin6_addr, &a6p->ip6,
333 			    sizeof(struct in6_addr));
334 			if (!STAILQ_EMPTY(&addr6))
335 				count = STAILQ_FIRST(&addr6)->count;
336 			else
337 				count = 0;
338 			STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries);
339 			STAILQ_FIRST(&addr6)->count = count + 1;
340 			break;
341 #endif
342 		default:
343 			err(1, "Address family %d not supported. Ignoring.\n",
344 			    res->ai_family);
345 			break;
346 		}
347 	}
348 
349 	return (error);
350 }
351 
352 static struct in_addr *
353 copy_addr4(void)
354 {
355 	size_t len;
356 	struct in_addr *ip4s, *p, ia;
357 	struct addr4entry *a4p;
358 
359 	if (STAILQ_EMPTY(&addr4))
360 		return NULL;
361 
362 	len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr);
363 
364 	ip4s = p = (struct in_addr *)malloc(len);
365 	if (ip4s == NULL)
366 	return (NULL);
367 
368 	bzero(p, len);
369 
370 	while (!STAILQ_EMPTY(&addr4)) {
371 		a4p = STAILQ_FIRST(&addr4);
372 		STAILQ_REMOVE_HEAD(&addr4, addr4entries);
373 		ia.s_addr = a4p->ip4.s_addr;
374 		bcopy(&ia, p, sizeof(struct in_addr));
375 		p++;
376 		free(a4p);
377 	}
378 
379 	return (ip4s);
380 }
381 
382 #ifdef INET6
383 static struct in6_addr *
384 copy_addr6(void)
385 {
386 	size_t len;
387 	struct in6_addr *ip6s, *p;
388 	struct addr6entry *a6p;
389 
390 	if (STAILQ_EMPTY(&addr6))
391 		return NULL;
392 
393 	len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr);
394 
395 	ip6s = p = (struct in6_addr *)malloc(len);
396 	if (ip6s == NULL)
397 		return (NULL);
398 
399 	bzero(p, len);
400 
401 	while (!STAILQ_EMPTY(&addr6)) {
402 		a6p = STAILQ_FIRST(&addr6);
403 		STAILQ_REMOVE_HEAD(&addr6, addr6entries);
404 		bcopy(&a6p->ip6, p, sizeof(struct in6_addr));
405 		p++;
406 		free(a6p);
407 	}
408 
409 	return (ip6s);
410 }
411 #endif
412 
413