Behaviour of access(x, X_OK) in 2.2 vs. 2.4
Subsystems (3)
Hi,
I'm still using linux 2.2 and I noticed today that the behaviour of the access() system call concerning the execution permission (X_OK), if invoked by uid 0, has been changed in 2.4. In 2.4 it seems to take the execute permission bit into account while in 2.2, for uid 0, it returns success (0) independent from it, although execve fails if invoked on a file without x bit. The difference can be demonstrated quite easily using the bash builtin test command on a file without x bit, which (like /usr/bin/access from tetex and unlike /usr/bin/test from sh-utils) seems to use access():
On 2.2:
bash# cd /tmp && touch xx && test -x xx && echo x || echo y x
although
bash# cd /tmp && cp /bin/echo . && chmod 0 echo && ./echo
bash: ./echo: Permission denied
On 2.4:
bash# cd /tmp && touch xx && test -x xx && echo x || echo y y
(Note this assumes an umask of 0022.)
I searched the web, newsgroups and mailing list archives about this problem, to no avail. Is there some backport, workaround or patch for 2.2 to get the same (and obsiously more sane) behaviour as in 2.4?
Please make sure you CC me if you reply as I'm not subscribed. -- Peter 'Rattacresh' Backes, [email protected]
PATCH (2.2): Fix for misbehaving access(x, X_OK)
Hi,
here is a patch for the quirk in 2.2 kernels I reported yesterday. I mostly took the changes from 2.4.9 to 2.4.10, see
http://lists.insecure.org/lists/linux-kernel/2001/Sep/0152.html,
and applied them to 2.2.25. Thus it is based on the patch by Christoph Hellwig.
Keywords:
access() system call, X_OK, sys_access(), permission(),
vfs_permission(), execute, x-bit, root, uid 0, bash, test -x,
/usr/bin/access -x, CAP_DAC_OVERRIDE,
/usr/lib/rpm/find-requires: /usr/lib/rpm/perl.req: /usr/bin/perl: bad interpreter: Permission denied
Please make sure you CC me if you reply as I'm not subscribed.
-- Peter 'Rattacresh' Backes, [email protected]
fs/namei.c renamed from fs/namei.c.old +16 -4
@@ -149,11 +149,23 @@ mode >>=6 ; else if (in_group_p(inode->i_gid)) mode >>= 3; -if (((mode & mask &S_IRWXO ) == mask) ||capable (CAP_DAC_OVERRIDE )) +/* +* If the DACs are ok we don't need any capability check. +*/ +if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; -/* read and search access */ -if ((mask ==S_IROTH ) || - (S_ISDIR (inode->i_mode) && !(mask & ~(S_IROTH |S_IXOTH )))) +/* +* Read/write DACs are always overridable. +* Executable DACs are overridable if at least one exec bit is set. +*/ +if ((mask & (MAY_READ |MAY_WRITE )) || (inode->i_mode &S_IXUGO )) +if (capable (CAP_DAC_OVERRIDE )) +return 0 ; + +/* +* Searching includes executable on directories, else just read. +*/ +if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE )))if (capable (CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;
fs/ext2/acl.c renamed from fs/ext2/acl.c.old +16 -3
@@ -51,10 +51,23 @@* Access is always granted for root. We now check last, * though, for BSD process accounting correctness */ -if (((mode & mask &S_IRWXO ) == mask) ||capable (CAP_DAC_OVERRIDE )) +/* +* If the DACs are ok we don't need any capability check. +*/ +if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; -if ((mask ==S_IROTH ) || - (S_ISDIR (inode->i_mode) && !(mask & ~(S_IROTH |S_IXOTH )))) +/* +* Read/write DACs are always overridable. +* Executable DACs are overridable if at least one exec bit is set. +*/ +if ((mask & (MAY_READ |MAY_WRITE )) || (inode->i_mode &S_IXUGO )) +if (capable (CAP_DAC_OVERRIDE )) +return 0 ; + +/* +* Searching includes executable on directories, else just read. +*/ +if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE )))if (capable (CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;
fs/ufs/acl.c renamed from fs/ufs/acl.c.old +16 -3
@@ -58,10 +58,23 @@* Access is always granted for root. We now check last, * though, for BSD process accounting correctness */ -if (((mode & mask &S_IRWXO ) == mask) ||capable (CAP_DAC_OVERRIDE )) +/* +* If the DACs are ok we don't need any capability check. +*/ +if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; -if ((mask ==S_IROTH ) || - (S_ISDIR (inode->i_mode) && !(mask & ~(S_IROTH |S_IXOTH )))) +/* +* Read/write DACs are always overridable. +* Executable DACs are overridable if at least one exec bit is set. +*/ +if ((mask & (MAY_READ |MAY_WRITE )) || (inode->i_mode &S_IXUGO )) +if (capable (CAP_DAC_OVERRIDE )) +return 0 ; + +/* +* Searching includes executable on directories, else just read. +*/ +if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE )))if (capable (CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;
fs/proc/inode.c renamed from fs/proc/inode.c.old +16 -4
@@ -145,11 +145,23 @@ mode >>=6 ; else if (in_group_p(inode->i_gid)) mode >>= 3; -if (((mode & mask &S_IRWXO ) == mask) ||capable (CAP_DAC_OVERRIDE )) +/* +* If the DACs are ok we don't need any capability check. +*/ +if (((mode & mask & (MAY_READ |MAY_WRITE |MAY_EXEC )) == mask))return 0 ; -/* read and search access */ -if ((mask ==S_IROTH ) || - (S_ISDIR (inode->i_mode) && !(mask & ~(S_IROTH |S_IXOTH )))) +/* +* Read/write DACs are always overridable. +* Executable DACs are overridable if at least one exec bit is set. +*/ +if ((mask & (MAY_READ |MAY_WRITE )) || (inode->i_mode &S_IXUGO )) +if (capable (CAP_DAC_OVERRIDE )) +return 0 ; + +/* +* Searching includes executable on directories, else just read. +*/ +if (mask ==MAY_READ || (S_ISDIR (inode->i_mode) && !(mask &MAY_WRITE )))if (capable (CAP_DAC_READ_SEARCH ))return 0 ;return -EACCES ;