jail.c revision 185435
146432Sphk/*
246432Sphk * ----------------------------------------------------------------------------
346432Sphk * "THE BEER-WARE LICENSE" (Revision 42):
446432Sphk * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
546432Sphk * can do whatever you want with this stuff. If we meet some day, and you think
646432Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
746432Sphk * ----------------------------------------------------------------------------
846432Sphk */
946432Sphk
10117280Scharnier#include <sys/cdefs.h>
11117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 185435 2008-11-29 14:32:14Z bz $");
12117280Scharnier
13112705Smaxim#include <sys/param.h>
1446155Sphk#include <sys/jail.h>
15185435Sbz#include <sys/queue.h>
16185435Sbz#include <sys/socket.h>
17158428Smatteo#include <sys/sysctl.h>
18185435Sbz#include <sys/types.h>
1978723Sdd
2046155Sphk#include <netinet/in.h>
2178723Sdd#include <arpa/inet.h>
22185435Sbz#include <netdb.h>
2346155Sphk
2478723Sdd#include <err.h>
25129848Smaxim#include <errno.h>
26112705Smaxim#include <grp.h>
27112705Smaxim#include <login_cap.h>
28133743Smaxim#include <paths.h>
29112705Smaxim#include <pwd.h>
3078723Sdd#include <stdio.h>
3178723Sdd#include <stdlib.h>
32185435Sbz#include <strings.h>
3378723Sdd#include <string.h>
3478723Sdd#include <unistd.h>
3578723Sdd
36185435Sbzstatic void		usage(void);
37185435Sbzstatic int		add_addresses(struct addrinfo *);
38185435Sbzstatic struct in_addr	*copy_addr4(void);
39185435Sbz#ifdef INET6
40185435Sbzstatic struct in6_addr	*copy_addr6(void);
41185435Sbz#endif
42185435Sbz
43133743Smaximextern char	**environ;
44112705Smaxim
45185435Sbzstruct addr4entry {
46185435Sbz	STAILQ_ENTRY(addr4entry)	addr4entries;
47185435Sbz	struct in_addr			ip4;
48185435Sbz	int				count;
49185435Sbz};
50185435Sbzstruct addr6entry {
51185435Sbz	STAILQ_ENTRY(addr6entry)	addr6entries;
52185435Sbz#ifdef INET6
53185435Sbz	struct in6_addr			ip6;
54185435Sbz#endif
55185435Sbz	int				count;
56185435Sbz};
57185435SbzSTAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4);
58185435SbzSTAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6);
59185435Sbz
60129848Smaxim#define GET_USER_INFO do {						\
61129848Smaxim	pwd = getpwnam(username);					\
62129848Smaxim	if (pwd == NULL) {						\
63129848Smaxim		if (errno)						\
64129848Smaxim			err(1, "getpwnam: %s", username);		\
65129848Smaxim		else							\
66129848Smaxim			errx(1, "%s: no such user", username);		\
67129848Smaxim	}								\
68129848Smaxim	lcap = login_getpwclass(pwd);					\
69129848Smaxim	if (lcap == NULL)						\
70129848Smaxim		err(1, "getpwclass: %s", username);			\
71129848Smaxim	ngroups = NGROUPS;						\
72129848Smaxim	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
73129848Smaxim		err(1, "getgrouplist: %s", username);			\
74129848Smaxim} while (0)
75129848Smaxim
7646155Sphkint
7746155Sphkmain(int argc, char **argv)
7846155Sphk{
79137808Sdelphij	login_cap_t *lcap = NULL;
8046155Sphk	struct jail j;
81137808Sdelphij	struct passwd *pwd = NULL;
82136051Sstefanf	gid_t groups[NGROUPS];
83185435Sbz	int ch, error, i, ngroups, securelevel;
84185435Sbz	int hflag, iflag, Jflag, lflag, uflag, Uflag;
85185435Sbz	char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip;
86133743Smaxim	static char *cleanenv;
87137807Sdelphij	const char *shell, *p = NULL;
88158475Smatteo	long ltmp;
89153056Sphilip	FILE *fp;
90185435Sbz	struct addrinfo hints, *res0;
9146155Sphk
92185435Sbz	hflag = iflag = Jflag = lflag = uflag = Uflag = 0;
93158475Smatteo	securelevel = -1;
94185435Sbz	jailname = username = JidFile = cleanenv = NULL;
95153056Sphilip	fp = NULL;
96112705Smaxim
97185435Sbz	while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) {
98112705Smaxim		switch (ch) {
99185435Sbz		case 'h':
100185435Sbz			hflag = 1;
101185435Sbz			break;
102113277Smike		case 'i':
103113277Smike			iflag = 1;
104113277Smike			break;
105153056Sphilip		case 'J':
106153056Sphilip			JidFile = optarg;
107153056Sphilip			Jflag = 1;
108153056Sphilip			break;
109185435Sbz		case 'n':
110185435Sbz			jailname = optarg;
111185435Sbz			break;
112158428Smatteo		case 's':
113158475Smatteo			ltmp = strtol(optarg, &ep, 0);
114158475Smatteo			if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp)
115158475Smatteo				errx(1, "invalid securelevel: `%s'", optarg);
116158475Smatteo			securelevel = ltmp;
117158428Smatteo			break;
118112705Smaxim		case 'u':
119112705Smaxim			username = optarg;
120129848Smaxim			uflag = 1;
121112705Smaxim			break;
122129848Smaxim		case 'U':
123129848Smaxim			username = optarg;
124129848Smaxim			Uflag = 1;
125129848Smaxim			break;
126133743Smaxim		case 'l':
127133743Smaxim			lflag = 1;
128133743Smaxim			break;
129112705Smaxim		default:
130112705Smaxim			usage();
131112705Smaxim		}
132113277Smike	}
133112705Smaxim	argc -= optind;
134112705Smaxim	argv += optind;
135112705Smaxim	if (argc < 4)
136112705Smaxim		usage();
137129848Smaxim	if (uflag && Uflag)
138129848Smaxim		usage();
139133743Smaxim	if (lflag && username == NULL)
140133743Smaxim		usage();
141129848Smaxim	if (uflag)
142129848Smaxim		GET_USER_INFO;
143131182Spjd	if (realpath(argv[0], path) == NULL)
144131182Spjd		err(1, "realpath: %s", argv[0]);
145131182Spjd	if (chdir(path) != 0)
146131182Spjd		err(1, "chdir: %s", path);
147185435Sbz	/* Initialize struct jail. */
14851399Sphk	memset(&j, 0, sizeof(j));
149185435Sbz	j.version = JAIL_API_VERSION;
150131182Spjd	j.path = path;
151112705Smaxim	j.hostname = argv[1];
152185435Sbz	if (jailname != NULL)
153185435Sbz		j.jailname = jailname;
154185435Sbz
155185435Sbz	/* Handle IP addresses. If requested resolve hostname too. */
156185435Sbz	bzero(&hints, sizeof(struct addrinfo));
157185435Sbz	hints.ai_protocol = IPPROTO_TCP;
158185435Sbz	hints.ai_socktype = SOCK_STREAM;
159185435Sbz	if (JAIL_API_VERSION < 2)
160185435Sbz		hints.ai_family = PF_INET;
161185435Sbz	else
162185435Sbz		hints.ai_family = PF_UNSPEC;
163185435Sbz	/* Handle hostname. */
164185435Sbz	if (hflag != 0) {
165185435Sbz		error = getaddrinfo(j.hostname, NULL, &hints, &res0);
166185435Sbz		if (error != 0)
167185435Sbz			errx(1, "failed to handle hostname: %s",
168185435Sbz			    gai_strerror(error));
169185435Sbz		error = add_addresses(res0);
170185435Sbz		freeaddrinfo(res0);
171185435Sbz		if (error != 0)
172185435Sbz			errx(1, "failed to add addresses.");
173185435Sbz	}
174185435Sbz	/* Handle IP addresses. */
175185435Sbz	hints.ai_flags = AI_NUMERICHOST;
176185435Sbz	ip = strtok(argv[2], ",");
177185435Sbz	while (ip != NULL) {
178185435Sbz		error = getaddrinfo(ip, NULL, &hints, &res0);
179185435Sbz		if (error != 0)
180185435Sbz			errx(1, "failed to handle ip: %s", gai_strerror(error));
181185435Sbz		error = add_addresses(res0);
182185435Sbz		freeaddrinfo(res0);
183185435Sbz		if (error != 0)
184185435Sbz			errx(1, "failed to add addresses.");
185185435Sbz		ip = strtok(NULL, ",");
186185435Sbz	}
187185435Sbz	/* Count IP addresses and add them to struct jail. */
188185435Sbz	if (!STAILQ_EMPTY(&addr4)) {
189185435Sbz		j.ip4s = STAILQ_FIRST(&addr4)->count;
190185435Sbz		j.ip4 = copy_addr4();
191185435Sbz		if (j.ip4s > 0 && j.ip4 == NULL)
192185435Sbz			errx(1, "copy_addr4()");
193185435Sbz	}
194185435Sbz#ifdef INET6
195185435Sbz	if (!STAILQ_EMPTY(&addr6)) {
196185435Sbz		j.ip6s = STAILQ_FIRST(&addr6)->count;
197185435Sbz		j.ip6 = copy_addr6();
198185435Sbz		if (j.ip6s > 0 && j.ip6 == NULL)
199185435Sbz			errx(1, "copy_addr6()");
200185435Sbz	}
201185435Sbz#endif
202185435Sbz
203153056Sphilip	if (Jflag) {
204153056Sphilip		fp = fopen(JidFile, "w");
205153056Sphilip		if (fp == NULL)
206153056Sphilip			errx(1, "Could not create JidFile: %s", JidFile);
207153056Sphilip	}
208113277Smike	i = jail(&j);
209113277Smike	if (i == -1)
210185435Sbz		err(1, "syscall failed with");
211113804Smike	if (iflag) {
212113277Smike		printf("%d\n", i);
213113804Smike		fflush(stdout);
214113804Smike	}
215153056Sphilip	if (Jflag) {
216153056Sphilip		if (fp != NULL) {
217153056Sphilip			fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
218153056Sphilip			    i, j.path, j.hostname, argv[2], argv[3]);
219153056Sphilip			(void)fclose(fp);
220153056Sphilip		} else {
221153056Sphilip			errx(1, "Could not write JidFile: %s", JidFile);
222153056Sphilip		}
223153056Sphilip	}
224158454Smaxim	if (securelevel > 0) {
225158454Smaxim		if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel,
226158454Smaxim		    sizeof(securelevel)))
227158454Smaxim			err(1, "Can not set securelevel to %d", securelevel);
228158454Smaxim	}
229112705Smaxim	if (username != NULL) {
230129848Smaxim		if (Uflag)
231129848Smaxim			GET_USER_INFO;
232133743Smaxim		if (lflag) {
233133743Smaxim			p = getenv("TERM");
234133743Smaxim			environ = &cleanenv;
235133743Smaxim		}
236112972Smaxim		if (setgroups(ngroups, groups) != 0)
237112972Smaxim			err(1, "setgroups");
238112972Smaxim		if (setgid(pwd->pw_gid) != 0)
239112972Smaxim			err(1, "setgid");
240112972Smaxim		if (setusercontext(lcap, pwd, pwd->pw_uid,
241157790Smaxim		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
242112972Smaxim			err(1, "setusercontext");
243113206Smaxim		login_close(lcap);
244112705Smaxim	}
245133743Smaxim	if (lflag) {
246133743Smaxim		if (*pwd->pw_shell)
247133743Smaxim			shell = pwd->pw_shell;
248133743Smaxim		else
249133743Smaxim			shell = _PATH_BSHELL;
250133743Smaxim		if (chdir(pwd->pw_dir) < 0)
251133743Smaxim			errx(1, "no home directory");
252133743Smaxim		setenv("HOME", pwd->pw_dir, 1);
253133743Smaxim		setenv("SHELL", shell, 1);
254133743Smaxim		setenv("USER", pwd->pw_name, 1);
255133743Smaxim		if (p)
256133743Smaxim			setenv("TERM", p, 1);
257133743Smaxim	}
258112972Smaxim	if (execv(argv[3], argv + 3) != 0)
259112972Smaxim		err(1, "execv: %s", argv[3]);
260113277Smike	exit(0);
26146155Sphk}
262112705Smaxim
263112705Smaximstatic void
264112705Smaximusage(void)
265112705Smaxim{
266112705Smaxim
267158428Smatteo	(void)fprintf(stderr, "%s%s%s\n",
268185435Sbz	     "usage: jail [-hi] [-n jailname] [-J jid_file] ",
269185435Sbz	     "[-s securelevel] [-l -u username | -U username] ",
270185435Sbz	     "path hostname [ip[,..]] command ...");
271112972Smaxim	exit(1);
272112705Smaxim}
273185435Sbz
274185435Sbzstatic int
275185435Sbzadd_addresses(struct addrinfo *res0)
276185435Sbz{
277185435Sbz	int error;
278185435Sbz	struct addrinfo *res;
279185435Sbz	struct addr4entry *a4p;
280185435Sbz	struct sockaddr_in *sai;
281185435Sbz#ifdef INET6
282185435Sbz	struct addr6entry *a6p;
283185435Sbz	struct sockaddr_in6 *sai6;
284185435Sbz#endif
285185435Sbz	int count;
286185435Sbz
287185435Sbz	error = 0;
288185435Sbz	for (res = res0; res && error == 0; res = res->ai_next) {
289185435Sbz		switch (res->ai_family) {
290185435Sbz		case AF_INET:
291185435Sbz			sai = (struct sockaddr_in *)(void *)res->ai_addr;
292185435Sbz			STAILQ_FOREACH(a4p, &addr4, addr4entries) {
293185435Sbz			    if (bcmp(&sai->sin_addr, &a4p->ip4,
294185435Sbz				sizeof(struct in_addr)) == 0) {
295185435Sbz				    err(1, "Ignoring duplicate IPv4 address.");
296185435Sbz				    break;
297185435Sbz			    }
298185435Sbz			}
299185435Sbz			a4p = (struct addr4entry *) malloc(
300185435Sbz			    sizeof(struct addr4entry));
301185435Sbz			if (a4p == NULL) {
302185435Sbz				error = 1;
303185435Sbz				break;
304185435Sbz			}
305185435Sbz			bzero(a4p, sizeof(struct addr4entry));
306185435Sbz			bcopy(&sai->sin_addr, &a4p->ip4,
307185435Sbz			    sizeof(struct in_addr));
308185435Sbz			if (!STAILQ_EMPTY(&addr4))
309185435Sbz				count = STAILQ_FIRST(&addr4)->count;
310185435Sbz			else
311185435Sbz				count = 0;
312185435Sbz			STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries);
313185435Sbz			STAILQ_FIRST(&addr4)->count = count + 1;
314185435Sbz			break;
315185435Sbz#ifdef INET6
316185435Sbz		case AF_INET6:
317185435Sbz			sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
318185435Sbz			STAILQ_FOREACH(a6p, &addr6, addr6entries) {
319185435Sbz			    if (bcmp(&sai6->sin6_addr, &a6p->ip6,
320185435Sbz				sizeof(struct in6_addr)) == 0) {
321185435Sbz				    err(1, "Ignoring duplicate IPv6 address.");
322185435Sbz				    break;
323185435Sbz			    }
324185435Sbz			}
325185435Sbz			a6p = (struct addr6entry *) malloc(
326185435Sbz			    sizeof(struct addr6entry));
327185435Sbz			if (a6p == NULL) {
328185435Sbz				error = 1;
329185435Sbz				break;
330185435Sbz			}
331185435Sbz			bzero(a6p, sizeof(struct addr6entry));
332185435Sbz			bcopy(&sai6->sin6_addr, &a6p->ip6,
333185435Sbz			    sizeof(struct in6_addr));
334185435Sbz			if (!STAILQ_EMPTY(&addr6))
335185435Sbz				count = STAILQ_FIRST(&addr6)->count;
336185435Sbz			else
337185435Sbz				count = 0;
338185435Sbz			STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries);
339185435Sbz			STAILQ_FIRST(&addr6)->count = count + 1;
340185435Sbz			break;
341185435Sbz#endif
342185435Sbz		default:
343185435Sbz			err(1, "Address family %d not supported. Ignoring.\n",
344185435Sbz			    res->ai_family);
345185435Sbz			break;
346185435Sbz		}
347185435Sbz	}
348185435Sbz
349185435Sbz	return (error);
350185435Sbz}
351185435Sbz
352185435Sbzstatic struct in_addr *
353185435Sbzcopy_addr4(void)
354185435Sbz{
355185435Sbz	size_t len;
356185435Sbz	struct in_addr *ip4s, *p, ia;
357185435Sbz	struct addr4entry *a4p;
358185435Sbz
359185435Sbz	if (STAILQ_EMPTY(&addr4))
360185435Sbz		return NULL;
361185435Sbz
362185435Sbz	len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr);
363185435Sbz
364185435Sbz	ip4s = p = (struct in_addr *)malloc(len);
365185435Sbz	if (ip4s == NULL)
366185435Sbz	return (NULL);
367185435Sbz
368185435Sbz	bzero(p, len);
369185435Sbz
370185435Sbz	while (!STAILQ_EMPTY(&addr4)) {
371185435Sbz		a4p = STAILQ_FIRST(&addr4);
372185435Sbz		STAILQ_REMOVE_HEAD(&addr4, addr4entries);
373185435Sbz		ia.s_addr = a4p->ip4.s_addr;
374185435Sbz		bcopy(&ia, p, sizeof(struct in_addr));
375185435Sbz		p++;
376185435Sbz		free(a4p);
377185435Sbz	}
378185435Sbz
379185435Sbz	return (ip4s);
380185435Sbz}
381185435Sbz
382185435Sbz#ifdef INET6
383185435Sbzstatic struct in6_addr *
384185435Sbzcopy_addr6(void)
385185435Sbz{
386185435Sbz	size_t len;
387185435Sbz	struct in6_addr *ip6s, *p;
388185435Sbz	struct addr6entry *a6p;
389185435Sbz
390185435Sbz	if (STAILQ_EMPTY(&addr6))
391185435Sbz		return NULL;
392185435Sbz
393185435Sbz	len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr);
394185435Sbz
395185435Sbz	ip6s = p = (struct in6_addr *)malloc(len);
396185435Sbz	if (ip6s == NULL)
397185435Sbz		return (NULL);
398185435Sbz
399185435Sbz	bzero(p, len);
400185435Sbz
401185435Sbz	while (!STAILQ_EMPTY(&addr6)) {
402185435Sbz		a6p = STAILQ_FIRST(&addr6);
403185435Sbz		STAILQ_REMOVE_HEAD(&addr6, addr6entries);
404185435Sbz		bcopy(&a6p->ip6, p, sizeof(struct in6_addr));
405185435Sbz		p++;
406185435Sbz		free(a6p);
407185435Sbz	}
408185435Sbz
409185435Sbz	return (ip6s);
410185435Sbz}
411185435Sbz#endif
412185435Sbz
413