jail.c revision 191668
1191668Sjamie/*-
2191668Sjamie * Copyright (c) 1999 Poul-Henning Kamp.
3191668Sjamie * All rights reserved.
4191668Sjamie *
5191668Sjamie * Redistribution and use in source and binary forms, with or without
6191668Sjamie * modification, are permitted provided that the following conditions
7191668Sjamie * are met:
8191668Sjamie * 1. Redistributions of source code must retain the above copyright
9191668Sjamie *    notice, this list of conditions and the following disclaimer.
10191668Sjamie * 2. Redistributions in binary form must reproduce the above copyright
11191668Sjamie *    notice, this list of conditions and the following disclaimer in the
12191668Sjamie *    documentation and/or other materials provided with the distribution.
13191668Sjamie *
14191668Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15191668Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16191668Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17191668Sjamie * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18191668Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19191668Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20191668Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21191668Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22191668Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23191668Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24191668Sjamie * SUCH DAMAGE.
2546432Sphk */
2646432Sphk
27117280Scharnier#include <sys/cdefs.h>
28117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 191668 2009-04-29 16:02:52Z jamie $");
29117280Scharnier
30112705Smaxim#include <sys/param.h>
3146155Sphk#include <sys/jail.h>
32185435Sbz#include <sys/queue.h>
33185435Sbz#include <sys/socket.h>
34158428Smatteo#include <sys/sysctl.h>
35185435Sbz#include <sys/types.h>
3678723Sdd
3746155Sphk#include <netinet/in.h>
3878723Sdd#include <arpa/inet.h>
39185435Sbz#include <netdb.h>
4046155Sphk
4178723Sdd#include <err.h>
42129848Smaxim#include <errno.h>
43112705Smaxim#include <grp.h>
44112705Smaxim#include <login_cap.h>
45133743Smaxim#include <paths.h>
46112705Smaxim#include <pwd.h>
4778723Sdd#include <stdio.h>
4878723Sdd#include <stdlib.h>
49185435Sbz#include <strings.h>
5078723Sdd#include <string.h>
5178723Sdd#include <unistd.h>
5278723Sdd
53185435Sbzstatic void		usage(void);
54185435Sbzstatic int		add_addresses(struct addrinfo *);
55185435Sbzstatic struct in_addr	*copy_addr4(void);
56185435Sbz#ifdef INET6
57185435Sbzstatic struct in6_addr	*copy_addr6(void);
58185435Sbz#endif
59185435Sbz
60133743Smaximextern char	**environ;
61112705Smaxim
62185435Sbzstruct addr4entry {
63185435Sbz	STAILQ_ENTRY(addr4entry)	addr4entries;
64185435Sbz	struct in_addr			ip4;
65185435Sbz	int				count;
66185435Sbz};
67185435Sbzstruct addr6entry {
68185435Sbz	STAILQ_ENTRY(addr6entry)	addr6entries;
69185435Sbz#ifdef INET6
70185435Sbz	struct in6_addr			ip6;
71185435Sbz#endif
72185435Sbz	int				count;
73185435Sbz};
74185435SbzSTAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4);
75185435SbzSTAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6);
76185435Sbz
77129848Smaxim#define GET_USER_INFO do {						\
78129848Smaxim	pwd = getpwnam(username);					\
79129848Smaxim	if (pwd == NULL) {						\
80129848Smaxim		if (errno)						\
81129848Smaxim			err(1, "getpwnam: %s", username);		\
82129848Smaxim		else							\
83129848Smaxim			errx(1, "%s: no such user", username);		\
84129848Smaxim	}								\
85129848Smaxim	lcap = login_getpwclass(pwd);					\
86129848Smaxim	if (lcap == NULL)						\
87129848Smaxim		err(1, "getpwclass: %s", username);			\
88129848Smaxim	ngroups = NGROUPS;						\
89129848Smaxim	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
90129848Smaxim		err(1, "getgrouplist: %s", username);			\
91129848Smaxim} while (0)
92129848Smaxim
9346155Sphkint
9446155Sphkmain(int argc, char **argv)
9546155Sphk{
96137808Sdelphij	login_cap_t *lcap = NULL;
9746155Sphk	struct jail j;
98137808Sdelphij	struct passwd *pwd = NULL;
99136051Sstefanf	gid_t groups[NGROUPS];
100185435Sbz	int ch, error, i, ngroups, securelevel;
101185435Sbz	int hflag, iflag, Jflag, lflag, uflag, Uflag;
102185435Sbz	char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip;
103133743Smaxim	static char *cleanenv;
104137807Sdelphij	const char *shell, *p = NULL;
105158475Smatteo	long ltmp;
106153056Sphilip	FILE *fp;
107185435Sbz	struct addrinfo hints, *res0;
10846155Sphk
109185435Sbz	hflag = iflag = Jflag = lflag = uflag = Uflag = 0;
110158475Smatteo	securelevel = -1;
111185435Sbz	jailname = username = JidFile = cleanenv = NULL;
112153056Sphilip	fp = NULL;
113112705Smaxim
114185435Sbz	while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) {
115112705Smaxim		switch (ch) {
116185435Sbz		case 'h':
117185435Sbz			hflag = 1;
118185435Sbz			break;
119113277Smike		case 'i':
120113277Smike			iflag = 1;
121113277Smike			break;
122153056Sphilip		case 'J':
123153056Sphilip			JidFile = optarg;
124153056Sphilip			Jflag = 1;
125153056Sphilip			break;
126185435Sbz		case 'n':
127185435Sbz			jailname = optarg;
128185435Sbz			break;
129158428Smatteo		case 's':
130158475Smatteo			ltmp = strtol(optarg, &ep, 0);
131158475Smatteo			if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp)
132158475Smatteo				errx(1, "invalid securelevel: `%s'", optarg);
133158475Smatteo			securelevel = ltmp;
134158428Smatteo			break;
135112705Smaxim		case 'u':
136112705Smaxim			username = optarg;
137129848Smaxim			uflag = 1;
138112705Smaxim			break;
139129848Smaxim		case 'U':
140129848Smaxim			username = optarg;
141129848Smaxim			Uflag = 1;
142129848Smaxim			break;
143133743Smaxim		case 'l':
144133743Smaxim			lflag = 1;
145133743Smaxim			break;
146112705Smaxim		default:
147112705Smaxim			usage();
148112705Smaxim		}
149113277Smike	}
150112705Smaxim	argc -= optind;
151112705Smaxim	argv += optind;
152112705Smaxim	if (argc < 4)
153112705Smaxim		usage();
154129848Smaxim	if (uflag && Uflag)
155129848Smaxim		usage();
156133743Smaxim	if (lflag && username == NULL)
157133743Smaxim		usage();
158129848Smaxim	if (uflag)
159129848Smaxim		GET_USER_INFO;
160131182Spjd	if (realpath(argv[0], path) == NULL)
161131182Spjd		err(1, "realpath: %s", argv[0]);
162131182Spjd	if (chdir(path) != 0)
163131182Spjd		err(1, "chdir: %s", path);
164185435Sbz	/* Initialize struct jail. */
16551399Sphk	memset(&j, 0, sizeof(j));
166185435Sbz	j.version = JAIL_API_VERSION;
167131182Spjd	j.path = path;
168112705Smaxim	j.hostname = argv[1];
169185435Sbz	if (jailname != NULL)
170185435Sbz		j.jailname = jailname;
171185435Sbz
172185435Sbz	/* Handle IP addresses. If requested resolve hostname too. */
173185435Sbz	bzero(&hints, sizeof(struct addrinfo));
174185435Sbz	hints.ai_protocol = IPPROTO_TCP;
175185435Sbz	hints.ai_socktype = SOCK_STREAM;
176185435Sbz	if (JAIL_API_VERSION < 2)
177185435Sbz		hints.ai_family = PF_INET;
178185435Sbz	else
179185435Sbz		hints.ai_family = PF_UNSPEC;
180185435Sbz	/* Handle hostname. */
181185435Sbz	if (hflag != 0) {
182185435Sbz		error = getaddrinfo(j.hostname, NULL, &hints, &res0);
183185435Sbz		if (error != 0)
184185435Sbz			errx(1, "failed to handle hostname: %s",
185185435Sbz			    gai_strerror(error));
186185435Sbz		error = add_addresses(res0);
187185435Sbz		freeaddrinfo(res0);
188185435Sbz		if (error != 0)
189185435Sbz			errx(1, "failed to add addresses.");
190185435Sbz	}
191185435Sbz	/* Handle IP addresses. */
192185435Sbz	hints.ai_flags = AI_NUMERICHOST;
193185435Sbz	ip = strtok(argv[2], ",");
194185435Sbz	while (ip != NULL) {
195185435Sbz		error = getaddrinfo(ip, NULL, &hints, &res0);
196185435Sbz		if (error != 0)
197185435Sbz			errx(1, "failed to handle ip: %s", gai_strerror(error));
198185435Sbz		error = add_addresses(res0);
199185435Sbz		freeaddrinfo(res0);
200185435Sbz		if (error != 0)
201185435Sbz			errx(1, "failed to add addresses.");
202185435Sbz		ip = strtok(NULL, ",");
203185435Sbz	}
204185435Sbz	/* Count IP addresses and add them to struct jail. */
205185435Sbz	if (!STAILQ_EMPTY(&addr4)) {
206185435Sbz		j.ip4s = STAILQ_FIRST(&addr4)->count;
207185435Sbz		j.ip4 = copy_addr4();
208185435Sbz		if (j.ip4s > 0 && j.ip4 == NULL)
209185435Sbz			errx(1, "copy_addr4()");
210185435Sbz	}
211185435Sbz#ifdef INET6
212185435Sbz	if (!STAILQ_EMPTY(&addr6)) {
213185435Sbz		j.ip6s = STAILQ_FIRST(&addr6)->count;
214185435Sbz		j.ip6 = copy_addr6();
215185435Sbz		if (j.ip6s > 0 && j.ip6 == NULL)
216185435Sbz			errx(1, "copy_addr6()");
217185435Sbz	}
218185435Sbz#endif
219185435Sbz
220153056Sphilip	if (Jflag) {
221153056Sphilip		fp = fopen(JidFile, "w");
222153056Sphilip		if (fp == NULL)
223153056Sphilip			errx(1, "Could not create JidFile: %s", JidFile);
224153056Sphilip	}
225113277Smike	i = jail(&j);
226113277Smike	if (i == -1)
227185435Sbz		err(1, "syscall failed with");
228113804Smike	if (iflag) {
229113277Smike		printf("%d\n", i);
230113804Smike		fflush(stdout);
231113804Smike	}
232153056Sphilip	if (Jflag) {
233153056Sphilip		if (fp != NULL) {
234153056Sphilip			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
235153056Sphilip			    i, j.path, j.hostname, argv[2], argv[3]);
236153056Sphilip			(void)fclose(fp);
237153056Sphilip		} else {
238153056Sphilip			errx(1, "Could not write JidFile: %s", JidFile);
239153056Sphilip		}
240153056Sphilip	}
241158454Smaxim	if (securelevel > 0) {
242158454Smaxim		if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel,
243158454Smaxim		    sizeof(securelevel)))
244158454Smaxim			err(1, "Can not set securelevel to %d", securelevel);
245158454Smaxim	}
246112705Smaxim	if (username != NULL) {
247129848Smaxim		if (Uflag)
248129848Smaxim			GET_USER_INFO;
249133743Smaxim		if (lflag) {
250133743Smaxim			p = getenv("TERM");
251133743Smaxim			environ = &cleanenv;
252133743Smaxim		}
253112972Smaxim		if (setgroups(ngroups, groups) != 0)
254112972Smaxim			err(1, "setgroups");
255112972Smaxim		if (setgid(pwd->pw_gid) != 0)
256112972Smaxim			err(1, "setgid");
257112972Smaxim		if (setusercontext(lcap, pwd, pwd->pw_uid,
258157790Smaxim		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
259112972Smaxim			err(1, "setusercontext");
260113206Smaxim		login_close(lcap);
261112705Smaxim	}
262133743Smaxim	if (lflag) {
263133743Smaxim		if (*pwd->pw_shell)
264133743Smaxim			shell = pwd->pw_shell;
265133743Smaxim		else
266133743Smaxim			shell = _PATH_BSHELL;
267133743Smaxim		if (chdir(pwd->pw_dir) < 0)
268133743Smaxim			errx(1, "no home directory");
269133743Smaxim		setenv("HOME", pwd->pw_dir, 1);
270133743Smaxim		setenv("SHELL", shell, 1);
271133743Smaxim		setenv("USER", pwd->pw_name, 1);
272133743Smaxim		if (p)
273133743Smaxim			setenv("TERM", p, 1);
274133743Smaxim	}
275112972Smaxim	if (execv(argv[3], argv + 3) != 0)
276112972Smaxim		err(1, "execv: %s", argv[3]);
277113277Smike	exit(0);
27846155Sphk}
279112705Smaxim
280112705Smaximstatic void
281112705Smaximusage(void)
282112705Smaxim{
283112705Smaxim
284158428Smatteo	(void)fprintf(stderr, "%s%s%s\n",
285185435Sbz	     "usage: jail [-hi] [-n jailname] [-J jid_file] ",
286185435Sbz	     "[-s securelevel] [-l -u username | -U username] ",
287185435Sbz	     "path hostname [ip[,..]] command ...");
288112972Smaxim	exit(1);
289112705Smaxim}
290185435Sbz
291185435Sbzstatic int
292185435Sbzadd_addresses(struct addrinfo *res0)
293185435Sbz{
294185435Sbz	int error;
295185435Sbz	struct addrinfo *res;
296185435Sbz	struct addr4entry *a4p;
297185435Sbz	struct sockaddr_in *sai;
298185435Sbz#ifdef INET6
299185435Sbz	struct addr6entry *a6p;
300185435Sbz	struct sockaddr_in6 *sai6;
301185435Sbz#endif
302185435Sbz	int count;
303185435Sbz
304185435Sbz	error = 0;
305185435Sbz	for (res = res0; res && error == 0; res = res->ai_next) {
306185435Sbz		switch (res->ai_family) {
307185435Sbz		case AF_INET:
308185435Sbz			sai = (struct sockaddr_in *)(void *)res->ai_addr;
309185435Sbz			STAILQ_FOREACH(a4p, &addr4, addr4entries) {
310185435Sbz			    if (bcmp(&sai->sin_addr, &a4p->ip4,
311185435Sbz				sizeof(struct in_addr)) == 0) {
312185435Sbz				    err(1, "Ignoring duplicate IPv4 address.");
313185435Sbz				    break;
314185435Sbz			    }
315185435Sbz			}
316185435Sbz			a4p = (struct addr4entry *) malloc(
317185435Sbz			    sizeof(struct addr4entry));
318185435Sbz			if (a4p == NULL) {
319185435Sbz				error = 1;
320185435Sbz				break;
321185435Sbz			}
322185435Sbz			bzero(a4p, sizeof(struct addr4entry));
323185435Sbz			bcopy(&sai->sin_addr, &a4p->ip4,
324185435Sbz			    sizeof(struct in_addr));
325185435Sbz			if (!STAILQ_EMPTY(&addr4))
326185435Sbz				count = STAILQ_FIRST(&addr4)->count;
327185435Sbz			else
328185435Sbz				count = 0;
329185435Sbz			STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries);
330185435Sbz			STAILQ_FIRST(&addr4)->count = count + 1;
331185435Sbz			break;
332185435Sbz#ifdef INET6
333185435Sbz		case AF_INET6:
334185435Sbz			sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
335185435Sbz			STAILQ_FOREACH(a6p, &addr6, addr6entries) {
336185435Sbz			    if (bcmp(&sai6->sin6_addr, &a6p->ip6,
337185435Sbz				sizeof(struct in6_addr)) == 0) {
338185435Sbz				    err(1, "Ignoring duplicate IPv6 address.");
339185435Sbz				    break;
340185435Sbz			    }
341185435Sbz			}
342185435Sbz			a6p = (struct addr6entry *) malloc(
343185435Sbz			    sizeof(struct addr6entry));
344185435Sbz			if (a6p == NULL) {
345185435Sbz				error = 1;
346185435Sbz				break;
347185435Sbz			}
348185435Sbz			bzero(a6p, sizeof(struct addr6entry));
349185435Sbz			bcopy(&sai6->sin6_addr, &a6p->ip6,
350185435Sbz			    sizeof(struct in6_addr));
351185435Sbz			if (!STAILQ_EMPTY(&addr6))
352185435Sbz				count = STAILQ_FIRST(&addr6)->count;
353185435Sbz			else
354185435Sbz				count = 0;
355185435Sbz			STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries);
356185435Sbz			STAILQ_FIRST(&addr6)->count = count + 1;
357185435Sbz			break;
358185435Sbz#endif
359185435Sbz		default:
360185435Sbz			err(1, "Address family %d not supported. Ignoring.\n",
361185435Sbz			    res->ai_family);
362185435Sbz			break;
363185435Sbz		}
364185435Sbz	}
365185435Sbz
366185435Sbz	return (error);
367185435Sbz}
368185435Sbz
369185435Sbzstatic struct in_addr *
370185435Sbzcopy_addr4(void)
371185435Sbz{
372185435Sbz	size_t len;
373185435Sbz	struct in_addr *ip4s, *p, ia;
374185435Sbz	struct addr4entry *a4p;
375185435Sbz
376185435Sbz	if (STAILQ_EMPTY(&addr4))
377185435Sbz		return NULL;
378185435Sbz
379185435Sbz	len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr);
380185435Sbz
381185435Sbz	ip4s = p = (struct in_addr *)malloc(len);
382185435Sbz	if (ip4s == NULL)
383185435Sbz	return (NULL);
384185435Sbz
385185435Sbz	bzero(p, len);
386185435Sbz
387185435Sbz	while (!STAILQ_EMPTY(&addr4)) {
388185435Sbz		a4p = STAILQ_FIRST(&addr4);
389185435Sbz		STAILQ_REMOVE_HEAD(&addr4, addr4entries);
390185435Sbz		ia.s_addr = a4p->ip4.s_addr;
391185435Sbz		bcopy(&ia, p, sizeof(struct in_addr));
392185435Sbz		p++;
393185435Sbz		free(a4p);
394185435Sbz	}
395185435Sbz
396185435Sbz	return (ip4s);
397185435Sbz}
398185435Sbz
399185435Sbz#ifdef INET6
400185435Sbzstatic struct in6_addr *
401185435Sbzcopy_addr6(void)
402185435Sbz{
403185435Sbz	size_t len;
404185435Sbz	struct in6_addr *ip6s, *p;
405185435Sbz	struct addr6entry *a6p;
406185435Sbz
407185435Sbz	if (STAILQ_EMPTY(&addr6))
408185435Sbz		return NULL;
409185435Sbz
410185435Sbz	len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr);
411185435Sbz
412185435Sbz	ip6s = p = (struct in6_addr *)malloc(len);
413185435Sbz	if (ip6s == NULL)
414185435Sbz		return (NULL);
415185435Sbz
416185435Sbz	bzero(p, len);
417185435Sbz
418185435Sbz	while (!STAILQ_EMPTY(&addr6)) {
419185435Sbz		a6p = STAILQ_FIRST(&addr6);
420185435Sbz		STAILQ_REMOVE_HEAD(&addr6, addr6entries);
421185435Sbz		bcopy(&a6p->ip6, p, sizeof(struct in6_addr));
422185435Sbz		p++;
423185435Sbz		free(a6p);
424185435Sbz	}
425185435Sbz
426185435Sbz	return (ip6s);
427185435Sbz}
428185435Sbz#endif
429185435Sbz
430