|
|
Subscribe / Log in / New account

LSM stacking and the future

LSM stacking and the future

Posted Nov 26, 2019 22:03 UTC (Tue) by vadim (subscriber, #35271)
In reply to: LSM stacking and the future by Cyberax
Parent article: LSM stacking and the future

Nope. You can have multiple wrappers that run multiple Apache copies.

No, I'm not talking about multiple Apache copies. I'm talking about Apache calling other binaries. That is, a situation where you have:

Wrapper -> Apache -> CGI_1
                  -> CGI_2
                  -> CGI_3

What I'm saying is that you have several problems there:

  1. The Wrapper makes it impossible for any of its children (Apache, or Apache's children) to pledge/unveil anything, because pledge/unveil work by listing what you will do, and closing off the rest, after which the functionality is closed off to any children. This means that if anything wants to drop privileges further, now it can't. If it thinks that's an error, it won't run. Otherwise it'll run with more privileges than it needs, and the Wrapper is actually compromising the security of it.
  2. This system means that you need to pledge/unveil everything Apache or any of its children might ever want, and grant that access to that Apache instance and every child. Which means Wrapper must pledge/unveil everything Apache, CGI_1, CGI_2, and CGI_3 at once. You can't allow things for Apache and deny them to the CGIs, or lockdown each CGI in its own particular way... unless you skip on locking down Apache of course.
  3. For pledge() specifically you can drop the lockdown on exec, but of course that now means the CGIs are free to do whatever they want.
  4. It's also an inflexible system in that it requires a full restart to change what you unveil. You must either unveil a subdirectory under which anything will be accessible regardless of what it is, or if you are selective, you only get to do it once in Wrapper, after which it's set in stone and requires a full restart of the Apache instance.
As I said, I'm interested in reliable _practical_ solutions that just work.

And I'm explaining why it's not very practical in practice

OK. From here on, files created in ~/public will have the apache_file_t label. However, since "file is an object blah-blah" if you move a file into ~/public it will NOT be automatically accessible. You need to remember to relabel it. The reverse is also true, if you move a file from ~/public it will still retain its labels and remain accessible.

That's not a bug, that's a feature. I mean that 100% seriously. SELinux doesn't work on paths, and isn't supposed to. This is exactly the behavior I want my system to have.

Let's compare with unveil(). You need to add access for ~/public, so you write a helper wrapper that does unveil() for that directory. Nothing else is affected, you don't need to modify your backup utility's policy. And unveil() can't be turned off, it's a core kernel feature.

Of course it can be turned off, what nonsense is that? "Core" nothing. It didn't exist once upon a time, so just install an older kernel. Or just hack it up. This looks like a promising place for a "return 0". Or perhaps here. Took me about 10 minutes and I never even touched BSD.

Besides which, look at that lovely BYPASSUNVEIL constant. And oh dear, there's a hardcoded list of bypassed rules right in the kernel source.


to post comments

LSM stacking and the future

Posted Nov 26, 2019 22:47 UTC (Tue) by Cyberax (✭ supporter ✭, #52523) [Link] (5 responses)

> 1. This means that if anything wants to drop privileges further, now it can't. If it thinks that's an error, it won't run. Otherwise it'll run with more privileges than it needs, and the Wrapper is actually compromising the security of it.
Nothing stops you from making unveil() nestable. Each successful invocation can further reduce the access. I think that's how pledge() works as well.

> 2. This system means that you need to pledge/unveil everything Apache or any of its children might ever want, and grant that access to that Apache instance and every child.
Sure. So does SELinux. Just at the labeling phase and the policy creation phase. I'm assuming that Apache simply runs the CGI scripts.

> 3. For pledge() specifically you can drop the lockdown on exec, but of course that now means the CGIs are free to do whatever they want.
Uh? Nope. pledge() is inherited across exec() calls.

> 4. It's also an inflexible system in that it requires a full restart to change what you unveil.
So does SELinux. You can't change labels of a running process.

> And I'm explaining why it's not very practical in practice
Well, no you have not.

> That's not a bug, that's a feature. I mean that 100% seriously. SELinux doesn't work on paths, and isn't supposed to.
And that's why it's dumb and is turned off in most cases.

> Of course it can be turned off, what nonsense is that? "Core" nothing. It didn't exist once upon a time, so just install an older kernel.
Nope. unveil() can't be turned off. You need to replace the kernel and reboot the system. Running unveil() on an older kernel also results in -ENOSYS.

Meanwhile, SELinux can be turned off with one command.

Want to convince me? Show me a simple script that does what you're proposing: creates a public directory and runs Apache with access to it. No need for CGIs. I'll show the corresponding unveil/pledge based wrapper.

LSM stacking and the future

Posted Nov 27, 2019 1:19 UTC (Wed) by vadim (subscriber, #35271) [Link] (4 responses)

> Nothing stops you from making unveil() nestable. Each successful invocation can further reduce the access.

Sure does: the interface. What unveil() does is first to forbid everything, then allow whatever you pass to unveil.

This means that if you don't block off unveil after making your list of exceptions, a child process or an exploit could just unveil("/") and unblock everything.

> I think that's how pledge() works as well.

pledge() has two modes:

1. Pass on the restrictions to the child. Great, unless your child can't work with those. So if you block something major, you're going to have a hard time exec()ing much after that.
2. Remove all restrictions from the child. Which means you restricted yourself, but your child can do whatever it wants.

> Sure. So does SELinux. Just at the labeling phase and the policy creation phase. I'm assuming that Apache simply runs the CGI scripts.

Nope! See, SELinux has the concept of transition rules: https://danwalsh.livejournal.com/23944.html

Which means, I can do this:

1. Confine apache, so that it can only do apache things.
2. Confine CGI, so that it can only do CGI things.
3. Write an apache -> CGI transition rule. Which means CGI rules don't pollute my Apache rules, and the CGI doesn't get to listen on ports.

This means I can have a setup where every piece is locked down to be able to do no more than it's supposed to.

> So does SELinux. You can't change labels of a running process.

But you can change the labels of files on disk, which means for instance I can take a running libvirt, and give it a disk image on a removable drive. All I need to do is to label it, and it works. I don't need to bring libvirt down and all my VMs with it, so that it can have /mnt/external added to its allowed paths list.

> Meanwhile, SELinux can be turned off with one command.

Which can be disabled with SELinux itself, if you want to. After that, reboot time.

> Want to convince me? Show me a simple script that does what you're proposing: creates a public directory and runs Apache with access to it. No need for CGIs. I'll show the corresponding unveil/pledge based wrapper.

setsebool -P httpd_enable_homedirs 1
chcon -R -t httpd_sys_content_t ~user/public_html

LSM stacking and the future

Posted Nov 27, 2019 1:23 UTC (Wed) by Cyberax (✭ supporter ✭, #52523) [Link] (3 responses)

> This means that if you don't block off unveil after making your list of exceptions, a child process or an exploit could just unveil("/") and unblock everything.
Uhh, no? unveil("/") will simply return -EPERM. So for example, you can only call unveil("~/public/www") if the parent unveiled("~/public").

LSM stacking and the future

Posted Nov 27, 2019 10:49 UTC (Wed) by vadim (subscriber, #35271) [Link] (2 responses)

Hmm, interesting. Are you saying unveil works differently after a fork()? Eg, as I understand it:

// the whole filesystem is available at the start

unveil("/tmp", "r"); // now only /tmp is visible
unveil("/var", "r"); // now I can see both /tmp and /var

Are you saying the second statement will fail if I insert a fork() (perhaps with an exec) in the middle?

LSM stacking and the future

Posted Nov 27, 2019 11:48 UTC (Wed) by johill (subscriber, #25196) [Link] (1 responses)

I think you have to call unveil(NULL, NULL) to "stop" the ability to unveil more, but typically you would of course do that since it's otherwise useless?

LSM stacking and the future

Posted Nov 27, 2019 12:11 UTC (Wed) by vadim (subscriber, #35271) [Link]

And that's exactly the point I'm making:

unveil is a nice, handy mechanism. But it doesn't nest well. Since unveil builds a list of what you want to allow, you need to lock it up with unveil(NULL, NULL). Once you do so, any further unveil(), whether under a currently locked directory or not fails.

This means it's not a good thing for things that could nest. Sample scenario:

We have a "convert_image" program that does some conversion. We secure it with unveil to ensure it doesn't touch anything it's supposed to, if say, libjpeg happens to have an exploit. Great. It works the way it should from the commandline.

Now that we have a well protected tool, we can call it from Apache and not worry much. Wonderful!

But, let's suppose that since it's so awesome, we've now applied unveil to apache too, which calls convert_image through a CGI. apache calls unveil(NULL, NULL) as it should, and eventually runs convert_image. At that point, one of two things happens:

A. convert_image notices it can't secure itself and refuses to work
B. convert_image ignores the failure and plows ahead, allowing an exploit to work within what Apache is allowed to do.

So, while an interesting tool, it's a limited one, with gotchas like the above.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds