skip to Main Content

I cannot manage to set the SGID bit from PHP.

I have this directory:

  4 drwxrwsr-x 12 www-data mygroup  4096 Oct  7 16:05 mydir

Note the SGID bit. So, if I simply create a directory into it from the shell with mkdir test, I get

  4 drwxr-sr-x  2 myuser   mygroup   4096 Oct  7 16:22 test

Note that the SGID bit is inherited. But I would want it group writable (which my umask 22 does not allow) so I can simply chmod 02775 test and I am perfectly happy:

  4 drwxrwsr-x  2 myuser   mygroup   4096 Oct  7 16:22 test

Now I would like to do the same from a PHP script. Naturally, I would expect this to work:

mkdir("/mydir/test2");
chmod("/mydir/test2", 02775);

But it does not and I get this instead (the SGID is reset):

  4 drwxrwxr-x  2 www-data mygroup   4096 Oct  7 16:30 test2

Here are a couple of other useful experiments:

mkdir("/mydir/test3");
mkdir("/mydir/test4");
passthru("chmod 02775 '/mydir/test4'");
mkdir("/mydir/test5");
passthru("chmod g+w '/mydir/test5'");

The results are

  4 drwxr-sr-x  2 www-data mygroup   4096 Oct  7 16:39 test3
  4 drwxrwxr-x  2 www-data mygroup   4096 Oct  7 16:39 test4
  4 drwxrwxr-x  2 www-data mygroup   4096 Oct  7 17:06 test5

Interestingly, mkdir() alone has preserved the SGID, but chmod() resets it, even through passthru().

I know that the PHP manual says for chmod that the command expects only three octal digits, but I read also this stackoverflow question and it looks like the manual contains obsolete information and others can affect the SGID. Besides, it should not affect the passthru() versions, should it? The same stackoverflow question mentions something about chmod() needing to be "the last to be called" after chown() and chgrp(), but I am not using any of those.

What am I doing wrong?

2

Answers


  1. Chosen as BEST ANSWER

    In the end, the only way I could find to obtain the permissions I need is to profit of the correct behaviour of mkdir() and avoid calling chmod() which appears to reset the SGID bit no matter what. The only way I can think of doing this is to change the umask with umask():

    $myumask = umask(2);
    mkdir("/mydir/test6");
    umask($myumask);
    

    This appears to work fine:

      4 drwxrwsr-x  2 www-data mygroup   4096 Oct  9 14:22 test6
    

    This leaves me with the issue raised in the note of https://www.php.net/manual/en/function.umask.php: that all threads of a multithreaded webserver share the same umask, obviously leading to undesired and unpredictable behaviour. Luckily, in my case, I could ascertain that all directory creations are done in a monothreaded context (essentially, on the first test run of a script) and thus I feel safe. Hence, this is a useful workaround but not a general solution.


  2. Is this running on a web server, or just as a CLI script? If CLI, user or root?

    Also, why directories? SGID is used on program files, to allow a user to run a program/script as another user, like "sudo". Have you tried testing this on files rather than directories?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search