kern/125009: access(2) grants root execute perms for
non-executable files
Jaakko Heinonen
jh at saunalahti.fi
Tue Jul 8 13:30:05 UTC 2008
The following reply was made to PR kern/125009; it has been noted by GNATS.
From: Jaakko Heinonen <jh at saunalahti.fi>
To: clemens fischer <ino-news at spotteswoode.dnsalias.org>,
bug-followup at FreeBSD.org
Cc:
Subject: Re: kern/125009: access(2) grants root execute perms for
non-executable files
Date: Tue, 8 Jul 2008 16:29:15 +0300
On 2008-06-26, clemens fischer wrote:
> this check works correctly for non-root, but any file accessible in
> any way for root _passes_ the "(access(argv[0], X_OK) < 0)" check, a
> subsequent execvp(3) fails. doing "git-init; date > fileA; git-commit
> -a" as a non-privileged user works as expected.
At first sight it may look like that it's not a bug. From FreeBSD
access(2) manual page:
Even if a process's real or effective user has appropriate privileges and
indicates success for X_OK, the file may not actually have execute per-
mission bits set. Likewise for R_OK and W_OK.
This is line with SUSv3 which states following:
If any access permissions are checked, each shall be checked
individually, as described in the Base Definitions volume of IEEE Std
1003.1-2001, Chapter 3, Definitions. If the process has appropriate
privileges, an implementation may indicate success for X_OK even if none
of the execute file permission bits are set.
However it boils down to how one defines "appropriate privileges".
execve(2) has a special check for root: a file must have at least one
execute bit set, otherwise execve(2) returns EACCES even for root.
Thus I think that it's reasonable to say that there aren't "appropriate
privileges" for root unless at least one execute bit is set for a
(regular) file.
This patch adds a similar execute bit check that execve(2) has to
access(2).
%%%
Index: sys/kern/vfs_syscalls.c
===================================================================
--- sys/kern/vfs_syscalls.c (revision 180121)
+++ sys/kern/vfs_syscalls.c (working copy)
@@ -2037,6 +2037,7 @@ vn_access(vp, user_flags, cred, td)
/* Flags == 0 means only check for existence. */
error = 0;
if (user_flags) {
+ struct vattr vattr;
flags = 0;
if (user_flags & R_OK)
flags |= VREAD;
@@ -2051,6 +2052,17 @@ vn_access(vp, user_flags, cred, td)
#endif
if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0)
error = VOP_ACCESS(vp, flags, cred, td);
+ if (error)
+ return (error);
+ /*
+ * When checking execute permission insure that at least one
+ * execute bit is on (except for directories) - otherwise root
+ * will always succeed.
+ */
+ if ((flags & VEXEC) &&
+ ((error = VOP_GETATTR(vp, &vattr, cred, td)) == 0) &&
+ (vattr.va_type != VDIR) && ((vattr.va_mode & 0111) == 0))
+ error = EACCES;
}
return (error);
}
%%%
However I think that it would be more logical to move that check down to
vaccess() and remove the extra check from exec_check_permissions(). But
there is a thing you have to note: vaccess() is not called when ACLs are
used. Thus the check must be added to vaccess_acl_posix1e() too.
Following patch should do it.
%%%
Index: sys/kern/kern_exec.c
===================================================================
--- sys/kern/kern_exec.c (revision 180335)
+++ sys/kern/kern_exec.c (working copy)
@@ -1291,13 +1291,9 @@ exec_check_permissions(imgp)
/*
* 1) Check if file execution is disabled for the filesystem that this
* file resides on.
- * 2) Insure that at least one execute bit is on - otherwise root
- * will always succeed, and we don't want to happen unless the
- * file really is executable.
- * 3) Insure that the file is a regular file.
+ * 2) Insure that the file is a regular file.
*/
if ((vp->v_mount->mnt_flag & MNT_NOEXEC) ||
- ((attr->va_mode & 0111) == 0) ||
(attr->va_type != VREG))
return (EACCES);
Index: sys/kern/subr_acl_posix1e.c
===================================================================
--- sys/kern/subr_acl_posix1e.c (revision 180335)
+++ sys/kern/subr_acl_posix1e.c (working copy)
@@ -85,8 +85,14 @@ vaccess_acl_posix1e(enum vtype type, uid
PRIV_VFS_LOOKUP, 0))
priv_granted |= VEXEC;
} else {
+ /*
+ * Insure that at least one execute bit is on - otherwise
+ * privileged user will always succeed, and we don't want to
+ * happen unless the file really is executable.
+ */
if ((acc_mode & VEXEC) && !priv_check_cred(cred,
- PRIV_VFS_EXEC, 0))
+ PRIV_VFS_EXEC, 0) && (acl_posix1e_acl_to_mode(acl) &
+ (S_IXUSR | S_IXGRP | S_IXOTH)))
priv_granted |= VEXEC;
}
Index: sys/kern/vfs_subr.c
===================================================================
--- sys/kern/vfs_subr.c (revision 180335)
+++ sys/kern/vfs_subr.c (working copy)
@@ -3517,8 +3517,14 @@ privcheck:
!priv_check_cred(cred, PRIV_VFS_LOOKUP, 0))
priv_granted |= VEXEC;
} else {
+ /*
+ * Insure that at least one execute bit is on - otherwise
+ * privileged user will always succeed, and we don't want to
+ * happen unless the file really is executable.
+ */
if ((acc_mode & VEXEC) && ((dac_granted & VEXEC) == 0) &&
- !priv_check_cred(cred, PRIV_VFS_EXEC, 0))
+ !priv_check_cred(cred, PRIV_VFS_EXEC, 0) &&
+ (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
priv_granted |= VEXEC;
}
%%%
I also tested how some other operating systems behave:
(access(2) X_OK call as root for a regular file which has no execute
bits set)
Mac OS X 10.4 returns EPERM (for users it returns EACCES and strangely
it returns 0 (OK) for device files)
Linux (recent Ubuntu kernel) returns EACCES
NetBSD returns EACCES
OpenBSD 4.3 returns 0 (same as FreeBSD)
> but this isn't a git-problem anyway: i took
> /usr/src/tools/regression/security/access/* and changed every check for
> "access(path, R_OK)" into "access(path, X_OK)". as the test files in
> this test are created modes 0400 and 0040, one would expect every check
> to fail when using X_OK, but the output is:
>
> : /src/localcode/test/access
> : 0 # ./testaccess
> : Effective uid was used instead of real uid in access().
> : Effective gid was used instead of real gid in access().
> : 2 errors seen.
I don't think that your change to the test and these errors are relevant
to the actual problem. I didn't check thoroughly though.
--
Jaakko
More information about the freebsd-bugs
mailing list