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