Bug 831

Summary: [FreeBSD]: Cryptographic digest doesn't work with shell script
Product: Sudo Reporter: Renato Botelho <garga>
Component: SudoAssignee: Todd C. Miller <Todd.Miller>
Status: RESOLVED FIXED    
Severity: normal    
Priority: low    
Version: 1.8.22   
Hardware: PC   
OS: FreeBSD   
Attachments: fexecve() test program
Fix for fexecve() of scripts on FreeBSD

Description Renato Botelho 2018-04-23 04:47:50 MDT
A FreeBSD user reported he is seeing a problem when he tries to use crypto digest in command specification on FreeBSD. After some tests I noted it was working as expected with binaries but failing as he described with shell scripts.

Here is FreeBSD bug for more details:

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=223587

In order to reproduce it on a FreeBSD system it's just needed a configuration like this:

$USER ALL = (ALL) NOPASSWD: sha256:$HASH /usr/local/bin/foo

$USER is testing username
%HASH is sha256 hash of /usr/local/bin/foo
/usr/local/bin/foo is a shell script

After login as $USER and run 'sudo /usr/local/bin/foo' it fails with following error:

/bin/sh: cannot open /dev/fd/4: No such file or directory
Comment 1 Todd C. Miller 2018-04-23 07:12:16 MDT
This is a bug in FreeBSD, not sudo.  Unlike Linux and Solaris, FreeBSD seems unable to execute a script using the fexecve() system call.

You can work around this limitation by adding the following in your sudoers file:

Defaults fdexec=never

At compile time, if you set ac_cv_func_fexecve=no in the environment, fexecve() support will not be built-in to sudo.  That may be best from a ports perspective.
Comment 2 Todd C. Miller 2018-04-23 07:14:24 MDT
Created attachment 508 [details]
fexecve() test program

The following program can be used to reproduce the problem on FreeBSD.

Works:

./fdexec /bin/ls

Fails:

./fdexec /some/script.sh
Comment 3 Renato Botelho 2018-04-23 10:01:56 MDT
(In reply to Todd C. Miller from comment #1)
> This is a bug in FreeBSD, not sudo.  Unlike Linux and Solaris,
> FreeBSD seems unable to execute a script using the fexecve() system
> call.
> 
> You can work around this limitation by adding the following in your
> sudoers file:
> 
> Defaults fdexec=never
> 
> At compile time, if you set ac_cv_func_fexecve=no in the
> environment, fexecve() support will not be built-in to sudo.  That
> may be best from a ports perspective.

Thanks for the advice, Todd.

JFYI, as documented in fexecve manpage:

CAVEATS
     If a program is setuid to a non-super-user, but is executed when the real
     uid is ``root'', then the program has some of the powers of a super-user
     as well.

     When executing an interpreted program through fexecve(), kernel supplies
     /dev/fd/n as a second argument to the interpreter, where n is the file
     descriptor passed in the fd argument to fexecve().  For this construction
     to work correctly, the fdescfs(5) filesystem shall be mounted on /dev/fd.

so I tested it and when /dev/fd is mounted, fexecve is cappable of running a shell script.

I'm considering to add, as you suggested, ac_cv_func_fexecve=no to build environment to get it disabled and keep things working. Do you see any negative side effect of it?

Thanks
Comment 4 Todd C. Miller 2018-04-23 10:56:52 MDT
I've added a workaround in https://www.sudo.ws/repos/sudo/rev/30f7c5d64104
This checks that /dev/fd/N is be present before using fexecve() on a script.
Comment 5 Todd C. Miller 2018-04-23 11:00:09 MDT
The downside of disabling fexdcve() entirely is that it creates a time of check vs. time of use race condition.  That TOCTOU already exists when the file is writable by the user but disabling fexecve() means it is an issue when the directory is writable even if the command is not.
Comment 6 Renato Botelho 2018-04-23 12:45:10 MDT
I accidentally committed it to FreeBSD ports, but I reverted after a few secondes. This patch silenced the error but I can't see the shell script output now.

sipessatti@x230:~ % poudriere
Usage: poudriere [-e etcdir] [-N] command [options]

Options:
    -A          -- Force colors, even if not in a TTY
    -e etcdir   -- Specify an alternate etc/ dir where poudriere configuration
                   resides.
    -N          -- Disable colors
    -v          -- Be verbose; show more information. Use twice to enable
                   debug output
Commands:
    bulk        -- Generate packages for given ports
    distclean   -- Remove old distfiles
    daemon      -- Launch the poudriere daemon
    help        -- Show usage
    image       -- Generate images
    jail        -- Manage the jails used by poudriere
    logclean    -- Remove old logfiles
    ports       -- Create, update or delete the portstrees used by poudriere
    pkgclean    -- Remove packages that are no longer needed
    options     -- Configure ports options
    queue       -- Queue a build request
    status      -- Get the status of builds
    testport    -- Launch a test build of a given port
    version     -- Show the version of poudriere

sipessatti@x230:~ % sudo poudriere

sipessatti@x230:~ % sudo -l
User sipessatti may run the following commands on x230:
    (ALL) NOPASSWD: /usr/local/bin/poudriere
Comment 7 Todd C. Miller 2018-04-23 13:47:10 MDT
I get missing output when fdescfs is mounted on /dev/fd with or without the patch.  If fdescfs is not mounted I get the expacted behavior.  It seems there are still issues remaining with sudo, fexecve() and scripts on FreeBSD.

You may just wish to disable fexecve use in sudo for now on FreeBSD.
Comment 8 Todd C. Miller 2018-04-23 20:45:23 MDT
If you apply the following two commits sudo should be able to fexecve(2) a script on FreeBSD when a digest is specified in sudoers:

https://www.sudo.ws/repos/sudo/rev/72c4b499c019
https://www.sudo.ws/repos/sudo/rev/d79f5125cc73

These changes will be in sudo 1.8.23.
Comment 9 Renato Botelho 2018-04-24 05:07:33 MDT
This one worked as expected. Thanks!
Comment 10 Todd C. Miller 2018-04-24 09:54:09 MDT
Created attachment 509 [details]
Fix for fexecve() of scripts on FreeBSD

There was a regression caused by the previous commits that is fixed by https://www.sudo.ws/repos/sudo/rev/e0e086b4e764.

Attached is a diff against 1.8 that includes all the relevant changes in one patch.