1193583Szec/*
2193583Szec * Copyright (c) 2002-2004 Marko Zec <zec@fer.hr>
3193583Szec * Copyright (c) 2009 University of Zagreb
4193583Szec * Copyright (c) 2009 FreeBSD Foundation
5193583Szec *
6193583Szec * Redistribution and use in source and binary forms, with or without
7193583Szec * modification, are permitted provided that the following conditions
8193583Szec * are met:
9193583Szec * 1. Redistributions of source code must retain the above copyright
10193583Szec *    notice, this list of conditions and the following disclaimer.
11193583Szec * 2. Redistributions in binary form must reproduce the above copyright
12193583Szec *    notice, this list of conditions and the following disclaimer in the
13193583Szec *    documentation and/or other materials provided with the distribution.
14193583Szec *
15193583Szec * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16193583Szec * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17193583Szec * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18193583Szec * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19193583Szec * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20193583Szec * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21193583Szec * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22193583Szec * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23193583Szec * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24193583Szec * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25193583Szec * SUCH DAMAGE.
26193583Szec *
27193583Szec * $FreeBSD$
28193583Szec */
29193583Szec
30195741Sjamie#include <sys/param.h>
31193583Szec#include <sys/ioctl.h>
32195741Sjamie#include <sys/jail.h>
33193583Szec#include <sys/socket.h>
34193583Szec
35195741Sjamie#include <net/if.h>
36195741Sjamie
37196409Szec#include <ctype.h>
38195741Sjamie#include <jail.h>
39193583Szec#include <stdio.h>
40193583Szec#include <stdlib.h>
41193583Szec#include <string.h>
42193583Szec#include <unistd.h>
43193583Szec
44196409Szectypedef enum {
45196409Szec	VI_SWITCHTO,
46196409Szec	VI_CREATE,
47196409Szec	VI_MODIFY,
48196409Szec	VI_DESTROY,
49196409Szec	VI_IFMOVE,
50196409Szec	VI_GET
51196409Szec} vi_cmd_t;
52193583Szec
53196409Szectypedef struct vimage_status {
54196409Szec	char name[MAXPATHLEN];		/* Must be first field for strcmp(). */
55196409Szec	char path[MAXPATHLEN];
56196409Szec	char hostname[MAXPATHLEN];
57196409Szec	char domainname[MAXPATHLEN];
58196409Szec	int jid;
59196409Szec	int parentjid;
60196409Szec	int vnet;
61196409Szec	int childcnt;
62196409Szec	int childmax;
63196409Szec	int cpuset;
64196409Szec	int rawsock;
65196409Szec	int socket_af;
66196409Szec	int mount;
67196409Szec} vstat_t;
68193583Szec
69196409Szec#define	VST_SIZE_STEP	1024
70196409Szec#define	MAXPARAMS	32
71196409Szec
72196409Szecstatic int getjail(vstat_t *, int, int);
73196409Szec
74196409Szecstatic char *invocname;
75196409Szec
76196409Szecstatic void
77196409Szecusage(void)
78196409Szec{
79196409Szec
80196409Szec	fprintf(stderr,
81196409Szec	    "usage: %s [-c | -m] vname [param=value ...]\n"
82196409Szec	    "       %s -d vname\n"
83196409Szec	    "       %s -l[rvj] [vname]\n"
84196409Szec	    "       %s -i vname ifname [newifname]\n"
85196409Szec	    "       %s vname [command ...]\n",
86196409Szec	    invocname, invocname, invocname, invocname, invocname);
87196409Szec	exit(1);
88196409Szec}
89196409Szec
90193583Szecint
91193583Szecmain(int argc, char **argv)
92193583Szec{
93196409Szec	struct jailparam params[MAXPARAMS];
94196409Szec	char ifname[IFNAMSIZ];
95195741Sjamie	struct ifreq ifreq;
96196409Szec	vi_cmd_t newcmd, cmd;
97196409Szec	int recurse = 0;
98196409Szec	int verbose = 0;
99196409Szec	int jid, i, s, namelen;
100196409Szec	int vst_size, vst_last;
101196409Szec	vstat_t *vst;
102196409Szec	char *str;
103196409Szec	char ch;
104193583Szec
105196409Szec	invocname = argv[0];
106193583Szec
107196409Szec	newcmd = cmd = VI_SWITCHTO; /* Default if no modifiers specified. */
108196409Szec	while ((ch = getopt(argc, argv, "cdijlmrv")) != -1) {
109196409Szec		switch (ch) {
110196409Szec		case 'c':
111196409Szec			newcmd = VI_CREATE;
112196409Szec			break;
113196409Szec		case 'm':
114196409Szec			newcmd = VI_MODIFY;
115196409Szec			break;
116196409Szec		case 'd':
117196409Szec			newcmd = VI_DESTROY;
118196409Szec			break;
119196409Szec		case 'l':
120196409Szec			newcmd = VI_GET;
121196409Szec			break;
122196409Szec		case 'i':
123196409Szec			newcmd = VI_IFMOVE;
124196409Szec			break;
125196409Szec		case 'r':
126196409Szec			recurse = 1;
127196409Szec			break;
128196409Szec		case 'v':
129196409Szec			verbose++;
130196409Szec			break;
131196409Szec		case 'j':
132196409Szec			verbose = 2;
133196409Szec			break;
134196409Szec		default:
135196409Szec			usage();
136195741Sjamie		}
137196409Szec		if (cmd == VI_SWITCHTO || cmd == newcmd)
138196409Szec			cmd = newcmd;
139196409Szec		else
140196409Szec			usage();
141196409Szec	}
142196409Szec	argc -= optind;
143196409Szec	argv += optind;
144193583Szec
145196409Szec	if ((cmd != VI_GET && (argc == 0 || recurse != 0 || verbose != 0)) ||
146196409Szec	    (cmd == VI_IFMOVE && (argc < 2 || argc > 3)) ||
147196409Szec	    (cmd == VI_MODIFY && argc < 2) || argc >= MAXPARAMS)
148196409Szec		usage();
149193583Szec
150193583Szec	switch (cmd) {
151193583Szec	case VI_GET:
152196409Szec		vst_last = 0;
153196409Szec		vst_size = VST_SIZE_STEP;
154196409Szec		if ((vst = malloc(vst_size * sizeof(*vst))) == NULL)
155196409Szec			break;
156196409Szec		if (argc == 1)
157196409Szec			namelen = strlen(argv[0]);
158196409Szec		else
159196409Szec			namelen = 0;
160196409Szec		jid = 0;
161196409Szec		while ((jid = getjail(&vst[vst_last], jid, verbose)) > 0) {
162196409Szec			/* Skip jails which do not own vnets. */
163196409Szec			if (vst[vst_last].vnet != 1)
164196409Szec				continue;
165196409Szec			/* Skip non-matching vnames / hierarchies. */
166196409Szec			if (namelen &&
167196409Szec			    ((strlen(vst[vst_last].name) < namelen ||
168196409Szec			    strncmp(vst[vst_last].name, argv[0], namelen) != 0)
169196409Szec			    || (strlen(vst[vst_last].name) > namelen &&
170196409Szec			    vst[vst_last].name[namelen] != '.')))
171196409Szec				continue;
172196409Szec			/* Skip any sub-trees if -r not requested. */
173196409Szec			if (!recurse &&
174196409Szec			    (strlen(vst[vst_last].name) < namelen ||
175196409Szec			    strchr(&vst[vst_last].name[namelen], '.') != NULL))
176196409Szec				continue;
177196409Szec			/* Grow vst table if necessary. */
178196409Szec			if (++vst_last == vst_size) {
179196409Szec				vst_size += VST_SIZE_STEP;
180196409Szec				vst = realloc(vst, vst_size * sizeof(*vst));
181196409Szec				if (vst == NULL)
182196409Szec					break;
183196409Szec			}
184196409Szec		}
185196409Szec		if (vst == NULL)
186196409Szec			break;
187196409Szec		/* Sort: the key is the 1st field in *vst, i.e. vimage name. */
188196409Szec		qsort(vst, vst_last, sizeof(*vst), (void *) strcmp);
189196409Szec		for (i = 0; i < vst_last; i++) {
190196409Szec			if (!verbose) {
191196409Szec				printf("%s\n", vst[i].name);
192196409Szec				continue;
193196409Szec			}
194193583Szec
195196409Szec			printf("%s:\n", vst[i].name);
196196409Szec			printf("    Path: %s\n", vst[i].path);
197196409Szec			printf("    Hostname: %s\n", vst[i].hostname);
198196409Szec			printf("    Domainname: %s\n", vst[i].domainname);
199196409Szec			printf("    Children: %d\n", vst[i].childcnt);
200196409Szec
201196409Szec			if (verbose < 2)
202196409Szec				continue;
203196409Szec
204196409Szec			printf("    Children limit: %d\n", vst[i].childmax);
205196409Szec			printf("    CPUsetID: %d\n", vst[i].cpuset);
206196409Szec			printf("    JID: %d\n", vst[i].jid);
207196409Szec			printf("    PJID: %d\n", vst[i].parentjid);
208196409Szec			printf("    Raw sockets allowed: %d\n", vst[i].rawsock);
209196409Szec			printf("    All AF allowed: %d\n", vst[i].socket_af);
210196409Szec			printf("    Mount allowed: %d\n", vst[i].mount);
211196409Szec		}
212196409Szec		free(vst);
213193583Szec		exit(0);
214193583Szec
215196409Szec	case VI_IFMOVE:
216196409Szec		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
217196409Szec			break;
218196409Szec		if ((jid = jail_getid(argv[0])) < 0)
219196409Szec			break;
220195741Sjamie		ifreq.ifr_jid = jid;
221196409Szec		strncpy(ifreq.ifr_name, argv[1], sizeof(ifreq.ifr_name));
222195741Sjamie		if (ioctl(s, SIOCSIFVNET, (caddr_t)&ifreq) < 0)
223196409Szec			break;
224196409Szec		close(s);
225196409Szec		if (argc == 3)
226196409Szec			snprintf(ifname, sizeof(ifname), "%s", argv[2]);
227196409Szec		else
228196409Szec			snprintf(ifname, sizeof(ifname), "eth0");
229196409Szec		ifreq.ifr_data = ifname;
230196409Szec		/* Do we need to rename the ifnet? */
231196409Szec		if (strcmp(ifreq.ifr_name, ifname) != 0) {
232196409Szec			/* Switch to the context of the target vimage. */
233196409Szec			if (jail_attach(jid) < 0)
234196409Szec				break;
235196409Szec			if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
236196409Szec				break;
237196409Szec			for (namelen = 0; isalpha(ifname[namelen]); namelen++);
238196409Szec			i = 0;
239196409Szec			/* Search for a free ifunit in target vnet.  Unsafe. */
240196409Szec			while (ioctl(s, SIOCSIFNAME, (caddr_t)&ifreq) < 0) {
241196409Szec				snprintf(&ifname[namelen],
242196409Szec				    sizeof(ifname) - namelen, "%d", i);
243196409Szec				/* Emergency brake. */
244196409Szec				if (i++ == IF_MAXUNIT)
245196409Szec					break;
246196409Szec			}
247196409Szec		}
248196409Szec		if (i < IF_MAXUNIT)
249196409Szec			printf("%s@%s\n", ifname, argv[0]);
250196409Szec		else
251196409Szec			printf("%s@%s\n", ifreq.ifr_name, argv[0]);
252193583Szec		exit(0);
253193583Szec
254193583Szec	case VI_CREATE:
255196421Szec		if (jail_setv(JAIL_CREATE,
256196409Szec		    "name", argv[0],
257196409Szec		    "vnet", NULL,
258196409Szec		    "host", NULL,
259196409Szec		    "persist", NULL,
260196409Szec		    "allow.raw_sockets", "true",
261196409Szec		    "allow.socket_af", "true",
262196409Szec		    "allow.mount", "true",
263196421Szec		    NULL) < 0)
264196409Szec			break;
265196409Szec		if (argc == 1)
266196409Szec			exit(0);
267196409Szec		/* Not done yet, proceed to apply non-default parameters. */
268196409Szec
269196409Szec	case VI_MODIFY:
270196409Szec		jailparam_init(&params[0], "name");
271196409Szec		jailparam_import(&params[0], argv[0]);
272196409Szec		for (i = 1; i < argc; i++) {
273196409Szec			for (str = argv[i]; *str != '=' && *str != 0; str++) {
274196409Szec				/* Do nothing - search for '=' delimeter. */
275196409Szec			}
276196409Szec			if (*str == 0)
277196409Szec				break;
278196409Szec			*str++ = 0;
279196409Szec			if (*str == 0)
280196409Szec				break;
281196409Szec			jailparam_init(&params[i], argv[i]);
282196409Szec			jailparam_import(&params[i], str);
283196409Szec		}
284196409Szec		if (i != argc)
285196409Szec			break;
286196409Szec		if (jailparam_set(params, i, JAIL_UPDATE) < 0)
287196409Szec			break;
288193583Szec		exit(0);
289193583Szec
290196409Szec	case VI_DESTROY:
291196409Szec		if ((jid = jail_getid(argv[0])) < 0)
292196409Szec			break;
293196409Szec		if (jail_remove(jid) < 0)
294196409Szec			break;
295196409Szec		exit(0);
296196409Szec
297193583Szec	case VI_SWITCHTO:
298196409Szec		if ((jid = jail_getid(argv[0])) < 0)
299196409Szec			break;
300195741Sjamie		if (jail_attach(jid) < 0)
301196409Szec			break;
302196409Szec		if (argc == 1) {
303196409Szec			printf("Switched to vimage %s\n", argv[0]);
304196409Szec			if ((str = getenv("SHELL")) == NULL)
305196409Szec				execlp("/bin/sh", invocname, NULL);
306193583Szec			else
307196409Szec				execlp(str, invocname, NULL);
308193583Szec		} else
309196409Szec			execvp(argv[1], &argv[1]);
310193583Szec		break;
311193583Szec
312193583Szec	default:
313196409Szec		/* Should be unreachable. */
314196409Szec		break;
315193583Szec	}
316193583Szec
317195741Sjamie	if (jail_errmsg[0])
318195741Sjamie		fprintf(stderr, "Error: %s\n", jail_errmsg);
319195741Sjamie	else
320195741Sjamie		perror("Error");
321193583Szec	exit(1);
322193583Szec}
323195741Sjamie
324195741Sjamiestatic int
325196409Szecgetjail(vstat_t *vs, int lastjid, int verbose)
326195741Sjamie{
327196409Szec	struct jailparam params[32];	/* Must be > max(psize). */
328196409Szec	int psize = 0;
329195741Sjamie
330196409Szec	bzero(params, sizeof(params));
331196409Szec	bzero(vs, sizeof(*vs));
332196409Szec
333196409Szec	jailparam_init(&params[psize], "lastjid");
334196409Szec	jailparam_import_raw(&params[psize++], &lastjid, sizeof lastjid);
335196409Szec
336196409Szec	jailparam_init(&params[psize], "vnet");
337196409Szec	jailparam_import_raw(&params[psize++], &vs->vnet, sizeof(vs->vnet));
338196409Szec
339196409Szec	jailparam_init(&params[psize], "name");
340196409Szec	jailparam_import_raw(&params[psize++], &vs->name, sizeof(vs->name));
341196409Szec
342196409Szec	if (verbose == 0)
343196409Szec		goto done;
344196409Szec
345196409Szec	jailparam_init(&params[psize], "path");
346196409Szec	jailparam_import_raw(&params[psize++], &vs->path, sizeof(vs->path));
347196409Szec
348196409Szec	jailparam_init(&params[psize], "host.hostname");
349196409Szec	jailparam_import_raw(&params[psize++], &vs->hostname,
350196409Szec	    sizeof(vs->hostname));
351196409Szec
352196409Szec	jailparam_init(&params[psize], "host.domainname");
353196409Szec	jailparam_import_raw(&params[psize++], &vs->domainname,
354196409Szec	    sizeof(vs->domainname));
355196409Szec
356196409Szec	jailparam_init(&params[psize], "children.cur");
357196409Szec	jailparam_import_raw(&params[psize++], &vs->childcnt,
358196409Szec	    sizeof(vs->childcnt));
359196409Szec
360196409Szec	if (verbose == 1)
361196409Szec		goto done;
362196409Szec
363196409Szec	jailparam_init(&params[psize], "children.max");
364196409Szec	jailparam_import_raw(&params[psize++], &vs->childmax,
365196409Szec	    sizeof(vs->childmax));
366196409Szec
367196409Szec	jailparam_init(&params[psize], "cpuset.id");
368196409Szec	jailparam_import_raw(&params[psize++], &vs->cpuset,
369196409Szec	    sizeof(vs->cpuset));
370196409Szec
371196409Szec	jailparam_init(&params[psize], "parent");
372196409Szec	jailparam_import_raw(&params[psize++], &vs->parentjid,
373196409Szec	    sizeof(vs->parentjid));
374196409Szec
375196409Szec	jailparam_init(&params[psize], "allow.raw_sockets");
376196409Szec	jailparam_import_raw(&params[psize++], &vs->rawsock,
377196409Szec	    sizeof(vs->rawsock));
378196409Szec
379196409Szec	jailparam_init(&params[psize], "allow.socket_af");
380196409Szec	jailparam_import_raw(&params[psize++], &vs->socket_af,
381196409Szec	    sizeof(vs->socket_af));
382196409Szec
383196409Szec	jailparam_init(&params[psize], "allow.mount");
384196409Szec	jailparam_import_raw(&params[psize++], &vs->mount, sizeof(vs->mount));
385196409Szec
386196409Szecdone:
387196409Szec	vs->jid = jailparam_get(params, psize, 0);
388196409Szec	jailparam_free(params, psize);
389196409Szec	return (vs->jid);
390195741Sjamie}
391