diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c index b4e06fb5d9ad..f34ebd44634e 100644 --- a/sys/fs/cd9660/cd9660_vnops.c +++ b/sys/fs/cd9660/cd9660_vnops.c @@ -155,7 +155,7 @@ cd9660_access(ap) } return (vaccess(vp->v_type, ip->inode.iso_mode, ip->inode.iso_uid, - ip->inode.iso_gid, ap->a_mode, ap->a_cred)); + ip->inode.iso_gid, ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 86444ec10e7b..0c81ded86a3a 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -125,7 +125,7 @@ devfs_access(ap) de = de->de_dir; return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid, - ap->a_mode, ap->a_cred)); + ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/fs/hpfs/hpfs_vnops.c b/sys/fs/hpfs/hpfs_vnops.c index 4f5a7354c665..dcfd2cf5bf8a 100644 --- a/sys/fs/hpfs/hpfs_vnops.c +++ b/sys/fs/hpfs/hpfs_vnops.c @@ -798,7 +798,7 @@ hpfs_access(ap) } return (vaccess(vp->v_type, hp->h_mode, hp->h_uid, hp->h_gid, - ap->a_mode, ap->a_cred)); + ap->a_mode, ap->a_cred, NULL)); } /* diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index 09f99e39d87f..f8426def67da 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -277,7 +277,7 @@ msdosfs_access(ap) } return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid, - ap->a_mode, ap->a_cred)); + ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/fs/ntfs/ntfs_vnops.c b/sys/fs/ntfs/ntfs_vnops.c index 06dab1c50c27..84b60ea824a9 100644 --- a/sys/fs/ntfs/ntfs_vnops.c +++ b/sys/fs/ntfs/ntfs_vnops.c @@ -478,7 +478,7 @@ ntfs_access(ap) } return (vaccess(vp->v_type, ip->i_mp->ntm_mode, ip->i_mp->ntm_uid, - ip->i_mp->ntm_gid, ap->a_mode, ap->a_cred)); + ip->i_mp->ntm_gid, ap->a_mode, ap->a_cred, NULL)); } /* diff --git a/sys/isofs/cd9660/cd9660_vnops.c b/sys/isofs/cd9660/cd9660_vnops.c index b4e06fb5d9ad..f34ebd44634e 100644 --- a/sys/isofs/cd9660/cd9660_vnops.c +++ b/sys/isofs/cd9660/cd9660_vnops.c @@ -155,7 +155,7 @@ cd9660_access(ap) } return (vaccess(vp->v_type, ip->inode.iso_mode, ip->inode.iso_uid, - ip->inode.iso_gid, ap->a_mode, ap->a_cred)); + ip->inode.iso_gid, ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/kern/vfs_export.c b/sys/kern/vfs_export.c index 02c84a887442..8b11cc3eca79 100644 --- a/sys/kern/vfs_export.c +++ b/sys/kern/vfs_export.c @@ -2986,54 +2986,106 @@ NDFREE(ndp, flags) } int -vaccess(type, file_mode, uid, gid, acc_mode, cred) +vaccess(type, file_mode, file_uid, file_gid, acc_mode, cred, privused) enum vtype type; mode_t file_mode; - uid_t uid; - gid_t gid; + uid_t file_uid; + gid_t file_gid; mode_t acc_mode; struct ucred *cred; + int *privused; { - mode_t mask; + mode_t dac_granted; +#ifdef CAPABILITIES + mode_t cap_granted; +#endif /* - * At this point, uid == 0 can do anything. - * XXX: should use suser() ? - * XXX: Should only check root-ness after other checks fail. + * Look for a normal, non-privileged way to access the file/directory + * as requested. If it exists, go with that. */ - if (cred->cr_uid == 0) - return (0); - mask = 0; + if (privused != NULL) + *privused = 0; - /* Otherwise, check the owner. */ - if (cred->cr_uid == uid) { - if (acc_mode & VEXEC) - mask |= S_IXUSR; - if (acc_mode & VREAD) - mask |= S_IRUSR; - if (acc_mode & VWRITE) - mask |= S_IWUSR; - return ((file_mode & mask) == mask ? 0 : EACCES); - } + dac_granted = 0; - /* Otherwise, check for all groups. */ - if (groupmember(gid, cred)) { - if (acc_mode & VEXEC) - mask |= S_IXGRP; - if (acc_mode & VREAD) - mask |= S_IRGRP; - if (acc_mode & VWRITE) - mask |= S_IWGRP; - return ((file_mode & mask) == mask ? 0 : EACCES); + /* Check the owner. */ + if (cred->cr_uid == file_uid) { + if (file_mode & S_IXUSR) + dac_granted |= VEXEC; + if (file_mode & S_IRUSR) + dac_granted |= VREAD; + if (file_mode & S_IWUSR) + dac_granted |= VWRITE; + + if ((acc_mode & dac_granted) == acc_mode) + return (0); + + goto privcheck; } - /* Otherwise, check everyone else. */ - if (acc_mode & VEXEC) - mask |= S_IXOTH; - if (acc_mode & VREAD) - mask |= S_IROTH; - if (acc_mode & VWRITE) - mask |= S_IWOTH; - return ((file_mode & mask) == mask ? 0 : EACCES); + /* Otherwise, check the groups (first match) */ + if (groupmember(file_gid, cred)) { + if (file_mode & S_IXGRP) + dac_granted |= VEXEC; + if (file_mode & S_IRGRP) + dac_granted |= VREAD; + if (file_mode & S_IWGRP) + dac_granted |= VWRITE; + + if ((acc_mode & dac_granted) == acc_mode) + return (0); + + goto privcheck; + } + + /* Otherwise, check everyone else. */ + if (file_mode & S_IXOTH) + dac_granted |= VEXEC; + if (file_mode & S_IROTH) + dac_granted |= VREAD; + if (file_mode & S_IWOTH) + dac_granted |= VWRITE; + if ((acc_mode & dac_granted) == acc_mode) + return (0); + +privcheck: + if (!suser_xxx(cred, NULL, PRISON_ROOT)) { + /* XXX audit: privilege used */ + if (privused != NULL) + *privused = 1; + return (0); + } + +#ifdef CAPABILITIES + /* + * Build a capability mask to determine if the set of capabilities + * satisfies the requirements when combined with the granted mask + * from above. + * For each capability, if the capability is required, bitwise + * or the request type onto the cap_granted mask. + */ + cap_granted = 0; + if ((acc_mode & VEXEC) && ((dac_granted & VEXEC) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_EXECUTE, PRISON_ROOT)) + cap_granted |= VEXEC; + + if ((acc_mode & VREAD) && ((dac_granted & VREAD) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_READ_SEARCH, PRISON_ROOT)) + cap_granted |= VREAD; + + if ((acc_mode & VWRITE) && ((dac_granted & VWRITE) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_WRITE, PRISON_ROOT)) + cap_granted |= VWRITE; + + if ((acc_mode & (cap_granted | dac_granted)) == mode) { + /* XXX audit: privilege used */ + if (privused != NULL) + *privused = 1; + return (0); + } +#endif + + return (EACCES); } diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 02c84a887442..8b11cc3eca79 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -2986,54 +2986,106 @@ NDFREE(ndp, flags) } int -vaccess(type, file_mode, uid, gid, acc_mode, cred) +vaccess(type, file_mode, file_uid, file_gid, acc_mode, cred, privused) enum vtype type; mode_t file_mode; - uid_t uid; - gid_t gid; + uid_t file_uid; + gid_t file_gid; mode_t acc_mode; struct ucred *cred; + int *privused; { - mode_t mask; + mode_t dac_granted; +#ifdef CAPABILITIES + mode_t cap_granted; +#endif /* - * At this point, uid == 0 can do anything. - * XXX: should use suser() ? - * XXX: Should only check root-ness after other checks fail. + * Look for a normal, non-privileged way to access the file/directory + * as requested. If it exists, go with that. */ - if (cred->cr_uid == 0) - return (0); - mask = 0; + if (privused != NULL) + *privused = 0; - /* Otherwise, check the owner. */ - if (cred->cr_uid == uid) { - if (acc_mode & VEXEC) - mask |= S_IXUSR; - if (acc_mode & VREAD) - mask |= S_IRUSR; - if (acc_mode & VWRITE) - mask |= S_IWUSR; - return ((file_mode & mask) == mask ? 0 : EACCES); - } + dac_granted = 0; - /* Otherwise, check for all groups. */ - if (groupmember(gid, cred)) { - if (acc_mode & VEXEC) - mask |= S_IXGRP; - if (acc_mode & VREAD) - mask |= S_IRGRP; - if (acc_mode & VWRITE) - mask |= S_IWGRP; - return ((file_mode & mask) == mask ? 0 : EACCES); + /* Check the owner. */ + if (cred->cr_uid == file_uid) { + if (file_mode & S_IXUSR) + dac_granted |= VEXEC; + if (file_mode & S_IRUSR) + dac_granted |= VREAD; + if (file_mode & S_IWUSR) + dac_granted |= VWRITE; + + if ((acc_mode & dac_granted) == acc_mode) + return (0); + + goto privcheck; } - /* Otherwise, check everyone else. */ - if (acc_mode & VEXEC) - mask |= S_IXOTH; - if (acc_mode & VREAD) - mask |= S_IROTH; - if (acc_mode & VWRITE) - mask |= S_IWOTH; - return ((file_mode & mask) == mask ? 0 : EACCES); + /* Otherwise, check the groups (first match) */ + if (groupmember(file_gid, cred)) { + if (file_mode & S_IXGRP) + dac_granted |= VEXEC; + if (file_mode & S_IRGRP) + dac_granted |= VREAD; + if (file_mode & S_IWGRP) + dac_granted |= VWRITE; + + if ((acc_mode & dac_granted) == acc_mode) + return (0); + + goto privcheck; + } + + /* Otherwise, check everyone else. */ + if (file_mode & S_IXOTH) + dac_granted |= VEXEC; + if (file_mode & S_IROTH) + dac_granted |= VREAD; + if (file_mode & S_IWOTH) + dac_granted |= VWRITE; + if ((acc_mode & dac_granted) == acc_mode) + return (0); + +privcheck: + if (!suser_xxx(cred, NULL, PRISON_ROOT)) { + /* XXX audit: privilege used */ + if (privused != NULL) + *privused = 1; + return (0); + } + +#ifdef CAPABILITIES + /* + * Build a capability mask to determine if the set of capabilities + * satisfies the requirements when combined with the granted mask + * from above. + * For each capability, if the capability is required, bitwise + * or the request type onto the cap_granted mask. + */ + cap_granted = 0; + if ((acc_mode & VEXEC) && ((dac_granted & VEXEC) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_EXECUTE, PRISON_ROOT)) + cap_granted |= VEXEC; + + if ((acc_mode & VREAD) && ((dac_granted & VREAD) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_READ_SEARCH, PRISON_ROOT)) + cap_granted |= VREAD; + + if ((acc_mode & VWRITE) && ((dac_granted & VWRITE) == 0) && + !cap_check_xxx(cred, p, CAP_DAC_WRITE, PRISON_ROOT)) + cap_granted |= VWRITE; + + if ((acc_mode & (cap_granted | dac_granted)) == mode) { + /* XXX audit: privilege used */ + if (privused != NULL) + *privused = 1; + return (0); + } +#endif + + return (EACCES); } diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c index 283167abc5fa..72b8e7804d0a 100644 --- a/sys/miscfs/kernfs/kernfs_vnops.c +++ b/sys/miscfs/kernfs/kernfs_vnops.c @@ -313,7 +313,7 @@ kernfs_access(ap) if ((amode & VWRITE) && (fmode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) return (EPERM); - return (vaccess(vp->v_tag, fmode, 0, 0, ap->a_mode, ap->a_cred)); + return (vaccess(vp->v_tag, fmode, 0, 0, ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c index 09f99e39d87f..f8426def67da 100644 --- a/sys/msdosfs/msdosfs_vnops.c +++ b/sys/msdosfs/msdosfs_vnops.c @@ -277,7 +277,7 @@ msdosfs_access(ap) } return (vaccess(vp->v_type, file_mode, pmp->pm_uid, pmp->pm_gid, - ap->a_mode, ap->a_cred)); + ap->a_mode, ap->a_cred, NULL)); } static int diff --git a/sys/ntfs/ntfs_vnops.c b/sys/ntfs/ntfs_vnops.c index 06dab1c50c27..84b60ea824a9 100644 --- a/sys/ntfs/ntfs_vnops.c +++ b/sys/ntfs/ntfs_vnops.c @@ -478,7 +478,7 @@ ntfs_access(ap) } return (vaccess(vp->v_type, ip->i_mp->ntm_mode, ip->i_mp->ntm_uid, - ip->i_mp->ntm_gid, ap->a_mode, ap->a_cred)); + ip->i_mp->ntm_gid, ap->a_mode, ap->a_cred, NULL)); } /* diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index a6f234d80bc0..de8c03876a66 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -555,7 +555,7 @@ int spec_vnoperate __P((struct vop_generic_args *)); int speedup_syncer __P((void)); int textvp_fullpath __P((struct proc *p, char **retbuf, char **retfreebuf)); int vaccess __P((enum vtype type, mode_t file_mode, uid_t uid, gid_t gid, - mode_t acc_mode, struct ucred *cred)); + mode_t acc_mode, struct ucred *cred, int *privused)); void vattr_null __P((struct vattr *vap)); int vcount __P((struct vnode *vp)); void vdrop __P((struct vnode *)); diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 298c1339ce4a..c836f5babde4 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -332,7 +332,7 @@ ufs_access(ap) return (EPERM); return (vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, - ap->a_mode, ap->a_cred)); + ap->a_mode, ap->a_cred, NULL)); } /* ARGSUSED */