1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 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 ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * 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/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/jail.h> 33 #include <sys/mount.h> 34 #include <err.h> 35 #include <jail.h> 36 #include <stdbool.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include <be.h> 42 43 #include "bectl.h" 44 45 static void jailparam_grow(void); 46 static void jailparam_add(const char *name, const char *val); 47 static int jailparam_del(const char *name); 48 static bool jailparam_addarg(char *arg); 49 static int jailparam_delarg(char *arg); 50 51 static int bectl_search_jail_paths(const char *mnt); 52 static int bectl_locate_jail(const char *ident); 53 54 /* We'll start with 8 parameters initially and grow as needed. */ 55 #define INIT_PARAMCOUNT 8 56 57 static struct jailparam *jp; 58 static int jpcnt; 59 static int jpused; 60 static char mnt_loc[BE_MAXPATHLEN + 1]; 61 62 static void 63 jailparam_grow(void) 64 { 65 66 jpcnt *= 2; 67 jp = realloc(jp, jpcnt * sizeof(*jp)); 68 if (jp == NULL) 69 err(2, "realloc"); 70 } 71 72 static void 73 jailparam_add(const char *name, const char *val) 74 { 75 int i; 76 77 for (i = 0; i < jpused; ++i) { 78 if (strcmp(name, jp[i].jp_name) == 0) 79 break; 80 } 81 82 if (i < jpused) 83 jailparam_free(&jp[i], 1); 84 else if (jpused == jpcnt) 85 /* The next slot isn't allocated yet */ 86 jailparam_grow(); 87 88 if (jailparam_init(&jp[i], name) != 0) 89 return; 90 if (jailparam_import(&jp[i], val) != 0) 91 return; 92 ++jpused; 93 } 94 95 static int 96 jailparam_del(const char *name) 97 { 98 int i; 99 char *val; 100 101 for (i = 0; i < jpused; ++i) { 102 if (strcmp(name, jp[i].jp_name) == 0) 103 break; 104 } 105 106 if (i == jpused) 107 return (ENOENT); 108 109 for (; i < jpused - 1; ++i) { 110 val = jailparam_export(&jp[i + 1]); 111 112 jailparam_free(&jp[i], 1); 113 /* 114 * Given the context, the following will really only fail if 115 * they can't allocate the copy of the name or value. 116 */ 117 if (jailparam_init(&jp[i], jp[i + 1].jp_name) != 0) { 118 free(val); 119 return (ENOMEM); 120 } 121 if (jailparam_import(&jp[i], val) != 0) { 122 jailparam_free(&jp[i], 1); 123 free(val); 124 return (ENOMEM); 125 } 126 free(val); 127 } 128 129 jailparam_free(&jp[i], 1); 130 --jpused; 131 return (0); 132 } 133 134 static bool 135 jailparam_addarg(char *arg) 136 { 137 char *name, *val; 138 139 if (arg == NULL) 140 return (false); 141 name = arg; 142 if ((val = strchr(arg, '=')) == NULL) { 143 fprintf(stderr, "bectl jail: malformed jail option '%s'\n", 144 arg); 145 return (false); 146 } 147 148 *val++ = '\0'; 149 if (strcmp(name, "path") == 0) { 150 if (strlen(val) > BE_MAXPATHLEN) { 151 fprintf(stderr, 152 "bectl jail: skipping too long path assignment '%s' (max length = %d)\n", 153 val, BE_MAXPATHLEN); 154 return (false); 155 } 156 strcpy(mnt_loc, val); 157 } 158 jailparam_add(name, val); 159 return (true); 160 } 161 162 static int 163 jailparam_delarg(char *arg) 164 { 165 char *name, *val; 166 167 if (arg == NULL) 168 return (EINVAL); 169 name = arg; 170 if ((val = strchr(name, '=')) != NULL) 171 *val++ = '\0'; 172 173 if (strcmp(name, "path") == 0) 174 *mnt_loc = '\0'; 175 return (jailparam_del(name)); 176 } 177 178 int 179 bectl_cmd_jail(int argc, char *argv[]) 180 { 181 char *bootenv, *mountpoint; 182 int jid, opt, ret; 183 bool default_hostname, default_name; 184 185 default_hostname = default_name = true; 186 jpcnt = INIT_PARAMCOUNT; 187 jp = malloc(jpcnt * sizeof(*jp)); 188 if (jp == NULL) 189 err(2, "malloc"); 190 191 jailparam_add("persist", "true"); 192 jailparam_add("allow.mount", "true"); 193 jailparam_add("allow.mount.devfs", "true"); 194 jailparam_add("enforce_statfs", "1"); 195 196 while ((opt = getopt(argc, argv, "o:u:")) != -1) { 197 switch (opt) { 198 case 'o': 199 if (jailparam_addarg(optarg)) { 200 /* 201 * optarg has been modified to null terminate 202 * at the assignment operator. 203 */ 204 if (strcmp(optarg, "name") == 0) 205 default_name = false; 206 if (strcmp(optarg, "host.hostname") == 0) 207 default_hostname = false; 208 } 209 break; 210 case 'u': 211 if ((ret = jailparam_delarg(optarg)) == 0) { 212 if (strcmp(optarg, "name") == 0) 213 default_name = true; 214 if (strcmp(optarg, "host.hostname") == 0) 215 default_hostname = true; 216 } else if (ret != ENOENT) { 217 fprintf(stderr, 218 "bectl jail: error unsetting \"%s\"\n", 219 optarg); 220 return (ret); 221 } 222 break; 223 default: 224 fprintf(stderr, "bectl jail: unknown option '-%c'\n", 225 optopt); 226 return (usage(false)); 227 } 228 } 229 230 argc -= optind; 231 argv += optind; 232 233 /* struct jail be_jail = { 0 }; */ 234 if (argc < 1) { 235 fprintf(stderr, "bectl jail: missing boot environment name\n"); 236 return (usage(false)); 237 } 238 if (argc > 2) { 239 fprintf(stderr, "bectl jail: too many arguments\n"); 240 return (usage(false)); 241 } 242 243 bootenv = argv[0]; 244 245 /* 246 * XXX TODO: if its already mounted, perhaps there should be a flag to 247 * indicate its okay to proceed?? 248 */ 249 if (*mnt_loc == '\0') 250 mountpoint = NULL; 251 else 252 mountpoint = mnt_loc; 253 if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) { 254 fprintf(stderr, "could not mount bootenv\n"); 255 return (1); 256 } 257 258 if (default_name) 259 jailparam_add("name", bootenv); 260 if (default_hostname) 261 jailparam_add("host.hostname", bootenv); 262 /* 263 * This is our indicator that path was not set by the user, so we'll use 264 * the path that libbe generated for us. 265 */ 266 if (mountpoint == NULL) 267 jailparam_add("path", mnt_loc); 268 jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH); 269 if (jid == -1) { 270 fprintf(stderr, "unable to create jail. error: %d\n", errno); 271 return (1); 272 } 273 274 jailparam_free(jp, jpused); 275 free(jp); 276 277 /* We're attached within the jail... good bye! */ 278 chdir("/"); 279 execl("/bin/sh", "/bin/sh", NULL); 280 return (0); 281 } 282 283 static int 284 bectl_search_jail_paths(const char *mnt) 285 { 286 char jailpath[MAXPATHLEN + 1]; 287 int jid; 288 289 jid = 0; 290 (void)mnt; 291 while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, 292 NULL)) != -1) { 293 if (strcmp(jailpath, mnt) == 0) 294 return (jid); 295 } 296 297 return (-1); 298 } 299 300 /* 301 * Locate a jail based on an arbitrary identifier. This may be either a name, 302 * a jid, or a BE name. Returns the jid or -1 on failure. 303 */ 304 static int 305 bectl_locate_jail(const char *ident) 306 { 307 nvlist_t *belist, *props; 308 char *mnt; 309 int jid; 310 311 /* Try the easy-match first */ 312 jid = jail_getid(ident); 313 if (jid != -1) 314 return (jid); 315 316 /* Attempt to try it as a BE name, first */ 317 if (be_prop_list_alloc(&belist) != 0) 318 return (-1); 319 320 if (be_get_bootenv_props(be, belist) != 0) 321 return (-1); 322 323 if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { 324 /* We'll attempt to resolve the jid by way of mountpoint */ 325 if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { 326 jid = bectl_search_jail_paths(mnt); 327 be_prop_list_free(belist); 328 return (jid); 329 } 330 331 be_prop_list_free(belist); 332 } 333 334 return (-1); 335 } 336 337 int 338 bectl_cmd_unjail(int argc, char *argv[]) 339 { 340 char path[MAXPATHLEN + 1]; 341 char *cmd, *name, *target; 342 int jid; 343 344 /* Store alias used */ 345 cmd = argv[0]; 346 347 if (argc != 2) { 348 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); 349 return (usage(false)); 350 } 351 352 target = argv[1]; 353 354 /* Locate the jail */ 355 if ((jid = bectl_locate_jail(target)) == -1) { 356 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, 357 target); 358 return (1); 359 } 360 361 bzero(&path, MAXPATHLEN + 1); 362 name = jail_getname(jid); 363 if (jail_getv(0, "name", name, "path", path, NULL) != jid) { 364 free(name); 365 fprintf(stderr, 366 "bectl %s: failed to get path for jail requested by '%s'\n", 367 cmd, target); 368 return (1); 369 } 370 371 free(name); 372 373 if (be_mounted_at(be, path, NULL) != 0) { 374 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", 375 cmd, target); 376 return (1); 377 } 378 379 jail_remove(jid); 380 unmount(path, 0); 381 382 return (0); 383 } 384