As a follow-up question to this one, I thought of another approach which builds off of @caf's answer for the case where I want to append to file name and create it if it does not exist.
Here is what I came up with:
- Create a temporary directory with mode 0700 in a system temporary directory on the same filesystem as file
name. Open filenamefor reading only andO_CREAT. The OS may follownameif it is a symbolic link.Usemkstempto create a temporary file in the temporary directory and attempt torenamethe temporary file that was created bymkstempto filename.
Open filenamefor reading only andO_CREAT | O_EXCL.- Iteratively attempt to make a hard link to
nameat a temporary name within the temporary directory. If ever thelinkcall fails due to an error other than "the link target exists" (errnoEEXIST), then exit. (Maybe someone has come along and removed the file atname, who knows?) - Use
lstatontemp_name(the hard link). IfS_ISLNK(lst.st_mode), then exit. opentemp_namefor writing & append (O_WRONLY | O_APPEND).- Write everything out. Close the file descriptor.
unlinkthe hard link.- Remove the temporary directory.
(All of this, by the way, is for an open source project that I am working on. You can view the source of my implementation of this approach here.)
Is this procedure safe against symbolic link attacks? For example, is it possible for a malicious process to ensure that the inode for name represents a regular file for the duration of the lstat check, then make the inode a symbolic link with the temp_name hard link now pointing to the new, symbolic link?
I am assuming that a malicious process cannot affect temp_name.
EDIT: link does not overwrite the target so creating a "placeholder" temporary file is not what I wanted to do. I have since updated my code and have updated the steps above.
EDIT2: I am now using an alternate procedure for step 2 to create file name if it does not exist which I do not think is susceptible to this problem.
EDIT3: Even better than renaming a temporary, empty, regular file to name, which also has the effect of unlinking name, then renaming, I can open the file O_RDONLY | O_CREAT | O_EXCL.
The POSIX standard for open states:
If
O_EXCLandO_CREATare set, andpathnames a symbolic link,open()shall fail and seterrnotoEEXIST, regardless of the contents of the symbolic link.