On Thu, 18 Jul 2019 at 09:13:52 -0700, Casey Schaufler wrote:
We have discussed what's currently being
called the "hideous" format, selinux='a:b:c:d',apparmor='x'
which
in the past, and concluded that the compatibility issues would be too
great.
I agree this might be too big a compat break for existing interfaces that
were designed with the assumption that there can only be one "big" LSM
at a time, like /proc/54321/attr/current and SO_PEERSEC. It would certainly
break the current libapparmor, and presumably libselinux as well.
However, I think it would be great to have multiple-"big"-LSM-aware
replacements for those interfaces, which present the various LSMs as
multiple parallel credentials.
I think it would also be valuable to take this opportunity to pin down
what can and can't be in a label, to an extent where people who want
to represent them in a similar encoding know what they can and can't
assume about their format. For example, when dbus-daemon reports an
unusual event (like rejecting a message due to policy rules or LSMs,
or hitting a resource limit that isn't normally meant to be reached),
the log entry contains miscellaneous information about the process for
debugging purposes, and it would be good if we could include all the LSM
labels in that string without ambiguity. This is essentially the same
problem that the audit subsystem has, but with fewer constraints, since
the audit subsystem has to meet externally-imposed security requirements
but our equivalent is just a nice-to-have for debugging.
Have you been following the discussions on setting a
"display" value
to specify which LSM data is presented by /proc/self/attr/current and
SO_PEERSEC? Briefly, a process can write the name of the LSM it wants
to see data from to /proc/self/attr/display, and the aforementioned
interfaces will use that LSM. If no value has been set the first LSM
registered that uses any of these interfaces gets the nod.
I'm vaguely aware of the discussion, but LSMs aren't a big part of my
D-Bus maintainer role, so I'm afraid I can't keep up with all of it.
Do you mean that if process 11111 writes (for example) "apparmor" into
/proc/11111/attr/display, and then reads /proc/22222/attr/current
or queries the SO_PEERSEC of a socket opened by process 22222,
it will specifically see 22222's AppArmor label and not 22222's SELinux
label? Or is the contents of /proc/22222/attr/current controlled
by /proc/22222/attr/display?
How is this meant to work for generic LSM-aware user-space processes? If
(for example) ps -Z 22222 wants to get both the AppArmor label and the
SELinux label for process 22222, is it meant to write "apparmor" into
attr/display, then read /proc/22222/attr/current, then write "selinux"
to attr/display, then read /proc/22222/attr/current again? That sounds
risky if another thread might be manipulating attr/display concurrently.
The D-Bus message bus/broker (reference implementation: dbus-daemon)
is somewhat tricky because it is returning data on behalf of processes
other than itself, so it would be difficult for it to choose a good
value for "display": there's no reason why it wouldn't be responding
to requests from NetworkManager that expect to see SELinux labels, and
also requests from lxd that expect to see AppArmor labels. Obviously
it can't put both in LinuxSecurityLabel without the same compatibility
issues you're discussing.
Also note that dbus-daemon is trusted but mostly unprivileged - it
starts as root, then drops privileges to a system user normally called
messagebus, dbus or _dbus for its normal operation (although it does
retain CAP_AUDIT_WRITE) - so it can't carry out privileged operations
on other processes' /proc entries, if that's what the API requires.
I would strongly prefer it if we could get this information from
the kernel in a way that is Linux-specific but LSM-agnostic, without
having to link to libapparmor, libselinux, libsmack and everyone else's
favourite LSM library. At the moment we only need to link to libraries
for the LSMs where dbus-daemon can carry out mediation (asking the LSM
whether to accept or reject messages), and we don't need the libraries
if we are just passing through identity information.
I would also prefer it if we can get this information from SO_PEERSEC
(or some newer SO_PEERSEC replacement) without having to manipulate
ambient/implicit state like attr/display; but dbus-daemon is
single-threaded, so if we must do that, it wouldn't be *so* horrible.
Ideally I would like to be able to get all the LSM labels in O(1)
syscalls. Perhaps something with the same (buffer,length) kernel <->
user-space API as SO_PEERSEC and SO_PEERGROUPS, but instead of returning
a single \0-terminated string, it could return either the "hideous" format,
or a byte-blob that looks something like this?
char buffer[ENOUGH_LENGTH] = { 0 };
socklen_t len = sizeof (buffer);
char[] expected =
"apparmor=unconfined\0"
"selinux=system_u:system_r:init_t:s0\0"
"\0"
;
getsockopt (fd, SOL_SOCKET, SO_PEERSECLABELS, &buffer, &length);
/* should return 0 */
/* now buffer should have the same bytes as expected, ending with
* "\0\0" */
(Obviously in real life you'd have a retry loop to get the length right,
like the SO_PEERSEC code in dbus does.)
Because GetConnectionCredentials() is extensible, if there is some way
to enumerate all the security labels and get their values individually,
we could have (pseudocode)
GetConnectionCredentials(":1.1") -> {
"UnixUserID": 0,
"ProcessID": 1,
"LinuxSecurityLabel.apparmor": "unconfined",
"LinuxSecurityLabel.selinux": "system_u:system_r:init_t:s0",
"LinuxSecurityLabel": "unconfined", /* deprecated */
}
or (using D-Bus' structured type system)
GetConnectionCredentials(":1.1") -> {
"UnixUserID": 0,
"ProcessID": 1,
"LinuxSecurityLabels": {
"apparmor": "unconfined",
"selinux": "system_u:system_r:init_t:s0",
},
"LinuxSecurityLabel": "unconfined", /* deprecated */
}
with LinuxSecurityLabel showing the first LSM registered for backwards
compatibility? Or we could make LinuxSecurityLabel always be in the
"hideous" format if you chose to go that way in the kernel interfaces:
it's defined in terms of SO_PEERSEC, so whatever you do at the kernel
level, D-Bus should mimic that.
smcv