From 44533b172284854c40a117608129a6643f735cc2 Mon Sep 17 00:00:00 2001 From: Robert Watson Date: Sun, 15 Jun 2003 06:36:19 +0000 Subject: [PATCH] Re-implement kernel access control for quotactl() as found in the UFS quota implementation. Push some quite broken access control logic out of ufs_quotactl() into the individual command implementations in ufs_quota.c; fix that logic. Pass in the thread argument to any quotactl command that will need to perform access control. o quotaon() requires privilege (PRISON_ROOT). o quotaoff() requires privilege (PRISON_ROOT). o getquota() requires that: If the type is USRQUOTA, either the effective uid match the requested quota ID, that the unprivileged_get_quota flag be set, or that the thread be privileged (PRISON_ROOT). If the type is GRPQUOTA, require that either the thread be a member of the group represented by the requested quota ID, that the unprivileged_get_quota flag be set, or that the thread be privileged (PRISON_ROOT). o setquota() requires privilege (PRISON_ROOT). o setuse() requires privilege (PRISON_ROOT). o qsync() requires no special privilege (consistent with what was present before, but probably not very useful). Add a new sysctl, security.bsd.unprivileged_get_quota, which when set to a non-zero value, will permit unprivileged users to query user quotas with non-matching uids and gids. Set this to 0 by default to be mostly consistent with the previous behavior (the same for USRQUOTA, but not for GRPQUOTA). Obtained from: TrustedBSD Project Sponsored by: DARPA, Network Associates Laboratories --- sys/ufs/ufs/quota.h | 6 ++--- sys/ufs/ufs/ufs_quota.c | 54 +++++++++++++++++++++++++++++++++++++--- sys/ufs/ufs/ufs_vfsops.c | 21 +++------------- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/sys/ufs/ufs/quota.h b/sys/ufs/ufs/quota.h index 60828a3afd1e..bcde29faf84d 100644 --- a/sys/ufs/ufs/quota.h +++ b/sys/ufs/ufs/quota.h @@ -183,12 +183,12 @@ void dqinit(void); void dqrele(struct vnode *, struct dquot *); void dquninit(void); int getinoquota(struct inode *); -int getquota(struct mount *, u_long, int, caddr_t); +int getquota(struct thread *, struct mount *, u_long, int, caddr_t); int qsync(struct mount *mp); int quotaoff(struct thread *td, struct mount *, int); int quotaon(struct thread *td, struct mount *, int, caddr_t); -int setquota(struct mount *, u_long, int, caddr_t); -int setuse(struct mount *, u_long, int, caddr_t); +int setquota(struct thread *, struct mount *, u_long, int, caddr_t); +int setuse(struct thread *, struct mount *, u_long, int, caddr_t); vfs_quotactl_t ufs_quotactl; #else /* !_KERNEL */ diff --git a/sys/ufs/ufs/ufs_quota.c b/sys/ufs/ufs/ufs_quota.c index 9bb434484f72..21c035513322 100644 --- a/sys/ufs/ufs/ufs_quota.c +++ b/sys/ufs/ufs/ufs_quota.c @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -58,6 +59,13 @@ __FBSDID("$FreeBSD$"); #include #include +SYSCTL_DECL(_security_bsd); + +static int unprivileged_get_quota = 0; +SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_get_quota, CTLFLAG_RW, + &unprivileged_get_quota, 0, + "Unprivileged processes may retrieve quotas for other uids and gids"); + static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries"); /* @@ -404,6 +412,10 @@ quotaon(td, mp, type, fname) int error, flags; struct nameidata nd; + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + vpp = &ump->um_quotas[type]; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td); flags = FREAD | FWRITE; @@ -491,6 +503,10 @@ quotaoff(td, mp, type) struct inode *ip; int error; + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + if ((qvp = ump->um_quotas[type]) == NULLVP) return (0); ump->um_qflags[type] |= QTF_CLOSING; @@ -546,7 +562,8 @@ quotaoff(td, mp, type) * Q_GETQUOTA - return current values in a dqblk structure. */ int -getquota(mp, id, type, addr) +getquota(td, mp, id, type, addr) + struct thread *td; struct mount *mp; u_long id; int type; @@ -555,6 +572,27 @@ getquota(mp, id, type, addr) struct dquot *dq; int error; + switch (type) { + case USRQUOTA: + if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) { + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + } + break; + + case GRPQUOTA: + if (!groupmember(id, td->td_ucred) && !unprivileged_get_quota) { + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + } + break; + + default: + return (EINVAL); + } + error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq); if (error) return (error); @@ -567,7 +605,8 @@ getquota(mp, id, type, addr) * Q_SETQUOTA - assign an entire dqblk structure. */ int -setquota(mp, id, type, addr) +setquota(td, mp, id, type, addr) + struct thread *td; struct mount *mp; u_long id; int type; @@ -579,6 +618,10 @@ setquota(mp, id, type, addr) struct dqblk newlim; int error; + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)); if (error) return (error); @@ -628,7 +671,8 @@ setquota(mp, id, type, addr) * Q_SETUSE - set current inode and block usage. */ int -setuse(mp, id, type, addr) +setuse(td, mp, id, type, addr) + struct thread *td; struct mount *mp; u_long id; int type; @@ -640,6 +684,10 @@ setuse(mp, id, type, addr) struct dqblk usage; int error; + error = suser_cred(td->td_ucred, PRISON_ROOT); + if (error) + return (error); + error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)); if (error) return (error); diff --git a/sys/ufs/ufs/ufs_vfsops.c b/sys/ufs/ufs/ufs_vfsops.c index 320f058b6b93..289e08bfc528 100644 --- a/sys/ufs/ufs/ufs_vfsops.c +++ b/sys/ufs/ufs/ufs_vfsops.c @@ -117,27 +117,14 @@ ufs_quotactl(mp, cmds, uid, arg, td) if (uid == -1) uid = td->td_ucred->cr_ruid; cmd = cmds >> SUBCMDSHIFT; - - switch (cmd) { - case Q_SYNC: - break; - case Q_GETQUOTA: - if (uid == td->td_ucred->cr_ruid) - break; - /* FALLTHROUGH */ - default: - if ((error = suser_cred(td->td_ucred, PRISON_ROOT)) != 0) - return (error); - } - type = cmds & SUBCMDMASK; if ((u_int)type >= MAXQUOTAS) return (EINVAL); + if (vfs_busy(mp, LK_NOWAIT, 0, td)) return (0); switch (cmd) { - case Q_QUOTAON: error = quotaon(td, mp, type, arg); break; @@ -147,15 +134,15 @@ ufs_quotactl(mp, cmds, uid, arg, td) break; case Q_SETQUOTA: - error = setquota(mp, uid, type, arg); + error = setquota(td, mp, uid, type, arg); break; case Q_SETUSE: - error = setuse(mp, uid, type, arg); + error = setuse(td, mp, uid, type, arg); break; case Q_GETQUOTA: - error = getquota(mp, uid, type, arg); + error = getquota(td, mp, uid, type, arg); break; case Q_SYNC: