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