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 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 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 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