aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs
diff options
context:
space:
mode:
authorEdward Tomasz Napierala <trasz@FreeBSD.org>2009-12-21 19:39:10 +0000
committerEdward Tomasz Napierala <trasz@FreeBSD.org>2009-12-21 19:39:10 +0000
commit9340fc72e6a63269b796c4e70aafbe579439b246 (patch)
tree4b32c0e46acd0bb797fce83987089212198f0f51 /sys/ufs
parent18814e50694f2a6eb45204984f6d62f92fddba9a (diff)
downloadsrc-9340fc72e6a63269b796c4e70aafbe579439b246.tar.gz
src-9340fc72e6a63269b796c4e70aafbe579439b246.zip
Implement NFSv4 ACL support for UFS.
Reviewed by: rwatson
Notes
Notes: svn path=/head/; revision=200796
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c42
-rw-r--r--sys/ufs/ffs/fs.h32
-rw-r--r--sys/ufs/ufs/acl.h2
-rw-r--r--sys/ufs/ufs/ufs_acl.c222
-rw-r--r--sys/ufs/ufs/ufs_lookup.c127
-rw-r--r--sys/ufs/ufs/ufs_vnops.c188
6 files changed, 541 insertions, 72 deletions
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index d3d7c2c60730..6eb89642645a 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -128,7 +128,7 @@ static struct buf_ops ffs_ops = {
static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr",
"noclusterw", "noexec", "export", "force", "from", "multilabel",
"snapshot", "nosuid", "suiddir", "nosymfollow", "sync",
- "union", NULL };
+ "union", "nfsv4acls", NULL };
static int
ffs_mount(struct mount *mp)
@@ -177,6 +177,15 @@ ffs_mount(struct mount *mp)
vfs_deleteopt(mp->mnt_opt, "snapshot");
}
+ if (vfs_getopt(mp->mnt_optnew, "nfsv4acls", NULL, NULL) == 0) {
+ if (mntorflags & MNT_ACLS) {
+ printf("WARNING: \"acls\" and \"nfsv4acls\" "
+ "options are mutually exclusive\n");
+ return (EINVAL);
+ }
+ mntorflags |= MNT_NFS4ACLS;
+ }
+
MNT_ILOCK(mp);
mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags;
MNT_IUNLOCK(mp);
@@ -360,6 +369,13 @@ ffs_mount(struct mount *mp)
MNT_IUNLOCK(mp);
}
+ if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
+ /* XXX: Set too late ? */
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= MNT_NFS4ACLS;
+ MNT_IUNLOCK(mp);
+ }
+
/*
* If this is a snapshot request, take the snapshot.
*/
@@ -834,7 +850,13 @@ ffs_mountfs(devvp, mp, td)
if ((fs->fs_flags & FS_ACLS) != 0) {
#ifdef UFS_ACL
MNT_ILOCK(mp);
+
+ if (mp->mnt_flag & MNT_NFS4ACLS)
+ printf("WARNING: ACLs flag on fs conflicts with "
+ "\"nfsv4acls\" mount option; option ignored\n");
+ mp->mnt_flag &= ~MNT_NFS4ACLS;
mp->mnt_flag |= MNT_ACLS;
+
MNT_IUNLOCK(mp);
#else
printf(
@@ -842,6 +864,24 @@ ffs_mountfs(devvp, mp, td)
mp->mnt_stat.f_mntonname);
#endif
}
+ if ((fs->fs_flags & FS_NFS4ACLS) != 0) {
+#ifdef UFS_ACL
+ MNT_ILOCK(mp);
+
+ if (mp->mnt_flag & MNT_ACLS)
+ printf("WARNING: NFSv4 ACLs flag on fs conflicts with "
+ "\"acls\" mount option; option ignored\n");
+ mp->mnt_flag &= ~MNT_ACLS;
+ mp->mnt_flag |= MNT_NFS4ACLS;
+
+ MNT_IUNLOCK(mp);
+#else
+ printf(
+"WARNING: %s: NFSv4 ACLs flag on fs but no ACLs support\n",
+ mp->mnt_stat.f_mntonname);
+#endif
+ }
+
ump->um_mountp = mp;
ump->um_dev = dev;
ump->um_devvp = devvp;
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index 7da18ea6c096..93c12a458432 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -393,22 +393,24 @@ CTASSERT(sizeof(struct fs) == 1376);
* flag to indicate that the indicies need to be rebuilt (by fsck) before
* they can be used.
*
- * FS_ACLS indicates that ACLs are administratively enabled for the
- * file system, so they should be loaded from extended attributes,
+ * FS_ACLS indicates that POSIX.1e ACLs are administratively enabled
+ * for the file system, so they should be loaded from extended attributes,
* observed for access control purposes, and be administered by object
- * owners. FS_MULTILABEL indicates that the TrustedBSD MAC Framework
- * should attempt to back MAC labels into extended attributes on the
- * file system rather than maintain a single mount label for all
- * objects.
- */
-#define FS_UNCLEAN 0x01 /* filesystem not clean at mount */
-#define FS_DOSOFTDEP 0x02 /* filesystem using soft dependencies */
-#define FS_NEEDSFSCK 0x04 /* filesystem needs sync fsck before mount */
-#define FS_INDEXDIRS 0x08 /* kernel supports indexed directories */
-#define FS_ACLS 0x10 /* file system has ACLs enabled */
-#define FS_MULTILABEL 0x20 /* file system is MAC multi-label */
-#define FS_GJOURNAL 0x40 /* gjournaled file system */
-#define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */
+ * owners. FS_NFS4ACLS indicates that NFSv4 ACLs are administratively
+ * enabled. This flag is mutually exclusive with FS_ACLS. FS_MULTILABEL
+ * indicates that the TrustedBSD MAC Framework should attempt to back MAC
+ * labels into extended attributes on the file system rather than maintain
+ * a single mount label for all objects.
+ */
+#define FS_UNCLEAN 0x0001 /* filesystem not clean at mount */
+#define FS_DOSOFTDEP 0x0002 /* filesystem using soft dependencies */
+#define FS_NEEDSFSCK 0x0004 /* filesystem needs sync fsck before mount */
+#define FS_INDEXDIRS 0x0008 /* kernel supports indexed directories */
+#define FS_ACLS 0x0010 /* file system has POSIX.1e ACLs enabled */
+#define FS_MULTILABEL 0x0020 /* file system is MAC multi-label */
+#define FS_GJOURNAL 0x0040 /* gjournaled file system */
+#define FS_FLAGS_UPDATED 0x0080 /* flags have been moved to new location */
+#define FS_NFS4ACLS 0x0100 /* file system has NFSv4 ACLs enabled */
/*
* Macros to access bits in the fs_active array.
diff --git a/sys/ufs/ufs/acl.h b/sys/ufs/ufs/acl.h
index b53e3284df1e..235261e51644 100644
--- a/sys/ufs/ufs/acl.h
+++ b/sys/ufs/ufs/acl.h
@@ -37,6 +37,8 @@
#ifdef _KERNEL
+int ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
+int ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td);
void ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl);
void ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip);
diff --git a/sys/ufs/ufs/ufs_acl.c b/sys/ufs/ufs/ufs_acl.c
index 68e5015c2d4e..c244e871cac6 100644
--- a/sys/ufs/ufs/ufs_acl.c
+++ b/sys/ufs/ufs/ufs_acl.c
@@ -141,6 +141,81 @@ ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
}
/*
+ * Retrieve NFSv4 ACL, skipping access checks. Must be used in UFS code
+ * instead of VOP_GETACL() when we don't want to be restricted by the user
+ * not having ACL_READ_ACL permission, e.g. when calculating inherited ACL
+ * or in ufs_vnops.c:ufs_accessx().
+ */
+int
+ufs_getacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
+{
+ int error, len;
+ struct inode *ip = VTOI(vp);
+
+ len = sizeof(*aclp);
+ bzero(aclp, len);
+
+ error = vn_extattr_get(vp, IO_NODELOCKED,
+ NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
+ &len, (char *) aclp, td);
+ aclp->acl_maxcnt = ACL_MAX_ENTRIES;
+ if (error == ENOATTR) {
+ /*
+ * Legitimately no ACL set on object, purely
+ * emulate it through the inode.
+ */
+ acl_nfs4_sync_acl_from_mode(aclp, ip->i_mode, ip->i_uid);
+
+ return (0);
+ }
+
+ if (error)
+ return (error);
+
+ if (len != sizeof(*aclp)) {
+ /*
+ * A short (or long) read, meaning that for
+ * some reason the ACL is corrupted. Return
+ * EPERM since the object DAC protections
+ * are unsafe.
+ */
+ printf("ufs_getacl_nfs4(): Loaded invalid ACL ("
+ "%d bytes), inumber %d on %s\n", len,
+ ip->i_number, ip->i_fs->fs_fsmnt);
+
+ return (EPERM);
+ }
+
+ error = acl_nfs4_check(aclp, vp->v_type == VDIR);
+ if (error) {
+ printf("ufs_getacl_nfs4(): Loaded invalid ACL "
+ "(failed acl_nfs4_check), inumber %d on %s\n",
+ ip->i_number, ip->i_fs->fs_fsmnt);
+
+ return (EPERM);
+ }
+
+ return (0);
+}
+
+static int
+ufs_getacl_nfs4(struct vop_getacl_args *ap)
+{
+ int error;
+
+ if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
+ return (EINVAL);
+
+ error = VOP_ACCESSX(ap->a_vp, VREAD_ACL, ap->a_td->td_ucred, ap->a_td);
+ if (error)
+ return (error);
+
+ error = ufs_getacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
+
+ return (error);
+}
+
+/*
* Read POSIX.1e ACL from an EA. Return error if its not found
* or if any other error has occured.
*/
@@ -209,7 +284,7 @@ ufs_getacl_posix1e(struct vop_getacl_args *ap)
* ACLs, remove this check.
*/
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
- return (EOPNOTSUPP);
+ return (EINVAL);
old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
@@ -282,10 +357,118 @@ ufs_getacl(ap)
} */ *ap;
{
+ if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+ return (EOPNOTSUPP);
+
+ if (ap->a_type == ACL_TYPE_NFS4)
+ return (ufs_getacl_nfs4(ap));
+
return (ufs_getacl_posix1e(ap));
}
/*
+ * Set NFSv4 ACL without doing any access checking. This is required
+ * e.g. by the UFS code that implements ACL inheritance, or from
+ * ufs_vnops.c:ufs_chmod(), as some of the checks have to be skipped
+ * in that case, and others are redundant.
+ */
+int
+ufs_setacl_nfs4_internal(struct vnode *vp, struct acl *aclp, struct thread *td)
+{
+ int error;
+ mode_t mode;
+ struct inode *ip = VTOI(vp);
+
+ KASSERT(acl_nfs4_check(aclp, vp->v_type == VDIR) == 0,
+ ("invalid ACL passed to ufs_setacl_nfs4_internal"));
+
+ if (acl_nfs4_is_trivial(aclp, ip->i_uid)) {
+ error = vn_extattr_rm(vp, IO_NODELOCKED,
+ NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME, td);
+
+ /*
+ * An attempt to remove ACL from a file that didn't have
+ * any extended entries is not an error.
+ */
+ if (error == ENOATTR)
+ error = 0;
+
+ } else {
+ error = vn_extattr_set(vp, IO_NODELOCKED,
+ NFS4_ACL_EXTATTR_NAMESPACE, NFS4_ACL_EXTATTR_NAME,
+ sizeof(*aclp), (char *) aclp, td);
+ }
+
+ /*
+ * Map lack of attribute definition in UFS_EXTATTR into lack of
+ * support for ACLs on the filesystem.
+ */
+ if (error == ENOATTR)
+ return (EOPNOTSUPP);
+
+ if (error)
+ return (error);
+
+ mode = ip->i_mode;
+
+ acl_nfs4_sync_mode_from_acl(&mode, aclp);
+
+ ip->i_mode &= ACL_PRESERVE_MASK;
+ ip->i_mode |= mode;
+ DIP_SET(ip, i_mode, ip->i_mode);
+ ip->i_flag |= IN_CHANGE;
+
+ VN_KNOTE_UNLOCKED(vp, NOTE_ATTRIB);
+
+ return (0);
+}
+
+static int
+ufs_setacl_nfs4(struct vop_setacl_args *ap)
+{
+ int error;
+ struct inode *ip = VTOI(ap->a_vp);
+
+ if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
+ return (EINVAL);
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+
+ if (ap->a_aclp == NULL)
+ return (EINVAL);
+
+ error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, ap->a_cred,
+ ap->a_td);
+ if (error)
+ return (error);
+
+ /*
+ * Authorize the ACL operation.
+ */
+ if (ip->i_flags & (IMMUTABLE | APPEND))
+ return (EPERM);
+
+ /*
+ * Must hold VWRITE_ACL or have appropriate privilege.
+ */
+ if ((error = VOP_ACCESSX(ap->a_vp, VWRITE_ACL, ap->a_cred, ap->a_td)))
+ return (error);
+
+ /*
+ * With NFSv4 ACLs, chmod(2) may need to add additional entries.
+ * Make sure it has enough room for that - splitting every entry
+ * into two and appending "canonical six" entries at the end.
+ */
+ if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
+ return (ENOSPC);
+
+ error = ufs_setacl_nfs4_internal(ap->a_vp, ap->a_aclp, ap->a_td);
+
+ return (0);
+}
+
+/*
* Set the ACL on a file.
*
* As part of the ACL is stored in the inode, and the rest in an EA,
@@ -302,7 +485,7 @@ ufs_setacl_posix1e(struct vop_setacl_args *ap)
struct oldacl *old;
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
- return (EOPNOTSUPP);
+ return (EINVAL);
/*
* If this is a set operation rather than a delete operation,
@@ -422,16 +605,43 @@ ufs_setacl(ap)
struct thread *td;
} */ *ap;
{
+ if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+ return (EOPNOTSUPP);
+
+ if (ap->a_type == ACL_TYPE_NFS4)
+ return (ufs_setacl_nfs4(ap));
return (ufs_setacl_posix1e(ap));
}
static int
+ufs_aclcheck_nfs4(struct vop_aclcheck_args *ap)
+{
+ int is_directory = 0;
+
+ if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0)
+ return (EINVAL);
+
+ /*
+ * With NFSv4 ACLs, chmod(2) may need to add additional entries.
+ * Make sure it has enough room for that - splitting every entry
+ * into two and appending "canonical six" entries at the end.
+ */
+ if (ap->a_aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
+ return (ENOSPC);
+
+ if (ap->a_vp->v_type == VDIR)
+ is_directory = 1;
+
+ return (acl_nfs4_check(ap->a_aclp, is_directory));
+}
+
+static int
ufs_aclcheck_posix1e(struct vop_aclcheck_args *ap)
{
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
- return (EOPNOTSUPP);
+ return (EINVAL);
/*
* Verify we understand this type of ACL, and that it applies
@@ -471,6 +681,12 @@ ufs_aclcheck(ap)
} */ *ap;
{
+ if ((ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0)
+ return (EOPNOTSUPP);
+
+ if (ap->a_type == ACL_TYPE_NFS4)
+ return (ufs_aclcheck_nfs4(ap));
+
return (ufs_aclcheck_posix1e(ap));
}
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
index ea73b1ff8616..a19068ee0759 100644
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -80,6 +80,61 @@ SYSCTL_INT(_debug, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
static int ufs_lookup_(struct vnode *, struct vnode **, struct componentname *,
ino_t *);
+static int
+ufs_delete_denied(struct vnode *vdp, struct vnode *tdp, struct ucred *cred,
+ struct thread *td)
+{
+ int error;
+
+#ifdef UFS_ACL
+ /*
+ * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt
+ *
+ * 3.16.2.1. ACE4_DELETE vs. ACE4_DELETE_CHILD
+ */
+
+ /*
+ * XXX: Is this check required?
+ */
+ error = VOP_ACCESS(vdp, VEXEC, cred, td);
+ if (error)
+ return (error);
+
+ error = VOP_ACCESSX(tdp, VDELETE, cred, td);
+ if (error == 0)
+ return (0);
+
+ error = VOP_ACCESSX(vdp, VDELETE_CHILD, cred, td);
+ if (error == 0)
+ return (0);
+
+ error = VOP_ACCESSX(vdp, VEXPLICIT_DENY | VDELETE_CHILD, cred, td);
+ if (error)
+ return (error);
+
+#endif /* !UFS_ACL */
+
+ /*
+ * Standard Unix access control - delete access requires VWRITE.
+ */
+ error = VOP_ACCESS(vdp, VWRITE, cred, td);
+ if (error)
+ return (error);
+
+ /*
+ * If directory is "sticky", then user must own
+ * the directory, or the file in it, else she
+ * may not delete it (unless she's root). This
+ * implements append-only directories.
+ */
+ if ((VTOI(vdp)->i_mode & ISVTX) &&
+ VOP_ACCESS(vdp, VADMIN, cred, td) &&
+ VOP_ACCESS(tdp, VADMIN, cred, td))
+ return (EPERM);
+
+ return (0);
+}
+
/*
* Convert a component of a pathname into a pointer to a locked inode.
* This is a very central and rather complicated routine.
@@ -410,8 +465,13 @@ notfound:
/*
* Access for write is interpreted as allowing
* creation of files in the directory.
+ *
+ * XXX: Fix the comment above.
*/
- error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
+ if (flags & WILLBEDIR)
+ error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
+ else
+ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
if (error)
return (error);
/*
@@ -498,12 +558,17 @@ found:
if (nameiop == DELETE && (flags & ISLASTCN)) {
if (flags & LOCKPARENT)
ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
- /*
- * Write access to directory required to delete files.
- */
- error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
- if (error)
+ if ((error = VFS_VGET(vdp->v_mount, ino,
+ LK_EXCLUSIVE, &tdp)) != 0)
return (error);
+
+ error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread);
+ if (error) {
+ vput(tdp);
+ return (error);
+ }
+
+
/*
* Return pointer to current entry in dp->i_offset,
* and distance past previous entry (if there
@@ -523,23 +588,10 @@ found:
if (dp->i_number == ino) {
VREF(vdp);
*vpp = vdp;
- return (0);
- }
- if ((error = VFS_VGET(vdp->v_mount, ino,
- LK_EXCLUSIVE, &tdp)) != 0)
- return (error);
- /*
- * If directory is "sticky", then user must own
- * the directory, or the file in it, else she
- * may not delete it (unless she's root). This
- * implements append-only directories.
- */
- if ((dp->i_mode & ISVTX) &&
- VOP_ACCESS(vdp, VADMIN, cred, cnp->cn_thread) &&
- VOP_ACCESS(tdp, VADMIN, cred, cnp->cn_thread)) {
vput(tdp);
- return (EPERM);
+ return (0);
}
+
*vpp = tdp;
return (0);
}
@@ -551,7 +603,11 @@ found:
* regular file, or empty directory.
*/
if (nameiop == RENAME && (flags & ISLASTCN)) {
- if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)))
+ if (flags & WILLBEDIR)
+ error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
+ else
+ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
+ if (error)
return (error);
/*
* Careful about locking second inode.
@@ -563,6 +619,33 @@ found:
if ((error = VFS_VGET(vdp->v_mount, ino,
LK_EXCLUSIVE, &tdp)) != 0)
return (error);
+
+ error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread);
+ if (error) {
+ vput(tdp);
+ return (error);
+ }
+
+#ifdef SunOS_doesnt_do_that
+ /*
+ * The only purpose of this check is to return the correct
+ * error. Assume that we want to rename directory "a"
+ * to a file "b", and that we have no ACL_WRITE_DATA on
+ * a containing directory, but we _do_ have ACL_APPEND_DATA.
+ * In that case, the VOP_ACCESS check above will return 0,
+ * and the operation will fail with ENOTDIR instead
+ * of EACCESS.
+ */
+ if (tdp->v_type == VDIR)
+ error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred, cnp->cn_thread);
+ else
+ error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread);
+ if (error) {
+ vput(tdp);
+ return (error);
+ }
+#endif
+
*vpp = tdp;
cnp->cn_flags |= SAVENAME;
return (0);
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 08b77ae07290..2d8f276c9270 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -88,7 +88,7 @@ __FBSDID("$FreeBSD$");
#include <ufs/ffs/ffs_extern.h>
-static vop_access_t ufs_access;
+static vop_accessx_t ufs_accessx;
static int ufs_chmod(struct vnode *, int, struct ucred *, struct thread *);
static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct thread *);
static vop_close_t ufs_close;
@@ -298,8 +298,8 @@ ufs_close(ap)
}
static int
-ufs_access(ap)
- struct vop_access_args /* {
+ufs_accessx(ap)
+ struct vop_accessx_args /* {
struct vnode *a_vp;
accmode_t a_accmode;
struct ucred *a_cred;
@@ -315,6 +315,7 @@ ufs_access(ap)
#endif
#ifdef UFS_ACL
struct acl *acl;
+ acl_type_t type;
#endif
/*
@@ -322,7 +323,7 @@ ufs_access(ap)
* unless the file is a socket, fifo, or a block or
* character device resident on the filesystem.
*/
- if (accmode & VWRITE) {
+ if (accmode & VMODIFY_PERMS) {
switch (vp->v_type) {
case VDIR:
case VLNK:
@@ -367,41 +368,63 @@ relock:
}
}
- /* If immutable bit set, nobody gets to write it. */
- if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT)))
+ /*
+ * If immutable bit set, nobody gets to write it. "& ~VADMIN_PERMS"
+ * is here, because without it, * it would be impossible for the owner
+ * to remove the IMMUTABLE flag.
+ */
+ if ((accmode & (VMODIFY_PERMS & ~VADMIN_PERMS)) &&
+ (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT)))
return (EPERM);
#ifdef UFS_ACL
- if ((vp->v_mount->mnt_flag & MNT_ACLS) != 0) {
+ if ((vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) != 0) {
+ if (vp->v_mount->mnt_flag & MNT_NFS4ACLS)
+ type = ACL_TYPE_NFS4;
+ else
+ type = ACL_TYPE_ACCESS;
+
acl = acl_alloc(M_WAITOK);
- error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred,
- ap->a_td);
+ if (type == ACL_TYPE_NFS4)
+ error = ufs_getacl_nfs4_internal(vp, acl, ap->a_td);
+ else
+ error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td);
switch (error) {
- case EOPNOTSUPP:
- error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
- ip->i_gid, ap->a_accmode, ap->a_cred, NULL);
- break;
case 0:
- error = vaccess_acl_posix1e(vp->v_type, ip->i_uid,
- ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL);
+ if (type == ACL_TYPE_NFS4) {
+ error = vaccess_acl_nfs4(vp->v_type, ip->i_uid,
+ ip->i_gid, acl, accmode, ap->a_cred, NULL);
+ } else {
+ error = vfs_unixify_accmode(&accmode);
+ if (error == 0)
+ error = vaccess_acl_posix1e(vp->v_type, ip->i_uid,
+ ip->i_gid, acl, accmode, ap->a_cred, NULL);
+ }
break;
default:
- printf(
-"ufs_access(): Error retrieving ACL on object (%d).\n",
- error);
+ if (error != EOPNOTSUPP)
+ printf(
+"ufs_accessx(): Error retrieving ACL on object (%d).\n",
+ error);
/*
* XXX: Fall back until debugged. Should
* eventually possibly log an error, and return
* EPERM for safety.
*/
- error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
- ip->i_gid, ap->a_accmode, ap->a_cred, NULL);
+ error = vfs_unixify_accmode(&accmode);
+ if (error == 0)
+ error = vaccess(vp->v_type, ip->i_mode, ip->i_uid,
+ ip->i_gid, accmode, ap->a_cred, NULL);
}
acl_free(acl);
- } else
+
+ return (error);
+ }
#endif /* !UFS_ACL */
+ error = vfs_unixify_accmode(&accmode);
+ if (error == 0)
error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
- ap->a_accmode, ap->a_cred, NULL);
+ accmode, ap->a_cred, NULL);
return (error);
}
@@ -608,11 +631,20 @@ ufs_setattr(ap)
* check succeeds.
*/
if (vap->va_vaflags & VA_UTIMES_NULL) {
- error = VOP_ACCESS(vp, VADMIN, cred, td);
+ /*
+ * NFSv4.1, draft 21, 6.2.1.3.1, Discussion of Mask Attributes
+ *
+ * "A user having ACL_WRITE_DATA or ACL_WRITE_ATTRIBUTES
+ * will be allowed to set the times [..] to the current
+ * server time."
+ *
+ * XXX: Calling it four times seems a little excessive.
+ */
+ error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td);
if (error)
error = VOP_ACCESS(vp, VWRITE, cred, td);
} else
- error = VOP_ACCESS(vp, VADMIN, cred, td);
+ error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td);
if (error)
return (error);
if (vap->va_atime.tv_sec != VNOVAL)
@@ -652,6 +684,32 @@ ufs_setattr(ap)
return (error);
}
+#ifdef UFS_ACL
+static int
+ufs_update_nfs4_acl_after_mode_change(struct vnode *vp, int mode,
+ int file_owner_id, struct ucred *cred, struct thread *td)
+{
+ int error;
+ struct acl *aclp;
+
+ aclp = acl_alloc(M_WAITOK);
+ error = ufs_getacl_nfs4_internal(vp, aclp, td);
+ /*
+ * We don't have to handle EOPNOTSUPP here, as the filesystem claims
+ * it supports ACLs.
+ */
+ if (error)
+ goto out;
+
+ acl_nfs4_sync_acl_from_mode(aclp, mode, file_owner_id);
+ error = ufs_setacl_nfs4_internal(vp, aclp, td);
+
+out:
+ acl_free(aclp);
+ return (error);
+}
+#endif /* UFS_ACL */
+
/*
* Mark this file's access time for update for vfs_mark_atime(). This
* is called from execve() and mmap().
@@ -689,7 +747,7 @@ ufs_chmod(vp, mode, cred, td)
* To modify the permissions on a file, must possess VADMIN
* for that file.
*/
- if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ if ((error = VOP_ACCESSX(vp, VWRITE_ACL, cred, td)))
return (error);
/*
* Privileged processes may set the sticky bit on non-directories,
@@ -706,11 +764,25 @@ ufs_chmod(vp, mode, cred, td)
if (error)
return (error);
}
+
+ /*
+ * Deny setting setuid if we are not the file owner.
+ */
+ if ((mode & ISUID) && ip->i_uid != cred->cr_uid) {
+ error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
+ if (error)
+ return (error);
+ }
+
ip->i_mode &= ~ALLPERMS;
ip->i_mode |= (mode & ALLPERMS);
DIP_SET(ip, i_mode, ip->i_mode);
ip->i_flag |= IN_CHANGE;
- return (0);
+#ifdef UFS_ACL
+ if ((vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)
+ error = ufs_update_nfs4_acl_after_mode_change(vp, mode, ip->i_uid, cred, td);
+#endif
+ return (error);
}
/*
@@ -742,14 +814,14 @@ ufs_chown(vp, uid, gid, cred, td)
* To modify the ownership of a file, must possess VADMIN for that
* file.
*/
- if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td)))
return (error);
/*
* To change the owner of a file, or change the group of a file to a
* group of which we are not a member, the caller must have
* privilege.
*/
- if ((uid != ip->i_uid ||
+ if (((uid != ip->i_uid && uid != cred->cr_uid) ||
(gid != ip->i_gid && !groupmember(gid, cred))) &&
(error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
return (error);
@@ -1397,6 +1469,33 @@ out:
return (error);
}
+#ifdef UFS_ACL
+static int
+ufs_do_nfs4_acl_inheritance(struct vnode *dvp, struct vnode *tvp,
+ mode_t child_mode, struct ucred *cred, struct thread *td)
+{
+ int error;
+ struct acl *parent_aclp, *child_aclp;
+
+ parent_aclp = acl_alloc(M_WAITOK);
+ child_aclp = acl_alloc(M_WAITOK | M_ZERO);
+
+ error = ufs_getacl_nfs4_internal(dvp, parent_aclp, td);
+ if (error)
+ goto out;
+ acl_nfs4_compute_inherited_acl(parent_aclp, child_aclp,
+ child_mode, VTOI(tvp)->i_uid, tvp->v_type == VDIR);
+ error = ufs_setacl_nfs4_internal(tvp, child_aclp, td);
+ if (error)
+ goto out;
+out:
+ acl_free(parent_aclp);
+ acl_free(child_aclp);
+
+ return (error);
+}
+#endif
+
/*
* Mkdir system call
*/
@@ -1630,6 +1729,13 @@ ufs_mkdir(ap)
acl_free(dacl);
dacl = acl = NULL;
}
+
+ if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) {
+ error = ufs_do_nfs4_acl_inheritance(dvp, tvp, dmode,
+ cnp->cn_cred, cnp->cn_thread);
+ if (error)
+ goto bad;
+ }
#endif /* !UFS_ACL */
/*
@@ -2117,6 +2223,7 @@ ufsfifo_pathconf(ap)
switch (ap->a_name) {
case _PC_ACL_EXTENDED:
+ case _PC_ACL_NFS4:
case _PC_ACL_PATH_MAX:
case _PC_MAC_PRESENT:
return (ufs_pathconf(ap));
@@ -2169,9 +2276,21 @@ ufs_pathconf(ap)
*ap->a_retval = 0;
#endif
break;
+
+ case _PC_ACL_NFS4:
+#ifdef UFS_ACL
+ if (ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS)
+ *ap->a_retval = 1;
+ else
+ *ap->a_retval = 0;
+#else
+ *ap->a_retval = 0;
+#endif
+ break;
+
case _PC_ACL_PATH_MAX:
#ifdef UFS_ACL
- if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS)
+ if (ap->a_vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS))
*ap->a_retval = ACL_MAX_ENTRIES;
else
*ap->a_retval = 3;
@@ -2466,6 +2585,13 @@ ufs_makeinode(mode, dvp, vpp, cnp)
}
acl_free(acl);
}
+
+ if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) {
+ error = ufs_do_nfs4_acl_inheritance(dvp, tvp, mode,
+ cnp->cn_cred, cnp->cn_thread);
+ if (error)
+ goto bad;
+ }
#endif /* !UFS_ACL */
ufs_makedirentry(ip, cnp, &newdir);
error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL);
@@ -2496,7 +2622,7 @@ struct vop_vector ufs_vnodeops = {
.vop_read = VOP_PANIC,
.vop_reallocblks = VOP_PANIC,
.vop_write = VOP_PANIC,
- .vop_access = ufs_access,
+ .vop_accessx = ufs_accessx,
.vop_bmap = ufs_bmap,
.vop_cachedlookup = ufs_lookup,
.vop_close = ufs_close,
@@ -2540,7 +2666,7 @@ struct vop_vector ufs_vnodeops = {
struct vop_vector ufs_fifoops = {
.vop_default = &fifo_specops,
.vop_fsync = VOP_PANIC,
- .vop_access = ufs_access,
+ .vop_accessx = ufs_accessx,
.vop_close = ufsfifo_close,
.vop_getattr = ufs_getattr,
.vop_inactive = ufs_inactive,