Running shell script as root via external binary I'm not an experienced Linux user and I wanted an easy way to run shell scripts as root from a PHP script, I came up with this:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;
#include &lt;strings.h&gt;

int main(int argc, char *argv[])
{
    if (access(argv[1], F_OK) != -1)
    {
        struct stat filestat;

        if (stat(argv[1], &amp;filestat) == 0)
        {
            if ((filestat.st_uid == 0) &amp;&amp; (filestat.st_gid == 0) &amp;&amp; (filestat.st_mode &amp; S_IXUSR) &amp;&amp; (!(filestat.st_mode &amp; S_IWOTH)))
            {
                char* match = strrchr(argv[1], '.');

                if ((match != NULL) &amp;&amp; (strcasecmp(match, ".sh") == 0))
                {
                    if (setuid(0) != -1)
                    {
                        execl("/usr/bin/sudo", "/usr/bin/sudo", argv[1], (char*) NULL);
                        return 0;
                    }
                }
            }
        }
    }

    return 1;
}


Any potential security issues with this? If so how can I improve it?

Example Usage:

Lets say I have a script located at:

/some/path/script.sh


With the following in it:

#!/bin/bash
echo $USER


Now lets say I compile the above C code to a binary and place it at:

/some/path/run-as


and do:

chown root:root /some/path/run-as
chmod 6755 /some/path/run-as


Now I run this PHP script owned by www-data (via browser / local apache web server):

&lt;?php
    echo exec('/some/path/run-as /some/path/script.sh');
?&gt;


I expect the script to output 'root' when ran.
This is an ambitious thing to try, with many pitfalls - especially for environments which are Web-accessible.  I urge you to learn how to configure and use sudo instead - that's not foolproof, but it is a good deal safer than rolling your own.

That said, I'll make some observations:


We're using argv[1] without checking whether any arguments were supplied (we should test argc &gt;= 2 before using argv[1] - possibly check argc == 2, since we ignore any extra arguments).
There's a time-of-check/time-of-use (TOCTOU) race, because we examine the supplied file and later assume the properties haven't changed (we we get to running it).
Using system causes the supplied argument to be parsed as a shell command, which will do word splitting, expand variables and globs, and more.
Using a shell script from a privileged program is vulnerable to the script being changed between the interpreter being started and it opening the script to read commands (e.g. by switching symlinks).


It's quite easy to exploit this script to execute anything I want on your system, if there's a directory I can write to:




ln -s /bin/true 'ls -l ~root; id; cd; echo rm -rf; true .sh'

/some/path/run-as ls*.sh



We can avoid using system() (and accept extra arguments if we choose), by using execv() instead:

            if (match &amp;&amp; !strcasecmp(match, ".sh"))
            {
                execv(argv[1], argv+1);
                /* if we got here, exec failed */
                /* so fall through to return 1 */
            }


This does still leave us vulnerable to a user creating a symbolic link to a root-owned file and rewriting it to point to the user's file between the access check and the exec(), though.



In short: please use sudo if you really have to run some things as root.  Configured properly, it can give tight control on the commands you can execute with elevated privileges.  It's not completely attack-proof, but it is much better than writing your own (it has had decades of scrutiny to improve it).