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