Module Name: src Committed By: riastradh Date: Thu Oct 18 14:22:58 UTC 2012
Modified Files: src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_vnops.c src/external/cddl/osnet/sys/kern: policy.c src/external/cddl/osnet/sys/sys: policy.h Log Message: Take a first whack at making zfs permissions work. zfs_access uses secpolicy_vnode_access, so it makes no sense for the latter to call VOP_ACCESS! Everything seems to return EACCES instead of EPERM, probably because that's what kauth returns. This should be fixed, but that may require some nontrivial surgery to zfs's calls to secpolicy_*, which is where kauth gets involved. This commit imports some code from illumos to implement the routine secpolicy_vnode_setattr. This shouldn't be outside dist/, but for now it is expedient to do so. We ought to fix that, along with all the other CDDL code outside dist/, when we next import a newer version of zfs. To generate a diff of this commit: cvs rdiff -u -r1.13 -r1.14 \ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c cvs rdiff -u -r1.3 -r1.4 src/external/cddl/osnet/sys/kern/policy.c cvs rdiff -u -r1.5 -r1.6 src/external/cddl/osnet/sys/sys/policy.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.13 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.14 --- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.13 Tue Oct 16 00:04:15 2012 +++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c Thu Oct 18 14:22:57 2012 @@ -1422,7 +1422,8 @@ top: * Create a new file object and update the directory * to reference it. */ - if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { + error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr); + if (error) { goto out; } @@ -4779,22 +4780,34 @@ zfs_netbsd_write(void *v) static int zfs_netbsd_access(void *v) { - struct vop_access_args *ap = v; + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + int mode = ap->a_mode; + mode_t zfs_mode = 0; + kauth_cred_t cred = ap->a_cred; + int error; /* - * ZFS itself only knowns about VREAD, VWRITE and VEXEC, the rest - * we have to handle by calling vaccess(). - */ - if ((ap->a_mode & ~(VREAD|VWRITE|VEXEC)) != 0) { - vnode_t *vp = ap->a_vp; - znode_t *zp = VTOZ(vp); - znode_phys_t *zphys = zp->z_phys; + * XXX This is really random, especially the left shift by six, + * and it exists only because of randomness in zfs_unix_to_v4 + * and zfs_zaccess_rwx in zfs_acl.c. + */ + if (mode & VREAD) + zfs_mode |= S_IROTH; + if (mode & VWRITE) + zfs_mode |= S_IWOTH; + if (mode & VEXEC) + zfs_mode |= S_IXOTH; + zfs_mode <<= 6; - return (vaccess(vp->v_type, zphys->zp_mode, zphys->zp_uid, - zphys->zp_gid, ap->a_mode, ap->a_cred)); - } + KASSERT(VOP_ISLOCKED(vp)); + error = zfs_access(vp, zfs_mode, 0, cred, NULL); - return (zfs_access(ap->a_vp, ap->a_mode, 0, ap->a_cred, NULL)); + return (error); } static int @@ -4849,13 +4862,19 @@ zfs_netbsd_lookup(void *v) NULL, NULL); /* - * Translate errors to match our namei insanity. + * Translate errors to match our namei insanity. Also, if the + * caller wants to create an entry here, it's apparently our + * responsibility as lookup to make sure that's permissible. + * Go figure. */ if (cnp->cn_flags & ISLASTCN) { switch (cnp->cn_nameiop) { case CREATE: case RENAME: if (error == ENOENT) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); + if (error) + break; error = EJUSTRETURN; break; } Index: src/external/cddl/osnet/sys/kern/policy.c diff -u src/external/cddl/osnet/sys/kern/policy.c:1.3 src/external/cddl/osnet/sys/kern/policy.c:1.4 --- src/external/cddl/osnet/sys/kern/policy.c:1.3 Mon Oct 15 22:50:25 2012 +++ src/external/cddl/osnet/sys/kern/policy.c Thu Oct 18 14:22:58 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: policy.c,v 1.3 2012/10/15 22:50:25 riastradh Exp $ */ +/* $NetBSD: policy.c,v 1.4 2012/10/18 14:22:58 riastradh Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -55,6 +55,31 @@ * SUCH DAMAGE. */ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, Joyent, Inc. All rights reserved. + */ + #include <sys/param.h> #include <sys/priv.h> #include <sys/vnode.h> @@ -84,7 +109,7 @@ secpolicy_zinject(kauth_cred_t cred) } int -secpolicy_fs_mount(kauth_cred_t cred, vnode_t *mvp, struct mount *vfsp) +secpolicy_fs_mount(kauth_cred_t cred, struct vnode *mvp, struct mount *vfsp) { return kauth_authorize_system(cred, KAUTH_SYSTEM_MOUNT, @@ -113,7 +138,7 @@ int secpolicy_vnode_stky_modify(kauth_cred_t cred) { - return (EPERM); + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int @@ -125,80 +150,69 @@ secpolicy_vnode_remove(kauth_cred_t cred int -secpolicy_vnode_owner(cred_t *cred, uid_t owner) +secpolicy_vnode_owner(kauth_cred_t cred, uid_t owner) { - uid_t uid; - uid = crgetuid(cred); - - if (owner == uid) - return (0); + if (owner == kauth_cred_getuid(cred)) + return (0); - return 0; -// return kauth_authorize_system(cred, KAUTH_SYSTEM_MOUNT, -// KAUTH_REQ_SYSTEM_MOUNT_NEW, vfsp, NULL, NULL); + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int -secpolicy_vnode_access(kauth_cred_t cred, struct vnode *vp, uint64_t owner, +secpolicy_vnode_access(kauth_cred_t cred, struct vnode *vp, uid_t owner, int mode) { - (void)owner; /* XXX ignore? */ - KASSERT(VOP_ISLOCKED(vp)); - return VOP_ACCESS(vp, mode, cred); + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } -/* - * Check privileges for setting xvattr attributes - */ int -secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype) +secpolicy_xvattr(xvattr_t *xvap, uid_t owner, kauth_cred_t cred, vtype_t vtype) { -/* return kauth_authorize_system(cred, KAUTH_SYSTEM_MOUNT, - KAUTH_REQ_SYSTEM_MOUNT_UPDATE, vfsp, NULL, NULL);*/ - return 0; + + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int secpolicy_vnode_setid_retain(kauth_cred_t cred, boolean_t issuidroot __unused) { - return (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)); + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int secpolicy_vnode_setids_setgids(kauth_cred_t cred, gid_t gid) { - if (!groupmember(gid, cred)) - return (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, - NULL)); - return (0); + if (groupmember(gid, cred)) + return (0); + + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int -secpolicy_vnode_chown(struct kauth_cred *cred, boolean_t check_self) +secpolicy_vnode_chown(kauth_cred_t cred, boolean_t check_self) { - return (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, - NULL)); - /* return (priv_check_cred(cred, PRIV_VFS_CHOWN, 0)); */ + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int -secpolicy_vnode_create_gid(struct kauth_cred *cred) +secpolicy_vnode_create_gid(kauth_cred_t cred) { - return (EPERM); + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int -secpolicy_vnode_setdac(struct kauth_cred *cred, uid_t owner) +secpolicy_vnode_setdac(kauth_cred_t cred, uid_t owner) { - return 0; - /*return (priv_check_cred(cred, PRIV_VFS_ADMIN, 0));*/ + if (owner == kauth_cred_getuid(cred)) + return (0); + + return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL); } int @@ -211,7 +225,7 @@ secpolicy_setid_setsticky_clear(struct v * is not a member of. Both of these are allowed in jail(8). */ if (vp->v_type != VDIR && (vap->va_mode & S_ISTXT)) { - if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) + if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)) return (EFTYPE); } /* @@ -220,17 +234,131 @@ secpolicy_setid_setsticky_clear(struct v */ if ((vap->va_mode & S_ISGID) != 0) return (secpolicy_vnode_setids_setgids(cred, ovap->va_gid)); - + return (0); } +/* + * XXX Copied from illumos. Should not be here; should be under + * external/cddl/osnet/dist. Not sure why it is even in illumos's + * policy.c rather than somewhere in vnode.c or something. + */ int secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap, const struct vattr *ovap, int flags, - int unlocked_access(void *, int, kauth_cred_t ), void *node) + int unlocked_access(void *, int, kauth_cred_t), void *node) { + int mask = vap->va_mask; + int error = 0; + boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; - return 0; + if (mask & AT_SIZE) { + if (vp->v_type == VDIR) { + error = EISDIR; + goto out; + } + + /* + * If ATTR_NOACLCHECK is set in the flags, then we don't + * perform the secondary unlocked_access() call since the + * ACL (if any) is being checked there. + */ + if (skipaclchk == B_FALSE) { + error = unlocked_access(node, VWRITE, cred); + if (error) + goto out; + } + } + if (mask & AT_MODE) { + /* + * If not the owner of the file then check privilege + * for two things: the privilege to set the mode at all + * and, if we're setting setuid, we also need permissions + * to add the set-uid bit, if we're not the owner. + * In the specific case of creating a set-uid root + * file, we need even more permissions. + */ + if ((error = secpolicy_vnode_setdac(cred, ovap->va_uid)) != 0) + goto out; + + if ((error = secpolicy_setid_setsticky_clear(vp, vap, + ovap, cred)) != 0) + goto out; + } else + vap->va_mode = ovap->va_mode; + + if (mask & (AT_UID|AT_GID)) { + boolean_t checkpriv = B_FALSE; + + /* + * Chowning files. + * + * If you are the file owner: + * chown to other uid FILE_CHOWN_SELF + * chown to gid (non-member) FILE_CHOWN_SELF + * chown to gid (member) <none> + * + * Instead of PRIV_FILE_CHOWN_SELF, FILE_CHOWN is also + * acceptable but the first one is reported when debugging. + * + * If you are not the file owner: + * chown from root PRIV_FILE_CHOWN + zone + * chown from other to any PRIV_FILE_CHOWN + * + */ + if (kauth_cred_getuid(cred) != ovap->va_uid) { + checkpriv = B_TRUE; + } else { + if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) || + ((mask & AT_GID) && vap->va_gid != ovap->va_gid && + !groupmember(vap->va_gid, cred))) { + checkpriv = B_TRUE; + } + } + /* + * If necessary, check privilege to see if update can be done. + */ + if (checkpriv && + (error = secpolicy_vnode_chown(cred, ovap->va_uid)) != 0) { + goto out; + } + + /* + * If the file has either the set UID or set GID bits + * set and the caller can set the bits, then leave them. + */ + secpolicy_setid_clear(vap, cred); + } + if (mask & (AT_ATIME|AT_MTIME)) { + /* + * If not the file owner and not otherwise privileged, + * always return an error when setting the + * time other than the current (ATTR_UTIME flag set). + * If setting the current time (ATTR_UTIME not set) then + * unlocked_access will check permissions according to policy. + */ + if (kauth_cred_getuid(cred) != ovap->va_uid) { + if (flags & ATTR_UTIME) + error = secpolicy_vnode_utime_modify(cred); + else if (skipaclchk == B_FALSE) { + error = unlocked_access(node, VWRITE, cred); + if (error == EACCES && + secpolicy_vnode_utime_modify(cred) == 0) + error = 0; + } + if (error) + goto out; + } + } + + /* + * Check for optional attributes here by checking the following: + */ + if (mask & AT_XVATTR) + error = secpolicy_xvattr((xvattr_t *)vap, ovap->va_uid, cred, + vp->v_type); +out: + return (error); } void @@ -240,10 +368,10 @@ secpolicy_setid_clear(struct vattr *vap, return; if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) { - vap->va_mask |= AT_MODE; - vap->va_mode &= ~(S_ISUID|S_ISGID); + vap->va_mask |= AT_MODE; + vap->va_mode &= ~(S_ISUID|S_ISGID); } - + return; } @@ -260,7 +388,7 @@ secpolicy_vnode_setdac(kauth_cred_t cred int secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap, const struct vattr *ovap, int flags, - int unlocked_access(void *, int, kauth_cred_t ), void *node) + int unlocked_access(void *, int, kauth_cred_t), void *node) { int mask = vap->va_mask; int error; @@ -351,7 +479,7 @@ secpolicy_setid_clear(struct vattr *vap, if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)) return; - + if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) { if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) { vap->va_mask |= AT_MODE; Index: src/external/cddl/osnet/sys/sys/policy.h diff -u src/external/cddl/osnet/sys/sys/policy.h:1.5 src/external/cddl/osnet/sys/sys/policy.h:1.6 --- src/external/cddl/osnet/sys/sys/policy.h:1.5 Mon Mar 1 11:19:40 2010 +++ src/external/cddl/osnet/sys/sys/policy.h Thu Oct 18 14:22:58 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: policy.h,v 1.5 2010/03/01 11:19:40 darran Exp $ */ +/* $NetBSD: policy.h,v 1.6 2012/10/18 14:22:58 riastradh Exp $ */ /*- * Copyright (c) 2007 Pawel Jakub Dawidek <p...@freebsd.org> @@ -38,34 +38,35 @@ #include <sys/vnode.h> struct mount; -struct ucred; struct vattr; struct vnode; -int secpolicy_zfs(struct kauth_cred *cred); -int secpolicy_sys_config(struct kauth_cred *cred, int checkonly); -int secpolicy_zinject(struct kauth_cred *cred); -int secpolicy_fs_mount(struct kauth_cred *cred, struct vnode *mvp, struct mount *vfsp); -int secpolicy_fs_unmount(struct kauth_cred *cred, struct mount *vfsp); -int secpolicy_basic_link(struct kauth_cred *cred); -int secpolicy_vnode_stky_modify(struct kauth_cred *cred); -int secpolicy_vnode_owner(cred_t *cred, uid_t owner); -int secpolicy_vnode_remove(struct kauth_cred *cred); -int secpolicy_vnode_access(struct kauth_cred *cred, struct vnode *vp, - uint64_t owner, int mode); -int secpolicy_vnode_chown(struct kauth_cred *cred, +int secpolicy_zfs(kauth_cred_t cred); +int secpolicy_sys_config(kauth_cred_t cred, int checkonly); +int secpolicy_zinject(kauth_cred_t cred); +int secpolicy_fs_mount(kauth_cred_t cred, struct vnode *mvp, + struct mount *vfsp); +int secpolicy_fs_unmount(kauth_cred_t cred, struct mount *vfsp); +int secpolicy_basic_link(kauth_cred_t cred); +int secpolicy_vnode_stky_modify(kauth_cred_t cred); +int secpolicy_vnode_owner(kauth_cred_t cred, uid_t owner); +int secpolicy_vnode_remove(kauth_cred_t cred); +int secpolicy_vnode_access(kauth_cred_t cred, struct vnode *vp, + uid_t owner, int mode); +int secpolicy_vnode_chown(kauth_cred_t cred, boolean_t check_self); -int secpolicy_vnode_setdac(struct kauth_cred *cred, uid_t owner); -int secpolicy_vnode_setattr(struct kauth_cred *cred, struct vnode *vp, +int secpolicy_vnode_setdac(kauth_cred_t cred, uid_t owner); +int secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap, const struct vattr *ovap, int flags, - int unlocked_access(void *, int, struct kauth_cred *), void *node); -int secpolicy_vnode_create_gid(struct kauth_cred *cred); -int secpolicy_vnode_setids_setgids(struct kauth_cred *cred, gid_t gid); -int secpolicy_vnode_setid_retain(struct kauth_cred *cred, boolean_t issuidroot); -void secpolicy_setid_clear(struct vattr *vap, struct kauth_cred *cred); + int unlocked_access(void *, int, kauth_cred_t), void *node); +int secpolicy_vnode_create_gid(kauth_cred_t cred); +int secpolicy_vnode_setids_setgids(kauth_cred_t cred, gid_t gid); +int secpolicy_vnode_setid_retain(kauth_cred_t cred, boolean_t issuidroot); +void secpolicy_setid_clear(struct vattr *vap, kauth_cred_t cred); int secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap, - const struct vattr *ovap, struct kauth_cred *cred); -int secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype); + const struct vattr *ovap, kauth_cred_t cred); +int secpolicy_xvattr(xvattr_t *xvap, uid_t owner, kauth_cred_t cred, + vtype_t vtype); #endif /* _KERNEL */