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
42 typedef enum {
43 VI_SWITCHTO,
44 VI_CREATE,
45 VI_MODIFY,
46 VI_DESTROY,
47 VI_IFMOVE,
48 VI_GET
49 } vi_cmd_t;
50
51 typedef 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
70 static int getjail(vstat_t *, int, int);
71
72 static char *invocname;
73
74 static void
usage(void)75 usage(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
88 int
main(int argc,char ** argv)89 main(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(¶ms[0], "name");
269 jailparam_import(¶ms[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(¶ms[i], argv[i]);
280 jailparam_import(¶ms[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
322 static int
getjail(vstat_t * vs,int lastjid,int verbose)323 getjail(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(¶ms[psize], "lastjid");
332 jailparam_import_raw(¶ms[psize++], &lastjid, sizeof lastjid);
333
334 jailparam_init(¶ms[psize], "vnet");
335 jailparam_import_raw(¶ms[psize++], &vs->vnet, sizeof(vs->vnet));
336
337 jailparam_init(¶ms[psize], "name");
338 jailparam_import_raw(¶ms[psize++], &vs->name, sizeof(vs->name));
339
340 if (verbose == 0)
341 goto done;
342
343 jailparam_init(¶ms[psize], "path");
344 jailparam_import_raw(¶ms[psize++], &vs->path, sizeof(vs->path));
345
346 jailparam_init(¶ms[psize], "host.hostname");
347 jailparam_import_raw(¶ms[psize++], &vs->hostname,
348 sizeof(vs->hostname));
349
350 jailparam_init(¶ms[psize], "host.domainname");
351 jailparam_import_raw(¶ms[psize++], &vs->domainname,
352 sizeof(vs->domainname));
353
354 jailparam_init(¶ms[psize], "children.cur");
355 jailparam_import_raw(¶ms[psize++], &vs->childcnt,
356 sizeof(vs->childcnt));
357
358 if (verbose == 1)
359 goto done;
360
361 jailparam_init(¶ms[psize], "children.max");
362 jailparam_import_raw(¶ms[psize++], &vs->childmax,
363 sizeof(vs->childmax));
364
365 jailparam_init(¶ms[psize], "cpuset.id");
366 jailparam_import_raw(¶ms[psize++], &vs->cpuset,
367 sizeof(vs->cpuset));
368
369 jailparam_init(¶ms[psize], "parent");
370 jailparam_import_raw(¶ms[psize++], &vs->parentjid,
371 sizeof(vs->parentjid));
372
373 jailparam_init(¶ms[psize], "allow.raw_sockets");
374 jailparam_import_raw(¶ms[psize++], &vs->rawsock,
375 sizeof(vs->rawsock));
376
377 jailparam_init(¶ms[psize], "allow.socket_af");
378 jailparam_import_raw(¶ms[psize++], &vs->socket_af,
379 sizeof(vs->socket_af));
380
381 jailparam_init(¶ms[psize], "allow.mount");
382 jailparam_import_raw(¶ms[psize++], &vs->mount, sizeof(vs->mount));
383
384 done:
385 vs->jid = jailparam_get(params, psize, 0);
386 jailparam_free(params, psize);
387 return (vs->jid);
388 }
389