Running OpenBMC Daemons/Applications as non root using D-Bus Session/User Bus.
Nirav Shah
nirav.j2.shah at linux.intel.com
Tue May 3 07:55:39 AEST 2022
Hello,
I am new to OpenBMC (and BMC ), so apologies if I am posting this in the
wrong place. I have been looking at this
<https://github.com/openbmc/openbmc/issues/3383> issue.Here is my
summary of the problem statement, please do comment and let me know if I
got this right.
1. The biggest challenge is the use of system bus and non root access
to the system bus.
2. As previously suggested an ACL based approach can work. (whether it
is using a D-Bus ACL configuration file or SELinux)
3. However, it does require an exact configuration to cover all
security scenarios (for MAC) and IMO “may” make debugging efforts
harder.
Coming from a desktop background (which additionally uses D-BUS
session/user bus for user isolation), I was investigating if having a
session bus would help. For OpenBMC, the idea would be to allow non root
application to communicate with each other and with root** applications
on a single session bus to begin with. This can be further augmented
using ACL based approaches if needed. I have a small POC, which tests
this on OpenBMC with D-Bus broker
To run the demo
* As root, copy files dbus_session.service and dbus_session.socket to
/etc/systemd/system/
* useradd nirav //or change the .service and .socket file to your user
* systemctl daemon-reload
* systemctl start dbus_session
* ps | grep dbus //will show an additional dbus-broker running
* compile dbus_server.c and dbus_client.c using yocto sdk or write a
receipe
* ssh as root, export
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/bus1 and ./dbus_server
* ssh as nirav, export
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/bus1 and ./dbus_client
With the POC I was able to …..
1. Show dbus_broker_launch “–scope user” works on OpenBMC (A session
busses can be created using the right system unit files and launcher
provided by D-Bus broker)
1. Since I am new to D-Bus broker and systemd I had to confirm this.
2. Show DBUS_SESSION_BUS_ADDRESS is the only env variable required by
root to access the session bus of another user. There is a
limitation here, discussed below.
As far as the actual solution, idea would be to have a configuration
file to specify which D-Bus interfaces can be on the session bus. An opt
in model which does not need any modification to existing and future
OpenBMC daemons/applications would be the goal but there are limitations …..
* For root**, to access another user’s session bus, its needs to
setuid/setgid to the corresponding user.
o This is because D-Bus actively blocks any user even uid 0 from
accessing another’s session bus. It would be a simple patch to
make an exception for root. But still something that needs to be
maintained.
* My POC was not using sdbus/plus. At the very least, modification
will be needed to sdbusplus, sdbus, phosphor-dbus and possibly to
object mapper. Not sure if more applications need to change.
* Supporting multiple session D-Buses will be really complicated for a
lot of reasons. So even if session bus is a reasonable idea (which I
need feedback on), I would not jump into having a session bus per
usecase from the get-go.
I am happy to start with a design document on git hub and also make some
code changes, but I had a few questions.
1. Your views on, if this a workable idea?
2. I am hoping I can isolate all the changes to sdbusplus, sdbus,
phosphor-dbus and object mapper. What else might need to change?
3. If I can make all these changes, I was thinking of starting with
BMCWeb as non root but since BMCWeb interfaces with a lot of daemons
that would be a big step. Any better ideas?
Thanks,
Nirav.
--
Nirav Shah
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20220502/e97ff5d0/attachment-0001.htm>
-------------- next part --------------
/*
*
* add-client.c: client program, takes two numbers as input,
* sends to server for addition,
* gets result from server,
* prints the result on the screen
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include <dbus/dbus.h>
const char *const INTERFACE_NAME = "org.nirav.dbus_example";
const char *const SERVER_BUS_NAME = "org.nirav.add_server";
const char *const CLIENT_BUS_NAME = "org.nirav.add_client";
const char *const SERVER_OBJECT_PATH_NAME = "/org/nirav/adder";
const char *const CLIENT_OBJECT_PATH_NAME = "/org/nirav/add_client";
const char *const METHOD_NAME = "add_numbers";
DBusError dbus_error;
void print_dbus_error (char *str);
int main (int argc, char **argv)
{
DBusConnection *conn;
int ret;
char input [80];
dbus_error_init (&dbus_error);
conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
if (dbus_error_is_set (&dbus_error))
print_dbus_error ("dbus_bus_get");
if (!conn)
exit (1);
printf ("Please type two numbers: ");
while (fgets (input, 78, stdin) != NULL) {
// Get a well known name
while (1) {
ret = dbus_bus_request_name (conn, CLIENT_BUS_NAME, 0, &dbus_error);
if (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
break;
if (ret == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
fprintf (stderr, "Waiting for the bus ... \n");
sleep (1);
continue;
}
if (dbus_error_is_set (&dbus_error))
print_dbus_error ("dbus_bus_get");
}
DBusMessage *request;
if ((request = dbus_message_new_method_call (SERVER_BUS_NAME, SERVER_OBJECT_PATH_NAME,
INTERFACE_NAME, METHOD_NAME)) == NULL) {
fprintf (stderr, "Error in dbus_message_new_method_call\n");
exit (1);
}
DBusMessageIter iter;
dbus_message_iter_init_append (request, &iter);
char *ptr = input;
if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) {
fprintf (stderr, "Error in dbus_message_iter_append_basic\n");
exit (1);
}
DBusPendingCall *pending_return;
if (!dbus_connection_send_with_reply (conn, request, &pending_return, -1)) {
fprintf (stderr, "Error in dbus_connection_send_with_reply\n");
exit (1);
}
if (pending_return == NULL) {
fprintf (stderr, "pending return is NULL");
exit (1);
}
dbus_connection_flush (conn);
dbus_message_unref (request);
dbus_pending_call_block (pending_return);
DBusMessage *reply;
if ((reply = dbus_pending_call_steal_reply (pending_return)) == NULL) {
fprintf (stderr, "Error in dbus_pending_call_steal_reply");
exit (1);
}
dbus_pending_call_unref (pending_return);
char *s;
if (dbus_message_get_args (reply, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) {
printf ("%s\n", s);
}
else
{
fprintf (stderr, "Did not get arguments in reply\n");
exit (1);
}
dbus_message_unref (reply);
if (dbus_bus_release_name (conn, CLIENT_BUS_NAME, &dbus_error) == -1) {
fprintf (stderr, "Error in dbus_bus_release_name\n");
exit (1);
}
printf ("Please type two numbers: ");
}
return 0;
}
void print_dbus_error (char *str)
{
fprintf (stderr, "%s: %s\n", str, dbus_error.message);
dbus_error_free (&dbus_error);
}
-------------- next part --------------
[Unit]
Description=D-Bus User Message Bus
Documentation=man:dbus-daemon(1)
Requires=dbus_session.socket
[Service]
Type=notify
User=nirav
Group=nirav
NotifyAccess=main
ExecStart=/usr/bin/dbus-broker-launch --scope user --audit
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
Slice=session.slice
-------------- next part --------------
[Unit]
Description=D-Bus User Message Bus Socket
[Socket]
ListenStream=%t/user/bus1
User=nirav
Group=nirav
ExecStartPost=-/bin/systemctl set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/user/bus1
-------------- next part --------------
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <ctype.h>
#include <dbus/dbus.h>
#include <errno.h>
const char *const INTERFACE_NAME = "org.nirav.dbus_example";
const char *const SERVER_BUS_NAME = "org.nirav.add_server";
const char *const OBJECT_PATH_NAME = "/org/nirav/adder";
const char *const METHOD_NAME = "add_numbers";
/* Remember the effective and real UIDs. */
static uid_t ruid = 1007;
const char *version = "0.1";
/* Restore the effective UID to its original value. */
void
do_setuid (void)
{
int status;
#ifdef _POSIX_SAVED_IDS
#if 0
status = setresuid (ruid, euid, 1062);
if (status < 0) {
if (errno == EPERM)
printf("Permission denied to sudo\n");
else
printf ("Couldn't set uid.i%d\n", errno);
}
status = setresgid (1062, 1062, 1062);
#else
status = setuid(ruid);
// status = setresgid(1007, 1007, 1007);
#endif
#else
status = setreuid (euid, ruid);
#endif
if (status < 0) {
if (errno == EPERM)
printf("Permission denied to sudo\n");
else
printf ("Couldn't set uid.i%d\n", errno);
exit (status);
}
}
DBusError dbus_error;
void print_dbus_error (char *str);
bool isinteger (char *ptr);
int main (int argc, char **argv)
{
DBusConnection *conn;
int ret;
uid_t lruid = 0, leuid=0, lsuid=0;
uid_t lrgid = 0, legid=0, lsgid=0;
if (0 == getresuid(&lruid, &leuid, &lsuid))
printf("ruid=%d, euid=%d, suid=%d\n", lruid, leuid, lsuid);
if (0 == getresgid(&lrgid, &legid, &lsgid))
printf("rgid=%d, egid=%d, sgid=%d\n", lrgid, legid, lsgid);
#if 0
printf("try to setuid\n");
/* Remember the real and effective user IDs. */
do_setuid();
if (0 == getresuid(&lruid, &leuid, &lsuid))
printf("New ruid=%d, euid=%d, suid=%d\n", lruid, leuid, lsuid);
if (0 == getresgid(&lrgid, &legid, &lsgid))
printf("New rgid=%d, egid=%d, sgid=%d\n", lrgid, legid, lsgid);
lruid = getuid ();
leuid = geteuid ();
fprintf(stderr, "new ruid=%d, new_euid=%d\n", lruid, leuid);
#endif
printf("starting1\n");
dbus_error_init (&dbus_error);
conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
if (dbus_error_is_set (&dbus_error))
print_dbus_error ("dbus_bus_get");
if (!conn)
exit (1);
// Get a well known name
ret = dbus_bus_request_name (conn, SERVER_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
if (dbus_error_is_set (&dbus_error))
print_dbus_error ("dbus_bus_get");
if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
fprintf (stderr, "Dbus: not primary owner, ret = %d\n", ret);
exit (1);
}
// Handle request from clients
while (1) {
// Block for msg from client
if (!dbus_connection_read_write_dispatch (conn, -1)) {
fprintf (stderr, "Not connected now.\n");
exit (1);
}
DBusMessage *message;
if ((message = dbus_connection_pop_message (conn)) == NULL) {
fprintf (stderr, "Did not get message\n");
continue;
}
if (dbus_message_is_method_call (message, INTERFACE_NAME, METHOD_NAME)) {
char *s;
char *str1 = NULL, *str2 = NULL;
const char space [4] = " \n\t";
long i, j;
bool error = false;
if (dbus_message_get_args (message, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) {
printf ("%s", s);
// Validate received message
str1 = strtok (s, space);
if (str1)
str2 = strtok (NULL, space);
if (!str1 || !str2)
error = true;
if (!error) {
if (isinteger (str1))
i = atol (str1);
else
error = true;
}
if (!error) {
if (isinteger (str2))
j = atol (str2);
else
error = true;
}
if (!error) {
// send reply
DBusMessage *reply;
char answer [40];
sprintf (answer, "Sum is %ld", i + j);
if ((reply = dbus_message_new_method_return (message)) == NULL) {
fprintf (stderr, "Error in dbus_message_new_method_return\n");
exit (1);
}
DBusMessageIter iter;
dbus_message_iter_init_append (reply, &iter);
char *ptr = answer;
if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) {
fprintf (stderr, "Error in dbus_message_iter_append_basic\n");
exit (1);
}
if (!dbus_connection_send (conn, reply, NULL)) {
fprintf (stderr, "Error in dbus_connection_send\n");
exit (1);
}
dbus_connection_flush (conn);
dbus_message_unref (reply);
}
else // There was an error
{
DBusMessage *dbus_error_msg;
char error_msg [] = "Error in input";
if ((dbus_error_msg = dbus_message_new_error (message, DBUS_ERROR_FAILED, error_msg)) == NULL) {
fprintf (stderr, "Error in dbus_message_new_error\n");
exit (1);
}
if (!dbus_connection_send (conn, dbus_error_msg, NULL)) {
fprintf (stderr, "Error in dbus_connection_send\n");
exit (1);
}
dbus_connection_flush (conn);
dbus_message_unref (dbus_error_msg);
}
}
else
{
print_dbus_error ("Error getting message");
}
}
}
return 0;
}
bool isinteger (char *ptr)
{
if (*ptr == '+' || *ptr == '-')
ptr++;
while (*ptr) {
if (!isdigit ((int) *ptr++))
return false;
}
return true;
}
void print_dbus_error (char *str)
{
fprintf (stderr, "%s: %s\n", str, dbus_error.message);
dbus_error_free (&dbus_error);
}
More information about the openbmc
mailing list