diwic / dbus-rs

D-Bus binding for the Rust language
Other
591 stars 133 forks source link

Any way to set uid in dbus authentication? #443

Closed YJDoc2 closed 1 year ago

YJDoc2 commented 1 year ago

Hello, thanks for your efforts on this library!

I was wondering if there is any way we can set the uid when authenticating with dbus using this library? Specifically, we are using this to communicate with dbus in youki , and we are currently trying to work on running rootless containers with podman. One of the issues we are facing when connecting to dbus, is that when creating a session dbus client, we use

Client::new_session().context("failed to create session dbus client")?

This call fails and thus the whole process fails. We debugged this, and reached to this analysis, which in short is this :

Is there any way either currently or with changes that we can specify uid for this? I tried to look into dbus-rs code and code of runc, and this is what I have figured out so far.

Thanks for help :)

diwic commented 1 year ago

Can you use the seteuid function to switch user temporarily?

YJDoc2 commented 1 year ago

Hey @diwic Thanks a lot for the suggestion! I tried it out and it partially worked, but I'm still running into some issues.

First of all, I found out that we don't need just a new private connection, but a private connection to a specific bus address, so I changed the code for that. After that I tried using setuid, but I'm running into some problems .

The code I'm using is :

let current_uid = nix::unistd::getuid();

let mut channel = Channel::open_private("unix:path=/run/user/1000/bus")?;

nix::unistd::setuid(new_uid.into()).unwrap();
channel.register()?;
nix::unistd::setuid(current_uid).unwrap();

let conn: dbus::blocking::Connection = channel.into();

here the new_uid is what the authentication uid should be, i.e the uid of non-root user who is launching the container process. The bus address is currently hardcoded, and will be passed in as param later, but I have verified that runc also uses the same one when running rootless.

The issues I'm running into are :

I checked the permission to the socket path, and it is srw-rw-rw , so even user id 1000 should be able to connect to it.

diwic commented 1 year ago

Secondly, I'm not able to reset the uid after the register call (I tested this by commenting it out). As I have changed uid to non-root user, I think it prevents me from changing back to root.

Try using seteuid, not setuid. Does this make a difference? If not I guess we're out of luck here.

YJDoc2 commented 1 year ago

Hey @diwic Thanks for help. I tried seteuid, and that doesn't work as well. I did a little digging , and the core issue seems to be with libdubs / dbus server itself. I tried stracing with stack trace, and even with using Channel::new_session or Channel::use_private , the actual authentication call originates from dbus (which, in hind-sight should have been obvious to me, as this crate uses ffi for that :sweat_smile: ).

The issue is : dbus as a spec, supports various auth methods, including external which we are using. As per spec, we can either provide a uid OR let the dbus server directly query it from kernel and assume that uid to be valid. However, in many implementations of dbus servers (especially older versions) the second option is not valid, i.e. caller must supply uid, otherwise the request is invalid. To comply with this many of the client libraries call getuid and pass that automatically as the uid for auth. I suspect that because it is a shared library and our call originates from a namespaced context (i.e. from inside a container) the getuid call (or geteuid call, whichever is used by the client library) gets it as 0, which is valid in the container context. At least this is my suspicion, otherwise with setuid or seteuid, this should have worked.

I also saw that this crate has code for native implementation of auth, but that feature is turned off as not yet ready. If I understand correctly, then external auth means that validate the connection itself at the time of connecting, and accept communication from that as authorized. If so, is there any way where we (youki) can own the initial authentication, and pass something (socket, some fd etc) to this crate, which will convert it to Channel, without running auth? Right now, I'm probably thinking of copying the code in this crate for native auth for that ; if that would be possible.

diwic commented 1 year ago

Since the libdbus code seems to use geteuid (see dbus-sysdeps-unix.c) I would have assumed seteuid would have helped it to get the id you want it to.

Anyhow, since this is out of my control, I think you might have better luck with zbus if the maintainer there is responsive. Or talk to libdbus upstream.

YJDoc2 commented 1 year ago

Hey sorry to keep this open. For anyone who comes looking later, the gist is : You can use seteuid call to temp change the uid, iff you want to connect to system/session bus. It didn't work in my case because I wanted to connect to a different bus, and uid I need for bus connection is different from uid I need for authentication.

See https://github.com/containers/youki/issues/2208 for a detailed discussion of this we did on youki.

Thanks diwic for quickly replying and helping out :pray: