xref: /freebsd/usr.sbin/jexec/jexec.c (revision c0020399a650364d0134f79f3fa319f84064372d)
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 			return (-1);
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; see if we have a jail ID to match as well.  */
178 		if (id > 0 && (jid <= 0 || id == jid)) {
179 			xid = id;
180 			count++;
181 		}
182 	}
183 
184 	free(p);
185 
186 	if (count == 1)
187 		return (xid);
188 	else if (count > 1)
189 		errx(1, "Could not uniquely identify the jail.");
190 	else
191 		return (-1);
192 }
193 
194 #define GET_USER_INFO do {						\
195 	pwd = getpwnam(username);					\
196 	if (pwd == NULL) {						\
197 		if (errno)						\
198 			err(1, "getpwnam: %s", username);		\
199 		else							\
200 			errx(1, "%s: no such user", username);		\
201 	}								\
202 	lcap = login_getpwclass(pwd);					\
203 	if (lcap == NULL)						\
204 		err(1, "getpwclass: %s", username);			\
205 	ngroups = NGROUPS;						\
206 	if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0)	\
207 		err(1, "getgrouplist: %s", username);			\
208 } while (0)
209 
210 int
211 main(int argc, char *argv[])
212 {
213 	int jid;
214 	login_cap_t *lcap = NULL;
215 	struct passwd *pwd = NULL;
216 	gid_t groups[NGROUPS];
217 	int ch, ngroups, uflag, Uflag;
218 	char *jailname, *username;
219 
220 	ch = uflag = Uflag = 0;
221 	jailname = username = NULL;
222 	jid = -1;
223 
224 	while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) {
225 		switch (ch) {
226 		case 'n':
227 			jailname = optarg;
228 			break;
229 		case 'u':
230 			username = optarg;
231 			uflag = 1;
232 			break;
233 		case 'U':
234 			username = optarg;
235 			Uflag = 1;
236 			break;
237 		default:
238 			usage();
239 		}
240 	}
241 	argc -= optind;
242 	argv += optind;
243 	if (argc < 2)
244 		usage();
245 	if (strlen(argv[0]) > 0) {
246 		jid = (int)strtol(argv[0], NULL, 10);
247 		if (errno)
248 			err(1, "Unable to parse jail ID.");
249 	}
250 	if (jid <= 0 && jailname == NULL) {
251 		fprintf(stderr, "Neither jail ID nor jail name given.\n");
252 		usage();
253 	}
254 	if (uflag && Uflag)
255 		usage();
256 	if (uflag)
257 		GET_USER_INFO;
258 	jid = lookup_jail(jid, jailname);
259 	if (jid <= 0)
260 		errx(1, "Cannot identify jail.");
261 	if (jail_attach(jid) == -1)
262 		err(1, "jail_attach(): %d", jid);
263 	if (chdir("/") == -1)
264 		err(1, "chdir(): /");
265 	if (username != NULL) {
266 		if (Uflag)
267 			GET_USER_INFO;
268 		if (setgroups(ngroups, groups) != 0)
269 			err(1, "setgroups");
270 		if (setgid(pwd->pw_gid) != 0)
271 			err(1, "setgid");
272 		if (setusercontext(lcap, pwd, pwd->pw_uid,
273 		    LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
274 			err(1, "setusercontext");
275 		login_close(lcap);
276 	}
277 	if (execvp(argv[1], argv + 1) == -1)
278 		err(1, "execvp(): %s", argv[1]);
279 	exit(0);
280 }
281 
282 static void
283 usage(void)
284 {
285 
286 	fprintf(stderr, "%s%s\n",
287 		"usage: jexec [-u username | -U username]",
288 		" [-n jailname] jid command ...");
289 	exit(1);
290 }
291