kern/125009: access(2) grants root execute perms for non-executable files

clemens fischer ino-news at spotteswoode.dnsalias.org
Thu Jun 26 14:10:04 UTC 2008


>Number:         125009
>Category:       kern
>Synopsis:       access(2) grants root execute perms for non-executable files
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jun 26 14:10:04 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     clemens fischer <ino-news at spotteswoode.dnsalias.org>
>Release:        FreeBSD 8.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD spotteswoode.dnsalias.org 8.0-CURRENT FreeBSD 8.0-CURRENT #21: Mon Jun 23 20:24:05 CEST 2008 root at spotteswoode.dnsalias.org:/usr/obj/usr/src/sys/spott_fbsd8_i386 i386

>Description:

i found the problem when trying out "git" (the VCS).  per default,
git-init(1) creates repos containing eg. pre-commit hooks, shell scripts
to verify commits etc.  they are mode 0644 or mode 0664 and can be
changed by the user.  to enable them, one has to "chmod +x" them.

unprivileged users can use (newer) git-releases without worrying about
this, but if root wants to operate a git repo, every commit aborts with
messages like: "fatal: exec .../.git/hooks/prepare-commit-msg failed."
for every hook involved.

after "ktrace git-commit", kdump(1) shows:

: 73042 git-commit CALL  access(0x8123320,X_OK)
: 73042 git-commit NAMI  "/home/src/bulk/ion/.git/hooks/prepare-commit-msg"
: 73042 git-commit RET   access 0
: 73042 git-commit CALL  fork
: 73042 git-commit RET   fork 73043/0x11d53
: 73043 git-commit RET   fork 0
: 73043 git-commit CALL  open(0x80f1b83,O_RDWR,<unused>0)
: 73043 git-commit NAMI  "/dev/null"
: 73042 git-commit CALL  wait4(0x11d53,0xbfbfd2cc,<invalid>0,0)
: 73043 git-commit RET   open 3
: 73043 git-commit CALL  dup2(0x3,0)
: 73043 git-commit RET   dup2 0
: 73043 git-commit CALL  close(0x3)
: 73043 git-commit RET   close 0
: 73043 git-commit CALL  dup2(0x2,0x1)
: 73043 git-commit RET   dup2 1
: 73043 git-commit CALL  execve(0x8123320,0xbfbfd728,0x8213400)
: 73043 git-commit NAMI  "/home/src/bulk/ion/.git/hooks/prepare-commit-msg"
: 73043 git-commit RET   execve -1 errno 13 Permission denied
: 73043 git-commit CALL  stat(0x8123320,0xbfbfcdf8)
: 73043 git-commit NAMI  "/home/src/bulk/ion/.git/hooks/prepare-commit-msg"
: 73043 git-commit STRU  struct stat {dev=117, ino=800809, mode=-rw-r--r-- , nlink=1, uid=0, gid=0, rdev=3262495, atime=1214332267, stime=1214331402, ctime=1214331402, birthtime=1214331402, size=1196, blksize=4096, blocks=4, flags=0x0 }
: 73043 git-commit RET   stat 0
: 73043 git-commit CALL  write(0x2,0xbfbfca38,0x45)
: 73043 git-commit GIO   fd 2 wrote 69 bytes
:       "fatal: exec /home/src/bulk/ion/.git/hooks/prepare-commit-msg failed.
:       "

i verified this in the current git source:  in
git/builtin-commit.c::run_hook() there's the sequence:

: if (access(argv[0], X_OK) < 0)
:   return 0;
:
: memset(&hook, 0, sizeof(hook));
: hook.argv = argv;
: hook.no_stdin = 1;
: hook.stdout_to_stderr = 1;
: hook.env = env;
:
: return run_command(&hook);

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.

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.


>How-To-Repeat:

you can either install ports/devel/git and do:

: git-init
: date > fileA
: git-add fileA
: git-commit

as 1) root and 2) somebody_else and watch the difference:

: (1) "fatal: exec /tmp/.git/hooks/pre-commit failed."

: (2) "Created initial commit..."

-or-

you could apply the following patch in
/usr/src/tools/regression/security/access/, and do "make; ./testaccess":

: /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'd expect each and every test to fail as none of the test files is
executable!

pseudo-patch replacing R_OK with X_OK (only for testing!):

: /src/localcode/test/access
: 0  # hg diff
: diff --git a/access/testaccess.c b/access/testaccess.c
: --- a/access/testaccess.c
: +++ b/access/testaccess.c
: @@ -216,14 +216,14 @@
:
:         errorseen = 0;
:
: -       error = access("test1", R_OK);
: +       error = access("test1", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "saved uid used instead of real uid\n");
:                 errorseen++;
:         }
:
:  #ifdef EACCESS_AVAILABLE
: -       error = eaccess("test1", R_OK);
: +       error = eaccess("test1", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "saved uid used instead of effective uid\n");
:                 errorseen++;
: @@ -245,7 +245,7 @@
:         }
:
:         /* Check that the real uid is used, not the effective uid */
: -       error = access("test2", R_OK);
: +       error = access("test2", /*R_OK*/ X_OK);
:         if (error) {
:                 fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
:                 errorseen++;
: @@ -253,7 +253,7 @@
:
:  #ifdef EACCESS_AVAILABLE
:         /* Check that the effective uid is used, not the real uid */
: -       error = eaccess("test3", R_OK);
: +       error = eaccess("test3", /*R_OK*/ X_OK);
:         if (error) {
:                 fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
:                 errorseen++;
: @@ -261,7 +261,7 @@
:  #endif
:
:         /* Check that the real uid is used, not the effective uid */
: -       error = access("test3", R_OK);
: +       error = access("test3", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
:                 errorseen++;
: @@ -269,7 +269,7 @@
:
:  #ifdef EACCESS_AVAILABLE
:         /* Check that the effective uid is used, not the real uid */
: -       error = eaccess("test2", R_OK);
: +       error = eaccess("test2", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
:                 errorseen++;
: @@ -299,13 +299,13 @@
:         }
:
:         /* Check that the saved gid is not used */
: -       error = access("test4", R_OK);
: +       error = access("test4", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "saved gid used instead of real gid\n");
:         }
:
:  #ifdef EACCESS_AVAILABLE
: -       error = eaccess("test4", R_OK);
: +       error = eaccess("test4", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "saved gid used instead of effective gid\n");
:                 errorseen++;
: @@ -313,7 +313,7 @@
:  #endif
:
:         /* Check that the real gid is used, not the effective gid */
: -       error = access("test5", R_OK);
: +       error = access("test5", /*R_OK*/ X_OK);
:         if (error) {
:                 fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
:                 errorseen++;
: @@ -321,7 +321,7 @@
:
:  #ifdef EACCESS_AVAILABLE
:         /* Check that the effective gid is used, not the real gid */
: -       error = eaccess("test6", R_OK);
: +       error = eaccess("test6", /*R_OK*/ X_OK);
:         if (error) {
:                 fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
:                 errorseen++;
: @@ -329,7 +329,7 @@
:  #endif
:
:         /* Check that the real gid is used, not the effective gid */
: -       error = access("test6", R_OK);
: +       error = access("test6", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
:                 errorseen++;
: @@ -337,7 +337,7 @@
:
:  #ifdef EACCESS_AVAILABLE
:         /* Check that the effective gid is used, not the real gid */
: -       error = eaccess("test5", R_OK);
: +       error = eaccess("test5", /*R_OK*/ X_OK);
:         if (!error) {
:                 fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
:                 errorseen++;

>Fix:

	


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list