tech-security archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: realpath(3)



    Date:        Fri, 26 May 2023 13:14:33 +0200
    From:        tlaronde%polynum.com@localhost
    Message-ID:  <ZHCUmTJ0+XDVSJfe%polynum.com@localhost>

  | But yes, there is a security even if it is not in the way I was originally
  | searching it: race condition.

That kind of race condition can occur in any application that acts
as you describe, realpath(3) really has nothing to do with it (especially
as in the situation you're positing, the application is deliberately
ignoring the error return from realpath() - which yes, is more or less
what mount used to do).

When an application cannot do otherwise than as you describe, there are
steps it could take to minimise the chances of a race like this happening
in an undetected way, but it is hard to completely avoid.  This is a problem
that is inherent in any two step process without some form of locking
involved.

The same thing could happen, more or less identically, if realpath() had
succeeded rather than failed, then the application slept (for whatever
reason) and then used the result - during the intervening period the
filesystem might have been updated in almost any conceivable way.

  | And on a "linguistic" level:
  |
  | I guess that a lot of people (me included) are parsing "realpath()" as
  | "giving the real path". But it is wrong: I see know that it must be
  | parsed as REALize PATH: do walk the path given to see if this route,
  | perhaps going and backing up (/..) reaches an accessible resource.

I am not quite sure what the difference is that you're trying to
describe here.

  | This is what the implementation does (and it seems to me quite right)
  | but it is not what POSIX describes...

I am reasonably sure that the result we return (in the non error case)
is exactly what POSIX requires.

  | From the POSIX description, one could provide an implementation that
  | first "reduce", syntactically,  the "somedir/../" and, after, verifies
  | if there is a symbolic link in the resulting string.

Perhaps one could, but it certainly is not required.   But note that it
really doesn't matter - many different paths could refer to the same
underlying file - the requirement is that one of those be returned,
and that that returned path not include any components that are "."
or ".." so no /bin/./././././ls type nonsense though that is a path
to /bin/ls , and no /usr/../tmp/../var/../bin/../usr/bin/../../bin/ls
type stuff either, though that is also a path (if I got it right) to
/bin/ls.   Further, none of the components is allowed to be a symlink,
so if I did "ln -s /bin /tmp/bin" then "/tmp/bin/ls" (which would refer
to the same file, and the same directory entry) is not permitted either.

The way the implementation arrives at the result isn't specified.  It
can use whatever algorithm it likes - but the result must be a symlink
free, "." free, ".." free, path which refers to the same directory entry
as would be found by simply resolving the pathname given to realpath(3)
as its first arg.

  | walking /usr/not_existing/../lib/libc.so will not get you to libc.so,
  | while "/usr/lib/libc.so" (reducing "/not_existing/../") is indeed an
  | existing resource.

That one should return an error, as the path "/usr/not_existing/../lib/libc.so"
does not reach anything, so there's no way to get to the same directory
entry (which is what POSIX requires) via any path at all.   An implementation
which simply removed the "not_existing/.." as being redundant would be
incorrect.

  | And in case of existing symbolic links, going through the symbolic link
  | or not will not get you at the same place relatively to '..'! So by side
  | effect, the implementation seems to be the only way to achieve sanely
  | the result, but the POSIX description is not on point.

Sorry, I'm not sure what you're getting at there.   The result must refer
to the file in the arg, and must be an absolute path (from '/') with no
symlinks.   Since we don't generally permit ("hard") links to directories,
there's really just one possible path to return.

Note that posix says "resolves to the same directory entry" (not to the
same file) which also implies it must be in the same directory, with the
same name.

If there's any defect here, it is perhaps that POSIX isn't specific about
what happens when the arg is itself (ie: its last component) is a symlink.
It says no symlinks in the returned path, but requires the same directory
entry entry (which is a symlink).   I think I'll ask (later).  But it
probably intends to say "resolves to the same directory entry as the
filename arg resolves to" rather than "resolves to the same directory
entry as the filename arg" (POSIX doesn't explicity say what exactly the
result must be the same as).

  | Still with a very fine comb: the NetBSD realpath(3) man page should
  | perhaps be corrected to replace "copies" with "stores"

I'd probably use "places" in another case, though "stores" would work
as well - but here, in NetBSD, I'd probably instead say "builds".

But I agree, the man page for realpath(3) is sub-optimal (in other ways
as well).   I will try to remember to take a look at that later tonight -
if not done within 24 hours from now, feel free to remind me (there are
other more important things that need doing than this).

kre



Home | Main Index | Thread Index | Old Index