Skip to content

Commit 5161e5d

Browse files
allanjudeRyan Moeller
authored andcommitted
Allow mounting snapshots in .zfs/snapshot as a regular user
Rather than doing a terrible credential swapping hack, we just check that the thing being mounted is a snapshot, and the mountpoint is the zfsctl directory, then we allow it. If the mount attempt is from inside a jail, on an unjailed dataset (mounted from the host, not by the jail), the ability to mount the snapshot is controlled by a new per-jail parameter: zfs.mount_snapshot Reviewed-by: Brian Behlendorf <[email protected]> Co-authored-by: Ryan Moeller <[email protected]> Signed-off-by: Ryan Moeller <[email protected]> Signed-off-by: Allan Jude <[email protected]> Sponsored-by: Modirum MDPay Sponsored-by: Klara Inc. Closes #13758
1 parent 92e0d9d commit 5161e5d

File tree

2 files changed

+275
-13
lines changed

2 files changed

+275
-13
lines changed

module/os/freebsd/spl/spl_vfs.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath,
125125
struct vfsconf *vfsp;
126126
struct mount *mp;
127127
vnode_t *vp, *mvp;
128-
struct ucred *cr;
129128
int error;
130129

131130
ASSERT_VOP_ELOCKED(*vpp, "mount_snapshot");
@@ -194,15 +193,8 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath,
194193
* mount(8) and df(1) output.
195194
*/
196195
mp->mnt_flag |= MNT_IGNORE;
197-
/*
198-
* XXX: This is evil, but we can't mount a snapshot as a regular user.
199-
* XXX: Is is safe when snapshot is mounted from within a jail?
200-
*/
201-
cr = td->td_ucred;
202-
td->td_ucred = kcred;
203-
error = VFS_MOUNT(mp);
204-
td->td_ucred = cr;
205196

197+
error = VFS_MOUNT(mp);
206198
if (error != 0) {
207199
/*
208200
* Clear VI_MOUNT and decrement the use count "atomically",

module/os/freebsd/zfs/zfs_vfsops.c

Lines changed: 274 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include <sys/dsl_dir.h>
6565
#include <sys/spa_boot.h>
6666
#include <sys/jail.h>
67+
#include <sys/osd.h>
6768
#include <ufs/ufs/quota.h>
6869
#include <sys/zfs_quota.h>
6970

@@ -90,6 +91,20 @@ int zfs_debug_level;
9091
SYSCTL_INT(_vfs_zfs, OID_AUTO, debug, CTLFLAG_RWTUN, &zfs_debug_level, 0,
9192
"Debug level");
9293

94+
struct zfs_jailparam {
95+
int mount_snapshot;
96+
};
97+
98+
static struct zfs_jailparam zfs_jailparam0 = {
99+
.mount_snapshot = 0,
100+
};
101+
102+
static int zfs_jailparam_slot;
103+
104+
SYSCTL_JAIL_PARAM_SYS_NODE(zfs, CTLFLAG_RW, "Jail ZFS parameters");
105+
SYSCTL_JAIL_PARAM(_zfs, mount_snapshot, CTLTYPE_INT | CTLFLAG_RW, "I",
106+
"Allow mounting snapshots in the .zfs directory for unjailed datasets");
107+
93108
SYSCTL_NODE(_vfs_zfs, OID_AUTO, version, CTLFLAG_RD, 0, "ZFS versions");
94109
static int zfs_version_acl = ZFS_ACL_VERSION;
95110
SYSCTL_INT(_vfs_zfs_version, OID_AUTO, acl, CTLFLAG_RD, &zfs_version_acl, 0,
@@ -1332,7 +1347,7 @@ zfs_mount(vfs_t *vfsp)
13321347
char *osname;
13331348
int error = 0;
13341349
int canwrite;
1335-
bool checkpointrewind;
1350+
bool checkpointrewind, isctlsnap = false;
13361351

13371352
if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&osname, NULL))
13381353
return (SET_ERROR(EINVAL));
@@ -1347,6 +1362,7 @@ zfs_mount(vfs_t *vfsp)
13471362
}
13481363

13491364
fetch_osname_options(osname, &checkpointrewind);
1365+
isctlsnap = (zfsctl_is_node(mvp) && strchr(osname, '@') != NULL);
13501366

13511367
/*
13521368
* Check for mount privilege?
@@ -1355,7 +1371,9 @@ zfs_mount(vfs_t *vfsp)
13551371
* we have local permission to allow it
13561372
*/
13571373
error = secpolicy_fs_mount(cr, mvp, vfsp);
1358-
if (error) {
1374+
if (error && isctlsnap) {
1375+
secpolicy_fs_mount_clearopts(cr, vfsp);
1376+
} else if (error) {
13591377
if (dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr) != 0)
13601378
goto out;
13611379

@@ -1392,8 +1410,27 @@ zfs_mount(vfs_t *vfsp)
13921410
*/
13931411
if (!INGLOBALZONE(curproc) &&
13941412
(!zone_dataset_visible(osname, &canwrite) || !canwrite)) {
1395-
error = SET_ERROR(EPERM);
1396-
goto out;
1413+
boolean_t mount_snapshot = B_FALSE;
1414+
1415+
/*
1416+
* Snapshots may be mounted in .zfs for unjailed datasets
1417+
* if allowed by the jail param zfs.mount_snapshot.
1418+
*/
1419+
if (isctlsnap) {
1420+
struct prison *pr;
1421+
struct zfs_jailparam *zjp;
1422+
1423+
pr = curthread->td_ucred->cr_prison;
1424+
mtx_lock(&pr->pr_mtx);
1425+
zjp = osd_jail_get(pr, zfs_jailparam_slot);
1426+
mtx_unlock(&pr->pr_mtx);
1427+
if (zjp && zjp->mount_snapshot)
1428+
mount_snapshot = B_TRUE;
1429+
}
1430+
if (!mount_snapshot) {
1431+
error = SET_ERROR(EPERM);
1432+
goto out;
1433+
}
13971434
}
13981435

13991436
vfsp->vfs_flag |= MNT_NFS4ACLS;
@@ -2343,3 +2380,236 @@ zfsvfs_update_fromname(const char *oldname, const char *newname)
23432380
mtx_unlock(&mountlist_mtx);
23442381
}
23452382
#endif
2383+
2384+
/*
2385+
* Find a prison with ZFS info.
2386+
* Return the ZFS info and the (locked) prison.
2387+
*/
2388+
static struct zfs_jailparam *
2389+
zfs_jailparam_find(struct prison *spr, struct prison **prp)
2390+
{
2391+
struct prison *pr;
2392+
struct zfs_jailparam *zjp;
2393+
2394+
for (pr = spr; ; pr = pr->pr_parent) {
2395+
mtx_lock(&pr->pr_mtx);
2396+
if (pr == &prison0) {
2397+
zjp = &zfs_jailparam0;
2398+
break;
2399+
}
2400+
zjp = osd_jail_get(pr, zfs_jailparam_slot);
2401+
if (zjp != NULL)
2402+
break;
2403+
mtx_unlock(&pr->pr_mtx);
2404+
}
2405+
*prp = pr;
2406+
2407+
return (zjp);
2408+
}
2409+
2410+
/*
2411+
* Ensure a prison has its own ZFS info. If zjpp is non-null, point it to the
2412+
* ZFS info and lock the prison.
2413+
*/
2414+
static void
2415+
zfs_jailparam_alloc(struct prison *pr, struct zfs_jailparam **zjpp)
2416+
{
2417+
struct prison *ppr;
2418+
struct zfs_jailparam *zjp, *nzjp;
2419+
void **rsv;
2420+
2421+
/* If this prison already has ZFS info, return that. */
2422+
zjp = zfs_jailparam_find(pr, &ppr);
2423+
if (ppr == pr)
2424+
goto done;
2425+
2426+
/*
2427+
* Allocate a new info record. Then check again, in case something
2428+
* changed during the allocation.
2429+
*/
2430+
mtx_unlock(&ppr->pr_mtx);
2431+
nzjp = malloc(sizeof (struct zfs_jailparam), M_PRISON, M_WAITOK);
2432+
rsv = osd_reserve(zfs_jailparam_slot);
2433+
zjp = zfs_jailparam_find(pr, &ppr);
2434+
if (ppr == pr) {
2435+
free(nzjp, M_PRISON);
2436+
osd_free_reserved(rsv);
2437+
goto done;
2438+
}
2439+
/* Inherit the initial values from the ancestor. */
2440+
mtx_lock(&pr->pr_mtx);
2441+
(void) osd_jail_set_reserved(pr, zfs_jailparam_slot, rsv, nzjp);
2442+
(void) memcpy(nzjp, zjp, sizeof (*zjp));
2443+
zjp = nzjp;
2444+
mtx_unlock(&ppr->pr_mtx);
2445+
done:
2446+
if (zjpp != NULL)
2447+
*zjpp = zjp;
2448+
else
2449+
mtx_unlock(&pr->pr_mtx);
2450+
}
2451+
2452+
/*
2453+
* Jail OSD methods for ZFS VFS info.
2454+
*/
2455+
static int
2456+
zfs_jailparam_create(void *obj, void *data)
2457+
{
2458+
struct prison *pr = obj;
2459+
struct vfsoptlist *opts = data;
2460+
int jsys;
2461+
2462+
if (vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys)) == 0 &&
2463+
jsys == JAIL_SYS_INHERIT)
2464+
return (0);
2465+
/*
2466+
* Inherit a prison's initial values from its parent
2467+
* (different from JAIL_SYS_INHERIT which also inherits changes).
2468+
*/
2469+
zfs_jailparam_alloc(pr, NULL);
2470+
return (0);
2471+
}
2472+
2473+
static int
2474+
zfs_jailparam_get(void *obj, void *data)
2475+
{
2476+
struct prison *ppr, *pr = obj;
2477+
struct vfsoptlist *opts = data;
2478+
struct zfs_jailparam *zjp;
2479+
int jsys, error;
2480+
2481+
zjp = zfs_jailparam_find(pr, &ppr);
2482+
jsys = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
2483+
error = vfs_setopt(opts, "zfs", &jsys, sizeof (jsys));
2484+
if (error != 0 && error != ENOENT)
2485+
goto done;
2486+
if (jsys == JAIL_SYS_NEW) {
2487+
error = vfs_setopt(opts, "zfs.mount_snapshot",
2488+
&zjp->mount_snapshot, sizeof (zjp->mount_snapshot));
2489+
if (error != 0 && error != ENOENT)
2490+
goto done;
2491+
} else {
2492+
/*
2493+
* If this prison is inheriting its ZFS info, report
2494+
* empty/zero parameters.
2495+
*/
2496+
static int mount_snapshot = 0;
2497+
2498+
error = vfs_setopt(opts, "zfs.mount_snapshot",
2499+
&mount_snapshot, sizeof (mount_snapshot));
2500+
if (error != 0 && error != ENOENT)
2501+
goto done;
2502+
}
2503+
error = 0;
2504+
done:
2505+
mtx_unlock(&ppr->pr_mtx);
2506+
return (error);
2507+
}
2508+
2509+
static int
2510+
zfs_jailparam_set(void *obj, void *data)
2511+
{
2512+
struct prison *pr = obj;
2513+
struct prison *ppr;
2514+
struct vfsoptlist *opts = data;
2515+
int error, jsys, mount_snapshot;
2516+
2517+
/* Set the parameters, which should be correct. */
2518+
error = vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys));
2519+
if (error == ENOENT)
2520+
jsys = -1;
2521+
error = vfs_copyopt(opts, "zfs.mount_snapshot", &mount_snapshot,
2522+
sizeof (mount_snapshot));
2523+
if (error == ENOENT)
2524+
mount_snapshot = -1;
2525+
else
2526+
jsys = JAIL_SYS_NEW;
2527+
if (jsys == JAIL_SYS_NEW) {
2528+
/* "zfs=new" or "zfs.*": the prison gets its own ZFS info. */
2529+
struct zfs_jailparam *zjp;
2530+
2531+
/*
2532+
* A child jail cannot have more permissions than its parent
2533+
*/
2534+
if (pr->pr_parent != &prison0) {
2535+
zjp = zfs_jailparam_find(pr->pr_parent, &ppr);
2536+
mtx_unlock(&ppr->pr_mtx);
2537+
if (zjp->mount_snapshot < mount_snapshot) {
2538+
return (EPERM);
2539+
}
2540+
}
2541+
zfs_jailparam_alloc(pr, &zjp);
2542+
if (mount_snapshot != -1)
2543+
zjp->mount_snapshot = mount_snapshot;
2544+
mtx_unlock(&pr->pr_mtx);
2545+
} else {
2546+
/* "zfs=inherit": inherit the parent's ZFS info. */
2547+
mtx_lock(&pr->pr_mtx);
2548+
osd_jail_del(pr, zfs_jailparam_slot);
2549+
mtx_unlock(&pr->pr_mtx);
2550+
}
2551+
return (0);
2552+
}
2553+
2554+
static int
2555+
zfs_jailparam_check(void *obj __unused, void *data)
2556+
{
2557+
struct vfsoptlist *opts = data;
2558+
int error, jsys, mount_snapshot;
2559+
2560+
/* Check that the parameters are correct. */
2561+
error = vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys));
2562+
if (error != ENOENT) {
2563+
if (error != 0)
2564+
return (error);
2565+
if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT)
2566+
return (EINVAL);
2567+
}
2568+
error = vfs_copyopt(opts, "zfs.mount_snapshot", &mount_snapshot,
2569+
sizeof (mount_snapshot));
2570+
if (error != ENOENT) {
2571+
if (error != 0)
2572+
return (error);
2573+
if (mount_snapshot != 0 && mount_snapshot != 1)
2574+
return (EINVAL);
2575+
}
2576+
return (0);
2577+
}
2578+
2579+
static void
2580+
zfs_jailparam_destroy(void *data)
2581+
{
2582+
2583+
free(data, M_PRISON);
2584+
}
2585+
2586+
static void
2587+
zfs_jailparam_sysinit(void *arg __unused)
2588+
{
2589+
struct prison *pr;
2590+
osd_method_t methods[PR_MAXMETHOD] = {
2591+
[PR_METHOD_CREATE] = zfs_jailparam_create,
2592+
[PR_METHOD_GET] = zfs_jailparam_get,
2593+
[PR_METHOD_SET] = zfs_jailparam_set,
2594+
[PR_METHOD_CHECK] = zfs_jailparam_check,
2595+
};
2596+
2597+
zfs_jailparam_slot = osd_jail_register(zfs_jailparam_destroy, methods);
2598+
/* Copy the defaults to any existing prisons. */
2599+
sx_slock(&allprison_lock);
2600+
TAILQ_FOREACH(pr, &allprison, pr_list)
2601+
zfs_jailparam_alloc(pr, NULL);
2602+
sx_sunlock(&allprison_lock);
2603+
}
2604+
2605+
static void
2606+
zfs_jailparam_sysuninit(void *arg __unused)
2607+
{
2608+
2609+
osd_jail_deregister(zfs_jailparam_slot);
2610+
}
2611+
2612+
SYSINIT(zfs_jailparam_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY,
2613+
zfs_jailparam_sysinit, NULL);
2614+
SYSUNINIT(zfs_jailparam_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY,
2615+
zfs_jailparam_sysuninit, NULL);

0 commit comments

Comments
 (0)