# SGID on Directories



## PMc (Apr 13, 2019)

Traditionally, as I remember, setting SGID on a directory makes the files which are created within to inherit the group of the directory.

For whatever reason, on my systems this is always the case, with or without SGID bit.
Nevertheless, the bit can be set, but then recursive cp(1) may fail in unexpected ways (without actually failing).


```
$ id
uid=1100(pmc) gid=20(staff) groups=20(staff),5(operator)
$ ls -ld /var/tmp
drwxrwxrwt  15 root  wheel  254 Apr 13 02:00 /var/tmp
$ mkdir /var/tmp/XXX
$ ls -ld /var/tmp/XXX
drwxr-xr-x  2 pmc  wheel  2 Apr 13 02:03 /var/tmp/XXX
```

Now the fancy part:


```
$ chgrp staff /var/tmp/XXX
$ chmod g+s /var/tmp/XXX
$ mkdir /var/tmp/XXX/YYY
$ ls -ld /var/tmp/XXX /var/tmp/XXX/YYY
drwxr-sr-x  3 pmc  staff  3 Apr 13 02:05 /var/tmp/XXX
drwxr-sr-x  2 pmc  staff  2 Apr 13 02:05 /var/tmp/XXX/YYY
$ cp -R /var/tmp/XXX /var/tmp/ZZZ
cp: chmod: /var/tmp/ZZZ/YYY: Operation not permitted
cp: chmod: /var/tmp/ZZZ: Operation not permitted
$ echo $?
1
$ ls -ld /var/tmp/ZZZ /var/tmp/ZZZ/YYY
drwxr-xr-x  3 pmc  wheel  3 Apr 13 02:07 /var/tmp/ZZZ
drwxr-xr-x  2 pmc  wheel  2 Apr 13 02:07 /var/tmp/ZZZ/YYY
```

We can see, cp has correctly copied the tree. According to the documentation, the `-R` flag copies directory permissions also. As the original directories have the SGID bit set, cp tries to set it on the destination also. But we cannot set an SGID bit with a group that we don't belong to, and the group of the new directories is wheel because that gets inherited without an SGID bit set.

I don't find any notice on the net about the directory SGID behaviour happening even without SGID, neither about a tunable knob where this can be switched. But if this was intentionally implemented in his way, the cp behaviour should also be changed to silently ignore these errors, instead of breaking build scripts.


----------



## SirDice (Apr 15, 2019)

Note that /var/tmp has the sticky(7) bit set.


----------



## PMc (Apr 15, 2019)

SirDice said:


> Note that /var/tmp has the sticky(7) bit set.



Yes. That is important for directores writeable by multiple users.

My question is rather: what functional effect comes from setting the sgid bit on a directory? The effect that it would usually have (inherit the group to the files below) does already happen by default. And some papers recommend to set sticky, but NOT sgid on /tmp or /var/tmp.


----------



## ljboiler (Apr 15, 2019)

chmod(1) and chmod(2) don't mention anything about the SGID doing anything special for directories, just executable files.  open(2) specifically states "When a new file is created it is given the group of the directory which contains it", and mkdir(2) mentions the same behavior when it comes to creating new directories.

Working in those tmp directories with the sticky bit set, you would need to a couple more steps and change the recursive copy slightly:


```
mkdir /var/tmp/ZZZ
chgrp staff /var/tmp/ZZZ
cp -R /var/tmp/XXX/. /var/tmp/ZZZ/
```


----------



## PMc (Apr 15, 2019)

ljboiler said:


> chmod(1) and chmod(2) don't mention anything about the SGID doing anything special for directories, just executable files.



So the conclusion is: SGID is a noop on directories, except that it can break `cp -R`.
On most other flavors of unix (and probably on linux) this is different. (And I thought that was POSIX about to avoid. Well, never mind.)


> Working in those tmp directories with the sticky bit set, you would need to a couple more steps and change the recursive copy slightly:
> 
> 
> ```
> ...



No, I need an interim directory anyway for read protection, that is not the problem. I was just used to add SGID to such directories, and the solution is to leave that away.

The actual task was to have a deploy done by admin, which should create a tree under /var/app, but should not overwrite the tree of the currently running app. Then during cutover, the application-user needs to bring this new tree into the actual place. The whole process should not need root permissions, and the directory should stay unreadable to other users.
My solution is to set /var/app to admin:wheel with 1777. Then the application users can create their directories below with 0770, and these will inherit wheel, so that admin can place the deploy into them, while they cannot be read by other app-users with their same app-group.
On FreeBSD this happens by default; on other systems one would need to take extra care that the directory gets group wheel and not the app-group.


----------



## ralphbsz (Apr 15, 2019)

PMc said:


> So the conclusion is: SGID is a noop on directories, except that it can break `cp -R`.
> On most other flavors of unix (and probably on linux) this is different. (And I thought that was POSIX about to avoid. Well, never mind.)


Does someone have half an hour to spare, and can look up the exact definition of SGID in the POSIX standard?  There are two possibilities.  Either POSIX specifies the exact behavior, in which case either *BSD or Linux is wrong.  And if it is *BSD, we might want to consider opening a PR to fix it, since POSIX compliance is valuable.  Or POSIX does not specify the behavior, in which case POSIX is less useful than it could be.


----------



## SirDice (Apr 24, 2019)

Haven't found the details from POSIX but I did find that BSD and SystemV have slightly different behaviors. Basically BSD always behaves as if setgid is set and SystemV based systems do not. 

On BSD systems a new file will have group set to the group of the directory. On SystemV systems it will have the primary group of the user that created the file. In this respect Linux appears to follow the SystemV behavior by default (but can be switched to BSD behavior with a mount option or the setgid on a directory).



```
O_CREAT
	      If  the file does	not exist it will be created.  The owner (user
	      ID) of the file is set to	the effective user ID of the  process.
	      The  group  ownership  (group ID)	is set either to the effective
	      group ID of the process or to the	group ID of the	parent	direc-
	      tory  (depending	on file	system type and	mount options, and the
	      mode of the parent directory, see	the  mount  options  bsdgroups
	      and sysvgroups described in mount(8)).
```
Excerpt from CentOS's open(2) man page.


```
When a new	file is	created	it is given the	group of the directory which
     contains it.
```
FreeBSD's open(2) man page.

That was an interesting search, learned a lot more than I expected


----------



## tommiie (Aug 14, 2019)

I'm currently studying for my LPIC-1 and wanted to test the sticky bit, and the `newgrp` command to change the effective group ID of the user and its effect on creating files. Anyway, I was quite surprises when testing this on my BSD machine as I was also not aware that BSD by default gives the new file the group of the directory containing the file.


----------

