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(¶ms[0], "name"); 271196409Szec jailparam_import(¶ms[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(¶ms[i], argv[i]); 282196409Szec jailparam_import(¶ms[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(¶ms[psize], "lastjid"); 334196409Szec jailparam_import_raw(¶ms[psize++], &lastjid, sizeof lastjid); 335196409Szec 336196409Szec jailparam_init(¶ms[psize], "vnet"); 337196409Szec jailparam_import_raw(¶ms[psize++], &vs->vnet, sizeof(vs->vnet)); 338196409Szec 339196409Szec jailparam_init(¶ms[psize], "name"); 340196409Szec jailparam_import_raw(¶ms[psize++], &vs->name, sizeof(vs->name)); 341196409Szec 342196409Szec if (verbose == 0) 343196409Szec goto done; 344196409Szec 345196409Szec jailparam_init(¶ms[psize], "path"); 346196409Szec jailparam_import_raw(¶ms[psize++], &vs->path, sizeof(vs->path)); 347196409Szec 348196409Szec jailparam_init(¶ms[psize], "host.hostname"); 349196409Szec jailparam_import_raw(¶ms[psize++], &vs->hostname, 350196409Szec sizeof(vs->hostname)); 351196409Szec 352196409Szec jailparam_init(¶ms[psize], "host.domainname"); 353196409Szec jailparam_import_raw(¶ms[psize++], &vs->domainname, 354196409Szec sizeof(vs->domainname)); 355196409Szec 356196409Szec jailparam_init(¶ms[psize], "children.cur"); 357196409Szec jailparam_import_raw(¶ms[psize++], &vs->childcnt, 358196409Szec sizeof(vs->childcnt)); 359196409Szec 360196409Szec if (verbose == 1) 361196409Szec goto done; 362196409Szec 363196409Szec jailparam_init(¶ms[psize], "children.max"); 364196409Szec jailparam_import_raw(¶ms[psize++], &vs->childmax, 365196409Szec sizeof(vs->childmax)); 366196409Szec 367196409Szec jailparam_init(¶ms[psize], "cpuset.id"); 368196409Szec jailparam_import_raw(¶ms[psize++], &vs->cpuset, 369196409Szec sizeof(vs->cpuset)); 370196409Szec 371196409Szec jailparam_init(¶ms[psize], "parent"); 372196409Szec jailparam_import_raw(¶ms[psize++], &vs->parentjid, 373196409Szec sizeof(vs->parentjid)); 374196409Szec 375196409Szec jailparam_init(¶ms[psize], "allow.raw_sockets"); 376196409Szec jailparam_import_raw(¶ms[psize++], &vs->rawsock, 377196409Szec sizeof(vs->rawsock)); 378196409Szec 379196409Szec jailparam_init(¶ms[psize], "allow.socket_af"); 380196409Szec jailparam_import_raw(¶ms[psize++], &vs->socket_af, 381196409Szec sizeof(vs->socket_af)); 382196409Szec 383196409Szec jailparam_init(¶ms[psize], "allow.mount"); 384196409Szec jailparam_import_raw(¶ms[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