Implement NFSv4 ACL support for UFS.

Reviewed by:	rwatson
This commit is contained in:
trasz 2009-12-21 19:39:10 +00:00
parent fdf85ee3e5
commit f04a989f2d
13 changed files with 619 additions and 91 deletions

View File

@ -251,8 +251,11 @@ dumpfs(const char *name)
printf("gjournal ");
if (fsflags & FS_FLAGS_UPDATED)
printf("fs_flags expanded ");
if (fsflags & FS_NFS4ACLS)
printf("nfsv4acls ");
fsflags &= ~(FS_UNCLEAN | FS_DOSOFTDEP | FS_NEEDSFSCK | FS_INDEXDIRS |
FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED);
FS_ACLS | FS_MULTILABEL | FS_GJOURNAL | FS_FLAGS_UPDATED |
FS_NFS4ACLS);
if (fsflags != 0)
printf("unknown flags (%#x)", fsflags);
putchar('\n');

View File

@ -54,6 +54,7 @@ struct mntopt {
#define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 }
#define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 }
#define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 }
#define MOPT_NFS4ACLS { "nfsv4acls", 0, MNT_NFS4ACLS, 0 }
/* Control flags. */
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
@ -87,7 +88,8 @@ struct mntopt {
MOPT_NOCLUSTERR, \
MOPT_NOCLUSTERW, \
MOPT_MULTILABEL, \
MOPT_ACLS
MOPT_ACLS, \
MOPT_NFS4ACLS
void getmntopts(const char *, const struct mntopt *, int *, int *);
void rmslashes(char *, char *);

View File

@ -120,11 +120,14 @@ takes effect.
The following options are available:
.Bl -tag -width indent
.It Cm acls
Enable Access Control Lists, or ACLS, which can be customized via the
Enable POSIX.1e Access Control Lists, or ACLs, which can be customized via the
.Xr setfacl 1
and
.Xr getfacl 1
commands.
This flag is mutually exclusive with
.Cm nfsv4acls
flag.
.It Cm async
All
.Tn I/O
@ -186,6 +189,15 @@ See
.Xr mac 4
for more information, which cause the multilabel mount flag to be set
automatically at mount-time.
.It Cm nfsv4acls
Enable NFSv4 ACLs, which can be customized via the
.Xr setfacl 1
and
.Xr getfacl 1
commands.
This flag is mutually exclusive with
.Cm acls
flag.
.It Cm noasync
Metadata I/O should be done synchronously, while data I/O should be done
asynchronously.

View File

@ -111,6 +111,7 @@ static struct opt {
{ MNT_SOFTDEP, "soft-updates" },
{ MNT_MULTILABEL, "multilabel" },
{ MNT_ACLS, "acls" },
{ MNT_NFS4ACLS, "nfsv4acls" },
{ MNT_GJOURNAL, "gjournal" },
{ 0, NULL }
};
@ -918,6 +919,7 @@ flags2opts(int flags)
if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir");
if (flags & MNT_MULTILABEL) res = catopt(res, "multilabel");
if (flags & MNT_ACLS) res = catopt(res, "acls");
if (flags & MNT_NFS4ACLS) res = catopt(res, "nfsv4acls");
return (res);
}

View File

@ -44,6 +44,7 @@
.Op Fl L Ar volname
.Op Fl l Cm enable | disable
.Op Fl m Ar minfree
.Op Fl N Cm enable | disable
.Op Fl n Cm enable | disable
.Op Fl o Cm space | time
.Op Fl p
@ -70,7 +71,7 @@ this option will cause all backups to be modified as well as the
primary super-block.
This is potentially dangerous - use with caution.
.It Fl a Cm enable | disable
Turn on/off the administrative ACL enable flag.
Turn on/off the administrative POSIX.1e ACL enable flag.
.It Fl e Ar maxbpg
Indicate the maximum number of blocks any single file can
allocate out of a cylinder group before it is forced to begin
@ -114,6 +115,8 @@ factor of three over the performance obtained at a 10% threshold.
If the value is raised above the current usage level,
users will be unable to allocate files until enough files have
been deleted to get under the higher threshold.
.It Fl N Cm enable | disable
Turn on/off the administrative NFSv4 ACL enable flag.
.It Fl n Cm enable | disable
Turn on/off soft updates.
.It Fl o Cm space | time

View File

@ -76,12 +76,12 @@ void printfs(void);
int
main(int argc, char *argv[])
{
char *avalue, *Jvalue, *Lvalue, *lvalue, *nvalue;
char *avalue, *Jvalue, *Lvalue, *lvalue, *Nvalue, *nvalue;
const char *special, *on;
const char *name;
int active;
int Aflag, aflag, eflag, evalue, fflag, fvalue, Jflag, Lflag, lflag;
int mflag, mvalue, nflag, oflag, ovalue, pflag, sflag, svalue;
int mflag, mvalue, Nflag, nflag, oflag, ovalue, pflag, sflag, svalue;
int ch, found_arg, i;
const char *chg[2];
struct ufs_args args;
@ -90,12 +90,12 @@ main(int argc, char *argv[])
if (argc < 3)
usage();
Aflag = aflag = eflag = fflag = Jflag = Lflag = lflag = mflag = 0;
nflag = oflag = pflag = sflag = 0;
avalue = Jvalue = Lvalue = lvalue = nvalue = NULL;
Nflag = nflag = oflag = pflag = sflag = 0;
avalue = Jvalue = Lvalue = lvalue = Nvalue = nvalue = NULL;
evalue = fvalue = mvalue = ovalue = svalue = 0;
active = 0;
found_arg = 0; /* At least one arg is required. */
while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:n:o:ps:")) != -1)
while ((ch = getopt(argc, argv, "Aa:e:f:J:L:l:m:N:n:o:ps:")) != -1)
switch (ch) {
case 'A':
@ -105,7 +105,7 @@ main(int argc, char *argv[])
case 'a':
found_arg = 1;
name = "ACLs";
name = "POSIX.1e ACLs";
avalue = optarg;
if (strcmp(avalue, "enable") &&
strcmp(avalue, "disable")) {
@ -187,6 +187,18 @@ main(int argc, char *argv[])
mflag = 1;
break;
case 'N':
found_arg = 1;
name = "NFSv4 ACLs";
Nvalue = optarg;
if (strcmp(Nvalue, "enable") &&
strcmp(Nvalue, "disable")) {
errx(10, "bad %s (options are %s)",
name, "`enable' or `disable'");
}
Nflag = 1;
break;
case 'n':
found_arg = 1;
name = "soft updates";
@ -255,10 +267,13 @@ main(int argc, char *argv[])
strlcpy(sblock.fs_volname, Lvalue, MAXVOLLEN);
}
if (aflag) {
name = "ACLs";
name = "POSIX.1e ACLs";
if (strcmp(avalue, "enable") == 0) {
if (sblock.fs_flags & FS_ACLS) {
warnx("%s remains unchanged as enabled", name);
} else if (sblock.fs_flags & FS_NFS4ACLS) {
warnx("%s and NFSv4 ACLs are mutually "
"exclusive", name);
} else {
sblock.fs_flags |= FS_ACLS;
warnx("%s set", name);
@ -349,6 +364,29 @@ main(int argc, char *argv[])
warnx(OPTWARN, "space", "<", MINFREE);
}
}
if (Nflag) {
name = "NFSv4 ACLs";
if (strcmp(Nvalue, "enable") == 0) {
if (sblock.fs_flags & FS_NFS4ACLS) {
warnx("%s remains unchanged as enabled", name);
} else if (sblock.fs_flags & FS_ACLS) {
warnx("%s and POSIX.1e ACLs are mutually "
"exclusive", name);
} else {
sblock.fs_flags |= FS_NFS4ACLS;
warnx("%s set", name);
}
} else if (strcmp(Nvalue, "disable") == 0) {
if ((~sblock.fs_flags & FS_NFS4ACLS) ==
FS_NFS4ACLS) {
warnx("%s remains unchanged as disabled",
name);
} else {
sblock.fs_flags &= ~FS_NFS4ACLS;
warnx("%s cleared", name);
}
}
}
if (nflag) {
name = "soft updates";
if (strcmp(nvalue, "enable") == 0) {
@ -423,16 +461,18 @@ usage(void)
fprintf(stderr, "%s\n%s\n%s\n%s\n",
"usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
" [-J enable | disable ] [-L volname] [-l enable | disable]",
" [-m minfree] [-n enable | disable] [-o space | time] [-p]",
" [-s avgfpdir] special | filesystem");
" [-m minfree] [-N enable | disable] [-n enable | disable]",
" [-o space | time] [-p] [-s avgfpdir] special | filesystem");
exit(2);
}
void
printfs(void)
{
warnx("ACLs: (-a) %s",
warnx("POSIX.1e ACLs: (-a) %s",
(sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
warnx("NFSv4 ACLs: (-N) %s",
(sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled");
warnx("MAC multilabel: (-l) %s",
(sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
warnx("soft updates: (-n) %s",

View File

@ -239,6 +239,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
#define MNT_NOATIME 0x10000000 /* disable update of file access time */
#define MNT_NOCLUSTERR 0x40000000 /* disable cluster read */
#define MNT_NOCLUSTERW 0x80000000 /* disable cluster write */
#define MNT_NFS4ACLS 0x00000010
/*
* NFS export related mount flags.
@ -274,7 +275,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \
MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \
MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | \
MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS)
MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | MNT_NFS4ACLS)
/* Mask of flags that can be updated. */
#define MNT_UPDATEMASK (MNT_NOSUID | MNT_NOEXEC | \
@ -282,7 +283,7 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
MNT_NOATIME | \
MNT_NOSYMFOLLOW | MNT_IGNORE | \
MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | \
MNT_ACLS | MNT_USER)
MNT_ACLS | MNT_USER | MNT_NFS4ACLS)
/*
* External filesystem command modifier flags.
@ -299,10 +300,6 @@ void __mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp);
#define MNT_BYFSID 0x08000000 /* specify filesystem by ID. */
#define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \
MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID)
/*
* Still available.
*/
#define MNT_SPARE_0x00000010 0x00000010
/*
* Internal filesystem control flags stored in mnt_kern_flag.
*

View File

@ -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;

View File

@ -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.
* 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 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 */
#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.

View File

@ -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);

View File

@ -140,6 +140,81 @@ ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
DIP_SET(ip, i_mode, ip->i_mode);
}
/*
* 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,9 +357,117 @@ 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.
*
@ -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));
}

View File

@ -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 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
/*
* 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 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
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 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
if (dp->i_number == ino) {
VREF(vdp);
*vpp = vdp;
vput(tdp);
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);
}
*vpp = tdp;
return (0);
}
@ -551,7 +603,11 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
* 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 @@ ufs_lookup_(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
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);

View File

@ -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 @@ ufs_access(ap)
}
}
/* 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 @@ ufs_rename(ap)
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,