Index: sbin/bectl/bectl_jail.c =================================================================== --- sbin/bectl/bectl_jail.c +++ sbin/bectl/bectl_jail.c @@ -40,10 +40,10 @@ #include #include - #include "bectl.h" -static void jailparam_grow(void); +#define MNTTYPE_ZFS 222 + static void jailparam_add(const char *name, const char *val); static int jailparam_del(const char *name); static bool jailparam_addarg(char *arg); @@ -51,84 +51,32 @@ static int bectl_search_jail_paths(const char *mnt); static int bectl_locate_jail(const char *ident); +static int bectl_jail_cleanup(char *mountpoint, int jid); -/* We'll start with 8 parameters initially and grow as needed. */ -#define INIT_PARAMCOUNT 8 - -static struct jailparam *jp; -static int jpcnt; -static int jpused; static char mnt_loc[BE_MAXPATHLEN]; +static nvlist_t *jailparams; -static void -jailparam_grow(void) -{ +/* + * TODO: Might want to do the opposite of this, so we catch invalid + * params before they reach the jail command which would prevent the jail from + * being created. + */ +static const char *disabled_params[] = { + "command", "exec.start", "nopersist", "persist", NULL }; - jpcnt *= 2; - jp = realloc(jp, jpcnt * sizeof(*jp)); - if (jp == NULL) - err(2, "realloc"); -} static void jailparam_add(const char *name, const char *val) { - int i; - for (i = 0; i < jpused; ++i) { - if (strcmp(name, jp[i].jp_name) == 0) - break; - } - - if (i < jpused) - jailparam_free(&jp[i], 1); - else if (jpused == jpcnt) - /* The next slot isn't allocated yet */ - jailparam_grow(); - - if (jailparam_init(&jp[i], name) != 0) - return; - if (jailparam_import(&jp[i], val) != 0) - return; - ++jpused; + nvlist_add_string(jailparams, name, val); } static int jailparam_del(const char *name) { - int i; - char *val; - - for (i = 0; i < jpused; ++i) { - if (strcmp(name, jp[i].jp_name) == 0) - break; - } - - if (i == jpused) - return (ENOENT); - - for (; i < jpused - 1; ++i) { - val = jailparam_export(&jp[i + 1]); - - jailparam_free(&jp[i], 1); - /* - * Given the context, the following will really only fail if - * they can't allocate the copy of the name or value. - */ - if (jailparam_init(&jp[i], jp[i + 1].jp_name) != 0) { - free(val); - return (ENOMEM); - } - if (jailparam_import(&jp[i], val) != 0) { - jailparam_free(&jp[i], 1); - free(val); - return (ENOMEM); - } - free(val); - } - jailparam_free(&jp[i], 1); - --jpused; + nvlist_remove_all(jailparams, name); return (0); } @@ -136,6 +84,7 @@ jailparam_addarg(char *arg) { char *name, *val; + size_t i, len; if (arg == NULL) return (false); @@ -156,6 +105,15 @@ } strlcpy(mnt_loc, val, sizeof(mnt_loc)); } + + for (i = 0; disabled_params[i] != NULL; i++) { + len = strlen(disabled_params[i]); + if (strncmp(disabled_params[i], name, len) == 0) { + fprintf(stderr, "invalid jail parameter: %s\n", name); + return (false); + } + } + jailparam_add(name, val); return (true); } @@ -176,10 +134,93 @@ return (jailparam_del(name)); } +static int +build_jailcmd(char ***argvp, bool interactive, int argc, char *argv[]) +{ + char *cmd, **jargv, *name, *val; + nvpair_t *nvp; + size_t i, iarg, nargv; + + nvp = NULL; + iarg = i = 0; + if (nvlist_size(jailparams, &nargv, NV_ENCODE_NATIVE) != 0) + return (1); + + /* + * Number of args + "/usr/sbin/jail", "-c", and ending NULL. + * If interactive also include command. + */ + nargv += 3; + if (interactive) + nargv++; + + jargv = *argvp = calloc(nargv, sizeof(jargv)); + if (jargv == NULL) + err(2, "calloc"); + + jargv[iarg++] = strdup("/usr/sbin/jail"); + jargv[iarg++] = strdup("-c"); + while ((nvp = nvlist_next_nvpair(jailparams, nvp)) != NULL) { + name = nvpair_name(nvp); + if (nvpair_value_string(nvp, &val) != 0) + continue; + + if (asprintf(&jargv[iarg++], "%s=%s", name, val) < 0) + goto error; + } + if (interactive) { + if (argc < 1) + cmd = strdup("/bin/sh"); + else + cmd = argv[0]; + + if (asprintf(&jargv[iarg++], "command=%s", cmd) < 0) + goto error; + } + + return (0); + +error: + for (; i < iarg-1; i++) { + free(jargv[i]); + } + free(jargv); + return(1); +} + +/* Remove jail and cleanup any non zfs mounts. */ +static int +bectl_jail_cleanup(char *mountpoint, int jid) +{ + struct statfs *mntbuf; + size_t i, searchlen, mntsize; + + if (jid >= 0 && jail_remove(jid) != 0) { + fprintf(stderr, "unable to remove jail"); + return (1); + } + + searchlen = strnlen(mountpoint, MAXPATHLEN); + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (strncmp(mountpoint, mntbuf[i].f_mntonname, searchlen) == 0 && + mntbuf[i].f_type != MNTTYPE_ZFS) { + + if (unmount(mntbuf[i].f_mntonname,0) != 0) { + fprintf(stderr, "bectl jail: unable to unmount filesystem %s", + mntbuf[i].f_mntonname); + return (1); + } + } + } + + return (0); +} + int bectl_cmd_jail(int argc, char *argv[]) { - char *bootenv, *mountpoint; + char *bootenv, **jargv, *mountpoint; int jid, mntflags, opt, ret; bool default_hostname, interactive, unjail; pid_t pid; @@ -187,10 +228,11 @@ /* XXX TODO: Allow shallow */ mntflags = BE_MNT_DEEP; default_hostname = interactive = unjail = true; - jpcnt = INIT_PARAMCOUNT; - jp = malloc(jpcnt * sizeof(*jp)); - if (jp == NULL) - err(2, "malloc"); + + if ((nvlist_alloc(&jailparams, NV_UNIQUE_NAME, 0)) != 0) { + fprintf(stderr, "nvlist_alloc() failed\n"); + return (1); + } jailparam_add("persist", "true"); jailparam_add("allow.mount", "true"); @@ -210,6 +252,8 @@ */ if (strcmp(optarg, "host.hostname") == 0) default_hostname = false; + } else { + return (1); } break; case 'U': @@ -236,13 +280,14 @@ argc -= optind; argv += optind; - /* struct jail be_jail = { 0 }; */ if (argc < 1) { fprintf(stderr, "bectl jail: missing boot environment name\n"); return (usage(false)); } bootenv = argv[0]; + argc--; + argv++; /* * XXX TODO: if its already mounted, perhaps there should be a flag to @@ -264,45 +309,42 @@ * This is our indicator that path was not set by the user, so we'll use * the path that libbe generated for us. */ - if (mountpoint == NULL) + if (mountpoint == NULL){ jailparam_add("path", mnt_loc); - /* Create the jail for now, attach later as-needed */ - jid = jailparam_set(jp, jpused, JAIL_CREATE); - if (jid == -1) { - fprintf(stderr, "unable to create jail. error: %d\n", errno); - return (1); + mountpoint = mnt_loc; } - jailparam_free(jp, jpused); - free(jp); - - /* We're not interactive, nothing more to do here. */ - if (!interactive) - return (0); + if ((build_jailcmd(&jargv, interactive, argc, argv)) != 0 ) { + fprintf(stderr, "unable to build argument list for jail command"); + return (1); + } pid = fork(); + switch(pid) { case -1: perror("fork"); return (1); case 0: - jail_attach(jid); - /* We're attached within the jail... good bye! */ - chdir("/"); - if (argc > 1) - execve(argv[1], &argv[1], NULL); - else - execl("/bin/sh", "/bin/sh", NULL); - fprintf(stderr, "bectl jail: failed to execute %s\n", - (argc > 1 ? argv[1] : "/bin/sh")); - _exit(1); + execv("/usr/sbin/jail", jargv); + fprintf(stderr, "bectl jail: failed to execute\n"); default: - /* Wait for the child to get back, see if we need to unjail */ waitpid(pid, NULL, 0); } + if (!interactive) + return (0); + if (unjail) { - jail_remove(jid); + /* + * We're not checking the jail id result here because, + * in the case of invalid param, or last command in + * jail was error the jail will not exist. + * bectl_jail_cleanup will only jail_remove if the + * jid is >= 0. + */ + jid = bectl_locate_jail(bootenv); + bectl_jail_cleanup(mountpoint, jid); be_unmount(be, bootenv, 0); } @@ -416,7 +458,7 @@ return (1); } - jail_remove(jid); + bectl_jail_cleanup(path, jid); be_unmount(be, target, 0); return (0);