|
|
Subscribe / Log in / New account

The kdbuswreck

The kdbuswreck

Posted Apr 25, 2015 1:09 UTC (Sat) by fandingo (guest, #67019)
In reply to: The kdbuswreck by kentonv
Parent article: The kdbuswreck

> As I understand it, passing FDs over dbus is a common thing to do

While it will continue to be possible with kdbus, it will probably become less common. The main reason to pass a traditional FD is performance, which was a major problem with userspace DBus due to memory copying routinely up to 11 times between calls. In the future, I expect services to prefer passing data using memfd* rather than handing over a FD (>512KiB was experimentally determined to be a good default threshold). That way the service can not only control the syscall operations but also validate individual operations and the data therein.

* Of course memfd behave as FDs.

> What I'm arguing against is expanding the use of crapabilities, as kdbus does.

I disagree that it's a material expansion. It just allows those same capabilities to use systemd tools rather than going directly to the kernel with syscalls. For example, a user with CAP_SYS_BOOT (i.e. with access to run a program with that cap) can essentially panic the kernel, triggering a reboot. Systemd allows that same capability to be utilized for an orderly shutdown via systemd. The orderly shutdown case -- at least in my opinion and the kdbus/systemd developers' -- is probably what most people consider natural. Same thing with CAP_SYS_KILL. A user automatically can kill things in their session from systemd policy, but this allows a user to kill units through systemd (i.e. systemctl kill FOO). Again, that's totally natural for a system using systemd; otherwise, users would have to manually kill each process in a service and be unable to deal with systemd unit automatic restart, unless they were uid == 0, which would allow her to run `systemctl stop/kill FOO`.

The way that systemd will use kdbus' capability metadata allows for essentially the same control that the syscalls allowed to go through systemd's more featureful equivalents.

> (I also object to dbus being awfully singleton-y with global namespaces and such, but that ship obviously sailed long ago, so maybe it's not useful to argue now. But: http://www.object-oriented-security.org/lets-argue/single...)

I don't really see how DBus qualifies. I guess you can only have one resource occupy a distinguished name (i.e. org.foo.bar), but it's difficult to envision how that could possibly work differently. Furthermore, your idea of passing a FD to the logon process seems like it introduces a much "worse" singleton. Perhaps I just don't understand the argument in that link.

Fundamentally, I don't see how the combination of privileged executors authorized via polkit, memfd, and a LSM don't offer the same functionality of that all 8 components of capsicum. Perhaps you could explain something that could be done with capsicum that cannot be done with a combination of what I mentioned.


to post comments

The kdbuswreck

Posted Apr 25, 2015 6:28 UTC (Sat) by kentonv (subscriber, #92073) [Link] (22 responses)

> I disagree that it's a material expansion.

I defer to Andy Lutomirski's arguments on LKML, since he's already said many of the same things I would say.

> I guess you can only have one resource occupy a distinguished name (i.e. org.foo.bar)

Yes, that's essentially the problem.

> it's difficult to envision how that could possibly work differently.

You don't really want "the" org.foo.bar, you want "an" org.foo.bar. Multiple applications should be able to export objects implementing the org.foo.bar interface and the user should be able to choose which one to use for each app (or choose none, i.e. disallow access).

> Furthermore, your idea of passing a FD to the logon process seems like it introduces a much "worse" singleton.

Not at all. The whole point is, any process can implement the "account management" interface for itself, and pass its own implementation down to children. This lets you do all kinds of magical things that are hard or infeasible today, like:
- Sandboxing: Just wrap the capability with an implementation that blocks or mocks out requests that you want to disallow.
- Testability: You can run an app against a mock capability instead of the real one for testing purposes.
- Monitoring/auditing: See what apps are doing by injecting an interceptor that logs requests.
- Composability: Apps can be composed on top of different back-ends to produce novel functionality. Like maybe instead of managing local users, you want to manage users on your remote server, but you want to use a GUI app that was written only with local users in mind. No problem, just swap the local cap for the remote one and it works. No need to go edit the GUI app to support a different kind of back-end.

> Perhaps you could explain something that could be done with capsicum that cannot be done with a combination of what I mentioned.

In addition to the above, expressing security policies in terms of capabilities is generally easier and less error-prone that expressing them in terms of ACLs or policy files. This is hard to prove in the space of an LWN post, but after working with them for a while you won't want to go back.

The kdbuswreck

Posted Apr 25, 2015 8:08 UTC (Sat) by cortana (subscriber, #24596) [Link] (21 responses)

I believe you've misunderstood the D-Bus model. You are confusing addresses with interfaces. This is understandable since in simple interfaces the same string is used to identify both!

In order to communicate with a peer, you need to talk to an address. For instance, "org.freedesktop.ColorManager". This is a well-known name, that the client would use to identify *who* it wants to talk to. The dbus-daemon has a set of rules that determine which processes are allowed to take ownership of a well-known name. This ensures that you are really talking to colord, and not an imposter. These are currently defined by files in /etc/dbus-1/systemd.d; I don't know what happens to them in the brave new kdbus world.

Once you've connected to an address, you can obtain a list of objects exported by the peer. Simple services will just export one object, such as "/org/freedesktop/ColorManager"; by convention these are similar to the address above, but with a slash instead of a period used to separate components.

More complex interfaces will export additional objects, for instance /org/freedesktop/ColorManager/devices/printer1, /org/freedesktop/ColorManager/devices/profiles/icc_{hexstring} and so on. In colord's case these are used to represent different devices and colour profiles on the system.

Having selected an object to talk to, for instance, /org/freedesktop/ColorManager, you now choose an *interface*. Even in the case of the simplest service, that just exports a single object, that object will export multiple interfaces. In our example we see 'org.freedesktop.DBus.Introspectable', 'org.freedesktop.DBus.Peer' and 'org.freedesktop.DBus.Properties' and finally 'org.freedesktop.ColorManager'.

Now, *these* are the interfaces that you mistook for addresses earlier. All the methods & properties supported by an object are accessed via one of these interfaces. So in order to find a list of the colour sensing devices attached to my computer, I would call the GetSensors() method of the 'org.freedesktop.ColorManager' interface of the /org/freedesktop/ColorManager object exported by the 'org.freedesktop.ColorManager' peer. Whereas if I wanted to see which interfaces an object implements, along with their associated methods and properties, I would call the Introspect() method of the 'org.freedesktop.DBus.Introspectable' interface of the same object. Those three 'org.freedesktop.DBus.*' interfaces, by the way, are supported by every object on the bus.

So, if you wanted to communicate with a special-purpose peer for testing, you would tell your code to connect to a different *address*, e.g. ":1.340" (these are non-well-known addresses that are assigned when a client connects to the dbus-daemon), but the same object and interface.

Hope that makes sense; if not then please install the dbus introspection tool "d-feet" and click around for a couple of minutes, things should become obvious then. For command-line introspection I usually use the 'qdbus' program, but it's much quicker to navigate the peers on a bus with d-feet, so start there.

Personally I think the D-Bus model is a little over-complex, and the fact that simple services use the same name for each of their address, object path _and_ interface makes things appear a more obscure than they really are, but we are where we are and D-Bus has gained a lot of adoption over the last 10 years, having replaced the IPC mechanisms previously used by both GNOME and KDE.

That said, I really do wish there was a dbus(7) man page that explained the above in simple terms that would be useful for busy sysadmins and users curious about the internals of their system. :)

The kdbuswreck

Posted Apr 25, 2015 12:27 UTC (Sat) by lsl (subscriber, #86508) [Link] (12 responses)

> Having selected an object to talk to, for instance, /org/freedesktop/ColorManager, you now choose an *interface*

That seems backwards to me. Why would I even care what object I talk to? I just want *some* object that implements the interface I need.

So when calling methods on the 'org.freedesktop.ColorManager' interface those get dispatched to an implementation that makes sense accorrding to local system configuration, say colord, KolorManager or whatever the user set up for this.

Is it possible to sanely use dbus this way? I mean, I can certainly enumerate the bus und search for something that implements the wanted interface but that doesn't seem reasonable.

So let's take a step back here. How would one implement the concept "I want this functionality but I don't care who provides it" in dbus? Are interfaces a red herring here and I better look at well-known names? What those resolve to is up to system configuration, right? So is this the point where it is commonly decided what program will handle my requests regarding color management? Whatever owns the name?

The kdbuswreck

Posted Apr 25, 2015 13:34 UTC (Sat) by mchapman (subscriber, #66589) [Link] (5 responses)

> How would one implement the concept "I want this functionality but I don't care who provides it" in dbus?

I would say that is *exactly* what D-Bus provides now. There is nothing in D-Bus's policy configuration that locks a service to a particular binary. A service name can be claimed by any process that matches that service's policy (for system services this is typically just a check that the connection was authenticated as root). Of course, only one D-Bus connection can own a service name at any particular time.

Service activation is a bit different, as D-Bus (or systemd, if it's doing the activation) needs to know which binary to launch when the service is requested. I suppose you could use something like alternatives to cater for multiple implementations of that service.

The kdbuswreck

Posted Apr 25, 2015 15:17 UTC (Sat) by mathstuf (subscriber, #69389) [Link]

> Service activation is a bit different, as D-Bus (or systemd, if it's doing the activation) needs to know which binary to launch when the service is requested. I suppose you could use something like alternatives to cater for multiple implementations of that service.

There should already be examples for this at the session level with the different NetworkManager UIs, kwallet vs. gnome-keyring, etc.

The kdbuswreck

Posted Apr 27, 2015 12:16 UTC (Mon) by javispedro (guest, #83660) [Link] (3 responses)

> I would say that is *exactly* what D-Bus provides now. There is nothing in D-Bus's policy configuration that locks a service to a particular binary. A service name can be claimed by any process that matches that service's policy (for system services this is typically just a check that the connection was authenticated as root). Of course, only one D-Bus connection can own a service name at any particular time.

You actually _cannot_ do what is being asked with current D-Bus, and it is one of my major gripes with it (which is why I prefer anything else over it). The "using the service name as what every other IPC would call interface name" idea works well until you realize you cannot register more than one service with the same name. Thus, you start seeing ugly tricks such as what MPRIS does, which require greping over the service names, etc.

The kdbuswreck

Posted Apr 27, 2015 12:54 UTC (Mon) by mchapman (subscriber, #66589) [Link] (2 responses)

> The "using the service name as what every other IPC would call interface name" idea works well until you realize you cannot register more than one service with the same name. Thus, you start seeing ugly tricks such as what MPRIS does, which require greping over the service names, etc.

I don't think it's necessarily an ugly trick. The difference between getting a list of service names with some prefix and in getting a list of services providing some object (assuming this were even possible with D-Bus) is mostly superficial.

If efficiency is the problem, getting a list of service names for things like MPRIS could be optimized by extending the protocol slightly, e.g. by having org.freedesktop.DBus.ListNames take a prefix as an argument.

But allowing a service name to be owned by at most one connection is essential for lsl's use case. You can't sanely dispatch "to an implementation that makes sense accorrding to local system configuration" if more than one such implementation is on the bus at the same time.

The kdbuswreck

Posted Apr 27, 2015 13:28 UTC (Mon) by MrWim (subscriber, #47432) [Link]

I don't think it's necessarily an ugly trick. The difference between getting a list of service names with some prefix and in getting a list of services providing some object (assuming this were even possible with D-Bus) is mostly superficial.

If efficiency is the problem, getting a list of service names for things like MPRIS could be optimized by extending the protocol slightly, e.g. by having org.freedesktop.DBus.ListNames take a prefix as an argument.

Indeed, this is the purpose of the arg0namespace match rule. You register for NameOwnerChanged events with some prefix, call ListNames (or ListActivatableNames) filtering on the prefix and then you can efficiently and asynchronously keep your local list of remote names up-to-date.

The kdbuswreck

Posted Apr 28, 2015 8:10 UTC (Tue) by javispedro (guest, #83660) [Link]

It introduces a bunch of additional problems. For example, what if you want the same process to expose more than one instance of the service? You will be hit by the fact that despite having multiple service names you still have one object namespace only... and no way to setup different policies, etc.

The MPRIS trick obviously works, but it puts the design of DBus upside down.

The kdbuswreck

Posted Apr 27, 2015 6:45 UTC (Mon) by krake (guest, #55996) [Link]

> That seems backwards to me. Why would I even care what object I talk to? I just want *some* object that implements the interface I need.

It will depend on the type of service, i.e. if there is some object related context.

For example, a service which provides functionality on a set of real world objects will expose these objects again as a set of D-Bus objects.
It makes it easier for programmers on both sides (service and clients) if there is a one-to-one mapping, e.g. NetworkManager exposing each network device as a separate object.

For a service that provides only one interface on one object, the convention seems to be to use the same name parts for the well-known connection name, the object path and the interface name (with respective separator characters).

The kdbuswreck

Posted Apr 27, 2015 7:30 UTC (Mon) by cortana (subscriber, #24596) [Link] (2 responses)

> That seems backwards to me. Why would I even care what object I talk to? I just want *some* object that implements the interface I need.

If you're saving passwords then you want to be sure that the 'org.freedesktop.secrets' address has not been taken by a password-stealing program.

The kdbuswreck

Posted Apr 27, 2015 9:06 UTC (Mon) by mchapman (subscriber, #66589) [Link] (1 responses)

> If you're saving passwords then you want to be sure that the 'org.freedesktop.secrets' address has not been taken by a password-stealing program.

That seems like a completely orthogonal problem to me.

I'm going to reiterate what I said in my other post: D-Bus *already provides* the ability for a client to talk to "any object that implements a particular interface": simply replace the word "object" with "service" and "interface" with "object".

The kdbuswreck

Posted Apr 27, 2015 9:16 UTC (Mon) by mchapman (subscriber, #66589) [Link]

> I'm going to reiterate what I said in my other post: D-Bus *already provides* the ability for a client to talk to "any object that implements a particular interface": simply replace the word "object" with "service" and "interface" with "object".

Meh, I screwed that comment up. I should have said: simply replace the word "object" with "connection" and "interface" with "service".

That is, a D-Bus client does not care what connection provides a particular service; it relies on bus policy for that to be authorized appropriately.

That being said, I have the feeling there is very little stopping some malicious piece of software from killing off gnome-keyring-daemon, say, and grabbing the org.freedesktop.secrets bus name before GNOME has a chance to restart the daemon.

The kdbuswreck

Posted Apr 27, 2015 15:17 UTC (Mon) by hp (guest, #5220) [Link] (1 responses)

> That seems backwards to me. Why would I even care what object I talk to? > I just want *some* object that implements the interface I need.

An interface is implemented by N objects, so for example an interface might be implemented by each open document in a word processor. I would say you do not want "some document that implements the Document interface" when you call `org.whatever.Document.Delete()`, you want the specific document you plan to delete :-)

*Services* are generally pluggable - i.e. the entire word processor application, could implement a set of objects (each with a set of interfaces) conforming to some sort of standard, potentially, and then you could interop with whichever word processor owns a certain `org.whatever.WordProcessor` service, or something.

Well-known name: like a DNS entry, a way to find an entire *program* to talk to (service locator)

Object path: equivalent to a pointer ... a specific instance of an object in the "object-oriented programming" sense of object

Interface: means same thing as in Java (set of methods on an object instance)

The fact that some programs have only one object instance with only one interface, in no way means that these are redundant.

Yes you can write a program in Java that only contains `class MyProgram` and `static MyProgram theInstanceOfMyProgram = new MyProgram()`.

This does not mean that Java should _only_ provide support for singleton objects!

The kdbuswreck

Posted Apr 27, 2015 18:59 UTC (Mon) by lsl (subscriber, #86508) [Link]

> An interface is implemented by N objects, so for example an interface might be implemented by each open document in a word processor. I would say you do not want "some document that implements the Document interface" when you call `org.whatever.Document.Delete()`, you want the specific document you plan to delete :-)

Ah ok, thanks. Didn't thought about it that way. For most of the stuff on my local system bus it wouldn't make a difference: it doesn't matter who tells me the hostname or who is going to set the timezone. But then there's logind (which I missed the last time), where it in fact matters whose session is going to be terminated.

The kdbuswreck

Posted Apr 25, 2015 19:55 UTC (Sat) by kentonv (subscriber, #92073) [Link] (7 responses)

Sorry, I don't think I've expressed my point very clearly.

When I say "singleton" what I essentially mean is "an object addressed by a global well-known name or path". The problems that I have with singletons are not fixed by saying "ok, you can have a list of objects with different names" -- all those objects are still singletons.

For example, the path "/org/freedesktop/ColorManager/devices/printer1" refers to the *same* device regardless of who is calling. The problem with this is that it means the calling code decides which printer to connect to. That's bad because:

1. It's probably the user, not the app, that knows best which printer to connect to. So now the app needs to implement a picker dialog. Many apps will skip this and just hard-code the first object. (In practice you don't usually see this problem for printers, but you *do* see it for, say, audio output devices. Every app that plays audio should be asking me which device to use, but, sadly, they do not. I must choose a system-wide default device, and I cannot easily have different apps playing to different speakers. Yes, some systems support advanced configuration of audio sources and destinations within the audio control panel, but my point is that we should have this kind of configurability for all resources.)

2. The app necessarily has the ability to enumerate the devices and connect to all of them. For security reasons, it would be better if the app *only* had the ability to connect to the device that I, as the user, chose for it to access. Traditionally desktop systems have made the unfortunate assumption that I trust all my apps to wield all the power of my user account, but I'd really prefer that each of my apps runs in a sandbox with only the power it needs to do its job.

What I want is for apps to make requests like "I need something that implements org.freedesktop.AudioOutput" (or whatever interface), and then the system displays a dialog to the *user* asking which device or service to use. The app only ever receives access to the device the user chooses, and the app can be used in a broader range of use cases without burdening the app developer with implementing the requisite configurability.

The kdbuswreck

Posted Apr 25, 2015 20:38 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link] (6 responses)

For example, the path "/org/freedesktop/ColorManager/devices/printer1" refers to the *same* file regardless of who is opening it. The problem with this is that it means the calling code decides which file to open. That's bad because:

1. It's probably the user, not the app, that knows best which file to open. So now the app needs to implement a picker dialog. Many apps will skip this and just hard-code the first file. (In practice you don't usually see this problem for files, but you *do* see it for, say, audio output devices. Every app that plays audio should be asking me which device to use, but, sadly, they do not. I must choose a system-wide default device, and I cannot easily have different apps playing to different speakers. Yes, some systems support advanced configuration of audio sources and destinations within the audio control panel, but my point is that we should have this kind of configurability for all resources.)

2. The app necessarily has the ability to enumerate the files and open all of them. For security reasons, it would be better if the app *only* had the ability to open the file that I, as the user, chose for it to access. Traditionally desktop systems have made the unfortunate assumption that I trust all my apps to wield all the power of my user account, but I'd really prefer that each of my apps runs in a sandbox with only the power it needs to do its job.
...

> What I want is for apps to make requests like "I need something that implements org.freedesktop.AudioOutput" (or whatever interface), and then the system displays a dialog to the *user* asking which device or service to use.
Try to watch DBUS with a sniffer. Now imagine that you have to MANUALLY select each and every endpoint.

The kdbuswreck

Posted Apr 25, 2015 21:03 UTC (Sat) by kentonv (subscriber, #92073) [Link] (5 responses)

> Try to watch DBUS with a sniffer. Now imagine that you have to MANUALLY select each and every endpoint.

Yes obviously what I'm describing can't be dropped on top of the existing set of dbus endpoints and just work. Lots of stuff would need to be redesigned and organized differently. It is possible to design such an environment and have it work well (CapDesk did it, and Sandstorm.io is doing it), but I don't honestly expect today's dbus-using desktop environments to entirely switch anytime soon. Still, it's useful for people to understand the ideal in order to guide improvements to what we have today.

The kdbuswreck

Posted Apr 25, 2015 22:42 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link] (4 responses)

I still have no idea how your design will be any different from the current one. You still need to have well-known starting points for lookups.

The kdbuswreck

Posted Apr 26, 2015 20:17 UTC (Sun) by luto (guest, #39314) [Link] (1 responses)

Not really. Yes, something needs to create a starting point, but that something could just be whatever creates the resource in the first place.

For example, gdm or logind could start my shell with access to an object implementing the "find a printer" interface. Programs that inherit access to that object would use it.

Sandboxed programs, on the other hand, might get access to a different "find a printer" interface that behaves differently.

dbus can do this right now. On my Fedora 21 system, my shell and everything it starts has access to a standard implementation of a lot of these things. It looks like:

DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-qB3T8DFwej,guid=1453a3565ca58487e6a024fe5538ad89

Too bad that doesn't seem to apply to the system bus.

The kdbuswreck

Posted Apr 27, 2015 4:12 UTC (Mon) by Cyberax (✭ supporter ✭, #52523) [Link]

There's nothing really special about the system bus. It's possible to override it for each application.

But as I understand, it was designed to be a globally visible namespace with access being controlled by PolKit.

The kdbuswreck

Posted Apr 26, 2015 20:19 UTC (Sun) by kentonv (subscriber, #92073) [Link]

The "starting point" would be a file descriptor inherited from the parent process. Today we have "standard input", "standard output", and "standard error"; now imagine adding "standard desktop" which is a socket that implements some protocol to talk to the desktop session.

This file descriptor would support a bunch of standard functionality that all apps need (like, opening windows, or raising notifications, etc., but NOT things where the user might want to choose the resource used or deny it for security reasons, like printers or audio devices or connected OAuth accounts). It would also have a way to say "I need an object (file descriptor) implementing protocol X", and that's when the user is prompted to choose which object to use. Once the user chooses something, the app can save a long-term token representing that choice and re-request the same object later using that token.

Yes, this is "standard desktop" FD is similar to the dbus session bus, except that I can decide which programs that I run are allowed to access it, and I can mock it out, audit usage, sandbox, etc., as described previously, and I can make choices about individual resources accessed by any app.

The kdbuswreck

Posted Apr 27, 2015 12:13 UTC (Mon) by javispedro (guest, #83660) [Link]

Yes, but you can make symlinks ;)


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