I am setting up a sort of personal dropbox for our customers on a CentOS 6.3 machine. The server will be accessible thru SFTP and a proprietary http service base on PHP. This machine will be in our DMZ so it has to be secure. Because of this I have apache running as an unprivileged user, hardened the security on apache, the OS, PHP, applied a lot of filtering in iptables and applied some restrictive TCP Wrappers. Now you might have suspected this one was coming, SELinux is also set to enforcing.

I'm setting up PAM to use MySQL so my users in the web application can login. These users will all be in a group that can use SSH only for SFTP and users will be chrooted to their own 'home' folder.

To allow this SELinux wants the folders to have the user_home_t tag. Also the parent directory needs to be writable by root only. If these restrictions are not met SELinux will kill the SSH pipe immediately.

The files that need to be accessible thru both http and SFTP so I have made a SELinux module to allow Apache to search/attr/read/write etc. to directories with the user_home_dir_t tag.

As sftp users are stored in MySQL I want to setup their home dirs upon user creation. This is a problem since Apache has no write access to the /home dir, it's only writable by root since it's required to keep SELinux and OpenSSH happy.

Basically I need to let Apache do only a few tasks as root and only within /home. So I need to somehow elevate the privileges temporarily or let root do these tasks for apache instead.

What I need to have apache do with root privileges is the following.

mkdir /home/userdir/
mkdir /home/userdir/userdir
chmod -R 0755 /home/userdir
umask 011 /home/userdir/userdir
chcon -R -t user_home_t /home/userdir
chown -R user:sftp_admin /home/userdir/userdir
chmod 2770 /home/userdir/userdir

This would create a home for the user, now I have an idea that might work, cron. That would mean the server needs to check for users that have no home every minute, then when creating users the interface would freeze for an average of 30 seconds before the account creation can be confirmed which I do not prefer. Does anybody know if something can be done with sudoers? Or any other idea's are welcome...

Thanks for your time!


Create a script that implements the commands you describe, and run setuid on it to enable it to run as root when anyone invokes it. See http://en.wikipedia.org/wiki/Setuid for more information on setuid.

From PHP, you can then just call system('/bin/setupNewUser'); as usual and the script will be ran as root.

Alternative solution that limits the injection opportunities and works on systems with setuid execution on scripts disabled:

Create a small program that does have setuid. An example listed below:

#include <stdlib.h>
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char* argv[])
    if (argc != 2)
        cout << "Invalid arguments";
        return 1;

    //make sure the argument passed to the script has only [a-z]
    for (char* i = argv[1]; *i != '\0'; i++)
       //check if the current character falls outside an allowed range
       if (!(
           //Allowed ranges:
           // between a and z
           ('a' <= *i && *i <= 'z') 
           // between 0 and 9
           || ('0' <= *i && *i <= '9')
           // '_' or '-'
           || ('_' == *i)
           || ('-' == *i)
           cout << "Illegal character in username: " << *i << endl;
           return 2;

    //append the username
    string command = string("/test/setupUser.sh ");
    command += string(argv[1]);

    //execute the command and return the result transparently
    // return system(command.c_str());
    // * OR *
    // call mkdir directly

Call the setuid program from php

system('/test/setupUser '.escapeshellarg($username));
  • 1
    Yes I had thought about this but I'm a bit worried about the fact that the script requires an argument $1 should contain the userdirectory that is to be created. I'm afraid somebody could slip a command into the argument. Like make_user.sh foobar\ rm -rf /boot;shutdown -h now; I'm not sure about the syntax of how to exploit this. And by the way it's not setuid doesn't apply to scripts AFAIK... Please correct me if I'm wrong. – Chris Nov 21 '12 at 22:50
  • @Chris Some operating systems do allow setuid scripts. The Daleks have a word for such things, but I prefer "security hole" myself. – voretaq7 Nov 21 '12 at 23:18
  • @voretaq7 Yes I know, it's possible on some systems but I picked CentOS for a reason ;) And I agree it should not be possible out of the box. – Chris Nov 22 '12 at 2:27
  • @Mitch thanks for you input I had seen an example of a C script that does a system call but it didn't pass the argument. I thought to find how to do it but got nervous about the potential security risk that I cannot predict since I know nothing about C programming. I cannot judge the safety or unsafety of this script, can anyone give me an opinion? – Chris Nov 22 '12 at 2:30
  • @Chris, speaking from the point of view of the person writing the C++ program listed above, it is impossible to completely escape validation of command line arguments, but the script above validates that there are only the characters a-z being passed passed to the setupUser.sh script - which would certainly limit injection opportunities. I cannot speak for escapeshellarg, but as it is used in many other PHP scripts far more worrisome than this, I would assume it works reasonably well. – Mitch Nov 23 '12 at 16:59

PLEASE Don't Do This!

At least not the way you suggest -- There is no sane reason for Apache to do ANYTHING as root.
The level of egregiousness of what you're suggesting should be apparent by the fact that Apache will refuse to run as root (except the mother process binding to port 80).

Also doing anything as root with untrusted user input is a terrible idea and a sure fire way to discover all the interesting things a hacker can type into a box do make your system do stuff you weren't expecting.

If you really want to do this:

  • Create the user directories manually.
    This one manual step with human validation will save your system. Allowing any untrusted user input within a mile of a root shell is just asking for trouble.
    • The directory owner should be the user.
    • The directory group should be the group Apache runs as.
    • The directory permissions should probably be either 4770 (rwsrwx---)

This will allow your user to read/write/etc. the contents of the directory, and the SetUID bit on the directory will force the user to be the owner of all files (so they can manipulate them when logged in using SCP/SFTP).

  • Fair warning thanks for your input, though I cannot leave it to be a manual action, my less technical colleagues need to understand how to create a user too. They have never seen a Linux CLI. Which is why I am trying to find a non to dangerous solution to my problem. – Chris Nov 22 '12 at 2:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.