Skip to content

Commit 595d3ac

Browse files
allanjudeRyan Moeller
andauthored
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 11e3416 commit 595d3ac

File tree

2 files changed

+274
-16
lines changed

2 files changed

+274
-16
lines changed

module/os/freebsd/spl/spl_vfs.c

Lines changed: 0 additions & 12 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 *pcr, *tcr;
129128
int error;
130129

131130
ASSERT_VOP_ELOCKED(*vpp, "mount_snapshot");
@@ -195,18 +194,7 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath,
195194
*/
196195
mp->mnt_flag |= MNT_IGNORE;
197196

198-
/*
199-
* XXX: This is evil, but we can't mount a snapshot as a regular user.
200-
* XXX: Is is safe when snapshot is mounted from within a jail?
201-
*/
202-
tcr = td->td_ucred;
203-
pcr = td->td_proc->p_ucred;
204-
td->td_ucred = kcred;
205-
td->td_proc->p_ucred = kcred;
206197
error = VFS_MOUNT(mp);
207-
td->td_ucred = tcr;
208-
td->td_proc->p_ucred = pcr;
209-
210198
if (error != 0) {
211199
/*
212200
* 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
@@ -63,6 +63,7 @@
6363
#include <sys/dmu_objset.h>
6464
#include <sys/dsl_dir.h>
6565
#include <sys/jail.h>
66+
#include <sys/osd.h>
6667
#include <ufs/ufs/quota.h>
6768
#include <sys/zfs_quota.h>
6869

@@ -88,6 +89,20 @@ int zfs_debug_level;
8889
SYSCTL_INT(_vfs_zfs, OID_AUTO, debug, CTLFLAG_RWTUN, &zfs_debug_level, 0,
8990
"Debug level");
9091

92+
struct zfs_jailparam {
93+
int mount_snapshot;
94+
};
95+
96+
static struct zfs_jailparam zfs_jailparam0 = {
97+
.mount_snapshot = 0,
98+
};
99+
100+
static int zfs_jailparam_slot;
101+
102+
SYSCTL_JAIL_PARAM_SYS_NODE(zfs, CTLFLAG_RW, "Jail ZFS parameters");
103+
SYSCTL_JAIL_PARAM(_zfs, mount_snapshot, CTLTYPE_INT | CTLFLAG_RW, "I",
104+
"Allow mounting snapshots in the .zfs directory for unjailed datasets");
105+
91106
SYSCTL_NODE(_vfs_zfs, OID_AUTO, version, CTLFLAG_RD, 0, "ZFS versions");
92107
static int zfs_version_acl = ZFS_ACL_VERSION;
93108
SYSCTL_INT(_vfs_zfs_version, OID_AUTO, acl, CTLFLAG_RD, &zfs_version_acl, 0,
@@ -1298,7 +1313,7 @@ zfs_mount(vfs_t *vfsp)
12981313
char *osname;
12991314
int error = 0;
13001315
int canwrite;
1301-
bool checkpointrewind;
1316+
bool checkpointrewind, isctlsnap = false;
13021317

13031318
if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&osname, NULL))
13041319
return (SET_ERROR(EINVAL));
@@ -1313,6 +1328,7 @@ zfs_mount(vfs_t *vfsp)
13131328
}
13141329

13151330
fetch_osname_options(osname, &checkpointrewind);
1331+
isctlsnap = (zfsctl_is_node(mvp) && strchr(osname, '@') != NULL);
13161332

13171333
/*
13181334
* Check for mount privilege?
@@ -1321,7 +1337,9 @@ zfs_mount(vfs_t *vfsp)
13211337
* we have local permission to allow it
13221338
*/
13231339
error = secpolicy_fs_mount(cr, mvp, vfsp);
1324-
if (error) {
1340+
if (error && isctlsnap) {
1341+
secpolicy_fs_mount_clearopts(cr, vfsp);
1342+
} else if (error) {
13251343
if (dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr) != 0)
13261344
goto out;
13271345

@@ -1358,8 +1376,27 @@ zfs_mount(vfs_t *vfsp)
13581376
*/
13591377
if (!INGLOBALZONE(curproc) &&
13601378
(!zone_dataset_visible(osname, &canwrite) || !canwrite)) {
1361-
error = SET_ERROR(EPERM);
1362-
goto out;
1379+
boolean_t mount_snapshot = B_FALSE;
1380+
1381+
/*
1382+
* Snapshots may be mounted in .zfs for unjailed datasets
1383+
* if allowed by the jail param zfs.mount_snapshot.
1384+
*/
1385+
if (isctlsnap) {
1386+
struct prison *pr;
1387+
struct zfs_jailparam *zjp;
1388+
1389+
pr = curthread->td_ucred->cr_prison;
1390+
mtx_lock(&pr->pr_mtx);
1391+
zjp = osd_jail_get(pr, zfs_jailparam_slot);
1392+
mtx_unlock(&pr->pr_mtx);
1393+
if (zjp && zjp->mount_snapshot)
1394+
mount_snapshot = B_TRUE;
1395+
}
1396+
if (!mount_snapshot) {
1397+
error = SET_ERROR(EPERM);
1398+
goto out;
1399+
}
13631400
}
13641401

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

0 commit comments

Comments
 (0)