How to successfully run Perl script with setuid() when used as cgi-bin?

Alex Reynolds picture Alex Reynolds · Feb 14, 2011 · Viewed 8.7k times · Source

I have a Perl script that is called either via Apache or on the command-line.

For testing purposes, I pass it the username I want the Perl script to operate with, and use POSIX::setuid to set the uid.

If I run the script from the command line, then the uid is set properly:

use CGI::Pretty qw/:standard/;
use POSIX qw(setuid getuid);

...
my ($pwName, $pwCode, $pwUid, $pwGid, $pwQuota, $pwComment, 
    $pwGcos, $pwHome, $pwLogprog) = getpwnam($username);

if ((defined $pwUid) && (getuid() == $pwUid)) {
    setuid($pwUid);
    print header;
    print Dumper $<;
}
else {
    print header(-status => 401);
    print "Could not setuid to correct uid (currently: )".getuid()."\n";
}

The command-line output shows the correct uid of the specified $username, instead of the uid of the test account that started running the script.

If I call the script via Apache, then the uid remains set to the id of the apache user, and never changes.

I don't believe I can use suExec here, because, after reading the documentation:

  1. I can't put a copy of this script into http://www.example.com/~username for every $username. The script needs to run from one location, and I need to specify the uid from within the script.

  2. I need to have the script run as the specified username at runtime, and not as a single username specified once in a virtual host directive in an Apache configuration file. Changing this configuration file and restarting Apache every time a new user runs this script is not realistic.

How do I get a Perl script running as a cgi-bin to change the uid correctly, when using setuid()?

Answer

Dave Sherohman picture Dave Sherohman · Feb 14, 2011

The only way you can setuid to an arbitrary uid is to run as root.[1]

I don't know about you, but the idea of a CGI program running as root gives me nightmares.

What is this code supposed to actually do after changing uid? Perhaps there's a way to accomplish this without having to setuid?

[1] Depending on your code and its security model, you may be able to collect the user's password and use su/sudo[2] to run a separate command-line program to run the actual operations outside of the web server environment, but su/sudo are able to do this because they're suid root and it would still open up most/all of the issues associated with running CGI code as root anyhow. Even if you filter out root as an invalid username, being able to masquerade as any arbitrary user opens up plenty of opportunities for abuse.

[2] sudo could even be configured to allow it without requiring a password, but there be dragons down that path. Be sure you know what you're doing if you attempt it, lest you give your users free reign to impersonate each other at will.