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