tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Common chmod and chown routines
Hi,
I would like to remove a little bit of duplicated code across our
file-system implementations by introducing two new functions:
common_chmod() and common_chown().
As the names imply, they will contain the common code found in the
file-system specific chmod and chown implementations. Calling the
common code a "policy", this policy is outlined in a comment above
each of the functions.
Notes:
- The changes affect ufs, ext2fs, ptyfs, tmpfs, and udf.
- The original chmod policy for ptyfs lacked common tests
wrt/sticky and sgit bits. What I suggest is, if the (or "a")
file-system ignores these bits, to remove them on entry to the
file-system specific implementation before proceeding.
Diff attached, please review. :)
Also: might be a naive question, but is there a reason for not storing a
"struct vattr" (or similar) in "struct vnode", so that whenever the
latter is available, we already have the owner, permissions, etc. in a
unified form? :)
Thanks,
-e.
Index: sys/kern/vfs_subr.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/vfs_subr.c,v
retrieving revision 1.370
diff -u -p -r1.370 vfs_subr.c
--- sys/kern/vfs_subr.c 30 Mar 2009 16:38:05 -0000 1.370
+++ sys/kern/vfs_subr.c 18 Apr 2009 04:25:12 -0000
@@ -3202,3 +3202,139 @@ vfs_mount_print(struct mount *mp, int fu
}
}
#endif /* DDB */
+
+/*
+ * Common routine to check if chmod() is allowed.
+ *
+ * Policy:
+ * - You must be root, or
+ * - You must own the file, and
+ * - You must not set the "sticky" bit (meaningless, see chmod(2))
+ * - You must be a member of the file's group if it's SGIDXXX: sticky/sgid
stuff
+ *
+ * cred - credentials of the invoker
+ * vp - vnode of the file-system object
+ * cur_uid, cur_gid - current uid/gid of the file-system object
+ * new_mode - new mode for the file-system object
+ *
+ * Returns 0 if the change is allowed, or an error value otherwise.
+ */
+int
+common_chmod(kauth_cred_t cred, struct vnode *vp, uid_t cur_uid, gid_t cur_gid,
+ mode_t new_mode)
+{
+ int error;
+
+ /* Superuser can always change mode. */
+ error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
+ if (!error) {
+ goto out;
+ }
+
+ /* Otherwise, user must own the file. */
+ if (kauth_cred_geteuid(cred) != cur_uid) {
+ goto out;
+ }
+
+ /*
+ * Non-root users can't set the sticky bit on files.
+ */
+ if ((vp->v_type != VDIR) && (new_mode & S_ISTXT)) {
+ error = EFTYPE;
+ goto out;
+ }
+
+ /*
+ * If the invoker is trying to set the SGID bit on the file, check
+ * group membership.
+ */
+ if (new_mode & S_ISGID) {
+ int ismember;
+
+ error = kauth_cred_ismember_gid(cred, cur_gid, &ismember);
+ if (error)
+ goto out;
+
+ if (!ismember) {
+ error = EPERM;
+ goto out;
+ }
+ }
+
+ error = 0;
+
+ out:
+ return (error);
+}
+
+/*
+ * Common routine to check if chown() is allowed.
+ *
+ * Policy:
+ * - You must be root, or
+ * - You must own the file, and
+ * - You must not try to change ownership, and
+ * - You must be member of the new group
+ *
+ * cred - credentials of the invoker
+ * cur_uid, cur_gid - current uid/gid of the file-system object
+ * new_uid, new_gid - target uid/gid of the file-system object
+ *
+ * Returns 0 if the change is allowed, or an error value otherwise.
+ */
+int
+common_chown(kauth_cred_t cred, uid_t cur_uid, gid_t cur_gid, uid_t new_uid,
+ gid_t new_gid)
+{
+ int error, ismember;
+
+ /*
+ * You can only change ownership of a file if:
+ * You are the superuser, or...
+ */
+ error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
+ if (!error) {
+ goto out;
+ }
+
+ /*
+ * You own the file and...
+ */
+ if (kauth_cred_geteuid(cred) == cur_uid) {
+ /*
+ * You don't try to change ownership, and...
+ */
+ if (new_uid != cur_uid) {
+ goto out;
+ }
+
+ /*
+ * You don't try to change group (no-op), or...
+ */
+ if (new_gid == cur_gid) {
+ error = 0;
+ goto out;
+ }
+
+ /*
+ * Your effective gid is the new gid, or...
+ */
+ if (kauth_cred_getegid(cred) == new_gid) {
+ error = 0;
+ goto out;
+ }
+
+ /*
+ * The new gid is one you're a member of.
+ */
+ ismember = 0;
+ if (kauth_cred_ismember_gid(cred, new_gid, &ismember) == 0 &&
ismember) {
+ error = 0;
+ goto out;
+ }
+ }
+
+ out:
+ return (error);
+}
+
Index: sys/fs/ptyfs/ptyfs_vnops.c
===================================================================
RCS file: /usr/cvs/src/sys/fs/ptyfs/ptyfs_vnops.c,v
retrieving revision 1.27
diff -u -p -r1.27 ptyfs_vnops.c
--- sys/fs/ptyfs/ptyfs_vnops.c 2 Jan 2008 11:48:43 -0000 1.27
+++ sys/fs/ptyfs/ptyfs_vnops.c 18 Apr 2009 04:45:32 -0000
@@ -464,10 +464,11 @@ ptyfs_chmod(struct vnode *vp, mode_t mod
struct ptyfsnode *ptyfs = VTOPTYFS(vp);
int error;
- if (kauth_cred_geteuid(cred) != ptyfs->ptyfs_uid &&
- (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)) != 0)
- return error;
+ error = common_chmod(cred, vp, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid,
+ mode);
+ if (error)
+ return (error);
+
ptyfs->ptyfs_mode &= ~ALLPERMS;
ptyfs->ptyfs_mode |= (mode & ALLPERMS);
return 0;
@@ -482,25 +483,16 @@ ptyfs_chown(struct vnode *vp, uid_t uid,
struct lwp *l)
{
struct ptyfsnode *ptyfs = VTOPTYFS(vp);
- int error, ismember = 0;
+ int error;
if (uid == (uid_t)VNOVAL)
uid = ptyfs->ptyfs_uid;
if (gid == (gid_t)VNOVAL)
gid = ptyfs->ptyfs_gid;
- /*
- * If we don't own the file, are trying to change the owner
- * of the file, or are not a member of the target group,
- * the caller's credentials must imply super-user privilege
- * or the call fails.
- */
- if ((kauth_cred_geteuid(cred) != ptyfs->ptyfs_uid || uid !=
ptyfs->ptyfs_uid ||
- (gid != ptyfs->ptyfs_gid &&
- !(kauth_cred_getegid(cred) == gid ||
- (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember))))
&&
- ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)) != 0))
- return error;
+
+ error = common_chown(cred, ptyfs->ptyfs_uid, ptyfs->ptyfs_gid, uid,
gid);
+ if (error)
+ return (error);
ptyfs->ptyfs_gid = gid;
ptyfs->ptyfs_uid = uid;
Index: sys/fs/udf/udf_vnops.c
===================================================================
RCS file: /usr/cvs/src/sys/fs/udf/udf_vnops.c,v
retrieving revision 1.38
diff -u -p -r1.38 udf_vnops.c
--- sys/fs/udf/udf_vnops.c 20 Mar 2009 23:06:52 -0000 1.38
+++ sys/fs/udf/udf_vnops.c 18 Apr 2009 04:28:52 -0000
@@ -935,9 +935,8 @@ udf_chown(struct vnode *vp, uid_t new_ui
kauth_cred_t cred)
{
struct udf_node *udf_node = VTOI(vp);
- uid_t euid, uid;
- gid_t egid, gid;
- int issuperuser, ismember;
+ uid_t uid;
+ gid_t gid;
int error;
#ifdef notyet
@@ -965,26 +964,10 @@ udf_chown(struct vnode *vp, uid_t new_ui
if ((gid_t) ((uint32_t) gid) != gid)
return EINVAL;
- /*
- * If we don't own the file, are trying to change the owner of the
- * file, or are not a member of the target group, the caller's
- * credentials must imply super-user privilege or the call fails.
- */
-
/* check permissions */
- euid = kauth_cred_geteuid(cred);
- egid = kauth_cred_getegid(cred);
- if ((error = kauth_cred_ismember_gid(cred, new_gid, &ismember)))
- return error;
- error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
- issuperuser = (error == 0);
-
- if (!issuperuser) {
- if ((new_uid != uid) || (euid != uid))
- return EPERM;
- if ((new_gid != gid) && !(egid == new_gid || ismember))
- return EPERM;
- }
+ error = common_chown(cred, uid, gid, new_uid, new_gid);
+ if (error)
+ return (error);
/* change the ownership */
udf_setownership(udf_node, new_uid, new_gid);
@@ -1000,9 +983,8 @@ static int
udf_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred)
{
struct udf_node *udf_node = VTOI(vp);
- uid_t euid, uid;
- gid_t egid, gid;
- int issuperuser, ismember;
+ uid_t uid;
+ gid_t gid;
int error;
#ifdef notyet
@@ -1019,22 +1001,9 @@ udf_chmod(struct vnode *vp, mode_t mode,
udf_getownership(udf_node, &uid, &gid);
/* check permissions */
- euid = kauth_cred_geteuid(cred);
- egid = kauth_cred_getegid(cred);
- if ((error = kauth_cred_ismember_gid(cred, gid, &ismember)))
- return error;
- error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
- issuperuser = (error == 0);
-
- if ((euid != uid) && !issuperuser)
- return EPERM;
- if (euid != 0) {
- if (vp->v_type != VDIR && (mode & S_ISTXT))
- return EFTYPE;
-
- if ((!ismember) && (mode & S_ISGID))
- return EPERM;
- }
+ error = common_chmod(cred, vp, uid, gid, mode);
+ if (error)
+ return (error);
/* change mode */
udf_setaccessmode(udf_node, mode);
Index: sys/fs/tmpfs/tmpfs_subr.c
===================================================================
RCS file: /usr/cvs/src/sys/fs/tmpfs/tmpfs_subr.c,v
retrieving revision 1.48
diff -u -p -r1.48 tmpfs_subr.c
--- sys/fs/tmpfs/tmpfs_subr.c 19 Jun 2008 19:03:44 -0000 1.48
+++ sys/fs/tmpfs/tmpfs_subr.c 18 Apr 2009 04:28:25 -0000
@@ -1017,7 +1017,7 @@ tmpfs_chflags(struct vnode *vp, int flag
int
tmpfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l)
{
- int error, ismember = 0;
+ int error;
struct tmpfs_node *node;
KASSERT(VOP_ISLOCKED(vp));
@@ -1032,21 +1032,9 @@ tmpfs_chmod(struct vnode *vp, mode_t mod
if (node->tn_flags & (IMMUTABLE | APPEND))
return EPERM;
- /* XXX: The following comes from UFS code, and can be found in
- * several other file systems. Shouldn't this be centralized
- * somewhere? */
- if (kauth_cred_geteuid(cred) != node->tn_uid &&
- (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)))
- return error;
- if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) {
- if (vp->v_type != VDIR && (mode & S_ISTXT))
- return EFTYPE;
-
- if ((kauth_cred_ismember_gid(cred, node->tn_gid,
- &ismember) != 0 || !ismember) && (mode & S_ISGID))
- return EPERM;
- }
+ error = common_chmod(cred, vp, node->tn_uid, node->tn_gid, mode);
+ if (error)
+ return (error);
node->tn_mode = (mode & ALLPERMS);
@@ -1071,7 +1059,7 @@ int
tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
struct lwp *l)
{
- int error, ismember = 0;
+ int error;
struct tmpfs_node *node;
KASSERT(VOP_ISLOCKED(vp));
@@ -1094,15 +1082,9 @@ tmpfs_chown(struct vnode *vp, uid_t uid,
if (node->tn_flags & (IMMUTABLE | APPEND))
return EPERM;
- /* XXX: The following comes from UFS code, and can be found in
- * several other file systems. Shouldn't this be centralized
- * somewhere? */
- if ((kauth_cred_geteuid(cred) != node->tn_uid || uid != node->tn_uid ||
- (gid != node->tn_gid && !(kauth_cred_getegid(cred) == node->tn_gid
||
- (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember))))
&&
- ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)) != 0))
- return error;
+ error = common_chown(cred, node->tn_uid, node->tn_gid, uid, gid);
+ if (error)
+ return (error);
node->tn_uid = uid;
node->tn_gid = gid;
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /usr/cvs/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.173
diff -u -p -r1.173 ufs_vnops.c
--- sys/ufs/ufs/ufs_vnops.c 22 Feb 2009 20:28:07 -0000 1.173
+++ sys/ufs/ufs/ufs_vnops.c 18 Apr 2009 04:47:24 -0000
@@ -640,21 +640,16 @@ static int
ufs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct lwp *l)
{
struct inode *ip;
- int error, ismember = 0;
+ int error;
UFS_WAPBL_JLOCK_ASSERT(vp->v_mount);
ip = VTOI(vp);
- if (kauth_cred_geteuid(cred) != ip->i_uid &&
- (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
NULL)))
+
+ error = common_chmod(cred, vp, ip->i_uid, ip->i_gid, mode);
+ if (error)
return (error);
- if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)) {
- if (vp->v_type != VDIR && (mode & S_ISTXT))
- return (EFTYPE);
- if ((kauth_cred_ismember_gid(cred, ip->i_gid, &ismember) != 0 ||
- !ismember) && (mode & ISGID))
- return (EPERM);
- }
+
ip->i_mode &= ~ALLPERMS;
ip->i_mode |= (mode & ALLPERMS);
ip->i_flag |= IN_CHANGE;
@@ -672,7 +667,7 @@ ufs_chown(struct vnode *vp, uid_t uid, g
struct lwp *l)
{
struct inode *ip;
- int error, ismember = 0;
+ int error = 0;
#ifdef QUOTA
uid_t ouid;
gid_t ogid;
@@ -685,19 +680,9 @@ ufs_chown(struct vnode *vp, uid_t uid, g
uid = ip->i_uid;
if (gid == (gid_t)VNOVAL)
gid = ip->i_gid;
- /*
- * If we don't own the file, are trying to change the owner
- * of the file, or are not a member of the target group,
- * the caller's credentials must imply super-user privilege
- * or the call fails.
- */
- if ((kauth_cred_geteuid(cred) != ip->i_uid || uid != ip->i_uid ||
- (gid != ip->i_gid &&
- !(kauth_cred_getegid(cred) == gid ||
- (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 &&
- ismember)))) &&
- ((error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)) != 0))
+
+ error = common_chown(cred, ip->i_uid, ip->i_gid, uid, gid);
+ if (error)
return (error);
#ifdef QUOTA
Index: sys/ufs/ext2fs/ext2fs_vnops.c
===================================================================
RCS file: /usr/cvs/src/sys/ufs/ext2fs/ext2fs_vnops.c,v
retrieving revision 1.83
diff -u -p -r1.83 ext2fs_vnops.c
--- sys/ufs/ext2fs/ext2fs_vnops.c 23 Nov 2008 10:09:25 -0000 1.83
+++ sys/ufs/ext2fs/ext2fs_vnops.c 18 Apr 2009 04:27:54 -0000
@@ -442,19 +442,12 @@ static int
ext2fs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct lwp *l)
{
struct inode *ip = VTOI(vp);
- int error, ismember = 0;
+ int error;
- if (kauth_cred_geteuid(cred) != ip->i_uid &&
- (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
- NULL)))
+ error = common_chmod(cred, vp, ip->i_uid, ip->i_gid, mode);
+ if (error)
return (error);
- if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)) {
- if (vp->v_type != VDIR && (mode & S_ISTXT))
- return (EFTYPE);
- if ((kauth_cred_ismember_gid(cred, ip->i_gid, &ismember) != 0 ||
- !ismember) && (mode & ISGID))
- return (EPERM);
- }
+
ip->i_e2fs_mode &= ~ALLPERMS;
ip->i_e2fs_mode |= (mode & ALLPERMS);
ip->i_flag |= IN_CHANGE;
@@ -472,23 +465,17 @@ ext2fs_chown(struct vnode *vp, uid_t uid
struct inode *ip = VTOI(vp);
uid_t ouid;
gid_t ogid;
- int error = 0, ismember = 0;
+ int error;
if (uid == (uid_t)VNOVAL)
uid = ip->i_uid;
if (gid == (gid_t)VNOVAL)
gid = ip->i_gid;
- /*
- * If we don't own the file, are trying to change the owner
- * of the file, or are not a member of the target group,
- * the caller must be superuser or the call fails.
- */
- if ((kauth_cred_geteuid(cred) != ip->i_uid || uid != ip->i_uid ||
- (gid != ip->i_gid &&
- !(kauth_cred_getegid(cred) == gid ||
- (kauth_cred_ismember_gid(cred, gid, &ismember) == 0 && ismember))))
&&
- (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
NULL)))
+
+ error = common_chown(cred, ip->i_uid, ip->i_gid, uid, gid);
+ if (error)
return (error);
+
ogid = ip->i_gid;
ouid = ip->i_uid;
Home |
Main Index |
Thread Index |
Old Index