GithubHelp home page GithubHelp logo

busd's People

Contributors

elmarco avatar renovate[bot] avatar whot avatar whynothugo avatar zeenix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

busd's Issues

API to acquire a connection fd from busd with pre-applied bus names & policies

This is a clone of an issue filed by @poettering on dbus-broker (bus1/dbus-broker#259) 3 years ago. Copy&pasting verbatim:

I'd like to add support for a ".busname" concept in systemd, where people can declare a dbus service name to acquire as unit file, and it would list the full service name plus the matching policy. PID 1 would then parse that, and pass this to dbus-broker via some driver API, and get an fd back that can be used via socket activation.

The would fix a bunch of issues for us:

  1. Solve the reordering mess when bus names are acquired that already have queued messages
  2. Fixes issues with exit-on-idle: messages would remain enqueued on the bus connection while the service goes down, and it may continue where it left of — except if half-read messages remain
  3. provide compatibility with the DynamicUser= concept of PID 1, as well as the "portable service" concept, as policy could be attached to the unit file itself

I'd suggest to drop the SASL and feature negotiation phase for sockets of this kind, i.e. sockets come readily set up for the main protocol of D-Bus. feature negotiation would have to take place in the unit files too, I guess.

Idea: dbus-broker's driver service would gain:

RegisterActivationService("sauaubas") → "has"

input:

 s → name to acquire
 au → list of uids that shall get access to this service
 au → list of gids that shall get access to this service
 b → boolean that decides if anyone else gets access
 as → feature flags requested ("unix-fd", …)

output:

  h → file handle of connection
  as → agreed on feature flags ("unix-fd", …), subset of same array of input

@poettering Can you please confirm this will be useful still, with a 👍 reaction?

Allow creating identifiable bus broker connections for containers

Following up on this long thread: https://fosstodon.org/@[email protected]/111002189225139704

The original proposal is here: https://bugs.freedesktop.org/show_bug.cgi?id=100344

And there's an ongoing discussion on the dbus issue tracker: https://gitlab.freedesktop.org/dbus/dbus/-/issues/171

This is required to eventually fix this issue: flatpak/xdg-desktop-portal#737

TLDR

Some dbus applications (e.g.: xdg-desktop-portal) receive requests from other peers and need to know whether that peers is in a sandbox. In case that they are in a sandbox, they need to know a bit more information about it (specifically: sandbox_engine, app_id and instance_id).

The following method would be used to pass a new fd to the broker. The broker would listen to new connections on this socket, and all client connections made through it would be tagged as belonging to the specified sandbox.

o.fd.DBus.Containers.AddContainerServer(
    s: container_type,           # e.g. "org.flatpak"
    s: app_identifier,           # e.g. "org.gnome.Recipes"
    a{sv}: metadata,             # defined by Flatpak in this case
    h: socket_to_accept,         # see link
    h: ready_notification,       # see link
    h: close_notification,       # see link
    a(usos): rules,              # see link, #Access control
) -> s: container_id

Some notes:

  • metadata should not be included here; any metadata that the sandbox engine needs it should store on its own memory.
  • I would add an instance_id field here (and below). This would also align with the Wayland protocol which has the exact same purpose.
  • rules is probably out of scope; see my notes on ACL below.

Aside from the above, other clients can use the following method to check whether they're receiving a message from a sandboxed peer:

o.fd.DBus.GetConnectionCredentials(s: bus_name) -> a{sv}
    {
        ... all the current fields ...,
        "ContainerID": <"47">,
        "ContainerType": <"org.flatpak">,
        "ContainerApp": <"org.gnome.Recipes">,
    }

The original link issue (from 2017) also proposes including ACL in this scope. I'm not sure if this is still desirable, since this can be handled out-of-process in an xdg-dbus-proxy instance.

performance benchmark issue

I found the performance is much worse than bus1 "dbus-broker" service on my OpenBMC platform "armv7-unknown-linux-gnueabihf", a 2-core SoC, the binary is built simply from below command:

~/workspace/zbus_broker/busd$ git log -1
commit ecfb268c3bb594f2a080df34d7214fd260f585fe (HEAD -> main, origin/main, origin/HEAD)
~/workspace/zbus_broker/busd$ cross build --release --target armv7-unknown-linux-gnueabihf

I modified the systemd/src/libsystemd/sd-bus/test-bus-benchmark.c test case, for it to run only 15000 round of IPCs.

For bus1 dbus-broker, I can get about 1050 round of IPC per second (but it will reach a system wide total bandwidth at 2100 round of IPC per second, if I run two test in parallel), but now I can get only 400 round of IPC per second on same platform (HW and SW):

root@xxxxxx:/tmp# time ./test-bus-benchmark
Total round: 15000
Copying/memfd are equally fast at 32768 bytes

real    0m38.844s
user    0m8.254s
sys     0m9.658s

Also, run two test in parallel, the performance also dropped like it's running on single thread(supposedly they shouldn't, as zbus support full parallelism, and I have a 2core SoC, and I can see the busd process is running in two threads from perf):

root@xxxx:/tmp# for i in {0..1}; do time /tmp/test-bus-benchmark & done
[2] 10105
[3] 10106
root@xxxx:/tmp# Total round: 15000
Copying/memfd are equally fast at 32768 bytes

real    1m15.207s
user    0m10.025s
sys     0m13.641s
Total round: 15000
Copying/memfd are equally fast at 32768 bytes

real    1m15.705s
user    0m10.562s
sys     0m13.344s

Please below source code for the benchmark test case.

/***
  This file is part of systemd.

  Copyright 2013 Lennart Poettering

  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

#include <assert.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/wait.h>
#include <sys/resource.h>
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdbool.h>

#include <sys/wait.h>
#include <unistd.h>

#include "sd-bus.h"

#include "alloc-util.h"
#include "bus-internal.h"
#include "constants.h"
#include "fd-util.h"
#include "missing_resource.h"
#include "string-util.h"
#include "time-util.h"

#define MAX_SIZE (16*1024)

static usec_t arg_loop_usec = 1000 * USEC_PER_MSEC;

typedef enum Type {
        TYPE_LEGACY,
        TYPE_DIRECT,
} Type;

static void server(sd_bus *b, size_t *result) {
        int r;

        for (;;) {
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;

                r = sd_bus_process(b, &m);
                assert_se(r >= 0);

                if (r == 0)
                        assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0);
                if (!m)
                        continue;

                if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
                        assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
                else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
                        const void *p;
                        size_t sz;

                        /* Make sure the mmap is mapped */
                        assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);

                        r = sd_bus_reply_method_return(m, NULL);
                        assert_se(r >= 0);
                } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
                        uint64_t res;
                        assert_se(sd_bus_message_read(m, "t", &res) > 0);

                        *result = res;
                        return;

                } else if (!sd_bus_message_is_signal(m, NULL, NULL))
                        assert_not_reached();
        }
}

static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
        bool *quit = userdata;
        *quit = true;
        return 1;
}

static void transaction(sd_bus *b, size_t sz, const char *server_name) {
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
        uint8_t *p;

        assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
        assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);

        memset(p, 0x80, sz);

        bool quit = false;
        // assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
        assert_se(sd_bus_call_async(b, NULL, m, quit_callback, &quit, 2000 * USEC_PER_MSEC) >= 0);
        while (!quit) {
                int r = sd_bus_process(b, NULL);
                assert_se(r >= 0);
                if (r == 0)
                        assert_se(sd_bus_wait(b, 100) >= 0);
        }
}

static void client_bisect(const char *address, const char *server_name) {
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
        size_t lsize, rsize, csize;
        sd_bus *b;
        int r;

        r = sd_bus_new(&b);
        assert_se(r >= 0);

        r = sd_bus_set_address(b, address);
        assert_se(r >= 0);

        r = sd_bus_start(b);
        assert_se(r >= 0);

        r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
        assert_se(r >= 0);

        lsize = 1;
        rsize = MAX_SIZE;

        b->use_memfd = 0;
        unsigned total_round = 0;
        for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
                usec_t t;
                unsigned n_memfd;

                for (n_memfd = 0; n_memfd < 1000; n_memfd++) {
                        transaction(b, csize, server_name);
                }

                total_round += n_memfd;
        }

        printf("Total round: %u\n", total_round);

        b->use_memfd = 1;
        assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
        assert_se(sd_bus_message_append(x, "t", csize) >= 0);
        assert_se(sd_bus_send(b, x, NULL) >= 0);

        sd_bus_unref(b);
}

static void client_chart(Type type, const char *address, const char *server_name, int fd) {
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
        size_t csize = 16;
        sd_bus *b;
        int r;

        r = sd_bus_new(&b);
        assert_se(r >= 0);

        if (type == TYPE_DIRECT) {
                r = sd_bus_set_fd(b, fd, fd);
                assert_se(r >= 0);
        } else {
                r = sd_bus_set_address(b, address);
                assert_se(r >= 0);

                r = sd_bus_set_bus_client(b, true);
                assert_se(r >= 0);
        }

        r = sd_bus_start(b);
        assert_se(r >= 0);

        r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
        assert_se(r >= 0);

        b->use_memfd = 0;
        unsigned total_round = 0;
        for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
                usec_t t;
                unsigned n_memfd;

                for (n_memfd = 0; n_memfd < 1000; n_memfd++) {
                        transaction(b, csize, server_name);
                }

                total_round += n_memfd;
        }

        printf("Total round: %u\n", total_round);
        b->use_memfd = 1;
        assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
        assert_se(sd_bus_message_append(x, "t", csize) >= 0);
        assert_se(sd_bus_send(b, x, NULL) >= 0);

        sd_bus_unref(b);
}

int main(int argc, char *argv[]) {
        enum {
                MODE_BISECT,
                MODE_CHART,
        } mode = MODE_BISECT;
        Type type = TYPE_LEGACY;
        int i, pair[2] = { -1, -1 };
        _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL;
        _cleanup_close_ int bus_ref = -1;
        const char *unique;
        cpu_set_t cpuset;
        size_t result;
        sd_bus *b;
        pid_t pid;
        int r;

        for (i = 1; i < argc; i++) {
                if (streq(argv[i], "chart")) {
                        mode = MODE_CHART;
                        continue;
                } else if (streq(argv[i], "legacy")) {
                        type = TYPE_LEGACY;
                        continue;
                } else if (streq(argv[i], "direct")) {
                        type = TYPE_DIRECT;
                        continue;
                }

                assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
        }

        assert_se(arg_loop_usec > 0);

        if (type == TYPE_LEGACY) {
                const char *e;

                e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
                assert_se(e);

                address = strdup(e);
                assert_se(address);
        }

        r = sd_bus_new(&b);
        assert_se(r >= 0);

        if (type == TYPE_DIRECT) {
                assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);

                r = sd_bus_set_fd(b, pair[0], pair[0]);
                assert_se(r >= 0);

                r = sd_bus_set_server(b, true, SD_ID128_NULL);
                assert_se(r >= 0);
        } else {
                r = sd_bus_set_address(b, address);
                assert_se(r >= 0);

                r = sd_bus_set_bus_client(b, true);
                assert_se(r >= 0);
        }

        r = sd_bus_start(b);
        assert_se(r >= 0);

        if (type != TYPE_DIRECT) {
                r = sd_bus_get_unique_name(b, &unique);
                assert_se(r >= 0);

                server_name = strdup(unique);
                assert_se(server_name);
        }

        sync();
        setpriority(PRIO_PROCESS, 0, -19);

        pid = fork();
        assert_se(pid >= 0);

        if (pid == 0) {
                CPU_ZERO(&cpuset);
                CPU_SET(0, &cpuset);
                pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);

                safe_close(bus_ref);
                sd_bus_unref(b);

                switch (mode) {
                case MODE_BISECT:
                        client_bisect(address, server_name);
                        break;

                case MODE_CHART:
                        client_chart(type, address, server_name, pair[1]);
                        break;
                }

                _exit(0);
        }

        CPU_ZERO(&cpuset);
        CPU_SET(1, &cpuset);
        pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);

        server(b, &result);

        if (mode == MODE_BISECT)
                printf("Copying/memfd are equally fast at %zu bytes\n", result);

        assert_se(waitpid(pid, NULL, 0) == pid);

        safe_close(pair[1]);
        sd_bus_unref(b);

        return 0;
}


Add Access Control to Container sockets

The Container1 interface proposed in #34 makes it possible to create new sockets which uniquely identify a container app instance. The only difference to the main socket is that connections from it are authenticated with a (sandbox engine, app id, app instance) triple which can be queried by services.

This is enough to get rid of a bunch of code in xdg-desktop-portal which tries to collect this authentication manually for flatpak and snap.

There are two other problems that can be solved by extending this Container1 interface:

Static access policy

Flatpak applications currently rely on xdg-dbus-proxy to restrict D-Bus access in addition to the usual D-Bus access control. By default almost no access is allowed and rules are added to broaden the access. Details are documented here https://github.com/flatpak/xdg-dbus-proxy/blob/8d87ac1664b42d1e73f370e4dd84e9c13d5e6108/flatpak-proxy.c#L49. This static access policy could be moved to the D-Bus broker itself to get rid of this proxy.

Client to Client communication

With the static access policy in place client to client communication becomes hard. Any app cannot by default talk to any other app because of the static access policy. If access to a bus name is specified in the flatpak manifest of an app, flatpak will create a rule to allow the access. Applications like firefox have addons which might want to talk to a particular bus name. It's impossible to enumerate all the bus names in the firefox flatpak manifest.

We need a better way to let flatpak apps talk to other apps.

Just giving access to all bus names owned by a flatpak app isn't a good idea: the flatpak app might not be aware that it's a flatpak app and that potentially malicious flatpak apps might talk to it. For example, if a password manager exposes a service to read the passwords and we expose this bus name to all flatpak applications, we've given all applications all the passwords.

We can only expose bus names when the application understands that potentially malicious actors are accessing it. For example, if the password manager exposes a service to all flatpak apps, it should first ask the user to authorize access by the applications which wants to use the service.

We need a mechanism for applications to opt-into exposing their bus name to all other, potentially malicious, applications.

One solution would be an interface for an app to own a bus name where a flag can be provided so that potentially malicious actors can connect to it. The static access policy would be extended by a flag which would allow connections on this socket to talk with bus names for which the previously mentioned flag was provided.

status of busd

Hello, I've found ur project yesterday and I am wondering if it's possible to replace dbus-daemon completely with this thing? how fast is it than dbus-broker? is there support of systemd integration?

Implement essential policy

busd will have to implement a policy. However, I don't think we need to go as far as existing implementations. Most of the policies are not super useful. More specifically, we'll want service-level policy but not method-level. Admins can specify which names can be owned by which users and who can make calls to them. In the D-Bus configuration XML language, this would mean enable configurations like:

    <allow send_destination="org.gnome.DisplayManager"/>

and not supporting send_interface or send_member:

    <allow send_destination="org.gnome.DisplayManager"/>
      send_interface="org.gnome.DisplayManager.Manager"/>
      send_member="GetRemoteHostname"/>

We should not error out on encountering unsupported configuration nodes though, but rather just warn about them.

Prerequisites: #78.

session bus on Windows

busd already works on Windows so it would be great for clients to be able to easily connect to it, w/o having to re-export the DBUS_SESSION_BUS_ADDRESS env.

Readiness notification

Feature request

Readiness notification allows a daemon to notify the service manager when it has finalised its start-up sequence. This is very important so the service manager can handle dependencies properly.

In particular, when a service depends on dbus, it needs to be started after the broker has started listening on the configured address, and not merely after the dbus broker process has started.

Prior art

s6 has a very well though out (yet also extremely simple) readiness notificationmechanism: the service manager hands over a file descriptor to a service, and the service is expected write \n to indicate that it is up and listening.

If the file descriptor is closed without anything being written to it, it is assumed that the service filed to start properly.

systemd uses a slightly different approach, and passes a socket where it expects the service to write READY=1\n when it is ready to accept connections (it also reuses this channel for other, unrelated communication).

The swaylock developers have figured out an approach that works well with both s6 and system: swaywm/swaylock#281. In essence, the service takes a file descriptor (parametrised via --ready-fd N), writes READY=1\n into it, and then closes it. This fulfils the expectation for both systemd and s6. (in the case of swaylock, it is common to want to suspend the system after it has been locked, so this is also used for ordering dependencies).

To summarise the idea: busd could be started with something like busd --ready-fd 3 and it would write READY=1\n to file descriptor 3 when it is ready to receive incoming connections, indicting to the service manager that dependant services can now be started.

Systemd integration

We need to install the socket and configuration files in the right places. When this is done, it should be possible to run busd as the system daemon w/o any major issues.

Pre-requisites: #77, #78, #79.

Ability to parse D-Bus configuration file(s)

To be a drop-in replacement for the existing bus implementations, we need to be able to parse D-Bus XML configuration. Moreover, we should provide default files with easy method to install them as well (#77 should help with that).

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Location: .github/renovate.json
Error type: The renovate configuration file contains some invalid settings
Message: packageRules[3]: Each packageRule must contain at least one match* or exclude* selector. Rule: {"rebaseWhen":"conflicted"}

Add meson build system

This is what will likely be expected of a modern system service, especially from package managers and/or folks who are used to building existing bus implementations themselves for any reason.

This will complicate the build itself. We'll need to figure out if we want to go the usual hacks to call Cargo from meson or use the latest cargo support in meson.

OTOH it will make it very easy to (un)install the binary and auxiliary data files.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.