• The trouble with symlinks? Bullshit!

    From Kaz Kylheku@21:1/5 to All on Mon Jul 25 02:22:38 2022
    So, LWN publishes an article

    https://lwn.net/Articles/899543/

    Some Sampba person working at Google claims that there is no
    safe way of using symlinks; they are fundamentally broken and
    wreck the whole POSIX filesystem API, rendering it insecure.

    In response, I wrote a library function whose interface
    looks like this:

    /*
    * safepatch_check error codes
    */
    enum {
    SAFEPATH_OK, /* path appears safe */
    SAFEPATH_UNSAFE, /* path traversible, unsafe */
    SAFEPATH_PERM, /* path not traversible due to perms */
    SAFEPATH_NOENT, /* component other than last doesn't exist */
    SAFEPATH_NOTDIR, /* interior path component isn't a directory */
    SAFEPATH_INVAL, /* path is invalid */
    SAFEPATH_NOMEM, /* out of memory */
    SAFEPATH_LOOP, /* more than 8 levels of symlink */
    SAFEPATH_TOOLONG, /* component or symlink target too long */
    };

    int safepath_check(const char *name);
    const char *safepath_strerr(int err);

    The git repository is here:

    https://www.kylheku.com/cgit/safepath/about/

    (Unfortunately, I don't yet have documentation or test cases, sorry.)

    The idea is that we walk a path component by component and validate that
    what we have seen so far is "safe". If the path we have seen so far is
    "safe", and we can validate that the next component is safe, then the
    path plust that component is also "safe".

    The function performs its own symlink resolution; it doesn't rely
    on the kernel. If "alpha" is a path which we believe to be "safe"
    and the next components is "alpha/beta/omega", and "beta"
    is a symlink, then because "alpha" is safe, we can trust readlink("alpha/beta"). That is, we can trust the link itself,
    not necessarily what it points to. We can substitute that
    ourselves. Say beta resolves to "gamma/delta". We produce "alpha/gamma/delta/omega". "alpha" is still safe, and so we
    examine "gamma", and keep going. If "gamma" is a directory
    which is not writable to any other user other than root, then
    we can mark it safe, and proceed to delta.

    This is all just induction. We start with some base hypothesis like
    that we can safely examine either the "/" or "." directory, and
    check its permissions, and find it to be safe.

    The inductive hypothesis is that if we can find some path to
    be safe, we can evaluate the next component and validate whether
    that is safe according to some cases, and then add it to the path.
    Or else proclaim that it's not safe.

    By induction, if we reach the end of the path, we have shown that
    it's safe.

    We can use system calls which use the entire left part of the path,
    because since that is safe, we can trust it to be free of tampering.

    I posted this to HackerNews; the Samba guy found it and commenting
    on it, saying it doesn't work.

    https://news.ycombinator.com/item?id=32216924

    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Kettlewell@21:1/5 to Kaz Kylheku on Mon Jul 25 11:03:41 2022
    Kaz Kylheku <[email protected]> writes:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    I think the discussion needs a clear statement of threat model. A file
    server (for instance, Samba) which has to expose a portion of the
    filesystem namespace controlled by untrusted users is a different story
    to a desktop application that only ever touches the current user’s
    files.

    --
    https://www.greenend.org.uk/rjk/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Kettlewell@21:1/5 to Richard Kettlewell on Mon Jul 25 12:10:15 2022
    Richard Kettlewell <[email protected]d> writes:
    Kaz Kylheku <[email protected]> writes:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    I think the discussion needs a clear statement of threat model. A file
    server (for instance, Samba) which has to expose a portion of the
    filesystem namespace controlled by untrusted users is a different story
    to a desktop application that only ever touches the current user’s
    files.

    To expand on this point:

    File servers are often supposed to limit what they will serve to
    particular subsets of the namespace. For example a web server might be configured to publish only a document root and a file server might be configured to publish only home directories. The latter must also avoid bypassing permissions and ACLs on user files (i.e. user A must not be
    able to read or modify user B’s private files).

    There’s several models for how such a server might run.

    A web server may run as a special web server user, _different_ from the owner(s) of the files and directories it serves. Your library will see
    all those files and directories as untrusted:

    root@araminta:/home/richard/junk/safepath# id
    uid=0(root) gid=0(root) groups=0(root)
    root@araminta:/home/richard/junk/safepath# ls -l testsp
    -rwxrwxr-x 1 richard richard 68536 Jul 25 11:24 testsp
    root@araminta:/home/richard/junk/safepath# ./testsp testsp
    safepath_check("testsp") == path contains untrusted component

    So it doesn’t seem to be suitable for this use case. The web server will refuse to serve any files.

    (The user of root is just illustrative; the point is that it’s a
    different user from the file owner.)

    The web server might run as the same user as the owner of the files and directories it serves. In this case it will report those files as safe
    ... but they will still be reported as safe if they are symlinks to
    something secret:

    richard@araminta:~/junk/safepath$ ls -l foo
    lrwxrwxrwx 1 richard richard 29 Jul 25 11:33 foo -> /home/richard/.ssh/id_ed25519
    richard@araminta:~/junk/safepath$ ./testsp foo
    safepath_check("foo") == path appears safe

    (The SSH key is just illustrative; a web server is more likely to have a
    TLS key.)

    A file server (NFS, Samba, etc) is usually more complicated:
    - a single instance may be serving to multiple users
    - the users are, for the most part, untrusted
    - it’s essential that user permissions and ACLs are honored

    Apart from that the issues are similar and have the same problems as
    above.


    Another use case for path verification is unpacking an untrusted archive
    (e.g. tar or zip). All the files will be owned by the calling user so
    your library will report them as trusted. But a symlink in the archive
    could point at an important file and allow it to be overwritten.

    (From memory) at least one device’s signed firmware updates were
    compromised by this - the update image and signature were contained in a
    tar which was unpacked without any checks on symlinks, allowing
    arbitrary files to be modified with an attacker-constructed tar
    containing a symlink to a target file and then new contents for the
    target file under the same name as the symlink. For example the target
    file could be the public signing key trusted by the next phase of the
    firmware update:

    richard@araminta:~/junk/safepath$ ls -l foo signing_key
    lrwxrwxrwx 1 richard richard 11 Jul 25 12:06 foo -> signing_key
    -rw-rw-r-- 1 richard richard 7 Jul 25 12:06 signing_key
    richard@araminta:~/junk/safepath$ ./testsp foo
    safepath_check("foo") == path appears safe

    So your library doesn’t seem suitable for this use case either.

    TLDR your library makes assumptions about both the trust placed in files
    owned by the current user and about which files are actually of
    interest, and these assumptions are violated in many use cases.

    --
    https://www.greenend.org.uk/rjk/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Richard Kettlewell on Mon Jul 25 13:39:51 2022
    On 2022-07-25, Richard Kettlewell <[email protected]d> wrote:
    A web server may run as a special web server user, _different_ from the owner(s) of the files and directories it serves. Your library will see
    all those files and directories as untrusted:

    root@araminta:/home/richard/junk/safepath# id
    uid=0(root) gid=0(root) groups=0(root)
    root@araminta:/home/richard/junk/safepath# ls -l testsp
    -rwxrwxr-x 1 richard richard 68536 Jul 25 11:24 testsp
    root@araminta:/home/richard/junk/safepath# ./testsp testsp
    safepath_check("testsp") == path contains untrusted component

    Thanks for taking the trouble to fetch and try this.

    This is a case where the application has invented a security model not recognized in the OS; it's manipulating a set of data belonging to different users (tagged with attributes from the OS security model, such as file ownerships) yet using a single account to do it (bypassing the OS model of running as the effective user.) The application itself is enforcing the
    idea that user Mallory cannot access the files of Alice.

    In this case, my library makes the right call. You are "root" or
    "www-data" or whoever, of course a directory controlled by "richard"
    is not safe.

    There could still be uses for a function like safepath_check in a web
    server, like that paths to its configuration files and whatnot have
    sane permissions.

    The web server might run as the same user as the owner of the files and directories it serves.
    ... but they will still be reported as safe if they are symlinks to
    something secret:

    richard@araminta:~/junk/safepath$ ls -l foo
    lrwxrwxrwx 1 richard richard 29 Jul 25 11:33 foo -> /home/richard/.ssh/id_ed25519
    richard@araminta:~/junk/safepath$ ./testsp foo
    safepath_check("foo") == path appears safe

    Likewise, the user cannot be protected from itself. An application
    cannot use a path prefix as a sandbox to ensure it only accesses a
    subtree. Even if it calls realpath on a path, and at that time the path contains nothing but directories which are only writable to the user,
    that same user could, before that path is used, change one of those
    components to be a symlink outside of that sandbox.

    Only the kernel can provide an inescapable subtree sandbox, with
    some chroot-type mechanism, or some way of disabling symlink following
    or whatever.

    If you want a file server which executes on behalf of user Bob to serve
    Bob files, and you want to implement some application-level security
    scheme that doesn't exist in the OS, like serving only a specific subset
    of all the files that the OS actually allows Bob to access, you have to make sure Bob has no way of executing arbitrary code as Bob on that entire machine: that file serving application has to be the only way Bob can do anything, and carefully written to enforce its own finer-grained access model.

    If Bob can log into the machine and plant random symlinks into the tree that the server is serving, then that may be intractable.

    Another use case for path verification is unpacking an untrusted archive (e.g. tar or zip). All the files will be owned by the calling user so
    your library will report them as trusted. But a symlink in the archive
    could point at an important file and allow it to be overwritten.

    Under this threat, we are not concered about some malicious parallel
    activity trying to exploit a TOCtoTOU so this is solvable;
    just not by my function.

    The unpacking utility can safely check that there are no external
    symlinks in the unpacked tree

    This is tricky because there can be cases where a tarball can
    make legitimate use of absolute symlinks. E.g. a filesystem tarball
    which holds the image for an embedded system could contain
    foo/bar -> /absolute/elsewhere/foo/bar
    type symlinks.

    The extraction function has to have the option of rewriting the targets
    of symlinks, if they are absolute. Like if we extract the archive into
    the directory /extract/to/here, it would be useful for the extraction to
    take all an absolute symlink target /absolute/there, and turn it either
    into the absolute target /extract/to/here/absolute/there or else a
    relative target like ../../absolute/there with the right number of .. components.

    You also have to watch for relative OR absolute symlink targets that
    climb out of the archive's root with too many .. components,

    (From memory) at least one device’s signed firmware updates were compromised by this - the update image and signature were contained in a
    tar which was unpacked without any checks on symlinks, allowing
    arbitrary files to be modified with an attacker-constructed tar
    containing a symlink to a target file and then new contents for the
    target file under the same name as the symlink. For example the target
    file could be the public signing key trusted by the next phase of the firmware update:

    richard@araminta:~/junk/safepath$ ls -l foo signing_key
    lrwxrwxrwx 1 richard richard 11 Jul 25 12:06 foo -> signing_key
    -rw-rw-r-- 1 richard richard 7 Jul 25 12:06 signing_key
    richard@araminta:~/junk/safepath$ ./testsp foo
    safepath_check("foo") == path appears safe

    So your library doesn’t seem suitable for this use case either.

    That's right; and of course, that is a "wontfix". There is no way to
    tell that this "foo -> signing_key" symlink a directory owned by "richard" is something that the person using the "robert" account doesn't want;
    there isn't anything in the filesystem which provides this information.

    TLDR your library makes assumptions about both the trust placed in files owned by the current user and about which files are actually of
    interest, and these assumptions are violated in many use cases.

    The model is "this user can access anything in the namespace according to the OS permission model; we would like to protect them from trusting paths controlled by other users."

    Security concepts fabricated by applications in user space, like

    - out of a set of files accessible to a user, only certain files should be of
    interest to a certain operation run by that user; or

    - multi-user security is being simulated by an application running
    in a single account's security context

    where file system manipulations outside of the application's control are possible are either not fixable at all, or not in this way --- and we have to acknowledge that the existence of symlinks is a major source of difficulty
    to coding these situations correctly.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Mon Jul 25 15:50:42 2022
    Kaz Kylheku <[email protected]> writes:

    [...]


    The idea is that we walk a path component by component and validate that
    what we have seen so far is "safe". If the path we have seen so far is "safe", and we can validate that the next component is safe, then the
    path plust that component is also "safe".

    The function performs its own symlink resolution; it doesn't rely
    on the kernel. If "alpha" is a path which we believe to be "safe"
    and the next components is "alpha/beta/omega", and "beta"
    is a symlink, then because "alpha" is safe, we can trust readlink("alpha/beta"). That is, we can trust the link itself,
    not necessarily what it points to. We can substitute that
    ourselves. Say beta resolves to "gamma/delta". We produce "alpha/gamma/delta/omega". "alpha" is still safe, and so we
    examine "gamma", and keep going. If "gamma" is a directory
    which is not writable to any other user other than root, then
    we can mark it safe, and proceed to delta.

    This is all just induction. We start with some base hypothesis like
    that we can safely examine either the "/" or "." directory, and
    check its permissions, and find it to be safe.

    [...]

    I posted this to HackerNews; the Samba guy found it and commenting
    on it, saying it doesn't work.

    https://news.ycombinator.com/item?id=32216924

    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    Provided I understood you correctly (I didn't look at the code), you're
    right and Allison is wrong. OTOH, code referring to a directory via file descriptor used in one of the *at calls would be immune to any
    modificiation of higher-level path components (as would code using the
    more traditional method of sitting on some directory by making it the
    cwd of the process).

    Whether or not this is actually the correct thing to do would be a
    different conversation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Rainer Weikusat on Mon Jul 25 16:37:07 2022
    On 2022-07-25, Rainer Weikusat <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:

    [...]


    The idea is that we walk a path component by component and validate that
    what we have seen so far is "safe". If the path we have seen so far is
    "safe", and we can validate that the next component is safe, then the
    path plust that component is also "safe".

    The function performs its own symlink resolution; it doesn't rely
    on the kernel. If "alpha" is a path which we believe to be "safe"
    and the next components is "alpha/beta/omega", and "beta"
    is a symlink, then because "alpha" is safe, we can trust
    readlink("alpha/beta"). That is, we can trust the link itself,
    not necessarily what it points to. We can substitute that
    ourselves. Say beta resolves to "gamma/delta". We produce
    "alpha/gamma/delta/omega". "alpha" is still safe, and so we
    examine "gamma", and keep going. If "gamma" is a directory
    which is not writable to any other user other than root, then
    we can mark it safe, and proceed to delta.

    This is all just induction. We start with some base hypothesis like
    that we can safely examine either the "/" or "." directory, and
    check its permissions, and find it to be safe.

    [...]

    I posted this to HackerNews; the Samba guy found it and commenting
    on it, saying it doesn't work.

    https://news.ycombinator.com/item?id=32216924

    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    Provided I understood you correctly (I didn't look at the code), you're
    right and Allison is wrong. OTOH, code referring to a directory via file descriptor used in one of the *at calls would be immune to any
    modificiation of higher-level path components (as would code using the
    more traditional method of sitting on some directory by making it the
    cwd of the process).

    But if modification of higher-level path components is possible at all
    then you didn't safely arrive at that directory in the first place.
    You have a stable reference to it, but so what?

    The provenance of that reference is bad.

    Speaking of which, my function has the flaw that when a relative
    path is given, it trusts the "." name, It checks that this directory
    is not writable to others and proceeds from there. But the process
    could have arrived at that working directory in the first place
    by following an untrusted symlink.

    Invoking safepath_check on the getcwd path might not be enough;
    the directory may be safe, but thanks to a misdirection, be the
    wrong directory.

    E.g. say you're in your home directory /home/u. You do "cd a/b"
    and hit "rm -rf x/y", expecting to be in /home/u/a/b.

    But say /home/u/a had permissions open, and b is a symlink
    someone injected. The cd ended up in /home/u/c/d where rm
    blew away the wrong x/y.

    In this situation, it wouldn't be good enough to call getcwd and then
    validate it with safe_path. getcwd will return /home/u/c/d, and that
    might have no components which are manipulated by the attacker.

    You have to first check that getcwd is the expected /home/u/a/b,
    and then check it. Or else check /home/u/a/b and then chdir to
    that absolute path.

    This relative path caveat needs to be spelled out to the potential user
    of safepath_check.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Mon Jul 25 20:58:10 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-25, Rainer Weikusat <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:

    [...]


    The idea is that we walk a path component by component and validate that >>> what we have seen so far is "safe". If the path we have seen so far is
    "safe", and we can validate that the next component is safe, then the
    path plust that component is also "safe".

    [...]

    I posted this to HackerNews; the Samba guy found it and commenting
    on it, saying it doesn't work.

    https://news.ycombinator.com/item?id=32216924

    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    Provided I understood you correctly (I didn't look at the code), you're
    right and Allison is wrong. OTOH, code referring to a directory via file
    descriptor used in one of the *at calls would be immune to any
    modificiation of higher-level path components (as would code using the
    more traditional method of sitting on some directory by making it the
    cwd of the process).

    But if modification of higher-level path components is possible at all
    then you didn't safely arrive at that directory in the first place.

    Modification is always possible because someone can always write to each
    of these directories. That's something you excluded because effective protection against root or the current user isn't possibly, anyway. I
    was just pointing out that neither root nor the current user can
    redirect an existing file descriptor, at least not with filesystem
    operations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Kettlewell@21:1/5 to Kaz Kylheku on Tue Jul 26 09:28:03 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-25, Richard Kettlewell <[email protected]d> wrote:
    A web server may run as a special web server user, _different_ from the
    owner(s) of the files and directories it serves. Your library will see
    all those files and directories as untrusted:
    [..]
    In this case, my library makes the right call. You are "root" or
    "www-data" or whoever, of course a directory controlled by "richard"
    is not safe.

    It’s not the right call for this use case (which is a common one among
    web servers). The web server would only return errors. It would be
    completely useless.

    TLDR your library makes assumptions about both the trust placed in files
    owned by the current user and about which files are actually of
    interest, and these assumptions are violated in many use cases.

    The model is "this user can access anything in the namespace according to the OS permission model; we would like to protect them from trusting paths controlled by other users."

    What’s the concrete use case which fits that model? It feels like you’re solving a different problem to the one that people actually have.

    Security concepts fabricated by applications in user space, like

    - out of a set of files accessible to a user, only certain files should be of
    interest to a certain operation run by that user; or

    - multi-user security is being simulated by an application running
    in a single account's security context

    where file system manipulations outside of the application's control are possible are either not fixable at all, or not in this way --- and we have to acknowledge that the existence of symlinks is a major source of difficulty
    to coding these situations correctly.

    As I understand it that’s more-or-less JRA’s point: the way symlinks
    escape the heirarchial structure of a filesystem makes it (at best) unreasonably hard to implement these higher-level security policies.

    --
    https://www.greenend.org.uk/rjk/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Richard Kettlewell on Tue Jul 26 15:54:26 2022
    Richard Kettlewell <[email protected]d> writes:
    Kaz Kylheku <[email protected]> writes:

    [...]

    Security concepts fabricated by applications in user space, like

    - out of a set of files accessible to a user, only certain files should be of
    interest to a certain operation run by that user; or

    - multi-user security is being simulated by an application running
    in a single account's security context

    where file system manipulations outside of the application's control are
    possible are either not fixable at all, or not in this way --- and we have to
    acknowledge that the existence of symlinks is a major source of difficulty >> to coding these situations correctly.

    As I understand it that’s more-or-less JRA’s point: the way symlinks escape the heirarchial structure of a filesystem makes it (at best) unreasonably hard to implement these higher-level security policies.

    The specific problem is that symlinks to directories can be created by unprivileged users and that path-walking is not an atomic
    operation. Because of this, no two resolutions of an identical path are guaranteed to end up accessing the same filesystem object.

    OTOH, whether or not this is actually a bug would be a good question:
    Samba is supposed to limit accesses to some subtree of the filesystem namespace. This subtree may include symlinks to directories outside of
    the substree. Some people believe this must be A Grievious Error Which
    Must Be Rectified! Other people might claim that such a symlink amounts
    to incorporating the subtree it's linking to into the subtree it belongs
    to and that this is a feature and the very reason why symlinks exist in
    the first place. Restricting a process to a filesystem namespace subtree
    can be accomplished in other ways (chroot and filesytem namespaces would
    come to mind here).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Richard Kettlewell on Tue Jul 26 19:59:27 2022
    On 2022-07-26, Richard Kettlewell <[email protected]d> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-25, Richard Kettlewell <[email protected]d> wrote:
    A web server may run as a special web server user, _different_ from the
    owner(s) of the files and directories it serves. Your library will see
    all those files and directories as untrusted:
    [..]
    In this case, my library makes the right call. You are "root" or
    "www-data" or whoever, of course a directory controlled by "richard"
    is not safe.

    It’s not the right call for this use case (which is a common one among
    web servers). The web server would only return errors. It would be
    completely useless.

    That's making the right call, "I am loudly useless for the internal
    security model you've invented in your application; don't use me".

    TLDR your library makes assumptions about both the trust placed in files >>> owned by the current user and about which files are actually of
    interest, and these assumptions are violated in many use cases.

    The model is "this user can access anything in the namespace according to the
    OS permission model; we would like to protect them from trusting paths
    controlled by other users."

    What’s the concrete use case which fits that model? It feels like you’re solving a different problem to the one that people actually have.

    The use case is extremely common: an application, runnning on behalf of
    some user, wants to read a trusted file, such as a configuration file,
    or program.

    You use trusted paths all the time. For instance you probably run
    /usr/bin/grep from time to time. You trust it due to a belief that
    not only us the grep executable owned by root and not writable to
    anyone else, but that root controls the three path components above:
    /, /usr, and /usr/bin.

    You can call safepath_check("/usr/bin/grep"), and the idea is that
    if it returns 1, and everything in taht function is coded right, you can
    trust that the permissions are sane: the path is not controlled by
    anyone other than either root, or you (your effective ID).

    This kind of path trust occurs all over the place; for instance when
    your shell opens /home/you/.bashrc. Or when ssh acceses /home/user/.ssh/known_hosts or other files.

    Security concepts fabricated by applications in user space, like

    - out of a set of files accessible to a user, only certain files should be of
    interest to a certain operation run by that user; or

    - multi-user security is being simulated by an application running
    in a single account's security context

    where file system manipulations outside of the application's control are
    possible are either not fixable at all, or not in this way --- and we have to
    acknowledge that the existence of symlinks is a major source of difficulty >> to coding these situations correctly.

    As I understand it that’s more-or-less JRA’s point: the way symlinks

    JRA's point seems to be vehemently that the POSIX file system
    model is entirely broken and you can't do anything about it.

    escape the heirarchial structure of a filesystem makes it (at best) unreasonably hard to implement these higher-level security policies.

    Is that really so? Or is it simply that the lack of symlinks gives
    you an illusion that inventing an application-level security policy
    (which uses real user accounts) is easy?

    If you have a box that runs some application which has its own security policies not enforced by the kernel, but using real system accounts and
    files owned by them, then you simply cannot allow any access to that
    system that doesn't go through the application, whether there are
    symlinks or not. (Not any access which uses those same accounts.)

    (Then you can banish symlinks easily, if you like; just don't have
    any functionality in the application which creates them.)

    But there is a real point to be made in that some things in the Unix
    security model (not just some application-invented one) are harder than necessary.

    Unix is multi-user, supporting the concept that a direcgtory could
    contain material owned by different users (perhaps part of the
    same collaborating group).

    Yet, it is not safe to operate in a mixed-ownership environment. For
    instance, if you recursively delete something in a tree where some of
    the directories are not owned by you, you can fall victim to a symlink misdirection.

    We avoid this in practice by with system installations which follow a
    silo pattern: each user has a home directory, which contains only
    objects owned by that user.

    Basically those are the two ways of using a Unix-like system safely:

    - the silo model with everyone just using their own directory, or
    certain kinds of shared directories that are owned by root, or else
    - a system that is inaccesible for multi-user remote login, which runs a
    dedicated application or application suite (exemplified by dedicated
    servers) or, in in more recent decades, embedded systems running
    Unix-likes.

    Symlinks are not broken if you stick to these usage patterns.

    Users who use a Unix-like system to share files in a common directory
    must all be friends who trust each other.

    If Mallory has a directory M which is writable to Alice, Alice can
    create a file M/A. Mallory can call remove("M/A") and Alice's
    file is gone.

    No evil symlinks were involved.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Kettlewell@21:1/5 to Kaz Kylheku on Tue Jul 26 22:55:28 2022
    Kaz Kylheku <[email protected]> writes:
    Richard Kettlewell <[email protected]d> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-25, Richard Kettlewell <[email protected]d> wrote:
    A web server may run as a special web server user, _different_ from the >>>> owner(s) of the files and directories it serves. Your library will see >>>> all those files and directories as untrusted:
    [..]
    In this case, my library makes the right call. You are "root" or
    "www-data" or whoever, of course a directory controlled by "richard"
    is not safe.

    It’s not the right call for this use case (which is a common one among
    web servers). The web server would only return errors. It would be
    completely useless.

    That's making the right call, "I am loudly useless for the internal
    security model you've invented in your application; don't use me".

    That’s what I mean by the ‘wrong call’, which is contextual to the application.

    TLDR your library makes assumptions about both the trust placed in files >>>> owned by the current user and about which files are actually of
    interest, and these assumptions are violated in many use cases.

    The model is "this user can access anything in the namespace according to the
    OS permission model; we would like to protect them from trusting paths
    controlled by other users."

    What’s the concrete use case which fits that model? It feels like you’re >> solving a different problem to the one that people actually have.

    The use case is extremely common: an application, runnning on behalf of
    some user, wants to read a trusted file, such as a configuration file,
    or program.

    You use trusted paths all the time. For instance you probably run /usr/bin/grep from time to time. You trust it due to a belief that
    not only us the grep executable owned by root and not writable to
    anyone else, but that root controls the three path components above:
    /, /usr, and /usr/bin.

    You can call safepath_check("/usr/bin/grep"), and the idea is that
    if it returns 1, and everything in taht function is coded right, you can trust that the permissions are sane: the path is not controlled by
    anyone other than either root, or you (your effective ID).

    We seem to have managed without safepath_check on executable paths for a
    few decades. I agree that there’s a theoretical issue there but it seems
    that trusting the value of PATH is largely sufficient in practice.

    Basically those are the two ways of using a Unix-like system safely:

    - the silo model with everyone just using their own directory, or
    certain kinds of shared directories that are owned by root, or else
    - a system that is inaccesible for multi-user remote login, which runs a
    dedicated application or application suite (exemplified by dedicated
    servers) or, in in more recent decades, embedded systems running
    Unix-likes.

    Symlinks are not broken if you stick to these usage patterns.

    It seems like your library solves the problems remaining after you
    define most of the use cases for path validation as invalid. If you
    don’t have those use cases then whatever but it’s not surprising that
    other people who do have those use cases don’t think the library solves
    their problem.

    --
    https://www.greenend.org.uk/rjk/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From William Ahern@21:1/5 to Kaz Kylheku on Thu Jul 28 15:39:17 2022
    Kaz Kylheku <[email protected]> wrote:
    So, LWN publishes an article

    https://lwn.net/Articles/899543/

    Some Sampba person working at Google claims that there is no
    safe way of using symlinks; they are fundamentally broken and
    wreck the whole POSIX filesystem API, rendering it insecure.

    In response, I wrote a library function whose interface
    looks like this:

    /*
    * safepatch_check error codes
    */
    enum {
    SAFEPATH_OK, /* path appears safe */
    SAFEPATH_UNSAFE, /* path traversible, unsafe */
    SAFEPATH_PERM, /* path not traversible due to perms */
    SAFEPATH_NOENT, /* component other than last doesn't exist */
    SAFEPATH_NOTDIR, /* interior path component isn't a directory */
    SAFEPATH_INVAL, /* path is invalid */
    SAFEPATH_NOMEM, /* out of memory */
    SAFEPATH_LOOP, /* more than 8 levels of symlink */
    SAFEPATH_TOOLONG, /* component or symlink target too long */
    };

    int safepath_check(const char *name);
    const char *safepath_strerr(int err);

    The git repository is here:

    https://www.kylheku.com/cgit/safepath/about/

    (Unfortunately, I don't yet have documentation or test cases, sorry.)

    The idea is that we walk a path component by component and validate that
    what we have seen so far is "safe". If the path we have seen so far is "safe", and we can validate that the next component is safe, then the
    path plust that component is also "safe".

    The function performs its own symlink resolution; it doesn't rely
    on the kernel. If "alpha" is a path which we believe to be "safe"
    and the next components is "alpha/beta/omega", and "beta"
    is a symlink, then because "alpha" is safe, we can trust readlink("alpha/beta"). That is, we can trust the link itself,
    not necessarily what it points to. We can substitute that
    ourselves. Say beta resolves to "gamma/delta". We produce "alpha/gamma/delta/omega". "alpha" is still safe, and so we
    examine "gamma", and keep going. If "gamma" is a directory
    which is not writable to any other user other than root, then
    we can mark it safe, and proceed to delta.

    This is all just induction. We start with some base hypothesis like
    that we can safely examine either the "/" or "." directory, and
    check its permissions, and find it to be safe.

    The inductive hypothesis is that if we can find some path to
    be safe, we can evaluate the next component and validate whether
    that is safe according to some cases, and then add it to the path.
    Or else proclaim that it's not safe.

    By induction, if we reach the end of the path, we have shown that
    it's safe.

    We can use system calls which use the entire left part of the path,
    because since that is safe, we can trust it to be free of tampering.

    I posted this to HackerNews; the Samba guy found it and commenting
    on it, saying it doesn't work.

    https://news.ycombinator.com/item?id=32216924

    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound. I haven't satifactorily
    proven to myself that there's not a problem with relative paths, like "./../foo" if a parent directory like "./../.." wouldn't pass safepath_check muster, but I can't yet figure out a way to break it and suspect it's
    sound. It's possible the Samba developer made my initial mistake
    of seeing

    int safepath_open(const char *name, int flags)
    {
    int res = safepath_check(name);

    if (res == SAFEPATH_OK)
    return open(name, flags);

    set_errno(res);
    return -1;
    }

    and reflexively assuming there's a TOCTTOU between safepath_check and open, without considering what safepath_check promises and its implications.

    The original claim by the Samba developer, as well as the LWN write-up,
    really rubbed me the wrong way, too. See my comment in the LWN thread. The follow-ups discussing SUID binaries are precisely the type of thing that
    expose faulty assumptions regarding path names. SUID exploits are a confused deputy type problem, a predictable consequence from assuming path names
    provide any kind of capability semantics. (Ditto for other aspects of the ambient environment, like environment variables.) Indeed, the Wikipedia article's exemplar for confused deputy involves file names. Just because
    path names, ACL systems, etc, *appear* to come close to providing capability semantics (or equivalent) doesn't justify the argument against symlinks. The conflation is fundamentally wrong and invites security holes even in the absence of symlinks.

    If we're being charitable, it is true that symlinks are at the very least highly problematic if you're writing a file server (e.g. Samba) that permits clients to create symlinks and the file server relies on the host's symlink facility rather than synthesizing the semantics. In that context, I can *empathize* with their frustration, notwithstanding that the pain should
    have been evident from the very beginning. Nonetheless, it's not really symlinks at fault here; it's the developers' fault for relying on broken problem and security models. The manifest convenience of symlinks (in that
    they were directly relied upon) as a primitive facility proves their
    utility. Such a convenience is hardly the only pitfall a file server
    developer needs to worry about. The history of Samba security is a parade of accidents arising from impedance mismatches among host filesystem semantics, SMB/CIFS semantics, and Sambas architectural model (e.g. more monolithic
    than heavily privilege separated), with mismatches often overlooked,
    ignored, or underestimated because of the emphasis placed on performance
    over security.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to William Ahern on Fri Jul 29 01:27:05 2022
    On 2022-07-28, William Ahern wrote:
    Kaz Kylheku <[email protected]> wrote:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound.

    It's an interesting question, but I don't agree at all (Sorry, I'm on
    team jra!).

    It does have to handle TOCTOU, because an attacker can force you to
    handle it, whether you want to or not.

    You said you would be delighted to see an example, so here is one
    example of using TOCTOU to defeat the testsp program:

    1. You can create a root owned symlink to any directory you want like
    this:

    $ cd /tmp/evil
    $ su &
    $ ls -l /proc/$!/cwd
    lrwxrwxrwx 1 root root 0 Jul 28 17:52 /proc/12657/cwd -> /tmp/evil

    2. You can change where this symlink points to by killing su, and then
    spamming fork() until the pid is recycled. This can take a while
    depending on pid_max.

    Here is an example implementation, only tested on Linux:

    https://gist.github.com/taviso/d7c2a3cb1896584f176e16fae3d59c64

    $ kill %1
    $ ~/recycle 12657 /bin/sh
    $ echo $$
    12657
    $ cd /opt
    $ ls -l /proc/$$/cwd
    lrwxrwxrwx 1 taviso taviso 0 Jul 28 18:14 /proc/12657/cwd -> /opt

    3. Putting it all together:

    # Attacker constructs a path to give to victim.
    attacker$ cd /etc
    attacker$ su &

    # Attacker gives victim the path "/proc/12700/cwd/passwd", which will
    # be considered safe because every component is owned by root.
    victim$ ./testsp /proc/12700/cwd/passwd
    safepath_check("/proc/12700/cwd/passwd") == path appears safe

    # Attacker changes the path after the check, but before the use.
    attacker$ kill %1
    attacker$ ~/recycle 12700 /bin/sh
    attacker$ cd /tmp
    attacker$ echo pwned > passwd

    # The victim opens an attacker controlled file.
    victim$ cat /proc/12700/cwd/passwd
    pwned

    This is a classic TOCTOU, the victim checked the path while it was safe,
    then the attacker changed it before it was used.

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From William Ahern@21:1/5 to Tavis Ormandy on Thu Jul 28 19:57:14 2022
    Tavis Ormandy <[email protected]> wrote:
    On 2022-07-28, William Ahern wrote:
    Kaz Kylheku <[email protected]> wrote:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound.

    It's an interesting question, but I don't agree at all (Sorry, I'm on
    team jra!).

    It does have to handle TOCTOU, because an attacker can force you to
    handle it, whether you want to or not.

    You said you would be delighted to see an example, so here is one
    example of using TOCTOU to defeat the testsp program:

    1. You can create a root owned symlink to any directory you want like
    this:

    $ cd /tmp/evil
    $ su &
    $ ls -l /proc/$!/cwd
    lrwxrwxrwx 1 root root 0 Jul 28 17:52 /proc/12657/cwd -> /tmp/evil

    procfs strikes again!

    Also once again proving how easy it is conflate lack of imagination with
    proof of security.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From William Ahern@21:1/5 to William Ahern on Thu Jul 28 21:29:02 2022
    William Ahern <[email protected]> wrote:
    Tavis Ormandy <[email protected]> wrote:
    On 2022-07-28, William Ahern wrote:
    Kaz Kylheku <[email protected]> wrote:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound.

    It's an interesting question, but I don't agree at all (Sorry, I'm on
    team jra!).

    It does have to handle TOCTOU, because an attacker can force you to
    handle it, whether you want to or not.

    You said you would be delighted to see an example, so here is one
    example of using TOCTOU to defeat the testsp program:

    1. You can create a root owned symlink to any directory you want like
    this:

    $ cd /tmp/evil
    $ su &
    $ ls -l /proc/$!/cwd
    lrwxrwxrwx 1 root root 0 Jul 28 17:52 /proc/12657/cwd -> /tmp/evil

    procfs strikes again!

    Also once again proving how easy it is conflate lack of imagination with proof of security.

    FWIW, fixing safepath_check to constrain paths to the same device mount
    (i.e. checking st_dev) might address this particular issue. But procfs
    should make clear that the scope of potential shenanigans which both safepath_check and the application (e.g. regarding implicit limitations)
    need to concern themselves with is simply too broad, especially considering
    the availability of openat and related interfaces on all modern Unix
    systems. Many systems, like macOS, lack some extended interfaces in the
    family, but even better than relying on openat-style interfaces is being
    more careful and deliberate to avoid situations where the application itself would or should care about these sorts of issues. (openat interfaces were originally merely intended to address multithreading problems, anyhow.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Fri Jul 29 06:05:04 2022
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-28, William Ahern wrote:
    Kaz Kylheku <[email protected]> wrote:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound.

    It's an interesting question, but I don't agree at all (Sorry, I'm on
    team jra!).

    It does have to handle TOCTOU, because an attacker can force you to
    handle it, whether you want to or not.

    You said you would be delighted to see an example, so here is one
    example of using TOCTOU to defeat the testsp program:

    1. You can create a root owned symlink to any directory you want like
    this:

    $ cd /tmp/evil
    $ su &
    $ ls -l /proc/$!/cwd
    lrwxrwxrwx 1 root root 0 Jul 28 17:52 /proc/12657/cwd -> /tmp/evil

    2. You can change where this symlink points to by killing su, and then
    spamming fork() until the pid is recycled. This can take a while
    depending on pid_max.

    Here is an example implementation, only tested on Linux:

    https://gist.github.com/taviso/d7c2a3cb1896584f176e16fae3d59c64

    Thank you; this is exactly the feedback I'm looking for.

    I will incorporate a fix into safepath_check against this.

    I will say that this was reported by Tavis Ormandy in
    comp.unix.programmer. Is this your discovery, or something known;
    should anyone else be referenced?

    So in effect, Linux, and possibly other systems with a similar
    /proc system, provide a privileged service that compliantly creates
    a root-owned symlink pointing wherever any unprivileged user wants;
    the user just has to change to that directory and fire up a setuid
    executable, which lives however briefly.

    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Rejecting /proc/<anything> would be prudent, but maybe that throws
    out the baby with the bathwater? You can trust things like
    /proc/meminfo and whatnot.

    I'm fully aware that /proc could be mounted anywhere, but I think won't
    bother with that possibility.

    I have a side observation here. If Mallory does this, on a GNU/Linux
    system:

    $ su - alice
    Password: # leave this running

    then we observe that the /proc/<pid> directory of su has
    group owner alice.

    Regardless of that, the directory is not accessible to alice;
    alice cannot read that link.

    I think this attack works only against root, since non-root users
    cannot read this cwd link. That's hardly a mitigation, but if
    that is true, the fix can take advantage of that.
    That is to say, it seems safepath_check doesn't have to care about /proc/<pid>/cwd when geteuid() != 0.

    Can Mallory create an Alice-owned /proc/<pid>, such that Alice can read /proc/<pid>/cwd? My instincts say that the system is borked from a
    security point of view if that is possible.

    This is a classic TOCTOU, the victim checked the path while it was safe,
    then the attacker changed it before it was used.

    If we fool root into pointing a symlink anywhere, that's already a
    problem even in the absence of a TOCtoTOU. The attacker doesn't have
    to change anything between the safepath_check and the access.

    E.g. victim wants to do remove("/proc/1234/cwd/foo").

    1. Attacker prepares /proc/1234/cwd/foo to target the wrong file,
    like /home/alice/homework-2022-07-28.odf.

    2. Victim calls safepath_check("/proc/1234/cwd/foo") -> SAFEPATH_OK

    No change between 2 and 3.

    3. Victim calls remove("/proc/1234/cwd/foo") -> homework gone

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Fri Jul 29 07:12:03 2022
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    You can absolutely do this. Here is another trick, you can create
    hardlinks to root-owned relative symlinks.

    Or, equivalently, move them around with rename. I've been playing
    with that.


    Here is an example, find any relative symlink. I guess you can do
    something like this:

    $ find /etc -type l -user root -ls | grep -v -- '-> /'

    On my system I have a bunch in /etc/ssl/certs, but it doesn't matter
    where:

    $ ls -l /etc/ssl/certs/76cb8f92.0
    lrwxrwxrwx 1 root root 26 May 23 15:19 /etc/ssl/certs/76cb8f92.0 -> Cybertrust_Global_Root.pem

    I can create a hardlink to that symlink:

    $ ln /etc/ssl/certs/76cb8f92.0 foobar
    $ stat foobar
    File: foobar -> Cybertrust_Global_Root.pem
    Size: 26 Blocks: 0 IO Block: 4096 symbolic link Device: 810h/2064d Inode: 30442 Links: 2
    Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)

    The ownership of the symlink usually doesn't matter though;
    the directory where it lives does.

    I put a link ownership check into safepath_check specifically for
    the situation of /tmp (or any other sticky bit directory that we
    trust, in spite of it being world writable.)

    In /tmp, anyone can create a symlink; you must trust only your own,
    or one owned by root.

    If you trust one owned by root, it would seem that /tmp would
    be susceptible to a hard link attack.

    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to Kaz Kylheku on Fri Jul 29 06:38:47 2022
    On 2022-07-29, Kaz Kylheku wrote:
    2. You can change where this symlink points to by killing su, and then
    spamming fork() until the pid is recycled. This can take a while
    depending on pid_max.

    Here is an example implementation, only tested on Linux:

    https://gist.github.com/taviso/d7c2a3cb1896584f176e16fae3d59c64

    Thank you; this is exactly the feedback I'm looking for.

    I will incorporate a fix into safepath_check against this.

    You're welcome :)


    This is a classic TOCTOU, the victim checked the path while it was safe,
    then the attacker changed it before it was used.

    If we fool root into pointing a symlink anywhere, that's already a
    problem even in the absence of a TOCtoTOU. The attacker doesn't have
    to change anything between the safepath_check and the access.

    You can absolutely do this. Here is another trick, you can create
    hardlinks to root-owned relative symlinks.

    Here is an example, find any relative symlink. I guess you can do
    something like this:

    $ find /etc -type l -user root -ls | grep -v -- '-> /'

    On my system I have a bunch in /etc/ssl/certs, but it doesn't matter
    where:

    $ ls -l /etc/ssl/certs/76cb8f92.0
    lrwxrwxrwx 1 root root 26 May 23 15:19 /etc/ssl/certs/76cb8f92.0 -> Cybertrust_Global_Root.pem

    I can create a hardlink to that symlink:

    $ ln /etc/ssl/certs/76cb8f92.0 foobar
    $ stat foobar
    File: foobar -> Cybertrust_Global_Root.pem
    Size: 26 Blocks: 0 IO Block: 4096 symbolic link Device: 810h/2064d Inode: 30442 Links: 2
    Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)

    Now I can make that link to whatever I want:

    $ ln /etc/passwd Cybertrust_Global_Root.pem

    Or make it a symlink to another relative symlink, etc, etc.

    It gets even more worrying when you think about tmpreaper or whatever
    running :)

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Fri Jul 29 14:09:39 2022
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Of course, that's not sufficient because of the many ways that same
    object can be named, like:

    /proc/net/../<digits>/fdinfo/../././cwd

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Tavis Ormandy on Fri Jul 29 15:01:16 2022
    Tavis Ormandy <[email protected]> writes:
    On 2022-07-28, William Ahern wrote:
    Kaz Kylheku <[email protected]> wrote:
    What do you think? Is there a gaping hole in the logic that cannot
    be repaired?

    From a cursory inspection it all looks sound.

    It's an interesting question, but I don't agree at all (Sorry, I'm on
    team jra!).

    It does have to handle TOCTOU, because an attacker can force you to
    handle it, whether you want to or not.

    "An attacker" can not generally do that. Only root or the user running
    the code can do that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to Kaz Kylheku on Fri Jul 29 14:14:09 2022
    On 2022-07-29, Kaz Kylheku wrote:
    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted


    It's configurable, it's the fs.protected_hardlinks sysctl. AFAIK it's
    not the default, your distro probably sets it.

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Fri Jul 29 15:27:41 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:

    [...]

    So in effect, Linux, and possibly other systems with a similar
    /proc system, provide a privileged service that compliantly creates
    a root-owned symlink pointing wherever any unprivileged user wants;

    the user just has to change to that directory and fire up a setuid executable, which lives however briefly.

    Provided the user can start a long-running process which runs as
    root. And that's because of the special semantics of the Linux proc
    filesystem.

    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    The function relies on the filesystem it works on having the usual UNIX semantic and the Linux proc-filesystem doesn't.

    [...]

    I'm fully aware that /proc could be mounted anywhere, but I think won't bother with that possibility.

    The file system type would probably need to be checked.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Fri Jul 29 15:44:46 2022
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku wrote:
    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted


    It's configurable, it's the fs.protected_hardlinks sysctl. AFAIK it's
    not the default, your distro probably sets it.

    Indeed it is; and the purpose of that sysctl is preventing exactly this
    /tmp muckery.

    Making that configurable at all seems like a poor decision, not to
    speak of the which way it defaults.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Fri Jul 29 17:26:26 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Of course, that's not sufficient because of the many ways that same
    object can be named, like:

    /proc/net/../<digits>/fdinfo/../././cwd

    The (linux-specific) statfs system call returns the filesystem type and
    can be used to determine if some symlink comes from a proc
    filesystem.

    For the original trick to work, the check must happen while a process
    running as root had the directory in question as cwd and the use must
    happen after a hostile process running under a different uid had managed
    to get the same pid as the former root process. That's possible. But I
    think that's a rather theoretic concern.

    There's obviously still the safe option: Use openat or walk the path
    with chdir. After each directory change, verify that what the process is currently working with is actually what it wants to work with.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Fri Jul 29 17:29:57 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku wrote:
    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted >>>

    It's configurable, it's the fs.protected_hardlinks sysctl. AFAIK it's
    not the default, your distro probably sets it.

    Indeed it is; and the purpose of that sysctl is preventing exactly this
    /tmp muckery.

    Making that configurable at all seems like a poor decision, not to
    speak of the which way it defaults.

    Sudden changes of semantics which have existed for a long time are
    usually imprudent. People often don't appreciate "Of course, it doesn't
    work. But look how secure it is!"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Rainer Weikusat on Fri Jul 29 18:10:34 2022
    On 2022-07-29, Rainer Weikusat <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku wrote:
    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted >>>>

    It's configurable, it's the fs.protected_hardlinks sysctl. AFAIK it's
    not the default, your distro probably sets it.

    Indeed it is; and the purpose of that sysctl is preventing exactly this
    /tmp muckery.

    Making that configurable at all seems like a poor decision, not to
    speak of the which way it defaults.

    Sudden changes of semantics which have existed for a long time are
    usually imprudent. People often don't appreciate "Of course, it doesn't
    work. But look how secure it is!"

    But distros have turned this on and people don't even know about it.

    I can hardly think of a valid use case for linking a file into /tmp
    that you do not own!

    Hard linking /your own file/ is allowed.

    /tmp$ touch foo
    /tmp$ ln foo bar

    What is the use case for linking a file /that you do not own/ into /tmp?

    That's very dubious given how /tmp works; in /tmp you're supposed to
    access your own stuff and keep out of anyone else's.

    You cannot remove (i.e. unlink) an object from /tmp you don't own; why
    would the opposite operation, link, be allowed?

    Also, root is allowed to hard link a non-root-owned file into /tmp; this setting/behavior doesn't affect the superuser. Systems applications
    running as superuser aren't affected by the restriction.

    The case pretty solid for ending the cautious period of configurability
    and making it a hard-coded behavior.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Jul 30 03:27:34 2022
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Of course, that's not sufficient because of the many ways that same
    object can be named, like:

    /proc/net/../<digits>/fdinfo/../././cwd

    OK; there is an updated version, git hash 2f27d6c386daff041017b7aaec51d0e50e603a8e.

    The pattern check now simplifies paths, squeezing out "..", "." and
    empty components; I wrote a string-to-string filtering function for
    this. No file system access takes place.

    The resulting simplified path is checked against a regular expression
    via regexec, then thrown away. (It would be insecure to use the
    simplified path for the main checks. because /good/good/evil/../good
    simplifies to /good/good/good.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to Kaz Kylheku on Sat Jul 30 04:37:17 2022
    On 2022-07-30, Kaz Kylheku wrote:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Of course, that's not sufficient because of the many ways that same
    object can be named, like:

    /proc/net/../<digits>/fdinfo/../././cwd

    OK; there is an updated version, git hash 2f27d6c386daff041017b7aaec51d0e50e603a8e.


    I think it's buggy, it doesn't handle "messy" symlinks correctly.

    A symlink can be "messy" foo -> bar/, or "clean" foo -> bar.

    I also think there is a security bug with multi-level symlinks.

    If you do something like

    $ ln -s /etc foo
    $ ln -s foo bar

    I think checking "bar/passwd" will actually test "barpasswd". I realize
    this was just a bug and not a design flaw. I can take a look after you
    fix it if you like it.

    Do you consider attacks in s[ug]id context valid, or do you want
    to call that out of scope? :)

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Sat Jul 30 07:42:52 2022
    On 2022-07-30, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-30, Kaz Kylheku wrote:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku <[email protected]> wrote:
    Basically whenever the safepath_check function encounters an absolute
    path, either initially or via a symlink, it needs to check that path
    against some unsafe patterns, like /proc/<digits>/cwd.

    Of course, that's not sufficient because of the many ways that same
    object can be named, like:

    /proc/net/../<digits>/fdinfo/../././cwd

    OK; there is an updated version, git hash
    2f27d6c386daff041017b7aaec51d0e50e603a8e.


    I think it's buggy, it doesn't handle "messy" symlinks correctly.

    A symlink can be "messy" foo -> bar/, or "clean" foo -> bar.

    I also think there is a security bug with multi-level symlinks.

    If you do something like

    $ ln -s /etc foo
    $ ln -s foo bar

    I think checking "bar/passwd" will actually test "barpasswd". I realize
    this was just a bug and not a design flaw. I can take a look after you
    fix it if you like it.

    I put out two fixes; one for the buggy splicing (missing "+ 1"
    in one fo the cases, causing the slash to be deleted).

    I also revised the treatment of consecutive slashes, and trailing
    slashes in link targets.

    When a path component is delimited by a following slash, if there are
    multiple slashes, the last of them is found and that then delimits the component. E.g. foo///bar will delimit a "foo//" component, punching
    a null byte temporarily over the third slash. The rest of the path
    is then "bar". At the start of an absolute path, we skip all slashes,
    not only one.

    When a link target already ends with a trailing slash, and there
    is a remaining path to be grafted on, we don't add a slash between
    them; we take advantage of the target's trailing slash.

    This also brings about conformance to a requirement stated in
    POSIX like this

    "If the contents of the symbolic link consist solely of <slash>
    characters, then all leading <slash> characters of the remaining
    pathname shall be omitted from the resulting combined pathname,
    leaving only the leading <slash> characters from the symbolic link
    contents."

    I have no idea what that is actually for, but it would make
    a difference in an implementation in which a name like //host/..
    with exactly two slashes has a special meaning. Whereas
    three or more slashes has the same meaning as one: ///host == /host.

    So, with that requirement being obeyed you could make a symlink like

    net -> //

    and then net/host should produce the expected //host and not ///host.

    Do you consider attacks in s[ug]id context valid, or do you want
    to call that out of scope? :)

    I care about the function being useful to a root user
    (geteuid() == 0) case, and that includes when getuid() != 0,
    which is important. A setuid root program should be able to use
    the function for something (e.g. reading a config file that should
    be secured) before dropping privileges.

    I care more about setuid root than setuid non-root, and setuid
    than setgid. :)

    On this topic, I have a feature idea. There could be an API whereby the application can declare friends. Friends are users or groups whom the
    user trusts. For instance if Alice declares that Bob is a friend, then
    Alice trusts path components that are controlled by Bob in the same way
    as those controlled by Alice.

    I believe this reflects the Unix model of sharing; Unix users can share
    files in common directories only if they trust each other. If Bob has
    shared a file with Alice, at any time, Bob can replace that with a
    malicious symbolic link, just after Alice has checked that it isn't.

    Friendship isn't recorded in Unix anywhere; the closest is the
    group model. If some people trust each other, they can persuade
    superuser to create a group which contains them.

    There is no concept in Unix of selecting which groups you trust.
    Superuser may assign you to various multiple groups. You might trust
    some small group of three people of which you're a member, and regard a directory writable to such a group as safe; yet you might not trust a
    directory writable to "users"; a catch-all group to which hundreds of
    users belong, most of whom you don't even know.

    This is where the friendship API would include group friends also: you
    could declare that you trust a certain group.

    safepath_check has hard-coded trust for any group which contains
    no members other than the effective user and superuser. In Linux
    distros, there is a practice of parallel groups: like if you
    create a user bob, a group bob is created and bob is its only
    member; the ID numbers are synchronized also. safepatch_check
    trusts this group (by virtue of it only containing the user).

    A "~/.safepathrc" config file could make these friendship declarations persistent. (Of course, .safepathrc will be checked with
    safepath_check before being used. :)

    There wouldn't be an /etc/safepathrc; friendship is personal
    thing that can't be centrally dictated. :)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to Kaz Kylheku on Sat Jul 30 15:59:17 2022
    On 2022-07-30, Kaz Kylheku wrote:
    I think checking "bar/passwd" will actually test "barpasswd". I realize
    this was just a bug and not a design flaw. I can take a look after you
    fix it if you like it.

    I put out two fixes; one for the buggy splicing (missing "+ 1"
    in one fo the cases, causing the slash to be deleted).

    I also revised the treatment of consecutive slashes, and trailing
    slashes in link targets.

    Thanks, seems okay to me.


    Do you consider attacks in s[ug]id context valid, or do you want
    to call that out of scope? :)


    I care more about setuid root than setuid non-root, and setuid
    than setgid. :)


    Cool, in that case, here is another trick. If you open a descriptor to a
    file and then unlink it, "(deleted)" is appended to the target by the
    kernel. This allows a user to effectively change a symlink they don't own.

    *but* here's the fun thing - it's not a real link, if you open it you
    get the original unlinked file.

    Reproduce like this, it requires fs.protected_hardlinks=0 (the default), although I know another way to do it if you want to enforce that via
    sysctl().

    $ ln /etc/passwd '/tmp/foo (deleted)'
    $ echo pwned > /tmp/foo
    $ exec 3< /tmp/foo
    $ rm /tmp/foo
    $ ls -l /dev/fd/3
    lr-x------ 1 taviso taviso 64 Jul 30 08:52 /dev/fd/3 -> '/tmp/foo (deleted)'

    # file descriptor inherited, because it's not O_CLOEXEC:
    $ ./testsp /dev/fd/3
    safepath_check("/dev/fd/3") == path appears safe

    # Look, it's not the real file!
    $ cat /dev/fd/3
    pwned

    I think you have to concede it is quite difficult to use symlinks
    safely, so jra did have a point :)

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Sat Jul 30 17:50:37 2022
    On 2022-07-30, Tavis Ormandy <[email protected]> wrote:
    safepath_check("/dev/fd/3") == path appears safe

    because /dev/fd is a symlink, this is the same as /proc/self/fd/3.

    this falls under the regex, but isn't checked because i restricted the
    check to euid == 0.

    probably, no symlinks should be trusted under /proc no matter who
    the effective user is.

    that doesn't require a regex, just a flag indicating "we currently
    processing a /proc absolute path", and fail every symlink if so.

    The only problem is that /dev/fd/<n> is an important feature in
    that it is used by bash process substitution.

    probably "if we are in /proc, and see a symlink, don't trust
    it if root, or else if the target contains (deleted)".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Jul 30 18:45:53 2022
    On 2022-07-30, Kaz Kylheku <[email protected]> wrote:
    probably "if we are in /proc, and see a symlink, don't trust
    it if root, or else if the target contains (deleted)".

    Unfortunately, that is not a good idea because it's an instant TOCtoTOU;
    the link can change from "whatever" to "whatever (deleted)" at any time,
    and that change can be perpetrated by a context that perpetrated
    without that being conditional on the permissions of the /proc
    directory where the link lives.

    The only reasonable conclusion is that symlinks under /proc (and thus
    /dev/fd) cannot be trusted. When safepath_check traverses /proc,
    it must report almost any encounter with a symlink as unsafe.

    Symlinks related to /proc's structure like /proc/self -> /proc/<n>
    are probably safe; just the ill-conceived ones that reference external
    files have to be blacklisted.

    Programs cannot trust paths generated by Bash process substitution.

    Even on Linux, Bash should be configured to implement process
    substitution using named FIFOs.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tavis Ormandy@21:1/5 to Kaz Kylheku on Sat Jul 30 19:19:58 2022
    On 2022-07-30, Kaz Kylheku wrote:
    On 2022-07-30, Tavis Ormandy <[email protected]> wrote:
    safepath_check("/dev/fd/3") == path appears safe

    because /dev/fd is a symlink, this is the same as /proc/self/fd/3.


    Of course, but I don't understand the point - you're rejecting this attack?

    It's okay, just want to make sure I understand the rules!

    If you want to make things harder for me you could also disallow /tmp,
    use close_range() and also require the other fs.protected_* sysctls! I
    think this makes it not very useful, but that was Jeremy's point, using symlinks safely is either hard or very restrictive!

    Tavis.

    --
    _o) $ lynx lock.cmpxchg8b.com
    /\\ _o) _o) $ finger [email protected]
    _\_V _( ) _( ) @taviso

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tavis Ormandy on Sat Jul 30 22:51:08 2022
    On 2022-07-30, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-30, Kaz Kylheku wrote:
    On 2022-07-30, Tavis Ormandy <[email protected]> wrote:
    safepath_check("/dev/fd/3") == path appears safe

    because /dev/fd is a symlink, this is the same as /proc/self/fd/3.


    Of course, but I don't understand the point - you're rejecting this attack?

    No; I was just making a note of that this falls under an existing
    namespace already covered by a check. I already fixed it so that it's
    applied regardless of the effective user ID.

    It's okay, just want to make sure I understand the rules!

    If you want to make things harder for me you could also disallow /tmp,

    That's not actually such a bad idea. Programs should generally not
    not open paths in /tmp that they didn't exclusively create themselves.

    The module should have an API for white-listing paths. If some
    path like /tmp/foo is somehow received from a trusted source, it could
    be marked as trusted; then that could later be revoked again.

    use close_range() and also require the other fs.protected_* sysctls! I

    Speaking of fs.protected_*: it's unfortunate that the unprivilged
    user is not allowed to examine these settings! How crazy is that?

    We we cannot ask the qeustion "are we on a Linux kernel with hard link protection enabled?" (Though the hypothesis can be tested in
    time-wasting, clumsy empirical ways.)

    I just put in a bit of a countermeasure against hard link attacks: any
    symlink with a hard link count > 1 is declared unsafe. The attacker must
    be able to unlink the original object to bring the link count down to 1.
    (Which may be possible if the attacker finds a suitable link in an
    unsecured directory, but I have a sense that we are raising the bar
    somewhat.)

    think this makes it not very useful, but that was Jeremy's point, using symlinks safely is either hard or very restrictive!

    I'd say that using the file system interface of Linux safely is hard.

    Jeremy is saying that the POSIX filesystem model is not safely usable;
    but POSIX doesn't specify any of this /proc braindamage.

    You cannot really blame the symlink, as such, for any of this.

    Some of it is not even normal symlinks, like the dangling "/path/to/file (deleted)" that magically points to a file that is not in the namespace.

    (That could have been designed differently, while retaining the the same approach: the link could rename to "/proc/deleted/path/to/file", where /proc/deleted/ doesn't exist. The link would work in the same magic
    way, but attackers have no way to create /proc/deleted/path/to/file.
    User space doing its own symlink resolution would safely conclude it's a dangling link.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rainer Weikusat@21:1/5 to Kaz Kylheku on Sun Jul 31 16:09:58 2022
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Rainer Weikusat <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2022-07-29, Tavis Ormandy <[email protected]> wrote:
    On 2022-07-29, Kaz Kylheku wrote:
    But, on Linux, I'm not able to hard link a root-owned file
    into /tmp. I tried this:

    # touch /tmp/foo # as root

    /tmp $ ln foo bar # as myself
    ln: failed to create hard link 'foo' => 'bar': Operation not permitted >>>>>

    It's configurable, it's the fs.protected_hardlinks sysctl. AFAIK it's
    not the default, your distro probably sets it.

    Indeed it is; and the purpose of that sysctl is preventing exactly this
    /tmp muckery.

    Making that configurable at all seems like a poor decision, not to
    speak of the which way it defaults.

    Sudden changes of semantics which have existed for a long time are
    usually imprudent. People often don't appreciate "Of course, it doesn't
    work. But look how secure it is!"

    But distros have turned this on and people don't even know about it.

    I can hardly think of a valid use case for linking a file into /tmp
    that you do not own!

    Hard linking /your own file/ is allowed.

    /tmp$ touch foo
    /tmp$ ln foo bar

    What is the use case for linking a file /that you do not own/ into
    /tmp?

    Someone who can write to a directory can create new directory entries.

    That's very dubious given how /tmp works; in /tmp you're supposed to
    access your own stuff and keep out of anyone else's.

    And /tmp is just a directory.

    You cannot remove (i.e. unlink) an object from /tmp you don't own; why
    would the opposite operation, link, be allowed?

    That's because /tmp usually has the so-called sticky bit set which stops unlinking of files one doesn't own from directories one also doesn't
    own.

    The case pretty solid for ending the cautious period of configurability
    and making it a hard-coded behavior.

    Why?

    I did a little internet search on this but the only issue I found was
    that of a program which used the directory its executable happened to be
    stored in to locate some supposedly trusted subprograms it would run
    under certain circumstances. That's clearly broken as designed.

    The patch which implemented this securit feature asserted that it would
    solve actual problems but didn't really spell them out (minus claiming
    that this would enable users to create backups of setuid-executables
    with security holes in them which would make fixing these more
    difficult). IMHO, that's another fine example of "Yes, we can!"-coding
    by Google employees.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)