1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #include <sys/param.h> 30 #include <sys/jail.h> 31 #include <sys/mount.h> 32 #include <sys/wait.h> 33 #include <err.h> 34 #include <jail.h> 35 #include <stdbool.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include <be.h> 41 #include "bectl.h" 42 43 #define MNTTYPE_ZFS 222 44 45 static void jailparam_add(const char *name, const char *val); 46 static int jailparam_del(const char *name); 47 static bool jailparam_addarg(char *arg); 48 static int jailparam_delarg(char *arg); 49 50 static int bectl_search_jail_paths(const char *mnt); 51 static int bectl_locate_jail(const char *ident); 52 static int bectl_jail_cleanup(char *mountpoint, int jid); 53 54 static char mnt_loc[BE_MAXPATHLEN]; 55 static nvlist_t *jailparams; 56 57 static const char *disabled_params[] = { 58 "command", "exec.start", "nopersist", "persist", NULL 59 }; 60 61 62 static void 63 jailparam_add(const char *name, const char *val) 64 { 65 66 nvlist_add_string(jailparams, name, val); 67 } 68 69 static int 70 jailparam_del(const char *name) 71 { 72 73 nvlist_remove_all(jailparams, name); 74 return (0); 75 } 76 77 static bool 78 jailparam_addarg(char *arg) 79 { 80 char *name, *val; 81 size_t i, len; 82 83 if (arg == NULL) 84 return (false); 85 name = arg; 86 if ((val = strchr(arg, '=')) == NULL) { 87 fprintf(stderr, "bectl jail: malformed jail option '%s'\n", 88 arg); 89 return (false); 90 } 91 92 *val++ = '\0'; 93 if (strcmp(name, "path") == 0) { 94 if (strlen(val) >= BE_MAXPATHLEN) { 95 fprintf(stderr, 96 "bectl jail: skipping too long path assignment '%s' (max length = %d)\n", 97 val, BE_MAXPATHLEN); 98 return (false); 99 } 100 strlcpy(mnt_loc, val, sizeof(mnt_loc)); 101 } 102 103 for (i = 0; disabled_params[i] != NULL; i++) { 104 len = strlen(disabled_params[i]); 105 if (strncmp(disabled_params[i], name, len) == 0) { 106 fprintf(stderr, "invalid jail parameter: %s\n", name); 107 return (false); 108 } 109 } 110 111 jailparam_add(name, val); 112 return (true); 113 } 114 115 static int 116 jailparam_delarg(char *arg) 117 { 118 char *name, *val; 119 120 if (arg == NULL) 121 return (EINVAL); 122 name = arg; 123 if ((val = strchr(name, '=')) != NULL) 124 *val++ = '\0'; 125 126 if (strcmp(name, "path") == 0) 127 *mnt_loc = '\0'; 128 return (jailparam_del(name)); 129 } 130 131 static int 132 build_jailcmd(char ***argvp, bool interactive, int argc, char *argv[]) 133 { 134 char *cmd, **jargv; 135 const char *name, *val; 136 nvpair_t *nvp; 137 size_t i, iarg, nargv; 138 139 cmd = NULL; 140 nvp = NULL; 141 iarg = i = 0; 142 if (nvlist_size(jailparams, &nargv, NV_ENCODE_NATIVE) != 0) 143 return (1); 144 145 /* 146 * Number of args + "/usr/sbin/jail", "-c", and ending NULL. 147 * If interactive also include command. 148 */ 149 nargv += 3; 150 if (interactive) { 151 if (argc == 0) 152 nargv++; 153 else 154 nargv += argc; 155 } 156 157 jargv = *argvp = calloc(nargv, sizeof(*jargv)); 158 if (jargv == NULL) 159 err(2, "calloc"); 160 161 jargv[iarg++] = strdup("/usr/sbin/jail"); 162 jargv[iarg++] = strdup("-c"); 163 while ((nvp = nvlist_next_nvpair(jailparams, nvp)) != NULL) { 164 name = nvpair_name(nvp); 165 if (nvpair_value_string(nvp, &val) != 0) 166 continue; 167 168 if (asprintf(&jargv[iarg++], "%s=%s", name, val) < 0) 169 goto error; 170 } 171 if (interactive) { 172 if (argc < 1) 173 cmd = strdup("/bin/sh"); 174 else { 175 cmd = argv[0]; 176 argc--; 177 argv++; 178 } 179 180 if (asprintf(&jargv[iarg++], "command=%s", cmd) < 0) { 181 goto error; 182 } 183 if (argc < 1) { 184 free(cmd); 185 cmd = NULL; 186 } 187 188 for (; argc > 0; argc--) { 189 if (asprintf(&jargv[iarg++], "%s", argv[0]) < 0) 190 goto error; 191 argv++; 192 } 193 } 194 195 return (0); 196 197 error: 198 if (interactive && argc < 1) 199 free(cmd); 200 for (; i < iarg - 1; i++) { 201 free(jargv[i]); 202 } 203 free(jargv); 204 return (1); 205 } 206 207 /* Remove jail and cleanup any non zfs mounts. */ 208 static int 209 bectl_jail_cleanup(char *mountpoint, int jid) 210 { 211 struct statfs *mntbuf; 212 size_t i, searchlen, mntsize; 213 214 if (jid >= 0 && jail_remove(jid) != 0) { 215 fprintf(stderr, "unable to remove jail"); 216 return (1); 217 } 218 219 searchlen = strnlen(mountpoint, MAXPATHLEN); 220 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 221 for (i = 0; i < mntsize; i++) { 222 if (strncmp(mountpoint, mntbuf[i].f_mntonname, searchlen) == 0 && 223 mntbuf[i].f_type != MNTTYPE_ZFS) { 224 225 if (unmount(mntbuf[i].f_mntonname, 0) != 0) { 226 fprintf(stderr, "bectl jail: unable to unmount filesystem %s", 227 mntbuf[i].f_mntonname); 228 return (1); 229 } 230 } 231 } 232 233 return (0); 234 } 235 236 int 237 bectl_cmd_jail(int argc, char *argv[]) 238 { 239 char *bootenv, **jargv, *mountpoint; 240 int i, jid, mntflags, opt, ret; 241 bool default_hostname, interactive, unjail; 242 pid_t pid; 243 244 245 /* XXX TODO: Allow shallow */ 246 mntflags = BE_MNT_DEEP; 247 default_hostname = interactive = unjail = true; 248 249 if ((nvlist_alloc(&jailparams, NV_UNIQUE_NAME, 0)) != 0) { 250 fprintf(stderr, "nvlist_alloc() failed\n"); 251 return (1); 252 } 253 254 jailparam_add("persist", "true"); 255 jailparam_add("allow.mount", "true"); 256 jailparam_add("allow.mount.devfs", "true"); 257 jailparam_add("enforce_statfs", "1"); 258 259 while ((opt = getopt(argc, argv, "bo:Uu:")) != -1) { 260 switch (opt) { 261 case 'b': 262 interactive = false; 263 break; 264 case 'o': 265 if (jailparam_addarg(optarg)) { 266 /* 267 * optarg has been modified to null terminate 268 * at the assignment operator. 269 */ 270 if (strcmp(optarg, "host.hostname") == 0) 271 default_hostname = false; 272 } else { 273 return (1); 274 } 275 break; 276 case 'U': 277 unjail = false; 278 break; 279 case 'u': 280 if ((ret = jailparam_delarg(optarg)) == 0) { 281 if (strcmp(optarg, "host.hostname") == 0) 282 default_hostname = true; 283 } else if (ret != ENOENT) { 284 fprintf(stderr, 285 "bectl jail: error unsetting \"%s\"\n", 286 optarg); 287 return (ret); 288 } 289 break; 290 default: 291 fprintf(stderr, "bectl jail: unknown option '-%c'\n", 292 optopt); 293 return (usage(false)); 294 } 295 } 296 297 argc -= optind; 298 argv += optind; 299 300 if (argc < 1) { 301 fprintf(stderr, "bectl jail: missing boot environment name\n"); 302 return (usage(false)); 303 } 304 305 bootenv = argv[0]; 306 argc--; 307 argv++; 308 309 /* 310 * XXX TODO: if its already mounted, perhaps there should be a flag to 311 * indicate its okay to proceed?? 312 */ 313 if (*mnt_loc == '\0') 314 mountpoint = NULL; 315 else 316 mountpoint = mnt_loc; 317 if (be_mount(be, bootenv, mountpoint, mntflags, mnt_loc) != BE_ERR_SUCCESS) { 318 fprintf(stderr, "could not mount bootenv\n"); 319 return (1); 320 } 321 322 if (default_hostname) 323 jailparam_add("host.hostname", bootenv); 324 325 /* 326 * This is our indicator that path was not set by the user, so we'll use 327 * the path that libbe generated for us. 328 */ 329 if (mountpoint == NULL) { 330 jailparam_add("path", mnt_loc); 331 mountpoint = mnt_loc; 332 } 333 334 if ((build_jailcmd(&jargv, interactive, argc, argv)) != 0) { 335 fprintf(stderr, "unable to build argument list for jail command\n"); 336 return (1); 337 } 338 339 pid = fork(); 340 341 switch (pid) { 342 case -1: 343 perror("fork"); 344 return (1); 345 case 0: 346 execv("/usr/sbin/jail", jargv); 347 fprintf(stderr, "bectl jail: failed to execute\n"); 348 return (1); 349 default: 350 waitpid(pid, NULL, 0); 351 } 352 353 for (i = 0; jargv[i] != NULL; i++) { 354 free(jargv[i]); 355 } 356 free(jargv); 357 358 /* Non-interactive (-b) mode means the jail sticks around. */ 359 if (interactive && unjail) { 360 /* 361 * We're not checking the jail id result here because in the 362 * case of invalid param, or last command in jail was an error 363 * the jail will not exist upon exit. bectl_jail_cleanup will 364 * only jail_remove if the jid is >= 0. 365 */ 366 jid = bectl_locate_jail(bootenv); 367 bectl_jail_cleanup(mountpoint, jid); 368 be_unmount(be, bootenv, 0); 369 } 370 371 return (0); 372 } 373 374 static int 375 bectl_search_jail_paths(const char *mnt) 376 { 377 int jid; 378 char lastjid[16]; 379 char jailpath[MAXPATHLEN]; 380 381 /* jail_getv expects name/value strings */ 382 snprintf(lastjid, sizeof(lastjid), "%d", 0); 383 384 while ((jid = jail_getv(0, "lastjid", lastjid, "path", &jailpath, 385 NULL)) != -1) { 386 387 /* the jail we've been looking for */ 388 if (strcmp(jailpath, mnt) == 0) 389 return (jid); 390 391 /* update lastjid and keep on looking */ 392 snprintf(lastjid, sizeof(lastjid), "%d", jid); 393 } 394 395 return (-1); 396 } 397 398 /* 399 * Locate a jail based on an arbitrary identifier. This may be either a name, 400 * a jid, or a BE name. Returns the jid or -1 on failure. 401 */ 402 static int 403 bectl_locate_jail(const char *ident) 404 { 405 nvlist_t *belist, *props; 406 const char *mnt; 407 int jid; 408 409 /* Try the easy-match first */ 410 jid = jail_getid(ident); 411 /* 412 * jail_getid(0) will always return 0, because this prison does exist. 413 * bectl(8) knows that this is not what it wants, so we should fall 414 * back to mount point search. 415 */ 416 if (jid > 0) 417 return (jid); 418 419 /* Attempt to try it as a BE name, first */ 420 if (be_prop_list_alloc(&belist) != 0) 421 return (-1); 422 423 if (be_get_bootenv_props(be, belist) != 0) 424 return (-1); 425 426 if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { 427 428 /* path where a boot environment is mounted */ 429 if (nvlist_lookup_string(props, "mounted", &mnt) == 0) { 430 431 /* looking for a jail that matches our bootenv path */ 432 jid = bectl_search_jail_paths(mnt); 433 be_prop_list_free(belist); 434 return (jid); 435 } 436 437 be_prop_list_free(belist); 438 } 439 440 return (-1); 441 } 442 443 int 444 bectl_cmd_unjail(int argc, char *argv[]) 445 { 446 char path[MAXPATHLEN]; 447 char *cmd, *name, *target; 448 int jid; 449 450 /* Store alias used */ 451 cmd = argv[0]; 452 453 if (argc != 2) { 454 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); 455 return (usage(false)); 456 } 457 458 target = argv[1]; 459 460 /* Locate the jail */ 461 if ((jid = bectl_locate_jail(target)) == -1) { 462 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, 463 target); 464 return (1); 465 } 466 467 bzero(&path, MAXPATHLEN); 468 name = jail_getname(jid); 469 if (jail_getv(0, "name", name, "path", path, NULL) != jid) { 470 free(name); 471 fprintf(stderr, 472 "bectl %s: failed to get path for jail requested by '%s'\n", 473 cmd, target); 474 return (1); 475 } 476 477 free(name); 478 479 if (be_mounted_at(be, path, NULL) != 0) { 480 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", 481 cmd, target); 482 return (1); 483 } 484 485 bectl_jail_cleanup(path, jid); 486 be_unmount(be, target, 0); 487 488 return (0); 489 } 490