From nobody Sat Feb 4 20:07:55 2023 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4P8Nqb4JWhz3nnsM; Sat, 4 Feb 2023 20:07:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4P8Nqb3kHLz3QK6; Sat, 4 Feb 2023 20:07:55 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1675541275; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=mjav1ddsFm1E3QsxnsL3DCTsB4C2n433d3QtNRRdbf4=; b=CKFGGqzfMq2/Wbfa+dMOrpEJocYaIgaPHdOxXciKPxD+HNAfLuks2W/76emCrzwrRUzBSK EL1IOWLGw4f/5ID1hj+7a3w1aDXqWak7NS6hbjuC9wbOdZVbyOYmEPNMNxA+v2H+34lly7 mAYlmN9lQ1tVAXMlEQhxogp17fXehpn5rcIf05tJSlHRunDeD971l2sZyzQurf2PQD68Dg uWlogfr3Gqf2jd0f9Q2jRwr+azKa5O7VieeKbragtYQ2owTOpJMo9W5YiH4XGE6hSDzP84 O+vdmstzxfEzTxDyMhsDR8EdbLydokps3cD4tym4wGIa1SML58O5xK9/yqCWqg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1675541275; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=mjav1ddsFm1E3QsxnsL3DCTsB4C2n433d3QtNRRdbf4=; b=hNa9Y3jXsLhifR2oKDMwOFmKrTPBm5y5oTZndAKlb4pTS8Rh+WPU9reYUicC3ACz2rqaSJ k9nNWRepLrBvGs501n37WfMZPWAjAW0Pg1ujWOarSH1/WK2oXRqCP24xrFz1Jx1Uczv3iu SnZuYgk5FB6F5fBU2g9YsSPsNycqtCbj61XAnR3UZAwlhhBDMUg9wjNrWgpEy2RWBs3GdT LvA5Au0lbCXH74u3ndkghnfa1ZRwU/Cmc5HUZ7DqdSYxxS4VVl8HQ5oR/bGczeSCbJZ7Rf hqvhIO6rNsbzpuEwE6zAz1QqV/gZczD+eC1u9QwAS1gkMEloM8Z37S57wJ7sNg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1675541275; a=rsa-sha256; cv=none; b=YfP7H3cqMsULE5zepyt2PpN/MNs/7/Y2o4ookClyCXPfwJTlzg7DLhDr3b4em4Kwd0rMyN iS40OUWm8YCaHW1hBif6EsG7h/i98wJ3g0Zsty0HU8VA6AdrzJKqc8wHu5byowCBzLkTgk Qdek4DYH6b8DN2aHpxY01w5Jqbzl8IzjMgDJIeUpZn1STwrrK9roVSnwIK3HGtCMc5bvp3 Pozt/J0ucEW6KxQuMkHEc4+5rCezJFB8ldONRBxW6lmdpM/E7Qmx6KREKyb1YOB5asR2wt 5LpZ6MzEXZCxQORlRiwW/b/DdA2Tzjm1aM6YoSiamE7yTGYGDTT5jjFYiAN0wQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4P8Nqb2lw9zSGj; Sat, 4 Feb 2023 20:07:55 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 314K7tHP034431; Sat, 4 Feb 2023 20:07:55 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 314K7tVb034430; Sat, 4 Feb 2023 20:07:55 GMT (envelope-from git) Date: Sat, 4 Feb 2023 20:07:55 GMT Message-Id: <202302042007.314K7tVb034430@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Martin Matuska Subject: git: 3d7a882488a4 - stable/13 - zfs: fix mounting snapshots in .zfs/snapshot as a regular user List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: mm X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 3d7a882488a4c1c69283614ae179df7c1d2f3a7e Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by mm: URL: https://cgit.FreeBSD.org/src/commit/?id=3d7a882488a4c1c69283614ae179df7c1d2f3a7e commit 3d7a882488a4c1c69283614ae179df7c1d2f3a7e Author: Martin Matuska AuthorDate: 2023-02-04 18:39:12 +0000 Commit: Martin Matuska CommitDate: 2023-02-04 19:50:33 +0000 zfs: fix mounting snapshots in .zfs/snapshot as a regular user This is a cherry-pick from vendor/openzfs/master resulting in a direct commit to stable/13 The cherry-pick to branch zfs-2.1-release has been proposed upstream. Vendor pull requeset merges: #13758 Allow mounting snapshots in .zfs/snapshot as a regular user #14218 Bugfix: Avoid a null pointer dereference in zfs_mount() on FreeBSD PR: 260160 (cherry picked from commit 595d3ac2ed61331124feda2cf5787c3dd4c7ae09) (cherry picked from commit d27a00283faf4ec2b997ff2376dee4a080e1ca7b) --- .../openzfs/module/os/freebsd/spl/spl_vfs.c | 10 +- .../openzfs/module/os/freebsd/zfs/zfs_vfsops.c | 279 ++++++++++++++++++++- 2 files changed, 276 insertions(+), 13 deletions(-) diff --git a/sys/contrib/openzfs/module/os/freebsd/spl/spl_vfs.c b/sys/contrib/openzfs/module/os/freebsd/spl/spl_vfs.c index 60ea627e975b..53ef46fb8ce5 100644 --- a/sys/contrib/openzfs/module/os/freebsd/spl/spl_vfs.c +++ b/sys/contrib/openzfs/module/os/freebsd/spl/spl_vfs.c @@ -125,7 +125,6 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, struct vfsconf *vfsp; struct mount *mp; vnode_t *vp, *mvp; - struct ucred *cr; int error; ASSERT_VOP_ELOCKED(*vpp, "mount_snapshot"); @@ -194,15 +193,8 @@ mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, * mount(8) and df(1) output. */ mp->mnt_flag |= MNT_IGNORE; - /* - * XXX: This is evil, but we can't mount a snapshot as a regular user. - * XXX: Is is safe when snapshot is mounted from within a jail? - */ - cr = td->td_ucred; - td->td_ucred = kcred; - error = VFS_MOUNT(mp); - td->td_ucred = cr; + error = VFS_MOUNT(mp); if (error != 0) { /* * Clear VI_MOUNT and decrement the use count "atomically", diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c index 248b2d924f8c..85449ebb9d97 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vfsops.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,20 @@ int zfs_debug_level; SYSCTL_INT(_vfs_zfs, OID_AUTO, debug, CTLFLAG_RWTUN, &zfs_debug_level, 0, "Debug level"); +struct zfs_jailparam { + int mount_snapshot; +}; + +static struct zfs_jailparam zfs_jailparam0 = { + .mount_snapshot = 0, +}; + +static int zfs_jailparam_slot; + +SYSCTL_JAIL_PARAM_SYS_NODE(zfs, CTLFLAG_RW, "Jail ZFS parameters"); +SYSCTL_JAIL_PARAM(_zfs, mount_snapshot, CTLTYPE_INT | CTLFLAG_RW, "I", + "Allow mounting snapshots in the .zfs directory for unjailed datasets"); + SYSCTL_NODE(_vfs_zfs, OID_AUTO, version, CTLFLAG_RD, 0, "ZFS versions"); static int zfs_version_acl = ZFS_ACL_VERSION; SYSCTL_INT(_vfs_zfs_version, OID_AUTO, acl, CTLFLAG_RD, &zfs_version_acl, 0, @@ -1332,7 +1347,7 @@ zfs_mount(vfs_t *vfsp) char *osname; int error = 0; int canwrite; - bool checkpointrewind; + bool checkpointrewind, isctlsnap = false; if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&osname, NULL)) return (SET_ERROR(EINVAL)); @@ -1347,6 +1362,8 @@ zfs_mount(vfs_t *vfsp) } fetch_osname_options(osname, &checkpointrewind); + isctlsnap = (mvp != NULL && zfsctl_is_node(mvp) && + strchr(osname, '@') != NULL); /* * Check for mount privilege? @@ -1355,7 +1372,9 @@ zfs_mount(vfs_t *vfsp) * we have local permission to allow it */ error = secpolicy_fs_mount(cr, mvp, vfsp); - if (error) { + if (error && isctlsnap) { + secpolicy_fs_mount_clearopts(cr, vfsp); + } else if (error) { if (dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr) != 0) goto out; @@ -1392,8 +1411,27 @@ zfs_mount(vfs_t *vfsp) */ if (!INGLOBALZONE(curproc) && (!zone_dataset_visible(osname, &canwrite) || !canwrite)) { - error = SET_ERROR(EPERM); - goto out; + boolean_t mount_snapshot = B_FALSE; + + /* + * Snapshots may be mounted in .zfs for unjailed datasets + * if allowed by the jail param zfs.mount_snapshot. + */ + if (isctlsnap) { + struct prison *pr; + struct zfs_jailparam *zjp; + + pr = curthread->td_ucred->cr_prison; + mtx_lock(&pr->pr_mtx); + zjp = osd_jail_get(pr, zfs_jailparam_slot); + mtx_unlock(&pr->pr_mtx); + if (zjp && zjp->mount_snapshot) + mount_snapshot = B_TRUE; + } + if (!mount_snapshot) { + error = SET_ERROR(EPERM); + goto out; + } } vfsp->vfs_flag |= MNT_NFS4ACLS; @@ -2343,3 +2381,236 @@ zfsvfs_update_fromname(const char *oldname, const char *newname) mtx_unlock(&mountlist_mtx); } #endif + +/* + * Find a prison with ZFS info. + * Return the ZFS info and the (locked) prison. + */ +static struct zfs_jailparam * +zfs_jailparam_find(struct prison *spr, struct prison **prp) +{ + struct prison *pr; + struct zfs_jailparam *zjp; + + for (pr = spr; ; pr = pr->pr_parent) { + mtx_lock(&pr->pr_mtx); + if (pr == &prison0) { + zjp = &zfs_jailparam0; + break; + } + zjp = osd_jail_get(pr, zfs_jailparam_slot); + if (zjp != NULL) + break; + mtx_unlock(&pr->pr_mtx); + } + *prp = pr; + + return (zjp); +} + +/* + * Ensure a prison has its own ZFS info. If zjpp is non-null, point it to the + * ZFS info and lock the prison. + */ +static void +zfs_jailparam_alloc(struct prison *pr, struct zfs_jailparam **zjpp) +{ + struct prison *ppr; + struct zfs_jailparam *zjp, *nzjp; + void **rsv; + + /* If this prison already has ZFS info, return that. */ + zjp = zfs_jailparam_find(pr, &ppr); + if (ppr == pr) + goto done; + + /* + * Allocate a new info record. Then check again, in case something + * changed during the allocation. + */ + mtx_unlock(&ppr->pr_mtx); + nzjp = malloc(sizeof (struct zfs_jailparam), M_PRISON, M_WAITOK); + rsv = osd_reserve(zfs_jailparam_slot); + zjp = zfs_jailparam_find(pr, &ppr); + if (ppr == pr) { + free(nzjp, M_PRISON); + osd_free_reserved(rsv); + goto done; + } + /* Inherit the initial values from the ancestor. */ + mtx_lock(&pr->pr_mtx); + (void) osd_jail_set_reserved(pr, zfs_jailparam_slot, rsv, nzjp); + (void) memcpy(nzjp, zjp, sizeof (*zjp)); + zjp = nzjp; + mtx_unlock(&ppr->pr_mtx); +done: + if (zjpp != NULL) + *zjpp = zjp; + else + mtx_unlock(&pr->pr_mtx); +} + +/* + * Jail OSD methods for ZFS VFS info. + */ +static int +zfs_jailparam_create(void *obj, void *data) +{ + struct prison *pr = obj; + struct vfsoptlist *opts = data; + int jsys; + + if (vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys)) == 0 && + jsys == JAIL_SYS_INHERIT) + return (0); + /* + * Inherit a prison's initial values from its parent + * (different from JAIL_SYS_INHERIT which also inherits changes). + */ + zfs_jailparam_alloc(pr, NULL); + return (0); +} + +static int +zfs_jailparam_get(void *obj, void *data) +{ + struct prison *ppr, *pr = obj; + struct vfsoptlist *opts = data; + struct zfs_jailparam *zjp; + int jsys, error; + + zjp = zfs_jailparam_find(pr, &ppr); + jsys = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; + error = vfs_setopt(opts, "zfs", &jsys, sizeof (jsys)); + if (error != 0 && error != ENOENT) + goto done; + if (jsys == JAIL_SYS_NEW) { + error = vfs_setopt(opts, "zfs.mount_snapshot", + &zjp->mount_snapshot, sizeof (zjp->mount_snapshot)); + if (error != 0 && error != ENOENT) + goto done; + } else { + /* + * If this prison is inheriting its ZFS info, report + * empty/zero parameters. + */ + static int mount_snapshot = 0; + + error = vfs_setopt(opts, "zfs.mount_snapshot", + &mount_snapshot, sizeof (mount_snapshot)); + if (error != 0 && error != ENOENT) + goto done; + } + error = 0; +done: + mtx_unlock(&ppr->pr_mtx); + return (error); +} + +static int +zfs_jailparam_set(void *obj, void *data) +{ + struct prison *pr = obj; + struct prison *ppr; + struct vfsoptlist *opts = data; + int error, jsys, mount_snapshot; + + /* Set the parameters, which should be correct. */ + error = vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys)); + if (error == ENOENT) + jsys = -1; + error = vfs_copyopt(opts, "zfs.mount_snapshot", &mount_snapshot, + sizeof (mount_snapshot)); + if (error == ENOENT) + mount_snapshot = -1; + else + jsys = JAIL_SYS_NEW; + if (jsys == JAIL_SYS_NEW) { + /* "zfs=new" or "zfs.*": the prison gets its own ZFS info. */ + struct zfs_jailparam *zjp; + + /* + * A child jail cannot have more permissions than its parent + */ + if (pr->pr_parent != &prison0) { + zjp = zfs_jailparam_find(pr->pr_parent, &ppr); + mtx_unlock(&ppr->pr_mtx); + if (zjp->mount_snapshot < mount_snapshot) { + return (EPERM); + } + } + zfs_jailparam_alloc(pr, &zjp); + if (mount_snapshot != -1) + zjp->mount_snapshot = mount_snapshot; + mtx_unlock(&pr->pr_mtx); + } else { + /* "zfs=inherit": inherit the parent's ZFS info. */ + mtx_lock(&pr->pr_mtx); + osd_jail_del(pr, zfs_jailparam_slot); + mtx_unlock(&pr->pr_mtx); + } + return (0); +} + +static int +zfs_jailparam_check(void *obj __unused, void *data) +{ + struct vfsoptlist *opts = data; + int error, jsys, mount_snapshot; + + /* Check that the parameters are correct. */ + error = vfs_copyopt(opts, "zfs", &jsys, sizeof (jsys)); + if (error != ENOENT) { + if (error != 0) + return (error); + if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) + return (EINVAL); + } + error = vfs_copyopt(opts, "zfs.mount_snapshot", &mount_snapshot, + sizeof (mount_snapshot)); + if (error != ENOENT) { + if (error != 0) + return (error); + if (mount_snapshot != 0 && mount_snapshot != 1) + return (EINVAL); + } + return (0); +} + +static void +zfs_jailparam_destroy(void *data) +{ + + free(data, M_PRISON); +} + +static void +zfs_jailparam_sysinit(void *arg __unused) +{ + struct prison *pr; + osd_method_t methods[PR_MAXMETHOD] = { + [PR_METHOD_CREATE] = zfs_jailparam_create, + [PR_METHOD_GET] = zfs_jailparam_get, + [PR_METHOD_SET] = zfs_jailparam_set, + [PR_METHOD_CHECK] = zfs_jailparam_check, + }; + + zfs_jailparam_slot = osd_jail_register(zfs_jailparam_destroy, methods); + /* Copy the defaults to any existing prisons. */ + sx_slock(&allprison_lock); + TAILQ_FOREACH(pr, &allprison, pr_list) + zfs_jailparam_alloc(pr, NULL); + sx_sunlock(&allprison_lock); +} + +static void +zfs_jailparam_sysuninit(void *arg __unused) +{ + + osd_jail_deregister(zfs_jailparam_slot); +} + +SYSINIT(zfs_jailparam_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, + zfs_jailparam_sysinit, NULL); +SYSUNINIT(zfs_jailparam_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, + zfs_jailparam_sysuninit, NULL);