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 void jailparam_del(const char *name); 48 static bool jailparam_addarg(char *arg); 49 static bool 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 void 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 /* Not found... technically successful */ 107 if (i == jpused) 108 return; 109 110 for (; i < jpused - 1; ++i) { 111 val = jailparam_export(&jp[i + 1]); 112 113 jailparam_free(&jp[i], 1); 114 jailparam_init(&jp[i], jp[i + 1].jp_name); 115 jailparam_import(&jp[i], val); 116 free(val); 117 } 118 119 jailparam_free(&jp[i], 1); 120 --jpused; 121 } 122 123 static bool 124 jailparam_addarg(char *arg) 125 { 126 char *name, *val; 127 128 if (arg == NULL) 129 return (false); 130 name = arg; 131 if ((val = strchr(arg, '=')) == NULL) { 132 fprintf(stderr, "bectl jail: malformed jail option '%s'\n", 133 arg); 134 return (false); 135 } 136 137 *val++ = '\0'; 138 if (strcmp(name, "path") == 0) { 139 if (strlen(val) > BE_MAXPATHLEN) { 140 fprintf(stderr, 141 "bectl jail: skipping too long path assignment '%s' (max length = %d)\n", 142 val, BE_MAXPATHLEN); 143 return (false); 144 } 145 strcpy(mnt_loc, val); 146 } 147 jailparam_add(name, val); 148 return (true); 149 } 150 151 static bool 152 jailparam_delarg(char *arg) 153 { 154 char *name, *val; 155 156 if (arg == NULL) 157 return (false); 158 name = arg; 159 if ((val = strchr(name, '=')) != NULL) 160 *val++ = '\0'; 161 162 if (strcmp(name, "path") == 0) 163 *mnt_loc = '\0'; 164 jailparam_del(name); 165 return (true); 166 } 167 168 int 169 bectl_cmd_jail(int argc, char *argv[]) 170 { 171 char *bootenv, *mountpoint; 172 int jid, opt; 173 bool default_hostname, default_name; 174 175 default_hostname = default_name = true; 176 jpcnt = INIT_PARAMCOUNT; 177 jp = malloc(jpcnt * sizeof(*jp)); 178 if (jp == NULL) 179 err(2, "malloc"); 180 181 jailparam_add("persist", "true"); 182 jailparam_add("allow.mount", "true"); 183 jailparam_add("allow.mount.devfs", "true"); 184 jailparam_add("enforce_statfs", "1"); 185 186 while ((opt = getopt(argc, argv, "o:u:")) != -1) { 187 switch (opt) { 188 case 'o': 189 if (jailparam_addarg(optarg)) { 190 /* 191 * optarg has been modified to null terminate 192 * at the assignment operator. 193 */ 194 if (strcmp(optarg, "name") == 0) 195 default_name = false; 196 if (strcmp(optarg, "host.hostname") == 0) 197 default_hostname = false; 198 } 199 break; 200 case 'u': 201 if (jailparam_delarg(optarg)) { 202 if (strcmp(optarg, "name") == 0) 203 default_name = true; 204 if (strcmp(optarg, "host.hostname") == 0) 205 default_hostname = true; 206 } 207 break; 208 default: 209 fprintf(stderr, "bectl jail: unknown option '-%c'\n", 210 optopt); 211 return (usage(false)); 212 } 213 } 214 215 argc -= optind; 216 argv += optind; 217 218 /* struct jail be_jail = { 0 }; */ 219 if (argc < 1) { 220 fprintf(stderr, "bectl jail: missing boot environment name\n"); 221 return (usage(false)); 222 } 223 if (argc > 2) { 224 fprintf(stderr, "bectl jail: too many arguments\n"); 225 return (usage(false)); 226 } 227 228 bootenv = argv[0]; 229 230 /* 231 * XXX TODO: if its already mounted, perhaps there should be a flag to 232 * indicate its okay to proceed?? 233 */ 234 if (*mnt_loc == '\0') 235 mountpoint = NULL; 236 else 237 mountpoint = mnt_loc; 238 if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) { 239 fprintf(stderr, "could not mount bootenv\n"); 240 return (1); 241 } 242 243 if (default_name) 244 jailparam_add("name", bootenv); 245 if (default_hostname) 246 jailparam_add("host.hostname", bootenv); 247 /* 248 * This is our indicator that path was not set by the user, so we'll use 249 * the path that libbe generated for us. 250 */ 251 if (mountpoint == NULL) 252 jailparam_add("path", mnt_loc); 253 jid = jailparam_set(jp, jpused, JAIL_CREATE | JAIL_ATTACH); 254 if (jid == -1) { 255 fprintf(stderr, "unable to create jail. error: %d\n", errno); 256 return (1); 257 } 258 259 jailparam_free(jp, jpused); 260 free(jp); 261 262 /* We're attached within the jail... good bye! */ 263 chdir("/"); 264 execl("/bin/sh", "/bin/sh", NULL); 265 return (0); 266 } 267 268 static int 269 bectl_search_jail_paths(const char *mnt) 270 { 271 char jailpath[MAXPATHLEN + 1]; 272 int jid; 273 274 jid = 0; 275 (void)mnt; 276 while ((jid = jail_getv(0, "lastjid", &jid, "path", &jailpath, 277 NULL)) != -1) { 278 if (strcmp(jailpath, mnt) == 0) 279 return (jid); 280 } 281 282 return (-1); 283 } 284 285 /* 286 * Locate a jail based on an arbitrary identifier. This may be either a name, 287 * a jid, or a BE name. Returns the jid or -1 on failure. 288 */ 289 static int 290 bectl_locate_jail(const char *ident) 291 { 292 nvlist_t *belist, *props; 293 char *mnt; 294 int jid; 295 296 /* Try the easy-match first */ 297 jid = jail_getid(ident); 298 if (jid != -1) 299 return (jid); 300 301 /* Attempt to try it as a BE name, first */ 302 if (be_prop_list_alloc(&belist) != 0) 303 return (-1); 304 305 if (be_get_bootenv_props(be, belist) != 0) 306 return (-1); 307 308 if (nvlist_lookup_nvlist(belist, ident, &props) == 0) { 309 /* We'll attempt to resolve the jid by way of mountpoint */ 310 if (nvlist_lookup_string(props, "mountpoint", &mnt) == 0) { 311 jid = bectl_search_jail_paths(mnt); 312 be_prop_list_free(belist); 313 return (jid); 314 } 315 316 be_prop_list_free(belist); 317 } 318 319 return (-1); 320 } 321 322 int 323 bectl_cmd_unjail(int argc, char *argv[]) 324 { 325 char path[MAXPATHLEN + 1]; 326 char *cmd, *name, *target; 327 int jid; 328 329 /* Store alias used */ 330 cmd = argv[0]; 331 332 if (argc != 2) { 333 fprintf(stderr, "bectl %s: wrong number of arguments\n", cmd); 334 return (usage(false)); 335 } 336 337 target = argv[1]; 338 339 /* Locate the jail */ 340 if ((jid = bectl_locate_jail(target)) == -1) { 341 fprintf(stderr, "bectl %s: failed to locate BE by '%s'\n", cmd, 342 target); 343 return (1); 344 } 345 346 bzero(&path, MAXPATHLEN + 1); 347 name = jail_getname(jid); 348 if (jail_getv(0, "name", name, "path", path, NULL) != jid) { 349 free(name); 350 fprintf(stderr, 351 "bectl %s: failed to get path for jail requested by '%s'\n", 352 cmd, target); 353 return (1); 354 } 355 356 free(name); 357 358 if (be_mounted_at(be, path, NULL) != 0) { 359 fprintf(stderr, "bectl %s: jail requested by '%s' not a BE\n", 360 cmd, target); 361 return (1); 362 } 363 364 jail_remove(jid); 365 unmount(path, 0); 366 367 return (0); 368 } 369