Closed CendioOssman closed 5 years ago
I am responsible for adding and then reverting the commit. It was requested that i revert this by @stephensmalley. I am not sure what his specific reasons were but it is obviously very broad access to allow. Its not least privilege.
The problem is that if you allow all domains to send messages to unconfined domains, then you are permitting all confined domains to escape confinement by sending messages to unconfined domains that trigger operations the confined domain shouldn't be allowed to perform. This was creating unintentional security holes in policy where a confined domain could easily escalate its access through some dbus service provided by an unconfined domain.
So could a documentation note be added about this gotcha?
And what would be the recommended step for us? We're not in a position to properly confine these domains right now, and this restriction is breaking them to the point of being entirely unusable.
We don't have any dbus services, so allowing send_msg
to specifically our domains should then be secure as it can only be used for replies?
My personal opinion: You have to start somewhere. DBUS is the main issue. So focus on the dbus avc denials and fix what you can when you can. You dont have to "properly confine" these domains, you just have to address this particular challenge.
Allowing the rule you suggested above is better than my commit, but it is still broad and it sets a bad precedent, especially knowing that in fedora these third party modules aren't really reviewed.
I'd like to avoid too much of whack-a-mole as we are shipping this policy to customers and we need to avoid things break randomly as Red Hat updates things. In this case we are not doing the dbus calls from our code, but it is something in the system libraries (seems to be user lookups). As such it is difficult to determine what the appropriate thing to add here is.
ok so its probably glibc nss, that is pretty limited generally init_dbus_chat (for nss_systemd) and theres a few optionals like resolved_dbus_chat (nss_resolve) and machined_dbus_chat (nss_mymachines)
If you address those three then i suspect you might have the majority covered.
Alright, thanks.
Any thoughts of grouping those under some basic user lookup macro so that not everyone needs to keep track of the latest nss modules? :)
Ideally these should arguably be covered in the auth_use_nsswitch() interface but i am not sure if that is the case in Fedora. In reference policy it is still a point of discussion on how to address nss in the future because it is also getting very broad.
... Also make it a dbus_system_bus_client() if needed.
Hah. And after an update the bug went away.
I turns out I got in dbus-broker instead of the older dbus-daemon, and dbus-broker does not seem to do an SELinux check on replies. So this is a good thing I guess and means the policy will now be applied more sanely on such systems? I.e. no more bi-directional allow rules just to make responses work.
Ah well, I still need to add something for systems that aren't running dbus-broker.
I doubt that. The thing with these userns lookups is that it only wants to translate them if there is something to translate. Meaning that if theres no files or processes with these userns UID/GIDs in the way of your process then it also wont need to request translation. In other words it make take some time to reproduce the event
hmm it seems that you are right ...
I could reproduce the issue every time before the upgrade, so I'm fairly sure the calls were made constantly.
These are the traces I got:
From sudo dbus-monitor --system
method call time=1546964053.400922 sender=:1.115 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method return time=1546964053.401056 sender=org.freedesktop.DBus -> destination=:1.115 serial=4294967295 reply_serial=1
string ":1.115"
signal time=1546964053.401170 sender=org.freedesktop.DBus -> destination=(null destination) serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.115"
string ""
string ":1.115"
signal time=1546964053.401713 sender=org.freedesktop.DBus -> destination=:1.115 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.115"
method call time=1546964053.401805 sender=:1.115 -> destination=org.freedesktop.systemd1 serial=2 path=/org/freedesktop/systemd1; interface=org.freedesktop.systemd1.Manager; member=GetDynamicUsers
method return time=1546964053.402059 sender=:1.1 -> destination=:1.115 serial=705 reply_serial=2
array [
]
signal time=1546964053.403557 sender=org.freedesktop.DBus -> destination=:1.115 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
string ":1.115"
signal time=1546964053.403709 sender=org.freedesktop.DBus -> destination=(null destination) serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.115"
string ":1.115"
string ""
method call time=1546964053.520837 sender=:1.116 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method return time=1546964053.520892 sender=org.freedesktop.DBus -> destination=:1.116 serial=4294967295 reply_serial=1
string ":1.116"
signal time=1546964053.520916 sender=org.freedesktop.DBus -> destination=(null destination) serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.116"
string ""
string ":1.116"
signal time=1546964053.520951 sender=org.freedesktop.DBus -> destination=:1.116 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.116"
method call time=1546964053.520973 sender=:1.116 -> destination=org.freedesktop.systemd1 serial=2 path=/org/freedesktop/systemd1; interface=org.freedesktop.systemd1.Manager; member=GetDynamicUsers
method return time=1546964053.521718 sender=:1.1 -> destination=:1.116 serial=706 reply_serial=2
array [
]
signal time=1546964053.522650 sender=org.freedesktop.DBus -> destination=:1.116 serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
string ":1.116"
signal time=1546964053.522805 sender=org.freedesktop.DBus -> destination=(null destination) serial=4294967295 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
string ":1.116"
string ":1.116"
string ""
It shows four call
:s with corresponding return
s. And I see only four calls to selinux_check_access()
in dbus-broker
:
Breakpoint 1, selinux_check_access (scon=0x555d4fde4d20 "system_u:system_r:thinlinc_webaccess_t:s0", tcon=0x555d4fc74358 "system_u:system_r:system_dbusd_t:s0-s0:c0.c1023", class=0x555d4e2d995e "dbus",
perm=0x555d4e2d9963 "send_msg", aux=0x0) at checkAccess.c:35
35 int selinux_check_access(const char *scon, const char *tcon, const char *class, const char *perm, void *aux) {
(gdb)
Continuing.
Breakpoint 1, selinux_check_access (scon=0x555d4fde4d20 "system_u:system_r:thinlinc_webaccess_t:s0", tcon=0x555d4fd5ad10 "system_u:system_r:init_t:s0", class=0x555d4e2d995e "dbus",
perm=0x555d4e2d9963 "send_msg", aux=0x0) at checkAccess.c:35
35 int selinux_check_access(const char *scon, const char *tcon, const char *class, const char *perm, void *aux) {
(gdb)
Continuing.
Breakpoint 1, selinux_check_access (scon=0x555d4fde4d20 "system_u:system_r:thinlinc_webaccess_t:s0", tcon=0x555d4fc74358 "system_u:system_r:system_dbusd_t:s0-s0:c0.c1023", class=0x555d4e2d995e "dbus",
perm=0x555d4e2d9963 "send_msg", aux=0x0) at checkAccess.c:35
35 int selinux_check_access(const char *scon, const char *tcon, const char *class, const char *perm, void *aux) {
(gdb)
Continuing.
Breakpoint 1, selinux_check_access (scon=0x555d4fde4d20 "system_u:system_r:thinlinc_webaccess_t:s0", tcon=0x555d4fd5ad10 "system_u:system_r:init_t:s0", class=0x555d4e2d995e "dbus",
perm=0x555d4e2d9963 "send_msg", aux=0x0) at checkAccess.c:35
35 int selinux_check_access(const char *scon, const char *tcon, const char *class, const char *perm, void *aux) {
(gdb)
Continuing.
This also corresponds well with how I'm reading the dbus-broker code:
https://github.com/bus1/dbus-broker/blob/66a3e764c4df9f555dd55bdb96e69336f07d07bc/src/bus/peer.c
You can see it doing a policy check (where the SELinux check is) in peer_queue_unicast()
for calls, but no such check in peer_queue_reply()
for the returns.
This makes it more in line with traditional unix domain sock behavior, but i wonder whether this is by design and whether this was discussed somewhere...
It is kinda mentioned here in the form of "Reply policies":
https://github.com/bus1/dbus-broker/wiki/Deviations
But it doesn't sound like a terribly firm design choice. And I'm not sure if they considered the SELinux aspect of it.
I am seeing bugs in dbus-broker:
[root@brutus ~]# date
Tue Jan 8 17:53:22 CET 2019
[root@brutus ~]# sesearch --auditallow
auditallow xserver.subj xserver.subj:x_server debug; [ xserver_object_manager ]:False
[root@brutus ~]# busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel
s "info"
[root@brutus ~]# journalctl -rb --grep granted | head -n 2
-- Logs begin at Sat 2018-12-29 11:53:13 CET, end at Tue 2019-01-08 17:53:33 CET. --
Jan 08 17:53:33 brutus audit[1327]: USER_AVC pid=1327 uid=81 auid=4294967295 ses=4294967295 subj=sys.id:sys.role:dbus.system.subj:s0 msg='avc: granted { send_msg } for scontext=wheel.id:sysadm.role:busctl.sysadm.subj:s0 tcontext=sys.id:sys.role:systemd.system.subj:s0 tclass=dbus
That auditallow rule is no longer loaded but dbus-broker still enforces it
@teg @dvdhrm any insights on the above?
seems it does not receive policyload notices anymore?
@CendioOssman this was intentional, I have now added a couple of sentences to the wiki to make it clearer. Let me know if it could still be improved.
@doverride sounds like we are missing some policy reload (which I thought libselinux was taking care of for us, but I'll have to refresh my memory some more). I take it you observe this after changing the SELinux policy at runtime, but not after a reboot?
Should be handled automagically by libselinux if using selinux_check_access(). If not, then that seems like a bug in libselinux or policy or kernel. Please confirm that compute_av reports the right result from the kernel for the auditallow vector?
Yes I thought selinux_access_check() would take care of that as well.
I observe this after changing the policy at runtime but i do not observe it on a reboot... indeed
I haven't tested compute_av because fedora does not ship that in its libselinux-utils package. Instead i tried the same with systemd (systemd is also an object manager so that is comparable)
It works as expected with systemd so that indicates to me that its not a policy, selinux or kernel issue.
dbus-broker: https://www.youtube.com/watch?v=f97fi7qFET8 systemd: https://www.youtube.com/watch?v=iY6_kGz01vg
@CendioOssman this was intentional, I have now added a couple of sentences to the wiki to make it clearer. Let me know if it could still be improved.
@teg, ehm... which wiki? I could not find anything. :)
And I was really thinking about this documentation, as that was what I checked:
Ah. Too many things discussed on this issue. :)
That change looks good to me. :+1:
Feel free to send a PR with what you think should be added to the XML
I'm not entirely sure what to add though. The warning part is fairly straightforward, but what should be the recommended alternative? It would be nice with something less vague than this:
diff --git a/policy/modules/system/unconfined.if b/policy/modules/system/unconfined.if
index 565e7129e..31c9eff94 100644
--- a/policy/modules/system/unconfined.if
+++ b/policy/modules/system/unconfined.if
@@ -111,13 +111,21 @@ interface(`unconfined_domain_noaudit',`
## <p>
## Make the specified domain unconfined and
## audit executable heap usage. With exception
-## of memory protections, usage of this interface
-## will result in the level of access the domain has
-## is like SELinux was not being used.
+## of memory protections and D-Bus access, usage of this
+## interface will result in the level of access the domain
+## has is like SELinux was not being used.
## </p>
## <p>
## Only completely trusted domains should use this interface.
## </p>
+## <p>
+## Note that the specified domain will be allowed to acquire
+## a D-Bus service name, and send messages to any other
+## domain. However, other domains will not automatically be
+## allowed to send responses back to the specified domain.
+## Any such communication must be explicitly allowed in
+## addition to making the specified domain unconfined.
+## </p>
## </desc>
## <param name="domain">
## <summary>
@CendioOssman at the risk of making this even more complicated: the original text was correct when dbus-broker was in use for DBus communication, and as that will be the default at least in Fedora with the next release, I would think that changing the text might confuse more than it enlightens?
Possibly. But it really depends on how quickly and totally dbus-daemon will be abandoned. E.g. when will the RHEL versions switch over or reach EOL?
This stuff potentially applies to more user space object managers. It is potentially not dbus specific. We cannot tell beforehand what user space decides to enforce. So it is hard to make any guarantees about user space object managers.
Fair enough, makes sense. Though mabe make the docs more generic then? So people don't assume this only applies to DBus, nor that it applies to all DBus implementations?
I haven't tested compute_av because fedora does not ship that in its libselinux-utils package. Instead i tried the same with systemd (systemd is also an object manager so that is comparable)
It works as expected with systemd so that indicates to me that its not a policy, selinux or kernel issue.
I would have preferred to replicate the exact same check via compute_av or alternatively calling selinux.selinux_check_access() from the python bindings and verifying correct behavior. In your systemd test case, was the rule also conditional and was the boolean initial value the same?
stephensmalley notifications@github.com writes:
I haven't tested compute_av because fedora does not ship that in its libselinux-utils package. Instead i tried the same with systemd (systemd is also an object manager so that is comparable)
It works as expected with systemd so that indicates to me that its not a policy, selinux or kernel issue.
I would have preferred to replicate the exact same check via compute_av or alternatively calling selinux.selinux_check_access() from the python bindings and verifying correct behavior. In your systemd test case, was the rule also conditional and was the boolean initial value the same?
Neither were conditional. I mess with policy pretty much all day every day. I would notice something like this. This dbus-broker issue is likely pretty new.
This dbus-broker version 17 is pretty recent and i wouldnt be surprised if the issue was introduced there.
I will see if i can find the time to look into this further maybe tomorrow or over the weekend but i am pretty confident that its just a bug in dbus-broker
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
-- Key fingerprint = 5F4D 3CDB D3F8 3652 FBD8 02D5 3B6C 5F1D 2C7B 6B02 https://sks-keyservers.net/pks/lookup?op=get&search=0x3B6C5F1D2C7B6B02 Dominick Grift
Fair enough, makes sense. Though mabe make the docs more generic then? So people don't assume this only applies to DBus, nor that it applies to all DBus implementations?
I will leave that decision to others. It is not that simple either. It seems that not everything it considered a "reply". I was all hopeful about this and set out to try it out here: https://github.com/DefenSec/dssp2-standard/commit/e5bae290654d7d9970229971aa615e1a4f1e1c5c
Turns out that either i didnt fully understand it, and/or that it practically makes little difference. In a lot of cases it seems to not just be a matter of just a "reply". Take for example this:
Jan 10 18:09:50 brutus dbus-broker[1327]: A security policy denied :1.9 to send signal /org/freedesktop/login1/session/_32:org.freedesktop.login1.Session.PauseDevice to :1.93.
Jan 10 18:09:50 brutus audit[1327]: USER_AVC pid=1327 uid=81 auid=4294967295 ses=4294967295 subj=sys.id:sys.role:dbus.system.subj:s0 msg='avc: denied { send_msg } for scontext=sys.id:sys.role:logind.subj:s0 tcontext=wheel.id:wheel.role:xserver.subj:s0 tclass=dbus permissive=0
exe="/usr/bin/dbus-broker" sauid=81 hostname=? addr=? terminal=?'
So systemd-logind has a name on the bus and provides services. xserver seemingly consumes such a service where i suppose it asks logind to keep it posted on something. So logind "replies" (i suppose indirectly), but the check is performed.
In the case of unconfined_domains you can't realy determine beforehand if the request an unconfined domain does can be replied to immediately or whether that requires some indirect reply that is checked after all.
In other words yes sometimes a rule to allow "replies" can be ommited but especially in the case of unconfined domains there is no way to know for sure if that is always the case. So youre practically most of the time better off explicitly allowing a reply.
I haven't tested compute_av because fedora does not ship that in its libselinux-utils package. Instead i tried the same with systemd (systemd is also an object manager so that is comparable) It works as expected with systemd so that indicates to me that its not a policy, selinux or kernel issue.
I would have preferred to replicate the exact same check via compute_av or alternatively calling selinux.selinux_check_access() from the python bindings and verifying correct behavior. In your systemd test case, was the rule also conditional and was the boolean initial value the same?
I think have jumped to gun and pointed fingers at dbus-broker too soon, because i cannot consistently produce this. I need more time to look into this
@doverride Only replies (including errors) to method calls are implicitly allowed whenever their triggering method call wasy. The reason for this is that there is a one-to-one correspondence between the request (the method call) and the reply, where the request clearly indicates that a reply is expected and should be allowed.
What you are seeing is a signal being denied, and signals are treated the same in dbus-broker and dbus-daemon. A signal is not sent in reply to a method, but is broadcast to the bus, and anyone having installed a match to it will receive it (unless there are policies in place to deny it). One could argue that maybe there should be a policy mechanism making it possible to restrict who can match on what, and hence removing the need for policies on the actual signals, but that is not the case, so the only way to put a policy on who can receive what signals is to require explicit permissions for a sender of a signal to send it to a given receiver.
As for the policy comments, it should be something along the lines that the interface does not allow return communications from confined domains via message based mechanisms such as dbus or SysV message queues.
@doverride Only replies (including errors) to method calls are implicitly allowed whenever their triggering method call wasy. The reason for this is that there is a one-to-one correspondence between the request (the method call) and the reply, where the request clearly indicates that a reply is expected and should be allowed.
What you are seeing is a signal being denied, and signals are treated the same in dbus-broker and dbus-daemon. A signal is not sent in reply to a method, but is broadcast to the bus, and anyone having installed a match to it will receive it (unless there are policies in place to deny it). One could argue that maybe there should be a policy mechanism making it possible to restrict who can match on what, and hence removing the need for policies on the actual signals, but that is not the case, so the only way to put a policy on who can receive what signals is to require explicit permissions for a sender of a signal to send it to a given receiver.
Thanks. I am going to try it again and implement signal subscribers. It is not easy to do policy development if dbus-broker (or maybe all object managers) only reloads policy one time at runtime (that seems to be the case but i am still looking into it)
@doverride Only replies (including errors) to method calls are implicitly allowed whenever their triggering method call wasy. The reason for this is that there is a one-to-one correspondence between the request (the method call) and the reply, where the request clearly indicates that a reply is expected and should be allowed. What you are seeing is a signal being denied, and signals are treated the same in dbus-broker and dbus-daemon. A signal is not sent in reply to a method, but is broadcast to the bus, and anyone having installed a match to it will receive it (unless there are policies in place to deny it). One could argue that maybe there should be a policy mechanism making it possible to restrict who can match on what, and hence removing the need for policies on the actual signals, but that is not the case, so the only way to put a policy on who can receive what signals is to require explicit permissions for a sender of a signal to send it to a given receiver.
Thanks. I am going to try it again and implement signal subscribers. It is not easy to do policy development if dbus-broker (or maybe all object managers) only reloads policy one time at runtime (that seems to be the case but i am still looking into it)
Welp, can't say i did not try. There's just too many signal providers. Need a way to differentiate between sending signals and method calls.
I will have another look at the policyload issue again. It sucks that doing a setenforce 0
has no effect on dbus-broker (and maybe others).
Must really be dbus-broker only issue
when you load a policy at runtime that affects dbus, all subsequent reloads are "ignored" by dbus-broker regardless of the changes
and even mac status changes are ignored in that case. So setenforce no longer affects dbus then.
@teg I think its this: https://github.com/bus1/dbus-broker/commit/5ad77724275cbb7831465cef0d93ca5b3084bd58#diff-5f25f6e3571b4ef6c9938932a3d8680f
I heve not exactly narrowed it down to one or more specific directives but commenting the above all out makes it work again.
BTW: systemctl edit --full
is broken in 240....
It is the PrivateNetwork=yes, dbus-broker needs access to the netlink-selinux-socket
@doverride thanks a lot for figuring this out! I'll follow up on the dbus-broker issue you created.
Originally reported to Red Hat since it was seen on modern Fedora:
https://bugzilla.redhat.com/show_bug.cgi?id=1647920
Seems to be a general refpolicy issue though.
The basic issue is that a domain with
unconfined_domain(my_domain_t)
will be allowed to send messages over dbus without issue. However the responses will often be rejected because there is no rule allowing the other domain tosend_msg
tomy_domain_t
.Example AVC where
thinlinc_webaccess_t
is unconfined:It looks like this bug was fixed here:
https://github.com/TresysTechnology/refpolicy-contrib/commit/6bef7a14757124c56fadc08c255e9dd6c29a15f9
But then reverted because of security issues here:
https://github.com/TresysTechnology/refpolicy-contrib/commit/bc147412848c53b2150490a2c5090a17a40fab82
Does anyone have any reference for those security problems?
If unconfined domains cannot use dbus by default, then this should be clearly documented for
unconfined_domain()
, and there should be some information on how to enable dbus for such domains. Explicitly listing every other domain (or using equivalent macros such asinit_dbus_chat()
) defeats the whole purpose ofunconfined_domain()
.What risk am I taking by adding this and allowing full dbus communication to my domain: