xref: /freebsd/tools/tools/vimage/vimage.c (revision b0b1dbdd)
1 /*
2  * Copyright (c) 2002-2004 Marko Zec <zec@fer.hr>
3  * Copyright (c) 2009 University of Zagreb
4  * Copyright (c) 2009 FreeBSD Foundation
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/ioctl.h>
32 #include <sys/jail.h>
33 #include <sys/socket.h>
34 
35 #include <net/if.h>
36 
37 #include <ctype.h>
38 #include <jail.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 typedef enum {
45 	VI_SWITCHTO,
46 	VI_CREATE,
47 	VI_MODIFY,
48 	VI_DESTROY,
49 	VI_IFMOVE,
50 	VI_GET
51 } vi_cmd_t;
52 
53 typedef struct vimage_status {
54 	char name[MAXPATHLEN];		/* Must be first field for strcmp(). */
55 	char path[MAXPATHLEN];
56 	char hostname[MAXPATHLEN];
57 	char domainname[MAXPATHLEN];
58 	int jid;
59 	int parentjid;
60 	int vnet;
61 	int childcnt;
62 	int childmax;
63 	int cpuset;
64 	int rawsock;
65 	int socket_af;
66 	int mount;
67 } vstat_t;
68 
69 #define	VST_SIZE_STEP	1024
70 #define	MAXPARAMS	32
71 
72 static int getjail(vstat_t *, int, int);
73 
74 static char *invocname;
75 
76 static void
77 usage(void)
78 {
79 
80 	fprintf(stderr,
81 	    "usage: %s [-c | -m] vname [param=value ...]\n"
82 	    "       %s -d vname\n"
83 	    "       %s -l[rvj] [vname]\n"
84 	    "       %s -i vname ifname [newifname]\n"
85 	    "       %s vname [command ...]\n",
86 	    invocname, invocname, invocname, invocname, invocname);
87 	exit(1);
88 }
89 
90 int
91 main(int argc, char **argv)
92 {
93 	struct jailparam params[MAXPARAMS];
94 	char ifname[IFNAMSIZ];
95 	struct ifreq ifreq;
96 	vi_cmd_t newcmd, cmd;
97 	int recurse = 0;
98 	int verbose = 0;
99 	int jid, i, s, namelen;
100 	int vst_size, vst_last;
101 	vstat_t *vst;
102 	char *str;
103 	char ch;
104 
105 	invocname = argv[0];
106 
107 	newcmd = cmd = VI_SWITCHTO; /* Default if no modifiers specified. */
108 	while ((ch = getopt(argc, argv, "cdijlmrv")) != -1) {
109 		switch (ch) {
110 		case 'c':
111 			newcmd = VI_CREATE;
112 			break;
113 		case 'm':
114 			newcmd = VI_MODIFY;
115 			break;
116 		case 'd':
117 			newcmd = VI_DESTROY;
118 			break;
119 		case 'l':
120 			newcmd = VI_GET;
121 			break;
122 		case 'i':
123 			newcmd = VI_IFMOVE;
124 			break;
125 		case 'r':
126 			recurse = 1;
127 			break;
128 		case 'v':
129 			verbose++;
130 			break;
131 		case 'j':
132 			verbose = 2;
133 			break;
134 		default:
135 			usage();
136 		}
137 		if (cmd == VI_SWITCHTO || cmd == newcmd)
138 			cmd = newcmd;
139 		else
140 			usage();
141 	}
142 	argc -= optind;
143 	argv += optind;
144 
145 	if ((cmd != VI_GET && (argc == 0 || recurse != 0 || verbose != 0)) ||
146 	    (cmd == VI_IFMOVE && (argc < 2 || argc > 3)) ||
147 	    (cmd == VI_MODIFY && argc < 2) || argc >= MAXPARAMS)
148 		usage();
149 
150 	switch (cmd) {
151 	case VI_GET:
152 		vst_last = 0;
153 		vst_size = VST_SIZE_STEP;
154 		if ((vst = malloc(vst_size * sizeof(*vst))) == NULL)
155 			break;
156 		if (argc == 1)
157 			namelen = strlen(argv[0]);
158 		else
159 			namelen = 0;
160 		jid = 0;
161 		while ((jid = getjail(&vst[vst_last], jid, verbose)) > 0) {
162 			/* Skip jails which do not own vnets. */
163 			if (vst[vst_last].vnet != 1)
164 				continue;
165 			/* Skip non-matching vnames / hierarchies. */
166 			if (namelen &&
167 			    ((strlen(vst[vst_last].name) < namelen ||
168 			    strncmp(vst[vst_last].name, argv[0], namelen) != 0)
169 			    || (strlen(vst[vst_last].name) > namelen &&
170 			    vst[vst_last].name[namelen] != '.')))
171 				continue;
172 			/* Skip any sub-trees if -r not requested. */
173 			if (!recurse &&
174 			    (strlen(vst[vst_last].name) < namelen ||
175 			    strchr(&vst[vst_last].name[namelen], '.') != NULL))
176 				continue;
177 			/* Grow vst table if necessary. */
178 			if (++vst_last == vst_size) {
179 				vst_size += VST_SIZE_STEP;
180 				vst = realloc(vst, vst_size * sizeof(*vst));
181 				if (vst == NULL)
182 					break;
183 			}
184 		}
185 		if (vst == NULL)
186 			break;
187 		/* Sort: the key is the 1st field in *vst, i.e. vimage name. */
188 		qsort(vst, vst_last, sizeof(*vst), (void *) strcmp);
189 		for (i = 0; i < vst_last; i++) {
190 			if (!verbose) {
191 				printf("%s\n", vst[i].name);
192 				continue;
193 			}
194 
195 			printf("%s:\n", vst[i].name);
196 			printf("    Path: %s\n", vst[i].path);
197 			printf("    Hostname: %s\n", vst[i].hostname);
198 			printf("    Domainname: %s\n", vst[i].domainname);
199 			printf("    Children: %d\n", vst[i].childcnt);
200 
201 			if (verbose < 2)
202 				continue;
203 
204 			printf("    Children limit: %d\n", vst[i].childmax);
205 			printf("    CPUsetID: %d\n", vst[i].cpuset);
206 			printf("    JID: %d\n", vst[i].jid);
207 			printf("    PJID: %d\n", vst[i].parentjid);
208 			printf("    Raw sockets allowed: %d\n", vst[i].rawsock);
209 			printf("    All AF allowed: %d\n", vst[i].socket_af);
210 			printf("    Mount allowed: %d\n", vst[i].mount);
211 		}
212 		free(vst);
213 		exit(0);
214 
215 	case VI_IFMOVE:
216 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
217 			break;
218 		if ((jid = jail_getid(argv[0])) < 0)
219 			break;
220 		ifreq.ifr_jid = jid;
221 		strncpy(ifreq.ifr_name, argv[1], sizeof(ifreq.ifr_name));
222 		if (ioctl(s, SIOCSIFVNET, (caddr_t)&ifreq) < 0)
223 			break;
224 		close(s);
225 		if (argc == 3)
226 			snprintf(ifname, sizeof(ifname), "%s", argv[2]);
227 		else
228 			snprintf(ifname, sizeof(ifname), "eth0");
229 		ifreq.ifr_data = ifname;
230 		/* Do we need to rename the ifnet? */
231 		if (strcmp(ifreq.ifr_name, ifname) != 0) {
232 			/* Switch to the context of the target vimage. */
233 			if (jail_attach(jid) < 0)
234 				break;
235 			if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
236 				break;
237 			for (namelen = 0; isalpha(ifname[namelen]); namelen++);
238 			i = 0;
239 			/* Search for a free ifunit in target vnet.  Unsafe. */
240 			while (ioctl(s, SIOCSIFNAME, (caddr_t)&ifreq) < 0) {
241 				snprintf(&ifname[namelen],
242 				    sizeof(ifname) - namelen, "%d", i);
243 				/* Emergency brake. */
244 				if (i++ == IF_MAXUNIT)
245 					break;
246 			}
247 		}
248 		if (i < IF_MAXUNIT)
249 			printf("%s@%s\n", ifname, argv[0]);
250 		else
251 			printf("%s@%s\n", ifreq.ifr_name, argv[0]);
252 		exit(0);
253 
254 	case VI_CREATE:
255 		if (jail_setv(JAIL_CREATE,
256 		    "name", argv[0],
257 		    "vnet", NULL,
258 		    "host", NULL,
259 		    "persist", NULL,
260 		    "allow.raw_sockets", "true",
261 		    "allow.socket_af", "true",
262 		    "allow.mount", "true",
263 		    NULL) < 0)
264 			break;
265 		if (argc == 1)
266 			exit(0);
267 		/* Not done yet, proceed to apply non-default parameters. */
268 
269 	case VI_MODIFY:
270 		jailparam_init(&params[0], "name");
271 		jailparam_import(&params[0], argv[0]);
272 		for (i = 1; i < argc; i++) {
273 			for (str = argv[i]; *str != '=' && *str != 0; str++) {
274 				/* Do nothing - search for '=' delimeter. */
275 			}
276 			if (*str == 0)
277 				break;
278 			*str++ = 0;
279 			if (*str == 0)
280 				break;
281 			jailparam_init(&params[i], argv[i]);
282 			jailparam_import(&params[i], str);
283 		}
284 		if (i != argc)
285 			break;
286 		if (jailparam_set(params, i, JAIL_UPDATE) < 0)
287 			break;
288 		exit(0);
289 
290 	case VI_DESTROY:
291 		if ((jid = jail_getid(argv[0])) < 0)
292 			break;
293 		if (jail_remove(jid) < 0)
294 			break;
295 		exit(0);
296 
297 	case VI_SWITCHTO:
298 		if ((jid = jail_getid(argv[0])) < 0)
299 			break;
300 		if (jail_attach(jid) < 0)
301 			break;
302 		if (argc == 1) {
303 			printf("Switched to vimage %s\n", argv[0]);
304 			if ((str = getenv("SHELL")) == NULL)
305 				execlp("/bin/sh", invocname, NULL);
306 			else
307 				execlp(str, invocname, NULL);
308 		} else
309 			execvp(argv[1], &argv[1]);
310 		break;
311 
312 	default:
313 		/* Should be unreachable. */
314 		break;
315 	}
316 
317 	if (jail_errmsg[0])
318 		fprintf(stderr, "Error: %s\n", jail_errmsg);
319 	else
320 		perror("Error");
321 	exit(1);
322 }
323 
324 static int
325 getjail(vstat_t *vs, int lastjid, int verbose)
326 {
327 	struct jailparam params[32];	/* Must be > max(psize). */
328 	int psize = 0;
329 
330 	bzero(params, sizeof(params));
331 	bzero(vs, sizeof(*vs));
332 
333 	jailparam_init(&params[psize], "lastjid");
334 	jailparam_import_raw(&params[psize++], &lastjid, sizeof lastjid);
335 
336 	jailparam_init(&params[psize], "vnet");
337 	jailparam_import_raw(&params[psize++], &vs->vnet, sizeof(vs->vnet));
338 
339 	jailparam_init(&params[psize], "name");
340 	jailparam_import_raw(&params[psize++], &vs->name, sizeof(vs->name));
341 
342 	if (verbose == 0)
343 		goto done;
344 
345 	jailparam_init(&params[psize], "path");
346 	jailparam_import_raw(&params[psize++], &vs->path, sizeof(vs->path));
347 
348 	jailparam_init(&params[psize], "host.hostname");
349 	jailparam_import_raw(&params[psize++], &vs->hostname,
350 	    sizeof(vs->hostname));
351 
352 	jailparam_init(&params[psize], "host.domainname");
353 	jailparam_import_raw(&params[psize++], &vs->domainname,
354 	    sizeof(vs->domainname));
355 
356 	jailparam_init(&params[psize], "children.cur");
357 	jailparam_import_raw(&params[psize++], &vs->childcnt,
358 	    sizeof(vs->childcnt));
359 
360 	if (verbose == 1)
361 		goto done;
362 
363 	jailparam_init(&params[psize], "children.max");
364 	jailparam_import_raw(&params[psize++], &vs->childmax,
365 	    sizeof(vs->childmax));
366 
367 	jailparam_init(&params[psize], "cpuset.id");
368 	jailparam_import_raw(&params[psize++], &vs->cpuset,
369 	    sizeof(vs->cpuset));
370 
371 	jailparam_init(&params[psize], "parent");
372 	jailparam_import_raw(&params[psize++], &vs->parentjid,
373 	    sizeof(vs->parentjid));
374 
375 	jailparam_init(&params[psize], "allow.raw_sockets");
376 	jailparam_import_raw(&params[psize++], &vs->rawsock,
377 	    sizeof(vs->rawsock));
378 
379 	jailparam_init(&params[psize], "allow.socket_af");
380 	jailparam_import_raw(&params[psize++], &vs->socket_af,
381 	    sizeof(vs->socket_af));
382 
383 	jailparam_init(&params[psize], "allow.mount");
384 	jailparam_import_raw(&params[psize++], &vs->mount, sizeof(vs->mount));
385 
386 done:
387 	vs->jid = jailparam_get(params, psize, 0);
388 	jailparam_free(params, psize);
389 	return (vs->jid);
390 }
391