diff options
author | jquast <contact@jeffquast.com> | 2014-08-24 23:46:10 -0700 |
---|---|---|
committer | jquast <contact@jeffquast.com> | 2014-08-24 23:46:10 -0700 |
commit | 67e6c4ac018a0dabe50962beba537612cfb4fa22 (patch) | |
tree | 1188f80867cdf7c76447e92faec3c7391f8399ec /pexpect/__init__.py | |
parent | 8d96042177a6986ae5b117e31916638309b2fd03 (diff) | |
download | pexpect-git-67e6c4ac018a0dabe50962beba537612cfb4fa22.tar.gz |
Closes issue #104 -- cannot execute sudo(8)
Previously, misinterpreted that os.access(file, X_OK)
always returns True on Solaris. Yes, but only for
the uid of 0. Python issue #13706 closed "not a bug"
reads to "just use os.stat()", so we went to great
lengths to do so quite exhaustively.
But this is wrong -- *only* when root, should we check
the file modes -- os.access of X_OK works perfectly
fine for non-root users.
And, we should only check if any of the executable bits
are set. Alas, it is true, you may execute that which
you may not read -- because as root, you can always read
it anyway.
Verified similar solution in NetBSD test.c (/bin/test),
OpenBSD ksh for its built-in test, and what FreeBSD/Darwin
for their implementation of which.c.
Diffstat (limited to 'pexpect/__init__.py')
-rw-r--r-- | pexpect/__init__.py | 48 |
1 files changed, 16 insertions, 32 deletions
diff --git a/pexpect/__init__.py b/pexpect/__init__.py index 4a34f15..57d8d91 100644 --- a/pexpect/__init__.py +++ b/pexpect/__init__.py @@ -2007,46 +2007,30 @@ class searcher_re(object): def is_executable_file(path): - """Checks that path is an executable regular file (or a symlink to a file). - - This is roughly ``os.path isfile(path) and os.access(path, os.X_OK)``, but - on some platforms :func:`os.access` gives us the wrong answer, so this - checks permission bits directly. + """Checks that path is an executable regular file, or a symlink to one. + + This is roughly ``os.path isfile(path) and os.access(path, os.X_OK)``, + except for root users, which are permitted to execute a file only if + any of the execute bits are set. """ # follow symlinks, fpath = os.path.realpath(path) - # return False for non-files (directories, fifo, etc.) if not os.path.isfile(fpath): + # non-files (directories, fifo, etc.) return False - # On Solaris, etc., "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." - # - # For this reason, it is necessary to explicitly check st_mode - - # get file mode using os.stat, and check if `other', - # that is anybody, may read and execute. mode = os.stat(fpath).st_mode - if mode & stat.S_IROTH and mode & stat.S_IXOTH: - return True - - # get current user's group ids, and check if `group', - # when matching ours, may read and execute. - user_gids = os.getgroups() + [os.getgid()] - if (os.stat(fpath).st_gid in user_gids and - mode & stat.S_IRGRP and mode & stat.S_IXGRP): - return True - - # finally, if file owner matches our effective userid, - # check if `user', may read and execute. - user_gids = os.getgroups() + [os.getgid()] - if (os.stat(fpath).st_uid == os.geteuid() and - mode & stat.S_IRUSR and mode & stat.S_IXUSR): - return True - - return False + + if os.getuid() == 0: + # when root, any permission bit of any section + # is fine, even if we do not own the file. + return bool(mode & (stat.S_IXUSR | + stat.S_IXGRP | + stat.S_IXOTH)) + + return os.access(fpath, os.X_OK) + def which(filename): '''This takes a given filename; tries to find it in the environment path; |