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]; 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 strlcpy(mnt_loc, val, sizeof(mnt_loc)); 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 jflags, jid, opt, ret; 183 bool default_hostname, default_name, interactive; 184 185 default_hostname = default_name = interactive = 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, "bo:u:")) != -1) { 197 switch (opt) { 198 case 'b': 199 interactive = false; 200 break; 201 case 'o': 202 if (jailparam_addarg(optarg)) { 203 /* 204 * optarg has been modified to null terminate 205 * at the assignment operator. 206 */ 207 if (strcmp(optarg, "name") == 0) 208 default_name = false; 209 if (strcmp(optarg, "host.hostname") == 0) 210 default_hostname = false; 211 } 212 break; 213 case 'u': 214 if ((ret = jailparam_delarg(optarg)) == 0) { 215 if (strcmp(optarg, "name") == 0) 216 default_name = true; 217 if (strcmp(optarg, "host.hostname") == 0) 218 default_hostname = true; 219 } else if (ret != ENOENT) { 220 fprintf(stderr, 221 "bectl jail: error unsetting \"%s\"\n", 222 optarg); 223 return (ret); 224 } 225 break; 226 default: 227 fprintf(stderr, "bectl jail: unknown option '-%c'\n", 228 optopt); 229 return (usage(false)); 230 } 231 } 232 233 argc -= optind; 234 argv += optind; 235 236 /* struct jail be_jail = { 0 }; */ 237 if (argc < 1) { 238 fprintf(stderr, "bectl jail: missing boot environment name\n"); 239 return (usage(false)); 240 } 241 242 bootenv = argv[0]; 243 244 /* 245 * XXX TODO: if its already mounted, perhaps there should be a flag to 246 * indicate its okay to proceed?? 247 */ 248 if (*mnt_loc == '\0') 249 mountpoint = NULL; 250 else 251 mountpoint = mnt_loc; 252 if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) { 253 fprintf(stderr, "could not mount bootenv\n"); 254 return (1); 255 } 256 257 if (default_name) 258 jailparam_add("name", bootenv); 259 if (default_hostname) 260 jailparam_add("host.hostname", bootenv); 261 262 jflags = JAIL_CREATE; 263 if (interactive) 264 jflags |= JAIL_ATTACH; 265 /* 266 * This is our indicator that path was not set by the user, so we'll use 267 * the path that libbe generated for us. 268 */ 269 if (mountpoint == NULL) 270 jailparam_add("path", mnt_loc); 271 jid = jailparam_set(jp, jpused, jflags); 272 if (jid == -1) { 273 fprintf(stderr, "unable to create jail. error: %d\n", errno); 274 return (1); 275 } 276 277 jailparam_free(jp, jpused); 278 free(jp); 279 280 if (interactive) { 281 /* We're attached within the jail... good bye! */ 282 chdir("/"); 283 if (argc > 1) 284 execve(argv[1], &argv[1], NULL); 285 else 286 execl("/bin/sh", "/bin/sh", NULL); 287 return (1); 288 } 289 290 return (0); 291 } 292 293 static int 294 bectl_search_jail_paths(const char *mnt) 295 { 296 char jailpath[MAXPATHLEN]; 297 int jid; 298 299 jid = 0; 300 (void)mnt; 301 while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, 302 NULL)) != -1) { 303 if (strcmp(jailpath, mnt) == 0) 304 return (jid); 305 } 306 307 return (-1); 308 } 309 310 /* 311 * Locate a jail based on an arbitrary identifier. This may be either a name, 312 * a jid, or a BE name. Returns the jid or -1 on failure. 313 */ 314 static int 315 bectl_locate_jail(const char *ident) 316 { 317 nvlist_t *belist, *props; 318 char *mnt; 319 int jid; 320 321 /* Try the easy-match first */ 322 jid = jail_getid(ident); 323 if (jid != -1) 324 return (jid); 325 326 /* Attempt to try it as a BE name, first */ 327 if (be_prop_list_alloc(&belist) != 0) 328 return (-1); 329 330 if (be_get_bootenv_props(be, belist) != 0) 331 return (-1); 332 333 if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { 334 /* We'll attempt to resolve the jid by way of mountpoint */ 335 if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { 336 jid = bectl_search_jail_paths(mnt); 337 be_prop_list_free(belist); 338 return (jid); 339 } 340 341 be_prop_list_free(belist); 342 } 343 344 return (-1); 345 } 346 347 int 348 bectl_cmd_unjail(int argc, char *argv[]) 349 { 350 char path[MAXPATHLEN]; 351 char *cmd, *name, *target; 352 int jid; 353 354 /* Store alias used */ 355 cmd = argv[0]; 356 357 if (argc != 2) { 358 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); 359 return (usage(false)); 360 } 361 362 target = argv[1]; 363 364 /* Locate the jail */ 365 if ((jid = bectl_locate_jail(target)) == -1) { 366 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, 367 target); 368 return (1); 369 } 370 371 bzero(&path, MAXPATHLEN); 372 name = jail_getname(jid); 373 if (jail_getv(0, "name", name, "path", path, NULL) != jid) { 374 free(name); 375 fprintf(stderr, 376 "bectl %s: failed to get path for jail requested by '%s'\n", 377 cmd, target); 378 return (1); 379 } 380 381 free(name); 382 383 if (be_mounted_at(be, path, NULL) != 0) { 384 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", 385 cmd, target); 386 return (1); 387 } 388 389 jail_remove(jid); 390 unmount(path, 0); 391 392 return (0); 393 } 394