Proposal Details
On *BSD and Solaris systems, open(2) and mkdir(2) will not create a file with the sticky bit set. The FreeBSD sticky(8) manpage lists this as a bug. I don't know the historical reasons for this behavior.
#8383 reported this as a bug in os.OpenFile and os.Mkdir, and subsequently CL 1673 changed OpenFile to set the sticky bit after creation when the file mode includes ModeSticky. (A later CL added the same behavior for Mkdir.)
This operation is racy: When creating a file with ModeSticky, we:
- Check to see if the file exists.
- Open the file (possibly creating it).
- If the file did not exist, stat it to find its current mode.
- If the file did not exist, chmod the file to add the sticky bit.
If the target file is created between steps 1 and 2, then we may chmod a file we did not create. I think this is of minimal concern.
If the target file, or some part of its path, is replaced with a symlink between steps 2 and 4, however, we will chmod the target of the link, which could be anything. This is difficult to exploit: It requires that a privileged process be creating a file with the sticky bit set in a location that is writable by an attacker. If an exploitable scenario exists, however, it should be fairly straightforward to convert to local privilege escalation.
We can avoid the race in OpenFile by using File.Chmod to chmod the actual file opened.
Avoiding the race in Mkdir is harder, because mkdir(2) doesn't return a handle to the new directory. We could work around it with a multi-phase dance of opening the parent directory, using mkdirat(2) to create the new directory, and then using openat(2) to open the newly-created directory. This still leaves a race condition--we might be opening a directory different than the one we just created--but at least we'll be certain that we're chmodding a directory in the exact same location as the one we created.
I propose that working around the OS behavior in this case is a mistake, and that rather than putting in complicated workarounds we should remove the behavior introduced by CL 1673, with a GODEBUG to reenable the original behavior, race conditions and all.
Proposal Details
On *BSD and Solaris systems,
open(2)andmkdir(2)will not create a file with the sticky bit set. The FreeBSD sticky(8) manpage lists this as a bug. I don't know the historical reasons for this behavior.#8383 reported this as a bug in
os.OpenFileandos.Mkdir, and subsequently CL 1673 changedOpenFileto set the sticky bit after creation when the file mode includesModeSticky. (A later CL added the same behavior forMkdir.)This operation is racy: When creating a file with
ModeSticky, we:If the target file is created between steps 1 and 2, then we may chmod a file we did not create. I think this is of minimal concern.
If the target file, or some part of its path, is replaced with a symlink between steps 2 and 4, however, we will chmod the target of the link, which could be anything. This is difficult to exploit: It requires that a privileged process be creating a file with the sticky bit set in a location that is writable by an attacker. If an exploitable scenario exists, however, it should be fairly straightforward to convert to local privilege escalation.
We can avoid the race in
OpenFileby usingFile.Chmodto chmod the actual file opened.Avoiding the race in
Mkdiris harder, becausemkdir(2)doesn't return a handle to the new directory. We could work around it with a multi-phase dance of opening the parent directory, usingmkdirat(2)to create the new directory, and then usingopenat(2)to open the newly-created directory. This still leaves a race condition--we might be opening a directory different than the one we just created--but at least we'll be certain that we're chmodding a directory in the exact same location as the one we created.I propose that working around the OS behavior in this case is a mistake, and that rather than putting in complicated workarounds we should remove the behavior introduced by CL 1673, with a GODEBUG to reenable the original behavior, race conditions and all.