Safepath is an excellent idea. > Think about it; why do you worry that the last component of a path you are > cat-ting can be turned into a symlink?
Exactly, even with paths that contain an additional directory such as `/home/user/.ssh/authorized_keys` you could set .ssh permissions to prevent symlinking by the user but the user needs write access to `authorized_keys` > Of course, that's if you have it has your CWD and from there use a relative > path. You are right it isn't ideal and doesn't fully solve the problem. My ideal utility would be file reader/writer where the action was atomic with a set of fine grained controls specified as arguments. Get the file descriptor, verify the file met some set of rules and only take the action if it did. These rules could be permission patterns on the path and file. util write "123456789" /abc/def/file123 /(root:root 600 to root:root 600)/(640 no symlink) including checking if the file matches a hash. Alternatively if there was some way to execute a set of commands in an atomic fashion with regard to the filesystem. On Fri, Mar 28, 2025 at 4:34 AM Kaz Kylheku <k...@kylheku.com> wrote: > > On 2025-03-25 07:19, Ethan Heilman wrote: > > Without this functionality in cat, users are likely to do the next > > easiest option which is to use two separate commands to first check if > > the file is a symlink and then read the file. This can be a source of > > bugs and harm security because using two commands introduces a TOCTOU > > (Time-of-Check to Time-of-Use) issue. This is because it is always > > possible after the symlink check has occurred, but before cat is run > > the file is replaced with symlink. Such race condition bugs are > > especially dangerous because the harmful behavior is triggered only in > > rare circumstances, allowing them to enter production and be hard to > > debug. > > I made a small C library module called safepath in this area; > it seems like something that might interest you given the > topics in your post. > > https://www.kylheku.com/cgit/safepath/about/ > > The idea is that we can beat the TOCtoTOU issue if we validate the > entire path from left to right, and check that every component > we visit is protected from tampering. > > Think about it; why do you worry that the last component of > a path you are cat-ting can be turned into a symlink? > > It's because the adversary has write permissions to that > directory which is the second-to-last component! > > Or else not that, then some higher directory earlier in the path, > which lets them control the interpretation of subsequent > components. > > What if there is no such component? E.g. you don't worry that > /usr/bin/grep can be replaced by a symlink by a malicious > user. That's because / is root-owned and unwritable to > others, and so is /bin. Moreover, you don't have to just > believe that; this is verifiable. > > Unfortunately, I am embarrassed to see I have not added > tests to safepath. > > (I wrote a version of it in another language that I put into > production; that one is covered by a bunch of tests.) > > > > This is not a problem for directories because one can check if a > > directory is a symlink safely with any later action because you can > > compare the expected file path after traversing into that directory > > but before taking the action. This is safe because someone can’t > > change the directory you are already in into a symlink after you have > > transverse it. > > Of course, that's if you have it has your CWD and from there use > a relative path. > > When we have to feed several path arguments to a function or utility, > we can't be in both their directories at once.