#514015 cdebootstrap - packages with symlinks to dirs (libc6) can lead to writes outside the new root #514015
- Package:
- cdebootstrap
- Source:
- cdebootstrap
- Description:
- Bootstrap a Debian system
- Submitter:
- Bastian Blank
- Date:
- 2017-11-17 12:30:08 UTC
- Severity:
- important
Packages with absolute symlinks to dirs like libc6 on amd64, ppc64 and s390x can lead to overwrites of files outside of the new root. Bastian
Both cdebootstrap and debootstrap uses plain tar to extract packages the first time. If one package (lib6) contains the symlink /lib64 -> /lib, another package (in this case libattr1) which includes files in /lib64, will be extracted into the host system and overwrite files there, as tar follows the symlinks. Bastian
Hi, just as an exercise what might be done to fix this: It would seem that the options are - not fix it (for now), - find something in current tar that works (I didn't), - switch off tar, - try to do something with tar (new upstream - vs. release: ugh - has a "apply to symlink target" --transform flag, but that also might need post-processing of symlinks at end of bootstrapping), - try to do some ld-preload(?) trickery for changing the way symlinks are read, - try to find some way to chroot before calling tar, one possibility would be copying required files to some temp CWD, make tar look there for the libraries, chrooting to the target and calling tar from (the unchanged) CWD, - add chroot option to tar (see patch). It would seem that in terms of total invasiveness, the last option is minimal, but it might be unwise to add options without consulting upstream (however, it once upstream agrees to have an option with a given spec, one could implement that locally). Of course, the patch introduces new strings (translator-:( ), but they only shown with the new option. But then, this is just an idea, I'm sure you'll come up with a good, and probably better solution. Kind regards T.
How about just fixing the offending package to use a relative symlink, like /lib64 -> lib? This is well within policy since they're adjacent objects in the same directory, and is less likely to break things or be a pain to maintain over time than any of the options above. I can't think of any negative consequence to this change, offhand? Adding a Debian-specific option to tar would certainly not be my first choice. Bdale
There is nothing. I intend to replace tar with a custom unpack routine in cdebootstrap. What would dpkg do with the symlinks in the the unpack phase? fakechroot have to do this someway, but I was unable to grok that code yet. Rather hacky. Would mean to include mklibs-copy or similar functionality. This is a root only option. Bastian
This would only affect one part of the problem. But this bug is about the problem that it will write outside of the new root if it encounters symlinks not that they currently exist. Bastian
Hi, thanks for your replies. Again, this is mostly because I needed some idle occupation and am not into sudoku. Apologies for presenting it a way that looked like a proposal. Bdale Garbee wrote: Well, having 1000 maintainers do the right thing is difficult, as proven by the release process. Other options do exist, my list probably is not exhaustive, but I would think that they cost a lot of code with a lot of opportunity to add bugs where things work now and it might be interesting to have an upper bound for how involved a minimal fix is. Thinking on a larger scale, what we actually want to do here is unpack a tarball into a chroot. I venture that this is a reasonable use case of tar and that it might just be possible to try to have upstream agree to have such an option in principle so that there is a plan to get rid of the debian-specificity of the option. Bastian Blank wrote: TBH, I'm not that impressed by the implementation of ar (or a subset) in dpkg and am not sure that reimplementing tar extraction it is a good path to take when I would think that an option for tar to do the right thing (whether internally munging symlinks when resolving paths or using chroot) might have a general use beyond bootstrapping Debian systems. I have not tested it, but I would venture the same thing (calling tar, ergo having the same problem) from when I last looked at it. Well, you'd have to intercept all the relevant calls (for dereferencing/reading symlinks). Seems doable, but IMO tar would be the better place. But if the masses want funny business, so be it. ... Yes. It was my impression that (c)debootstrap are supposed to be called by root. At least I always used to call them as root when I was a developer. Kind regards T.
dpkg does not call tar, it has it own tar implementation (which explains why we don't support yet POSIX tar archives inside .deb). I'm not sure how it behaves in the situation that matters for this bug. Try it with dpkg --root=/target/dir/ … Cheers,
Thanks for stepping in to correct me here. dpkg does have its own tar, only dpkg-deb calls tar. From looking at the code, it does the right thing. Kind regards T.
No, it does not. | $ dpkg-deb -c ../test.deb | drwxr-x--- waldi/waldi 0 2009-02-07 17:09 ./ | drwxr-x--- waldi/waldi 0 2009-02-07 17:09 ./lib64/ | -rw-r----- waldi/waldi 5 2009-02-07 17:09 ./lib64/test | $ l -ad lib* | drwxr-x--- 2 waldi waldi 4096 7. Feb 17:09 lib/ | lrwxrwxrwx 1 waldi waldi 4 7. Feb 17:09 lib64 -> /lib/ | $ sudo dpkg --root . -i ../test.deb | (Reading database ... 3 files and directories currently installed.) | Preparing to replace test 1 (using ../test.deb) ... | Unpacking replacement test ... | Setting up test (1) ... | $ l lib*/test /lib/test | -rw-r----- 1 waldi waldi 5 7. Feb 17:09 lib64/test | -rw-r----- 1 waldi waldi 5 7. Feb 17:09 /lib/test Bastian
* Bdale Garbee (bdale@gag.com) [090922 19:14]: I don't see a case where a package needs a absolute symlink. If there is no use case, it'd be easy to just stop such packages in dak from entering the archive. Cheers, Andi
Hi, The attached patch makes cdebootstrap chroot to the target directory before running tar, which should prevent it from following symlinks out of the target directory. I've verified that it works on i386. I've tried to ensure that it will work on other architectures, but I haven't tested it on any. It requires that ldd be available, which may be a problem for debian-installer.
I doesn't. It calls /bin/sh within the chroot, which of course fails when installing for a different architecture. This is easily reproduced: cdebootstrap -aarmel squeeze squeeze
severity 514015 important severity 514016 important thanks While there are many good arguments made here about why (c)debootstrap should handle these symlinks safely, fundamentally, if you can install two packages at the same time that disagree about whether a given path is a symlink or a directory, there's a bug in those packages. All co-installable packages need to agree on symlinks and directories, otherwise installing a package that uses a symlink, followed by installing a package that uses a directory, then removing the package that shipped the symlink will leave orphaned files on the filesystem that dpkg won't be able to find anymore. And using relative symlinks instead of absolute symlinks won't fix this, either. So *that* bug should be treated as RC, wherever it occurs, but I don't think we should treat this bug in *debootstrap as RC.
This bug can ruin the host! Steps to reproduce. * Host should be squeeze amd64. I used absolutely fresh squeeze with few packages. It have normal squeeze's debootstrap 1.0.26+squeeze1 * Run in it: # debootstrap --variant=minbase squeeze /tmp/target http://archive.debian.org/debian # debootstrap --variant=minbase wheezy /tmp/target http://deb.debian.org/debian * The first debootstrap was OK, the second debootstrap stopped after this: === I: Extracting debianutils... I: Extracting diffutils... I: Extracting dpkg... I: Extracting e2fslibs... I: Extracting e2fsprogs... I: Extracting libcomerr2... I: Extracting libss2... I: Extracting libc-bin... I: Extracting libc6... === * After this point nearly any command doesn't work AT HOST!!! After this point the dynamic linker is not available. For example, /bin/true gives this: "bash: /bin/true: No such file or directory". "ldconfig" as root fixes the situation. But this quiet possible the user simply would not guess he should type "ldconfig". Moreover, I think if he has no already opened root shell, he cannot open it (I think "sudo" will not work and I think attempting to log in in /dev/tty1 will not work, too). So, this is quiet possible the user will simply power off the computer. And then (I think) he will unable to boot the computer anymore. Yes, this is still possible to restore the computer (I think something like "linux /vmlinuz rw init=/sbin/ldconfig" from GRUB), but this is possible the user will not guess the right command. So he will simply reinstall OS. So, this is absolutely critical bug, which can force average user to reinstalling OS. Moreover, in the time while the dynamic linker is missing, other running programs may experience data loss. So, this is critical data-loss bug! You may say that this has little probability that someone will run debootstrap two times in same dir with different debian releases. Yes, this has little probability. But I run into this problem once. And this is not important to speak about probabilities when the consequences are such bad (I mean OS reinstalling). The same bug is reproducible with squeeze's cdebootstrap (i. e. 0.5.7). The same bug is reproducible with squeeze's dpkg-deb (dpkg 1.15.11). Okey, how I run into this problem? Well, in fact I was developing my own debootstrap replacement. And I occasionally did run it in one directory two times. Then all commands stopped to work. Fortunately I had root shell opened and fortunately I was smart enough to type "ldconfig" into it. Then I checked that same problem applies not only my program, but also to original debootstrap. Okey, how to reproduce with without risk of crashing your host? Well, let's assume you has host A with any OS. Create chroot environment B with squeeze using debootstrap. Then chroot into it, create dir C and run debootstrap two times on it as I described above. This will crash B, but not A. It seems the bug is not reproducible when host is something newer than squeeze. So, I did not open separate bug report and posted my problem here. But the root cause of the issue is bugs #514015 / #514016, so these bugs (#514015 / #514016) should be fixed. It is possible that #514015 / #514016 for some reason will cause some another critical "reinstall this OS" bug. One possible fix for my problem: check (in both debootstrap and cdebootstrap) that target is empty or non-existent. Now some more info about my bug. This is part of file hierarchy after first debootstrap: /lib /lib/ld-2.11.3.so /lib/ld-linux-x86-64.so.2 -> ld-2.11.3.so /lib64 -> /lib /tmp/target/lib /tmp/target/lib/ld-2.11.3.so /tmp/target/lib/ld-linux-x86-64.so.2 -> ld-2.11.3.so /tmp/target/lib64 -> /lib This is contents of libc6_2.13-38+deb7u10_amd64.deb from wheezy (it will be extracted during second debootstrap): /lib /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 -> ld-2.11.3.so /lib/x86_64-linux-gnu/ld-2.13.so /lib64 /lib64/ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.13.so Now second debootstrap extracts libc6_2.13-38+deb7u10_amd64.deb into /tmp/target. Debootstrap tries to put package's "/lib64/ld-linux-x86-64.so.2" into system's "/tmp/target/lib64/ld-linux-x86-64.so.2", but "/tmp/target/lib64" is symlink to /lib, so debootstrap writes to host's /lib64/ld-linux-x86-64.so.2 . So, now host's /lib64/ld-linux-x86-64.so.2 is symlink and it points to file /lib/x86_64-linux-gnu/ld-2.13.so , which is non-existent on host. So, now host's dynamic linker name ( /lib64/ld-linux-x86-64.so.2 , hardcoded into nearly all dynamic binaries) is symlink to non-exist file. == Askar Safin http://vk.com/safinaskar
Also, FreeBSD's tar has option "--chroot". And (if I remember correctly) it is used in installation process. (Also, please see my previous letter in this bug report if you missed it.) == Askar Safin http://vk.com/safinaskar
And I still think that debootstrap and cdebootstrap should check whether target is empty as a safety measure in any case. Also, I thought bug I reported still applies to modern hosts. If you have modern host (say, stretch) and perform double debootstrap (one squeeze and one wheezy) into same dir, this (as I thought) will write to host's /lib/ld-linux-x86-64.so.2 . Fortunately, this will not cause any bad consequences. But now I performed tests. I used stretch host, stretch's debootstrap 1.0.89 and stretch's cdebootstrap 0.7.7+b1. And I see no bug. When that libc6 extracts during second (c)debootstrap, symlink /tmp/target/lib64 is replaced with regular dir /tmp/target/lib64 and then (c)debootstrap writes to this dir. So, there is no (even harmless) leak to host. Maybe tar changed? So, it seems that the bug #514015/#514016 is fixed. But I am not sure, it is possible that my observation is due to special structure of Debian packages. What if some malformed untrusted Debian packages may still cause leaked files? == Askar Safin http://vk.com/safinaskar