xref: /freebsd/usr.sbin/jexec/jexec.c (revision 8655c70597b0e0918c82114b1186df5669b83eb6)
1 /*-
2  * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
3  * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include <sys/param.h>
31 #include <sys/jail.h>
32 #include <sys/sysctl.h>
33 
34 #include <netinet/in.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <login_cap.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <unistd.h>
44 
45 static void	usage(void);
46 
47 #ifdef SUPPORT_OLD_XPRISON
48 static
49 char *lookup_xprison_v1(void *p, char *end, int *id)
50 {
51 	struct xprison_v1 *xp;
52 
53 	if (id == NULL)
54 		errx(1, "Internal error. Invalid ID pointer.");
55 
56 	if ((char *)p + sizeof(struct xprison_v1) > end)
57 		errx(1, "Invalid length for jail");
58 
59 	xp = (struct xprison_v1 *)p;
60 
61 	*id = xp->pr_id;
62 	return ((char *)(xp + 1));
63 }
64 #endif
65 
66 static
67 char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname)
68 {
69 	struct xprison *xp;
70 	char *q;
71 	int ok;
72 
73 	if (id == NULL)
74 		errx(1, "Internal error. Invalid ID pointer.");
75 
76 	if ((char *)p + sizeof(struct xprison) > end)
77 		errx(1, "Invalid length for jail");
78 
79 	xp = (struct xprison *)p;
80 	ok = 1;
81 
82 	/* Jail state and name. */
83 	if (xp->pr_state < 0 || xp->pr_state >=
84 	    (int)((sizeof(prison_states) / sizeof(struct prison_state))))
85 		errx(1, "Invalid jail state.");
86 	else if (xp->pr_state != PRISON_STATE_ALIVE)
87 		ok = 0;
88 	if (jailname != NULL) {
89 		if (xp->pr_name[0] == '\0')
90 			ok = 0;
91 		else if (strcmp(jailname, xp->pr_name) != 0)
92 			ok = 0;
93 	}
94 
95 	q = (char *)(xp + 1);
96 	/* IPv4 addresses. */
97 	q += (xp->pr_ip4s * sizeof(struct in_addr));
98 	if ((char *)q > end)
99 		errx(1, "Invalid length for jail");
100 	/* IPv6 addresses. */
101 	q += (xp->pr_ip6s * sizeof(struct in6_addr));
102 	if ((char *)q > end)
103 		errx(1, "Invalid length for jail");
104 
105 	if (ok)
106 		*id = xp->pr_id;
107 	return (q);
108 }
109 
110 static int
111 lookup_jail(int jid, char *jailname)
112 {
113 	size_t i, j, len;
114 	void *p, *q;
115 	int version, id, xid, count;
116 
117 	if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
118 		err(1, "sysctlbyname(): security.jail.list");
119 
120 	j = len;
121 	for (i = 0; i < 4; i++) {
122 		if (len <= 0)
123 			exit(0);
124 		p = q = malloc(len);
125 		if (p == NULL)
126 			err(1, "malloc()");
127 
128 		if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
129 			if (errno == ENOMEM) {
130 				free(p);
131 				p = NULL;
132 				len += j;
133 				continue;
134 			}
135 			err(1, "sysctlbyname(): security.jail.list");
136 		}
137 		break;
138 	}
139 	if (p == NULL)
140 		err(1, "sysctlbyname(): security.jail.list");
141 	if (len < sizeof(int))
142 		errx(1, "This is no prison. Kernel and userland out of sync?");
143 	version = *(int *)p;
144 	if (version > XPRISON_VERSION)
145 		errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
146 
147 	count = 0;
148 	xid = -1;
149 	for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
150 		version = *(int *)q;
151 		if (version > XPRISON_VERSION)
152 			errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
153 		id = -1;
154 		switch (version) {
155 #ifdef SUPPORT_OLD_XPRISON
156 		case 1:
157 			if (jailname != NULL)
158 				errx(1, "Version 1 prisons did not "
159 				    "support jail names.");
160 			q = lookup_xprison_v1(q, (char *)p + len, &id);
161 			break;
162 		case 2:
163 			errx(1, "Version 2 was used by multi-IPv4 jail "
164 			    "implementations that never made it into the "
165 			    "official kernel.");
166 			/* NOTREACHED */
167 			break;
168 #endif
169 		case 3:
170 			q = lookup_xprison_v3(q, (char *)p + len, &id, jailname);
171 			break;
172 		default:
173 			errx(1, "Prison unknown. Kernel/userland out of sync?");
174 			/* NOTREACHED */
175 			break;
176 		}
177 		/* Possible match. */
178 		if (id > 0) {
179 			/* Do we have a jail ID to match as well? */
180 			if (jid > 0) {
181 				if (jid == id) {
182 					xid = id;
183 					count++;
184 				}
185 			} else {
186 				xid = id;
187 				count++;
188 			}
189 		}
190 	}
191 
192 	free(p);
193 
194 	if (count != 1)
195 		errx(1, "Could not uniquely identify the jail.");
196 
197 	return (xid);
198 }
199 
200 #define GET_USER_INFO do {						\
201 	pwd = getpwnam(username);					\
202 	if (pwd == NULL) {						\
203 		if (errno)						\
204 			err(1, "getpwnam: %s", username);		\
205 		else							\
206 			errx(1, "%s: no such user", username);		\
207 	}								\
208 	lcap = login_getpwclass(pwd);					\
209 	if (lcap == NULL)						\
210 		err(1, "getpwclass: %s", username);			\
211 	ngroups = NGROUPS;						\
212 	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
213 		err(1, "getgrouplist: %s", username);			\
214 } while (0)
215 
216 int
217 main(int argc, char *argv[])
218 {
219 	int jid;
220 	login_cap_t *lcap = NULL;
221 	struct passwd *pwd = NULL;
222 	gid_t groups[NGROUPS];
223 	int ch, ngroups, uflag, Uflag;
224 	char *jailname, *username;
225 
226 	ch = uflag = Uflag = 0;
227 	jailname = username = NULL;
228 	jid = -1;
229 
230 	while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) {
231 		switch (ch) {
232 		case 'n':
233 			jailname = optarg;
234 			break;
235 		case 'u':
236 			username = optarg;
237 			uflag = 1;
238 			break;
239 		case 'U':
240 			username = optarg;
241 			Uflag = 1;
242 			break;
243 		default:
244 			usage();
245 		}
246 	}
247 	argc -= optind;
248 	argv += optind;
249 	if (argc < 2)
250 		usage();
251 	if (strlen(argv[0]) > 0) {
252 		jid = (int)strtol(argv[0], NULL, 10);
253 		if (errno)
254 			err(1, "Unable to parse jail ID.");
255 	}
256 	if (jid <= 0 && jailname == NULL) {
257 		fprintf(stderr, "Neither jail ID nor jail name given.\n");
258 		usage();
259 	}
260 	if (uflag && Uflag)
261 		usage();
262 	if (uflag)
263 		GET_USER_INFO;
264 	jid = lookup_jail(jid, jailname);
265 	if (jid <= 0)
266 		errx(1, "Cannot identify jail.");
267 	if (jail_attach(jid) == -1)
268 		err(1, "jail_attach(): %d", jid);
269 	if (chdir("/") == -1)
270 		err(1, "chdir(): /");
271 	if (username != NULL) {
272 		if (Uflag)
273 			GET_USER_INFO;
274 		if (setgroups(ngroups, groups) != 0)
275 			err(1, "setgroups");
276 		if (setgid(pwd->pw_gid) != 0)
277 			err(1, "setgid");
278 		if (setusercontext(lcap, pwd, pwd->pw_uid,
279 		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
280 			err(1, "setusercontext");
281 		login_close(lcap);
282 	}
283 	if (execvp(argv[1], argv + 1) == -1)
284 		err(1, "execvp(): %s", argv[1]);
285 	exit(0);
286 }
287 
288 static void
289 usage(void)
290 {
291 
292 	fprintf(stderr, "%s%s\n",
293 		"usage: jexec [-u username | -U username]",
294 		" [-n jailname] jid command ...");
295 	exit(1);
296 }
297