Re: realpath(3)

Robert Elz <[email protected]> Fri, 26 May 2023 19:46:28 +0700
Newsgroups gmane.os.netbsd.devel.security
Message-ID <[email protected]>
    Date:        Fri, 26 May 2023 13:14:33 +0200
    From:        tlaronde=40polynum.com
    Message-ID:  <ZHCUmTJ0+XDVSJfe=40polynum.com>

  =7C But yes, there is a security even if it is not in the way I was ori=
ginally
  =7C 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 prob=
lem
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.

  =7C And on a =22linguistic=22 level:
  =7C
  =7C I guess that a lot of people (me included) are parsing =22realpath(=
)=22 as
  =7C =22giving the real path=22. But it is wrong: I see know that it mus=
t be
  =7C parsed as REALize PATH: do walk the path given to see if this route=
,
  =7C 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.

  =7C This is what the implementation does (and it seems to me quite righ=
t)
  =7C 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.

  =7C From the POSIX description, one could provide an implementation tha=
t
  =7C first =22reduce=22, syntactically,  the =22somedir/../=22 and, afte=
r, verifies
  =7C 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 =22.=22
or =22..=22 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 =22ln -s /bin /tmp/bin=22 then =22/tmp/bin/ls=22 (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, =22.=22 free, =22..=22 free, path which refers to the same director=
y entry
as would be found by simply resolving the pathname given to realpath(3)
as its first arg.

  =7C walking /usr/not_existing/../lib/libc.so will not get you to libc.s=
o,
  =7C while =22/usr/lib/libc.so=22 (reducing =22/not_existing/../=22) is =
indeed an
  =7C existing resource.

That one should return an error, as the path =22/usr/not_existing/../lib/=
libc.so=22
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 implementa=
tion
which simply removed the =22not_existing/..=22 as being redundant would b=
e
incorrect.

  =7C And in case of existing symbolic links, going through the symbolic =
link
  =7C or not will not get you at the same place relatively to '..'=21 So =
by side
  =7C effect, the implementation seems to be the only way to achieve sane=
ly
  =7C 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 (=22hard=22) links to directo=
ries,
there's really just one possible path to return.

Note that posix says =22resolves to the same directory entry=22 (not to t=
he
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 =22resolves to the same directory entry as the
filename arg resolves to=22 rather than =22resolves to the same directory=

entry as the filename arg=22 (POSIX doesn't explicity say what exactly th=
e
result must be the same as).

  =7C Still with a very fine comb: the NetBSD realpath(3) man page should=

  =7C perhaps be corrected to replace =22copies=22 with =22stores=22

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

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