1 /*
2 * Copyright (c) 2002-2004 Marko Zec <[email protected]>
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
usage(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
main(int argc,char ** argv)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(¶ms[0], "name");
271 jailparam_import(¶ms[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(¶ms[i], argv[i]);
282 jailparam_import(¶ms[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
getjail(vstat_t * vs,int lastjid,int verbose)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(¶ms[psize], "lastjid");
334 jailparam_import_raw(¶ms[psize++], &lastjid, sizeof lastjid);
335
336 jailparam_init(¶ms[psize], "vnet");
337 jailparam_import_raw(¶ms[psize++], &vs->vnet, sizeof(vs->vnet));
338
339 jailparam_init(¶ms[psize], "name");
340 jailparam_import_raw(¶ms[psize++], &vs->name, sizeof(vs->name));
341
342 if (verbose == 0)
343 goto done;
344
345 jailparam_init(¶ms[psize], "path");
346 jailparam_import_raw(¶ms[psize++], &vs->path, sizeof(vs->path));
347
348 jailparam_init(¶ms[psize], "host.hostname");
349 jailparam_import_raw(¶ms[psize++], &vs->hostname,
350 sizeof(vs->hostname));
351
352 jailparam_init(¶ms[psize], "host.domainname");
353 jailparam_import_raw(¶ms[psize++], &vs->domainname,
354 sizeof(vs->domainname));
355
356 jailparam_init(¶ms[psize], "children.cur");
357 jailparam_import_raw(¶ms[psize++], &vs->childcnt,
358 sizeof(vs->childcnt));
359
360 if (verbose == 1)
361 goto done;
362
363 jailparam_init(¶ms[psize], "children.max");
364 jailparam_import_raw(¶ms[psize++], &vs->childmax,
365 sizeof(vs->childmax));
366
367 jailparam_init(¶ms[psize], "cpuset.id");
368 jailparam_import_raw(¶ms[psize++], &vs->cpuset,
369 sizeof(vs->cpuset));
370
371 jailparam_init(¶ms[psize], "parent");
372 jailparam_import_raw(¶ms[psize++], &vs->parentjid,
373 sizeof(vs->parentjid));
374
375 jailparam_init(¶ms[psize], "allow.raw_sockets");
376 jailparam_import_raw(¶ms[psize++], &vs->rawsock,
377 sizeof(vs->rawsock));
378
379 jailparam_init(¶ms[psize], "allow.socket_af");
380 jailparam_import_raw(¶ms[psize++], &vs->socket_af,
381 sizeof(vs->socket_af));
382
383 jailparam_init(¶ms[psize], "allow.mount");
384 jailparam_import_raw(¶ms[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