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 * $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 44typedef enum { 45 VI_SWITCHTO, 46 VI_CREATE, 47 VI_MODIFY, 48 VI_DESTROY, 49 VI_IFMOVE, 50 VI_GET 51} vi_cmd_t; 52 53typedef 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 72static int getjail(vstat_t *, int, int); 73 74static char *invocname; 75 76static void 77usage(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 90int 91main(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 324static int 325getjail(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 386done: 387 vs->jid = jailparam_get(params, psize, 0); 388 jailparam_free(params, psize); 389 return (vs->jid); 390} 391