[HN Gopher] The trouble with symbolic links ___________________________________________________________________ The trouble with symbolic links Author : jwilk Score : 196 points Date : 2022-07-22 09:04 UTC (13 hours ago) (HTM) web link (lwn.net) (TXT) w3m dump (lwn.net) | forrestthewoods wrote: | Hard linking files isn't useful in my experience because it | requires every tool working with that file to never delete it and | recreate it. However that's what exactly many tools do. So the | only way to reliably "share" a file is with a symlink. At least | this is true for my workflows. | jmillikin wrote: | IMO the article's conclusion is backwards. There's nothing wrong | with symlinks when files are opened with openat(), since in | principal the program (or controlling program) should always be | in control of the filesystem layout. It's open() that causes | problems, and complex interactions with symlinks in attacker- | controlled directories are just one of them. | | The POSIX file API was designed before the concept of capability | passing (arguably, before the concept of computer security in | general). A modern replacement would look more like Fuchsia, | where child processes are provided file access scoped to their | parent process's authority. This same scoping can also be used | within a process, for example to implement a server that can | "self-chroot". > So, more functions following the | pattern of openat() had to be created > [...] > Some | are still missing, like getxattrat() and setxattrat(). | | The functions to get/set xattrs on a file descriptor (rather than | a path) are fgetxattr() and fsetxattr(). They're not usable for | the specific case of a file descriptor opened with O_PATH, but | that restriction is both documented and reasonable -- O_PATH | doesn't allow operations that inspect the state of the file | itself, such as reading/writing. | | A better example might have been listxattr() vs flistxattr(), | because the former works on a file without read permissions, but | the latter fails on a descriptor opened with O_PATH. | listxattr("xattr-chmod000.txt", NULL, 0) = 14 | listxattr("xattr-chmod000.txt", "user.testattr\0", 14) = 14 | getxattr("xattr-chmod000.txt", "user.testattr", NULL, 0) = -1 | EACCES (Permission denied) | | vs openat(AT_FDCWD, "xattr-chmod000.txt", | O_RDONLY|O_PATH) = 3 flistxattr(3, NULL, 0) | = -1 EBADF (Bad file descriptor) | jerf wrote: | "There's nothing wrong with symlinks when files are opened with | openat(), since in principal the program (or controlling | program) should always be in control of the filesystem layout. | It's open() that causes problems, and complex interactions with | symlinks in attacker-controlled directories are just one of | them." | | One of my minor annoyances with new languages is the continued | persistence of open-based file APIs, with openat APIs shoved | off to the side if they are even implemented at all. If you | start from scratch with an openat-based API, it's not even that | hard; you basically get a file object, just one with some | different attributes and methods (or appropriate local ideas), | most of which you don't care about, and it's not that hard to | work with if you start with that from day one. It can be quite | hard to backport something deeply based on string-based path | manipulation into the *at-based APIs, though. | | I haven't deeply studied it but you ought to be able to | simulate an openat-based API on a conventional filesystem that | doesn't support it. It may not immunize you to security issues, | but at least the code ought to be as portable as any other code | that starts getting detailed about its interactions with | filesystems, which is already "kinda, not very, some elbow | grease required"... it's not like the bar is sky high because | all that stuff already works perfectly across all platforms and | filesystems anyhow. | ziml77 wrote: | Interestingly, Windows actually did exactly what's proposed at | the end when MS added them in Vista. To minimize the security | issues with symlinks, you had to elevate to admin to create them. | | It was only during the life of Windows 10 that they even added | the option to not have to elevate to create them. It was done | specifically because symlinks are often shared across systems | since they end up in places like git repos and npm packages: | https://blogs.windows.com/windowsdeveloper/2016/12/02/symlin... | faragon wrote: | You could create symlinks since Windows Vista without | permission elevation, if having disabled the UAC ("User Account | Control"). | ziml77 wrote: | Disabling UAC meant there was nowhere to elevate to because | you just had admin rights all the time. | faragon wrote: | You're right, thank you. | alerighi wrote: | There is an option (that is proposed when you install git) to | allow normal users to create symlinks. The fact is that | symlinks are very useful to a developer, and a lot of | development tools make use of them. | alerighi wrote: | Well there is the solution: work with file descriptors and not | with paths. POSIX should be extended to make sure all functions | that take a path has also the version that takes the file | descriptor (to avoid the /proc/self/fd/%d hack, that is not | portable to non-Linux OS that don't have /proc, and on Linux | requires /proc to be mounted that is not always the case for | example in sandboxes and chroots). | | You don't also only have problem with symlinks if you work with | paths, but with any kind of paths. For example is wrong to check | with the path if a file exists and then do something with it, | because it can as well be deleted, modified, etc. You have to | work with file descriptors, and use only one function (open) to | resolve the path into a descriptor one time (that is also more | efficient, since resolving a path is computationally expensive, | especially on modern filesystems). | bhawks wrote: | A posix filesystem by itself is not a defensible security | perimeter. Symlinks introduce security problems but there are | other sources as well. If you have a system where processes with | different trust profiles share a common view of a file system you | have to assume one can manipulate the filesystem state to subvert | the other. | | Android has dealt with this via locking down and isolating apps | to their own filesystems. Cross app communication and data | sharing utilizes IPC primitives that have rich caller/Callie | information that can be used to build capabilities and | authn/authz checks. | | The posix filesystem just doesn't have the | abstractions/expressiveness one would need to build a robust | security perimeter between untrusted apps. | philsnow wrote: | > The posix filesystem just doesn't have the | abstractions/expressiveness one would need to build a robust | security perimeter between untrusted apps. | | This, absolutely, but I think it's even worse than that; in my | mind the value prop of k8s is twofold: declarative | configuration and isolation that forces apps to be able to | interact over a very small boundary, the network overlay. | stjohnswarts wrote: | Does anyone else read something like this | | _Jeremy Allison gave a talk titled "The UNIX Filesystem API is | profoundly broken: What to do about it?"._ | | and immediately shut down on the person saying it because he all | know it's extremely hyperbolic given what is happening in | reality? | | It's great to not like something and point out its flaws as far | as you use them and "here's the great idea to fix those issues" | but to try to offend you audience and the Unix community as the | first words you see in a talk is a great way to have people shut | off and think "oh great another neckbeard with an overinflated | sense of self" | yyyk2 wrote: | No, there is absolutely nothing broken about symbolic links. What | is the issue here is accessing user files as root. That is | inherently unsafe in POSIX and also affects hardlinks as well | (even more so, since you have to "follow" hardlinks | http://michael.orlitzky.com/articles/posix_hardlink_heartach...). | marcosdumay wrote: | I really think that an "open the file as this user or group (also | constrained to the current user's permissions)" option will solve | the security problems better than the "open the file relative to | this root" one. Or, failing that, an usable capability system | (not SELinux). | | I don't think just checking for symlinks really solves anything. | It may make your bugs harder to exploit, what is always good, but | people use symlinks, so you have to support them, so the bugs | will stay there. | smelbe wrote: | There doesn't seem to be a way to batch together operations that | involve walking through directories and symlinks to do something | to a file. This seems to be a major source of complexity. | rwmj wrote: | I always thought Unix v7+ should have added some kind way to do | atomic groups of syscalls, eg: | begin_transaction (); lstat ("/path", ...); lstat | ("/path/foo", ...); commit (); | | In Unix v7 mkdir was not a system call. It was a setuid program | implemented using mknod + link. That was racy so the mkdir(2) | system call was added. But it could have been solved more | generally (and more elegantly) by adding transactions. | | It could also solve the whole thing with ending up with zero- | length files because you didn't use the right incantation to | update a file atomically on ext4 | (https://thunk.org/tytso/blog/2009/03/12/delayed- | allocation-a...). | bhawks wrote: | A general purpose transactional interface widene the error | space to include cross process deadlocks / denial of service | not to mention performance issues. | amaranth wrote: | Wasn't making userspace handle these kinds of things a big | part of "worse is better"? | masklinn wrote: | Turns out when facing adversarial actors worse is just | worse. | klodolph wrote: | Could you elaborate? Seems like there's a bunch of things that | can't be batched together on an ordinary file, without | involving symlinks. | masklinn wrote: | What they (and TFA) are saying is that there is no | transactional view of the FS. If you could work in | "repeatable read" (only and always see the state of the FS | before you started the transaction) symlink races wouldn't be | possible. | klodolph wrote: | Right, but there is no transactional view with or without | symlinks. | masklinn wrote: | Without symlinks it doesn't matter, because by definition | a symlink race requires a symlink to be involved. | js2 wrote: | I'm sorry symlinks are a thorn in Jeremy's side, but they are | useful from a user's perspective. Hard links don't fill the same | need. You can't normally hard link directories. If a file has | multiple links, finding them all normally requires scanning the | entire file system, so deleting a file now becomes harder. A file | with multiple links doesn't have an obvious canonical path. | | As an example of all these issues, I manage a bunch of Mac build | hosts with multiple Xcode versions installed. We only retain the | most recent patch release of each major.minor version, but drop | compatibility symlinks in place for the other versions. On macOS, | an application is just a directory. So for example we'll have: | Xcode-13.0.app -> Xcode-13.0.1.app Xcode-13.0.1.app | | From a simple "ls" it's obvious which versions are installed and | which are just compatibility shims. Symlinks are just so damn | convenient for use cases like this. Hard links don't cut the | mustard here. | | So there are more reasons for symlinks than just "hard links are | restricted to linking within the same filesystem", but yes, that | too. | | Probably I'm just lacking imagination and there's a solution that | offers the advantages of symlinks with none of the downsides, but | in my experience, we see this sort of indirection all over the | computing landscape, so it seems like there's a fundamental need | for it. | lxgr wrote: | > You can't normally hard link directories. | | That's only to avoid loops, as far as I understand. Symlinks do | allow loops, but require application programmers to handle | them. So maybe we just need better APIs/API contracts around | loops, rather than two types of links? | | > If a file has multiple links, finding them all normally | requires scanning the entire file system | | Couldn't this pretty easily be solved at the file system level? | Just store a back pointer from a file to each of its names. | | The fact that it's possible to break symlinks very easily by | deleting the pointed-to file (name) is a problem as well: | Wouldn't application developers usually, or at least sometimes, | want to know about the fact that they are about to break a link | (or conversely, not deleting the final copy of a file and not | just a reference to it)? | | > So there are more reasons for symlinks than just "hard links | are restricted to linking within the same filesystem" | | I think this might be the only real (technical/historical) | limitation. The rest could probably be worked around, but maybe | having two distinct types of links, with these other binary | decisions (allowing loops, making deletion explicit vs. a | matter of referenc counting) being more or less arbitrarily | bucketed into those two types based on what was easier to | implement. | XorNot wrote: | Hard links don't have a canonical name though - they're all | equally the same file, and this is really a problem: opening | and editing a file in one location, edits it in all of them | without you knowing what those locations might be. | | Symlinks at least explicitly declare the dependency and how | it should mutate. | | A classic being /etc/resolve.conf symlinks - if I'm untarring | and restore a symlink for it, I'm currently saying the file | should have content from somewhere else on the system - not | that the file _is_ specific content. | masklinn wrote: | > Hard links don't have a canonical name though - they're | all equally the same file, and this is really a problem: | opening and editing a file in one location, edits it in all | of them without you knowing what those locations might be. | | That is something the filesystem could store tho, in the | same way it stores the number of links to a file it could | be a bit more capable and store the links themselves | (possibly in a xattr). | | > Symlinks at least explicitly declare the dependency and | how it should mutate. | | They only declare one dependency one way, it's not like a | symlink gives you all the other symlinks to the terminal | location it will affect. | GoblinSlayer wrote: | Symlinks do that too even inevitably: no matter how you | change the file, it changes at all links and you can't | prevent it; systemd uses this feature when it creates | dependency references (the linked dependency must never | differ from the source, what hard links don't ensure). | mzs wrote: | ELOOP errno | jandrese wrote: | > Couldn't this pretty easily be solved at the file system | level? Just store a back pointer from a file to each of its | names. | | In theory yes, but no filesystem does this as far as I know. | js2 wrote: | I tried to construct my argument to make it clear that I'm | aware there are ways to solve the issues with hard links, but | they have their own sets of trade-offs. | | For hard links, it's not only that they can cause loops. | There are the other issues I outlined (linking across file | systems, no single canonical representation of the file in | the file system, finding all the links to the file, etc). | | There's no "just store a back pointer." That will obviously | introduce its own set of complexities and trade-offs. Where | do you store the pointers? What's the API for viewing them? | What's the CLI for viewing them? Is it a new switch to `ls`? | A new CLI entirely? How do you keep the pointers up to date? | What sort of locking is needed when updating the pointers? | What about `fsck`? How do you get this implemented across the | multitude of Unix and Unix-like OS's and file systems? | | (As an aside, I've been really trying to stop using the word | "just" lately as I've learned that things are rarely so | simple to justify the word.) | | Again, I'm not saying there isn't a better solution, but I | don't think it's patching up hard links. I think it's | something outside the box of both hard links and symbolic | links. | lxgr wrote: | > [...] I think it's something outside the box of both hard | links and symbolic links. | | Absolutely agreed - given your examples and all the other | challenges around backwards compatibility with decades of | application code, I'd also assume it would be something new | entirely. | | But my guess is that it would be able to meet the existing | use cases of both. | DiggyJohnson wrote: | Re: Symlink analysis: Well said. | | > (As an aside, I've been really trying to stop using the | word "just" lately as I've learned that things are rarely | so simple to justify the word.) | | Me too! I realized how it immediately frustrated me to hear | it used about my domains. I'm constantly having to work to | not seem as short/blunt/know-it-all as I _feel_. I think | this word is a connotation trap, because when I use it | feels inoffensive, but when I hear it seems blunt and | dismissive and I'm quick to assume the person doesn't | understand or empathize with the complexities of the | situation. That's a long way of saying I really enjoyed | your aside. | lloeki wrote: | >> You can't normally hard link directories. | | > That's only to avoid loops, as far as I understand | | Later HFS+ does support directory hard links, a feature | introduced for Time Machine IIRC, but generally unavailable | to the user. | [deleted] | kazinator wrote: | Symlink loops are handled in the pathname resolution function | in the kernel. Too many indirections of symlinks (typically | around forty or so?) result in the resolution bailing with an | ELOOP errno. | gweinberg wrote: | I at first read "errno" as "emo" and was trying to picture | what that would look like. | waynesonfire wrote: | symlinks are great, I don't see why we would remove such | feature. The author pointed out a bunch of issues around atomic | operations related to symlinks which in my view are valid. | Similar TOCTOU race exists with PIDs, see | https://lwn.net/Articles/773459/ | | Not sure whether the pid issue was ever resolved, havn't | checked in on that in a while. | IshKebab wrote: | Symlinks are great from a "just make it work!" point of view | but they're absolutely terrible from a "make it robust, sane | and secure" point of view. | | All of the points in the article are valid but there's even | simpler stuff like the fact that you can't canonicalise paths | (resolve ..) without reading the filesystem. | | This should be required reading: | https://9p.io/sys/doc/lexnames.html | GoblinSlayer wrote: | https://man7.org/linux/man-pages/man2/pidfd_open.2.html | OnlyMortal wrote: | I take it an "alias" isn't good in this case? An alias does | follow a move of the original - usually. | yjftsjthsd-h wrote: | Interesting; how does that work under the hood? | OnlyMortal wrote: | https://en.m.wikipedia.org/wiki/Alias_(Mac_OS) | js2 wrote: | It's macOS magic that requires HFS/APFS and doesn't work at | the POSIX layer. It would not work for my use case, no. | | An alias is like a hybrid between a symbolic link and a | hard link. Like a symbolic link, it's its own file type | whose contents point to the original, but like a hard link | it points to the original using its ID, not its path. So an | alias works even if the original is moved, but it does not | increase the original's link count and is its own distinct | entity in the file system. | rob_c wrote: | The trouble with symbolic links is users who don't understand/use | linking coming from alternate platforms and developing on/for | UNIX. | | This is a feature in the same way that shellsock was used as a | feature for many years by experts. Thankfully I'm expecting | something like POSIX to save us this time. | cosmiccatnap wrote: | Features add complexity and robust features add robust | complexity. Robust features that span a core component like | filesystem handling span many utilities. | | Maybe we should do the same search on samba vulnerabilities have | have him take a look in the mirror... | jhallenworld wrote: | Another problem with hard links: you can not hard link a | directory. It would make ".." ambiguous. | | Also with hard link directories, you would want to be able | "rmdir" non-empty directories, just to delete the link. But then | you have the problem of reference loops, so how do you reclaim | space reliably? You would need a garbage collection algorithm to | find data not reachable by root. | jmillikin wrote: | Whether directories can be hardlinked depends on the filesystem | and OS. When macOS switched from HFS+ to APFS, one of the | changes was that they dropped support for directory hardlinks. | | https://developer.apple.com/library/archive/documentation/Fi... | jhallenworld wrote: | I'm not a macOS user, but these sure seem to add complexity: | | https://stackoverflow.com/questions/80875/what-is-the- | unix-c... | | The filesystem has to check for and disallow loops. | samatman wrote: | A filesystem can afford a very, very slow, mark and sweep. | nine_k wrote: | The GC algorithms for hardlinked directories can be the same as | for harlinked files: reference counting. | jhallenworld wrote: | Only if there are no loops.. | mongol wrote: | "X is fundamentally broken" is a tired trope. To me, something is | broken if it is no longer working as intended. It used to work, | but now it does not - it is broken. | | If something works as intended, but its utility is limited, and | it can be improved, it is not broken. | joosters wrote: | Symlinks work as intended, but they cause a lot of unintended | security vulnerabilities, i.e. they break lots of otherwise- | functioning code. | | You can play with your words and redefine their meanings, but | the vulnerabilities remain. | pif wrote: | > they break lots of otherwise-functioning code. | | There is no otherwise! POSIX has symbolic links: if your | software does not function with symbolic links, it does not | function on POSIX. | IshKebab wrote: | Of course there is otherwise. Windows (more or less) | doesn't have symlinks. Plan9 doesn't have symlinks. You | don't _have_ to have symlinks. | | Can you not imagine anything other than POSIX? | mongol wrote: | They have been around for 40+ years, they don't break code | unless we are talking about code predating their | introduction. It is not me playing with words, I am just | pointing out a tired trope. | indymike wrote: | > You can play with your words and redefine their meanings, | but the vulnerabilities remain. | | I don't think OP was trying to play with words, I do think | there's an absolute "this symlink thing is a vuln" vs an | absolute "I use symlinks to make X work" argument. Symlinks | have always been at the line between the absolutes. They do | enable a great deal of functionality but they can be a | security risk, and source of bugs when developers don't | handle them correctly. That said, they are heavily used | feature on unix like oses. My /usr/bin on Ubuntu has 48 of | them (most were put there by apt installed packages). | rob_c wrote: | > but they cause a lot of unintended security vulnerabilities | | No, bad coders on UNIX platforms do this. | | The code may be valid code, but if it's intending to support | running on UNIX it should do it properly not assuming it's on | a FAT32 filesystem in 2022. | | Or, even better, run the code you don't trust on fat32 | filesystems, see how far that gets. | joosters wrote: | Don't victim blame. | | Do you really think that using open(), stat(), lstat() (!), | realpath(), mkdir(), rename() etc etc etc is a sign of a | bad coder? The problem is that the APIs set you up for | unexpected failure, and even some of the provided | workarounds to 'safely' handle symlinks don't do it well | enough. | | In the case of symlinks, I think it's fair to blame the | tools rather than the workman. | zx8080 wrote: | API is always simplification and is not supposed to be | used without understanding concepts and reality under the | hood. | | Example: wanna show 1M POI in browser on some small | territory. Openmaps/googlemaps API allows that, no prob. | Looks good, yeah? Sorry, doesn't work. Because 1M is too | large to show and browser gets stuck. | | The API do not prevent _all_ kinds of legshooting | engineers invent. | rob_c wrote: | Don't worry, they don't listen to speaking out against | bigG or others for being bad | rob_c wrote: | This is not victim blame. RTFM READ IT!!! | | Most sane languages and low level tools describe what you | want and how to work correctly. | | If you don't want this feature in the filesystem, move to | one that doesn't support it, or better yet submit a patch | to run the filesytem you want with this feature | deactivted for "security concerns". | | Demanding a whole OS change the way it works for | bad/lazy/inept coders is akin to 2 people getting blind | drunk and blaming the other person or the drink for the | stupid things they did. Take some responsibility. | com2kid wrote: | > Demanding a whole OS change the way it works for | bad/lazy/inept coders is akin to 2 people getting blind | drunk and blaming the other person or the drink for the | stupid things they did. Take some responsibility. | | And if people were just more careful, none of rust's | memory safety stuff is needed! Also, why do modern | languages hand hold multi-threading so much, just give | developers some mutex primitives and let them have at it, | the good coders will be just fine! | | Of course the rest of us will have to deal with machines | getting pwned due to security bugs, but hey, at least the | "well written" programs won't have those problems... | rob_c wrote: | Stop defending poor coding and lack of skill. | | Everything you're saying is an excusory situation for | hiring poor coders at minimum wage who can't or won't | read documentation. This is 80IQ points South of frankly | most of the conversations on here. | | Yes the rest of us cope with security incidents. There | will always be security incidents. Stop defending | practices that leads to them. | com2kid wrote: | > Stop defending poor coding and lack of skill. | | This is a hopelessly elitist attitude. Also it is a | useless one, over 1000 CVEs, yelling "be better at your | job!" is just going to result in another 1000 CVEs. That | is exactly what happened for decades with buggy C code, | buffer overflows and use after frees, for a long time the | refrain was "just do better!". | | Well millions of dollars of damages later, it turns out | berating people to "just do better" doesn't actually make | things any better. A combination of static analysis and | runtime tooling, and then the eventual creation of new | programming languages that allow for correct modeling of | memory ownership, is what the industry en masse has | decided on. | | For APIs that get misused? The solution is to provide | higher level APIs that allow programmers to easily | accomplish the correct thing in a secure manner. | | As an aside, and in general, when designing software, I | want to maximize the amount of brain power I am | dedicating to solving the business problem at hand. | Dealing with poorly designed insecure APIs detracts from | me getting my actual job done. | | > Stop defending practices that leads to them. | | The practice in this case is the direct use of filesystem | APIs that were designed in the 1970s for a very different | security ecosystem than what exists today. | | Lots of things designed in the 1970s are not secure by | default. Heck most things designed in the 1970s, outside | of maybe some IBM Mainframe stuff, was not designed to be | secure by default. | | What you are arguing is that instead of buying a fire | extinguisher to put in the kitchen of an old house, | people should just try and not set things on fire. | | I mean, yeah, sure, good goal, but _buy the fire | extinguisher anyway_. | rob_c wrote: | How is it hopelessly elitist to call out insecure code as | being INSECURE!!! | | My whole point is the same as yours fix it at the source. | You seem to think hacking off the hands of some coders is | safer (I may agree). But why not try to EDUCATE THEM?!? | | Education costs 1000s of dollars at most rather than your | hypothetical billion dollar APPLICATION LEVEL hack. | | Why are you so elitist to assume people can't cope with | these concepts? | | My whole point is that they need to be tought they're | running on a Unix server rather than an 1998 SD card. The | rest of your complaining is either you don't understand | this or are trying to excuse bad or insecure practice as | acceptable. If this is your case. RUN THE CODE IN AN | ENVIRONMENT WHERE THIS CAN'T HAPPEN. Seriously there are | filesystems and options for this. | | Calling for these features to be removed from extX, ZFS | or other shows you don't understand storage technologies | well enough. | com2kid wrote: | > How is it hopelessly elitist to call out insecure code | as being INSECURE!!! | | If a lot of code, written by a lot of different | engineers, all ends up being insecure, it is worth | asking, why is code dealing with this particular domain | so often insecure? | | > But why not try to EDUCATE THEM?!? | | You can do that, and of course we should, but here is the | thing about security: | | The good guys have to write secure code _every_ time, or | else the attackers guys win. | | Eternal vigilance is inhumanly hard to maintain. A better | solution is to write higher level APIs or API wrappers | that don't have these flaws. | | > Why are you so elitist to assume people can't cope with | these concepts? | | Sure they can, but how many concepts can people cope with | at once? Humans have a limit for how much they can juggle | in their head. A huge part of software engineering is | picking what abstraction layer to operate at. If I am | writing code that deals with tons of string parsing and | manipulation, I'd be a fool to write it in C or C++. Now | I've done that when I needed the performance, but | managing a massive number of strings in native code is | easily 5x the work compared to using a GC language that | also automatically tracks string length. | | C is the wrong abstraction there. And indeed an obscene | number of security holes have historically centered | around string processing in C. That is because on top of | managing all the business logic (which may be obscenely | complicated by itself!) engineers now have to do so in a | language that is _really_ bad at dealing with strings and | they have to do a lot of mental work to ensure the code | is correct. | | If I am manually flipping bits in hardware, well, JS can | do it (I have seen it!) but honestly, that shouldn't be | anyone's language of choice for directly interfacing with | hardware. | | (Doing that in C, really fun!) | | > Calling for these features to be removed from extX, ZFS | or other shows you don't understand storage technologies | well enough. | | I am not saying that. I am saying that the original POSIX | APIs make writing secure code around symlinks hard, and I | am saying that solely based on the fact that a bunch of | security holes around POSIX APIs and symlinks exist! | | This isn't some shocking statement. The original POSIX | APIs make a lot of things hard. | the8472 wrote: | Part of the problem is that handles on directories on which | one can then use the the *at family of syscalls are not | first-class citizens in many programming languages. Which | in turn might be due to portability concerns with windows, | e.g. Java's SecureDirectoryStream isn't available there[0]. | Apparently windows does have an openat-like API[1], but | it's low-level. | | Programmers aren't using them because the language standard | libraries point them in the wrong direction. | | [0] https://github.com/google/guava/wiki/Release21#user- | content-... [1] https://github.com/rust- | lang/rust/blob/1c63ec48b8cbf553d291a... | rob_c wrote: | This a serious issue in support of the language for | running on a UNIX system. | | Frankly blaming the kernel/filesystem for this is like | saying I want my 720p monitor to display 8k correctly, it | must be the display-drivers fault... | ziml77 wrote: | Weird that you need to dip down to an Nt* function to | open a child given a parent handle. It's not like it's | unheard of to be able to do that in the Windows API. The | registry is also a hierarchical system and opening any | key requires passing in a parent key handle (the root | handles are predefined). | ChrisSD wrote: | It's a quirk of history, imho. If Win32 had been written | without concern for what came before, it probably would | have more closely followed NT conventions. | | But it wasn't. It followed on from Win16 and DOS so, to | an extent, it emulated DOS-style path and file handling. | After all, that's what developers and users were familiar | with. The Windows registry did not have all this baggage | so it followed the style of the NT kernel. | | Though this doesn't explain why Win32 never added | CreateFileRelativeToDirectoryHandleW | littlestymaar wrote: | That "X - designed in the 70s when we had no idea of anything | regarding computers - is fundamentally broken" isn't so | surprising after all. | | In fact, computers are probably the only place in the entire | technology landscape where we keep using almost unmodified | stuff from the 70s and decided we cannot change it because | there's too much things relying on it. | | I don't like breaking everything all the time more than anyone, | but maybe one time every 20 or 30 years is OK... | SiempreViernes wrote: | I take it that here "technology landscape" means something | like "the gui portion of the software stack", right? | Beltalowda wrote: | > In fact, computers are probably the only place in the | entire technology landscape where we keep using almost | unmodified stuff from the 70s and decided we cannot change it | because there's too much things relying on it. | | Bridges and buildings from the 1970s (and much older) are | still working fine today. | | The thing is, if I do decide to replace my bridge or building | because it's old and outdated then I can just replace that | one thing without affecting much else. With computers, that | is obviously not the case: you need to replace the entire | city. | | Plus, it's not really the case that we "keep using almost | unmodified stuff from the 70s"; while many concepts remained | the same and things remained compatible, things have been | greatly extended and modified since; it's like those old | buildings that were built during the middle ages (or | sometimes even earlier) that have been changed and upgraded | extensively throughout the centuries to the point you really | need to know where to look to see it's actually a centuries- | old building. | mmis1000 wrote: | > Bridges and buildings from the 1970s (and much older) are | still working fine today. | | I am not sure about that, floods here go past the 200 years | average line at the time that many bridges or buildings was | designed. And actually breaks a lot of buildings. | | Climate change these days is just as unexpected as hackers | these days to who we were. | Shorel wrote: | It would be like upgrading the train network to increase | the distance between rails, to increase the size of cargo | that can be transported. | | Not just one city needs the upgrade, all of them will need | it, and all the related infrastructure like bridges and | tunnels too. | com2kid wrote: | > Bridges and buildings from the 1970s (and much older) are | still working fine today. | | With modern earthquake straps added, and I bet the locks | got replaced a few times over, also the building likely had | its insulation improved, better venting added, a sprinkler | system, fire exits, and a wheelchair ramp put in at some | point. | | Are there a few quaint stone bridges from 1700 still in | use? Sure, going over the neighborhood creek. But all the | bridges around me have undergone serious upgrades or | retrofitting over the decades. | toast0 wrote: | Internal plumbing is largely unchanged. Sure, there's more | flexible pipe, and a lot more plastic pipe, and a lot more | quarter turn valves, but thread pitch and pipe diameters are | largely unchanged and unchangable. | kllrnohj wrote: | I don't think that's entirely true. There's plenty of major | systems that have made fundamentally incompatible breaking | changes in order to move things forward. Windows did that | with Vista, Android did that with Linux (eg, app sandboxing | per UID, heavily restricted filesystem access to shared | directories), etc.. | | It's kinda mainly desktop/server Linux where there's this | inability to move forward. | nine_k wrote: | If it used to work without letting others pwn your account or | box, and not it fails to, it's broken. | 3pt14159 wrote: | The word "broken" came from before we had constant arms races | in technology. Obviously we don't call clubs broken because we | now have rapid artillery, but there was enough time between | clubs and swords, and swords and guns to allow transitions | away. | | When I'm exposed to a core OS feature I expect by default that | it should not come with expected, critical security | vulnerabilities. It is rational to expect a user to use the | basic features of an OS and expect them not to cause severe | issues. You can say it's not broken, and that's true in as much | as they're still _functional_ , but if by broken one means the | larger question of "is this reliable and safe?" then I think | the answer is pretty clear that symlinks are broken. | ploxiln wrote: | In the modern world, the demand seems to be that every tool | be perfectly safe in every situation no matter what you do | (and it seems practically nothing lives up to this demand, | given the ever increasing river of silly CVEs for almost | every component, like regex DoS on build tools). | | It's important to understand the scope of the issue. If you | create and operate on your own symlinks in your own folders, | there is no problem. The problem is when a more privileged | user operates on folders that can be written to by less | privileged users, for example system daemons (like a /tmp | cleanup, or a web server serving /home/*/www), or suid | binaries. These things need to be written very carefully, it | is now clear. | | But if I'm working with my own files, media, source code, | build tools, web pages, etc, in my own folders, then symlinks | are still fine. | | And there is an existing setting to mitigate a couple common | forms of the issue that does exist when accessing folders | other users can write to: | https://www.kernel.org/doc/html/latest/admin- | guide/sysctl/fs... | 3pt14159 wrote: | I appreciate the reply, but after thinking about it I think | it's more akin to someone having been sold a house only to | be told eight years later that the seller of the house knew | that if someone tied a shoelace to the front door and | pulled on it, then the entire house would explode. | | Could we consider it a broken doorknob Pierce? | seoaeu wrote: | > In the modern world, the demand seems to be that every | tool be perfectly safe in every situation no matter what | you do | | The problem is that there is such a huge number of tools in | widespread use that each one causing even a few security | vulnerabilities means that the ecosystem overall is | constantly vulnerable | shadowgovt wrote: | Don't hard links suffer from the issue that because they're | actually links to a specific file, not path pointers, that you | can replace the target file thinking you have updated something | in the system and instead have stale hard links lying around, | referencing the older version when you intended to replace the | older version for all users? | | I think that in a hypothetical world where symlinks worked like | hard links, we'd be swapping out the security complaints in this | post for articles about how hard it is to upgrade a POSIX system | properly, tools and tricks you can use to make sure you truly | replaced all instances of a binary with a known vulnerability, | and so on. | jhallenworld wrote: | Hardlinks exist, so you already have this problem. Tar has to | keep track of inode numbers to recreate hard links for example. | shadowgovt wrote: | What I mean is that it's pretty SOP to have a package that | installs a new command to work by installing to /opt/my- | package and then symlinking /usr/bin/my-cmd to /opt/my- | package/my-cmd. | | In the absence of symlinks, that link would be hard and dpkg | et. al would have to do package management by deleting the | /usr/bin/my-cmd link and re-creating it instead of letting it | ride, trusting that it will point to the correct thing when | the update completes because the target bin will have | changed. | partialzero wrote: | Maybe I'm being naive, but I don't get how "pathnames as a | concept are now utterly broken in POSIX". Isn't this "merely" a | problem that the resolution of the path name is dynamic and can | change between inspection and use? Wouldn't a practice of | resolving pathnames once (recursively, atomically, whatever) into | an immutable, opaque, direct handle, such as file descriptor, | before use solve this issue? I realize what I just said may be | tantamount to "all file io ops taking path strings are broken" - | but that seems like a problem with the initial API design, not | with the concept of having a level of indirection in path name | resolution itself. | bityard wrote: | This is basically what I was going to say. The article spends a | lot of time arguing that TOCTOU patterns introduce security | vulnerabilities, which I think all programmers (should!) | already know but then comes to the weird conclusion that we'd | just be better off without symlinks instead of designing an API | to work with them atomically. | | Kinda reminds me of how a lot of UX changes happen: "This | really popular feature is a bit kludgy and hard to maintain, | let's just rewrite the whole app without it! (Instead of doing | the work required to make it not suck.)" | drdec wrote: | Almost all the TOCTOU examples given in the article could be | modified not to involve symlinks and still be valid. | amluto wrote: | I personally think that hard links should go away. I have trouble | thinking of any use for hard links that isn't better served by | CoW links. Hard links have quite surprising properties with | respect to chmod, they are awkward to handle in archival tools, | and even reliably identifying them is awkward to impossible in | general. | kazinator wrote: | > _An application running as root may try to check that | /data/mydir is a regular directory (not a symlink) before opening | the file /data/mydir/passwd. In between the time the program does | the directory check and the file open, an attacker could replace | the mydir directory with a symlink to /etc, and now the file | opened is, unexpectedly, /etc/passwd. This is a kind of race | condition known as a time-of-check-to-time-of-use (TOCTOU) race._ | | That application is doing the wrong check; it should be | validating that every component of the path is a directory which | is only writable to root. | | First you stat("/"). OK, that is a directory and writable only to | root: so no non-root process can put a symlink there. Next we | check "/data". OK, that's a directory, and since we know / is | owned by root and not world-writable, /data cannot be replaced by | a symlink. | | And so on ... | | This can easily be made into a function like | safe_path("/data/dir/path/to/mypasswd") which returns true only | if no pathname component is something which a user other than the | caller, or root, could tamper with to point to a different file. | | The open system call should have a flag for this, O_SAFE. That | would alter the behavior of the name resolution function | (traditionally, "namei") to do these checks along the path. | | The path could have symlinks, if they are not tamperable from the | POV of the calling user. | | Typically superuser applications in Unix rely on filesystem | structure. They set environment variables like PATH carefully, | and stick to accessing data in known directories that had better | be safe. If /data/mydir/passwd is something that is manipulated | by a root application, then the system is misconfigured if any of | these is writable to a non-root user: /, /data, /data/mydir or | /data/mydir/passwd. | | If that is the case, you don't need symlinks to wreak havoc on | the application. You can, for instance, write your own password | into that password file and then falsely authenticate with that | app. | GoblinSlayer wrote: | >Clients that have write access to the exported part of the file | system under a share via SMB1 unix extensions or NFS can create | symlinks that | | So, it's not symlinks broken, but SMB1 unix extensions broken | when exposed to the world for symlink creation. AIU this features | doesn't even serve windows interoperability. And if the author | wanted to disable all symlinks altogether, what is the purpose of | these extensions? ___________________________________________________________________ (page generated 2022-07-22 23:00 UTC)