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
44typedef enum {
45	VI_SWITCHTO,
46	VI_CREATE,
47	VI_MODIFY,
48	VI_DESTROY,
49	VI_IFMOVE,
50	VI_GET
51} vi_cmd_t;
52
53typedef 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
72static int getjail(vstat_t *, int, int);
73
74static char *invocname;
75
76static void
77usage(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
90int
91main(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
324static int
325getjail(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
386done:
387	vs->jid = jailparam_get(params, psize, 0);
388	jailparam_free(params, psize);
389	return (vs->jid);
390}
391