[apparmor] AppArmor DBus mediation experimentation
John Johansen
john.johansen at canonical.com
Thu Sep 22 00:37:15 UTC 2011
This is the first public airing of apparmor dbus mediation (there have
been some previous iterations that I didn't bother pushing out as they
where extremely buggy and killed dbus often). It is still very
experimental, and at this point the policy language extension, and even
details of the mediation are still very much up in the air.
I know I have been talking about this for a while, sorry they have
taken so long, but hopeful most of the crashers are out of them.
I have stuck some 64bit debs up at
people.canonical.com/~jj/aa-dbus.tgz
I'll work at getting the code up somewhere in next few days. I don't
think its quite so important as its evolving and indeed the dbus
patch is an ugly hack, so we can play with it while a more generic
LSM like solution is developed.
A little about DBUS
DBus pretends to be object oriented, but in reality that how the upper language
binding layers are engineered. The bus it self is really just a message passing
bus, with extra address information to support the object oriented upper layers.
A dbus address consists of
- socket/port that the task communicates with dbus over. We ignore this in the
dbus portion of the policy, but it is mediated by the file rules.
- connection/bus name. Basically each new connection get a unique name like
:34-907
but a connection can acquire (bind) to a well known name (you only do this if
you are providing a service)
com.acme.Foo
- message/operation type. method_call, signal, aquire, method_return, error
- object/path. Yet another name for the "object" being communicated with. They
look like file paths.
/com/acme/foo
- interface. An object can support multiple interfaces, and message can specify
which interface its intended for (this is only needed if two interface overlap).
They look like connection/bus names
com.acme.Foo
- method. The name of method being called. They look like
ListName
- actual message pay load (we don't do anything with it currently)
And of course there are multiple buses (system and session).
Notes:
- Mediation is done on acquiring a service name (somewhat like bind of a network
address), and sending of messages. The sending of messages is done with a
symmetric mediation, ie. the source is checked that it can send the message
and the destination that it can receive the message. So a single send may
result in the creation of 2 audit messages.
- The patch does not provide any ability to configure so apparmor
dbus mediation is on if you have apparmor enabled. To disable set
apparmor=0 as a kernel boot parameter
- I have rewritten and restructured the auditing path, so that there
shouldn't be any extra allocations and copying. It also is only
constructed when an audit message is to be emitted.
- The auditd path is broken at the moment but I have not built against
it. So that isn't a problem atm.
- The code is faster and does less copying of buffers and fewer syscalls,
not that this should really be a problem as dbus has quite enough
overhead of its own, so our mediation should hopefully be minimal.
With that said, when we move to the proper patch things will be faster
as caching of state can be done removing some kernel round trips.
- I have fixed the issue with session and system bus, now they should
work correctly if specified.
- There is a known problem with failing method calls from tasks that have
already died or exited, currently we reject the message as the context
the message came from can not be established. Unfortunately network
manager relies on this behavior, and breaks when we do this. So for now,
we toggle complain mode for these messages, so you get an "ALLOW" log for
them. There is no way to make them go away. The log will also have
serror/terror and info fields for these messages.
Long term this will get fixed by socket labeling and get peersec in the
kernel but I am just not there yet.
- send/receive Messages show up in pairs, with source and target switched.
Unless one side of the pair is allowed, in which case the one that is
allowed is not output.
- There is no way to force auditing of matches currently
- if you have apparmor libs in /usr/local/lib/ remove them, they will keep
dbus from starting.
- Only the dbus operations acquire and method_call, and signal are handled
correctly "method_return", "error", are not currently handled but logged.
These are going to require more design as they often don't have anything
more than the dbus unique destination set.
eg.
dest=":1.16"
- if something dies you should be able to reboot with apparmor=0 and dbus
will disable trying to do apparmor mediation.
- the current patch does not like older kernels. It will fail all
permission requests. We need to add a new state that detects apparmor
is available but doesn't support dbus.
- dumping the exe for unconfined tasks would really help figure out what
needs profiles.
- log messages missing the pid= field are from the dbus itself
- log messages missing the tpid= field are to the dbus itself
DBUS policy syntax
The policy rules are designed similar to the networking rules. To allow all dbus
messages, acquiring of service names etc. you just need the rule
dbus,
While the rule
dbus system com.foo acquire,
Is very specific in stating that the profile is allow to acquire the com.foo
service name on the system bus. The policy format currently supported is
as follows
['audit'] ['deny'] dbus ['system'|'session'] [['address=']<address>] ['path='<path>] ['interface='<interface>] ['method='<method>] [<perms>] ','
<perms> := <perm> | '(' (<perm> ','|[\w]) <perm> ')'
<perm> := r, w, rw, send, receive, acquire, bind, read, write
For the permissions
r maps to read which maps to receive,
w maps to write which maps to send,
bind maps to acquire
The ordering is currently important but in the future it could be more
flexible as most components are named if they are specified. If any
component is left off a rule then it will match everything of that type.
ie.
dbus com.foo,
will allow sending messages to com.foo, receiving messages from com.foo,
and acquiring the service name com.foo on both the session and system buses.
dbus system com.foo w,
allows sending messages too com.foo on the system bus. Which can also
be expressed as
dbus system com.foo send,
Any single permission is allow to be specified by it self, but if more
than one permission is specified it must appear in a list similar to the
flags format.
dbus system com.foo (send, receive),
The commas within the parenthesis are optional, plain whitespace delimitation
works just as well. The only exception the paren rule for multiple permissions
is rw, which can be used outside of or inside of parens.
Note it is legal to specify
dbus system com.foo (rw, send receive),
A few more examples of valid rules
deny dbus system com.foo path="/foo /bar" interface=com.bar method=fred rw,
dbus address=com.foo path=/foo/bar (send receive),
dbus com.foo acquire,
Now that the basis of the current policy is out of the way we can not a couple
of things.
1. Current policy doesn't allow specifying a local and a remote, and I am not
sure I can see a case where it makes sense to do so. The address is mapped
to both local and remote.
ie.
dbus com.foo rw,
means allow receiving messages from com.foo, and sending messages to com.foo
Generally it would be good practice to split up dbus rules when addresses
and or permissions are specified. And we may consider enforcing such a
rule.
dbus,
mapping to everything makes sense. But the dbus com.foo case is a little
confusing if you think about it and throwing bind/acquire into the mix
just makes it worse.
2. acquire/bind only apply the the address= expression, which makes including
it in rules with path and method information a little weird. Again
acquire should be used in a separate rule.
3. addresses, paths, interfaces and methods support apparmor regexs, so don't
forget to escape special characters.
4. addresses, paths, interfaces, and methods can all be quoted.
5. The profile= flag is not currently supported, which means you can't specify
the target profile of the communication.
dbus profile=/foo/bar, #only allow communicating of task confined by /foo/bar
This is planned for just not implemented yet.
More information about the AppArmor
mailing list