Bug 746 - Wrong first param to execve if rules have several matches
Wrong first param to execve if rules have several matches
Status: RESOLVED FIXED
Product: Sudo
Classification: Unclassified
Component: Sudo
1.8.16
PC Linux
: low normal
Assigned To: Todd C. Miller
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2016-06-09 03:02 MDT by juanleon
Modified: 2016-06-18 05:57 MDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description juanleon 2016-06-09 03:02:20 MDT
I am using sudo 1.8.16 in Linux (Ubuntu 16.04) and I am seeing a very strange
behaviour that looks like a bug to me (issue did not happen in older Ubuntu
distributions).

This is the relevant configuration for my user.

----------------
# sudo -u foouser "sudo" -l
Matching Defaults entries for foouser on xprod:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User foouser may run the following commands on xprod:
    (root) NOPASSWD: /absolute/path/*/tools/directories.php change-ownership
----------------

Then, I invoke

sudo -n -k /absolute/path/foo/tools/directories.php change-ownership

And argv[0] (as seen in script directories.php) is not
/absolute/path/foo/tools/directories.php (as I wrote in command line) but
/absolute/path/bar/tools/directories.php (another path that also exists, and has
the same inode number (because of symbolic links)).

When I use strace on the above command to see why this is happening I see this:

----------------
...
setresgid(-1, 0, -1)        = 0
setgroups(1, [0])           = 0
open("/absolute/path", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 8
fstat(8, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getdents(8, /* 23 entries */, 32768) = 712
getdents(8, /* 0 entries */, 32768) = 0
close(8)                    = 0
stat("/absolute/path/bar/tools", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/absolute/path/foo/tools", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/absolute/path/zap/tools", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/absolute/path/bar/tools/directories.php", {st_mode=S_IFREG|0755, st_size=347, ...}) = 0
stat("/absolute/path/foo/tools/directories.php", {st_mode=S_IFREG|0755, st_size=347, ...}) = 0
stat("/absolute/path/zap/tools/directories.php", {st_mode=S_IFREG|0755, st_size=347, ...}) = 0
...
execve("/absolute/path/bar/tools/directories.php", ["/absolute/path/foo/tools/directories.php", "change-ownership"], [/* 15 vars */] <unfinished ...>
...
----------------

So, the first parameter of execve looks wrong (uses "bar"), while the second is
right (uses "foo").  I am not sure why sudo is checking all the files that match
its rule (/absolute/path/*/tools/directories.php) or why is picking
one at random, when I am specifying which one I want to execute.

Is this a bug?  Is this a feature?  Any advice on how to workaround this issue
would be greatly appreciated (since bad things happen to me if argv[0] looks
wrong on the tool I am calling).
Comment 1 Todd C. Miller 2016-06-09 09:27:23 MDT
Sudo matches based on the inode and device so it is possible for it to call execve() on a different file path than you might expect as long as it has the same base name.

Since the argv[0] passed to execve() is what the user specified, this shouldn't be an issue.  However, it appears that recent versions of Linux change argv[0] for scripts based on the execve() path.  No other version of Unix I'm aware of does that.
Comment 2 juanleon 2016-06-09 09:44:39 MDT
    "Sudo matches based on the inode and device so it is possible for it to call execve() on a different file path than you might expect as long as it has the same base name"

I am curious about why is that done.  I mean, in the strace I can see sudo is looking for (calling stat on) all the scripts that match the rule.  I fail to see the need of it, since in the command I specify the full path.

Using a different name breaks the convention used by execve.  I quote the manual (in Linux):

    "argv  is  an array of argument strings passed to the new program.  By convention, the first of these strings should contain the filename associated with the file being executed."

I would agree that what sudo is doing is not against POSIX execve() specification ("The argument path points to a pathname that identifies the new process image file", "The value in argv[0] should point to a filename that is associated with the process  being  started  by one of the exec functions.")

It seems like recent Linuxes assume the convention is a requirement.  Would it be hard to honour that convention in Linux?
Comment 3 Todd C. Miller 2016-06-09 09:55:36 MDT
Using the inode and dev helps avoid time of check vs. time of use issues with the '!' operator.  As a workaround for your specific case, try adding the following line to sudoers:

Defaults fast_glob

That will cause sudo to use fnmatch() instead of glob() for wild card matched.
Comment 4 Todd C. Miller 2016-06-09 12:27:51 MDT
This is fixed by https://www.sudo.ws/repos/sudo/rev/29bdba0cf2eb which will be in sudo 1.8.17.
Comment 5 juanleon 2016-06-10 01:28:02 MDT
Wow that was fast!

Thanks for the fast_glob workaround, too.
Comment 6 Todd C. Miller 2016-06-18 05:56:35 MDT
Fixed in sudo 1.8.17 which is now available.