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