Bug 455844

Summary: [RFE] Dynamic linker should not open shared libraries in world-writable directories
Product: Red Hat Enterprise Linux 6 Reporter: Alain Roy <roy>
Component: glibcAssignee: Carlos O'Donell <codonell>
Status: CLOSED WONTFIX QA Contact: qe-baseos-tools-bugs
Severity: medium Docs Contact:
Priority: low    
Version: 6.5CC: fweimer, jakub, jan.kratochvil, law, matt, mfranc
Target Milestone: rcKeywords: FutureFeature
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2014-02-04 19:20:42 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Alain Roy 2008-07-18 09:19:27 UTC
I reported this to secalert (#24818), and they recommended that I
report it to Red Hat's bugzilla system, so I am doing so. 

I'm the Software Coordinator of the Open Science Grid (OSG), which is a US-based
grid computing effort, <http://www.opensciencegrid.org>. OSG provides a large
software stack that can be installed on a variety of platforms, primarily Linux.
We ship pre-built binaries, and our software cache can be found at
<http://vdt.cs.wisc.edu>.

We recently came across a security vulnerability in our code due to oversight on
our part, and we fixed the problem. However, the examination of the problem
pointed out what we believe is a flaw in the Linux dynamic linker. I'm not sure
what the best fix is, but we decided to bring it to your attention.

===== Our security problem =====
A few things come together to cause a security problem.

1) We build our software with Condor (a batch job processing system), so our
software is built in a directory called /home/condor/execute/dir_12021. We did a
naive build with "configure/make/make install" so we could create a tarball that
we re-distribute. We passed a --prefix to configure that refers to
/home/condor/execute because that is where we have permission to build. This has
the side effect of setting various hard-coded paths to be in
/home/condor/execute. This doesn't normally cause us any problems, but it also
set the ELF binaries' RPATHs to be in /home/condor/execute, mostly because of
default usage of libtool.

2) Condor often has a execute directory named /home/condor/execute, so that
directory is reasonably likely to be found on our users' computers, because
about 50% of them use Condor.

3) Until last week's Condor 7.0.2 release, Condor's execute directory (such as
/home/condor/execute) was world-writable.

4) ld defaults to setting RPATH instead of RUNPATH.

5) RPATH is used before LD_LIBRARY_PATH, so it cannot be overridden. (This is
unlike RUNPATH, which can be overridden by LD_LIBRARY_PATH.)

Conclusion: On computers with /home/condor/execute installed as a world-writable
directory, it's really easy to inject code into many of the programs that we
shipped. Drop in a new shared library, and it runs in everyone's binary.

While this is "just" a local exploit, it's a pretty big hole.

===== Our mitigation =====
a) We got rid of RPATH.

b) In the future, we'll build with a better prefix and use DESTDIR to make our
tarballs, wherever possible.

c) Condor released a new version that keeps /home/condor/execute from being
world-writable.

d) Users could optionally move /home/condor/execute to another location, as an
alternative fix.

===== Linux's dynamic linker =====
While the problem was entirely our fault, we think that part of the problem
still needs to be mitigated in Linux's dynamic linker. I would like to prevent
this problem from biting other users in the future.

Right now, the dynamic linker (ld.so) is willing to open the first shared
library it finds that matches the name it is looking for. It has rules about
what directories it searches for shared libraries (for example, don't search
RPATH if RUNPATH is set), but I couldn't find that it checks permissions at all.

I believe that by default, the dynamic linker should not load a library if the
path that is traversed to access the library contains any untrusted file system
objects. For example, if a library is located at /tmp/slink/foo.so and
/tmp/slink is a symbolic link to /home/user/lib, then the following need to be
trusted:

/
/tmp
/home
/home/user
/home/user/lib
/home/user/lib/foo.so

If any of those is world-writable, or better yet, can be written to by a user
you don't trust, the dynamic linker should ignore that shared library and
continue searching for a shared library in the remainder of the search paths.
Jim Kupsch's paper is of assistance here:

   http://pages.cs.wisc.edu/~kupsch/safefile/

Although it isn't the dynamic linker's fault that the search path is set up
badly (it could be the developer/packager that made a bad RPATH, or the user
that made a bad LD_LIBRARY_PATH), the dynamic linker should prevent users from
behavior that could inject code.

There are a lot of details for which I don't know the answers.

1) Should there be a way to override to this behavior? Perhaps an
LD_ALLOW_UNTRUSTED_DIRS environment variable?

2) How should you specify exactly what directories are safe or not? See Jim
Kupsch's paper for one answer. (above)

3) Will the performance hit from doing this check be worse than the security
improvement? For example, what if LD_LIBRARY_PATH specifies directories on a
slow NFS server?

4) Should the same standard be applied to running of the executable itself?

Please note that this would have prevented similar problems for other people,
such as <http://secunia.com/advisories/30803/>. 

Thanks for considering this problem. Please let us know if you have any questions.

Alain Roy <roy.edu>
OSG Software Coordinator

Mine Altunay <maltunay>
OSG Security Coordinator

Comment 5 Jeff Law 2014-02-04 19:20:42 UTC
The dynamic loader is doing exactly what the application author requested e.g. searching in hard coded rpaths.  We will not be changing the dynamic loader's behaviour to match the request in this BZ.