summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2014-12-04 18:32:55 +0100
committerDavid Herrmann <dh.herrmann@gmail.com>2014-12-04 18:32:55 +0100
commit57c72e1667ed24ae46763d69232ffd0700cf86b5 (patch)
treee79b0a20270278d09f69a28067bd6e6dd155e0cf
parent0a86c1a9d8066267b878dfeddc5e0087dda6a37b (diff)
authority: add authorization and authentication daemonauthority
WIP
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am21
l---------src/authority/Makefile1
-rw-r--r--src/authority/authorityd-agent.c197
-rw-r--r--src/authority/authorityd-authentication.c111
-rw-r--r--src/authority/authorityd-authorization.c136
-rw-r--r--src/authority/authorityd-dbus.c758
-rw-r--r--src/authority/authorityd-manager.c163
-rw-r--r--src/authority/authorityd-object.c226
-rw-r--r--src/authority/authorityd-provider.c66
-rw-r--r--src/authority/authorityd-subject.c510
-rw-r--r--src/authority/authorityd.c67
-rw-r--r--src/authority/authorityd.h197
13 files changed, 2454 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 06d411a93..6263c6117 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@
/systemd-activate
/systemd-analyze
/systemd-ask-password
+/systemd-authorityd
/systemd-backlight
/systemd-binfmt
/systemd-bootchart
diff --git a/Makefile.am b/Makefile.am
index 7b43733eb..e0cb35788 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -199,6 +199,7 @@ AM_CPPFLAGS = \
-I $(top_srcdir)/src \
-I $(top_builddir)/src/shared \
-I $(top_srcdir)/src/shared \
+ -I $(top_srcdir)/src/authority \
-I $(top_srcdir)/src/network \
-I $(top_srcdir)/src/login \
-I $(top_srcdir)/src/journal \
@@ -5008,6 +5009,26 @@ lib_LTLIBRARIES += \
endif
# ------------------------------------------------------------------------------
+systemd_authorityd_SOURCES = \
+ src/authority/authorityd.h \
+ src/authority/authorityd.c \
+ src/authority/authorityd-manager.c \
+ src/authority/authorityd-dbus.c \
+ src/authority/authorityd-provider.c \
+ src/authority/authorityd-agent.c \
+ src/authority/authorityd-authentication.c \
+ src/authority/authorityd-authorization.c \
+ src/authority/authorityd-subject.c \
+ src/authority/authorityd-object.c
+
+systemd_authorityd_LDADD = \
+ libsystemd-internal.la \
+ libsystemd-shared.la
+
+rootlibexec_PROGRAMS += \
+ systemd-authorityd
+
+# ------------------------------------------------------------------------------
if ENABLE_RESOLVED
systemd_resolved_SOURCES = \
src/resolve/resolved.c \
diff --git a/src/authority/Makefile b/src/authority/Makefile
new file mode 120000
index 000000000..d0b0e8e00
--- /dev/null
+++ b/src/authority/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/authority/authorityd-agent.c b/src/authority/authorityd-agent.c
new file mode 100644
index 000000000..8db87bf65
--- /dev/null
+++ b/src/authority/authorityd-agent.c
@@ -0,0 +1,197 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "set.h"
+#include "util.h"
+
+static Set *free_subject_set(Set *set) {
+ Subject *s;
+
+ while ((s = set_steal_first(set)))
+ subject_unref(s);
+
+ set_free(set);
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, free_subject_set);
+
+int agent_new(Agent **out, Manager *m) {
+ _cleanup_(agent_freep) Agent *ag = NULL;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ ag = new0(Agent, 1);
+ if (!ag)
+ return -ENOMEM;
+
+ ag->manager = m;
+ sprintf(ag->id, "%" PRIu64, ++m->ag_ids);
+
+ r = hashmap_ensure_allocated(&m->ag_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->ag_map, ag->id, ag);
+ if (r < 0)
+ return r;
+
+ *out = ag;
+ ag = NULL;
+ return 0;
+}
+
+Agent *agent_free(Agent *ag) {
+ if (!ag)
+ return NULL;
+
+ ag->subjects = free_subject_set(ag->subjects);
+ free(ag->language);
+
+ if (ag->owner_id) {
+ hashmap_remove_value(ag->manager->ag_by_owner, ag->owner_id, ag);
+ ag->owner_track = sd_bus_track_unref(ag->owner_track);
+ free(ag->owner_id);
+ }
+
+ hashmap_remove_value(ag->manager->ag_map, ag->id, ag);
+ free(ag);
+
+ return NULL;
+}
+
+static int agent_read_attribute(Agent *ag, const char *key, sd_bus_message *m) {
+ int r;
+
+ assert(ag);
+ assert(key);
+ assert(m);
+
+ if (!strcmp(key, "Language")) {
+ const char *str;
+
+ r = sd_bus_message_read(m, "v", "s", &str);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&ag->language, str);
+ if (r < 0)
+ return r;
+ } else if (!strcmp(key, "Subjects")) {
+ _cleanup_(free_subject_setp) Set *set = NULL;
+
+ set = set_new(NULL);
+ if (!set)
+ return -ENOMEM;
+
+ r = sd_bus_message_enter_container(m, 'v', "aa{sv}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, 'a', "a{sv}");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(subject_unrefp) Subject *s = NULL;
+
+ r = subject_new(&s);
+ if (r < 0)
+ return r;
+
+ r = subject_read(s, m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ /* TODO: make sure unprivileged agents cannot register for arbitrary subjects */
+
+ r = set_put(set, s);
+ if (r < 0)
+ return r;
+
+ s = NULL;
+ }
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ free_subject_set(ag->subjects);
+ ag->subjects = set;
+ set = NULL;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int agent_read(Agent *ag, sd_bus_message *m) {
+ int r;
+
+ assert(ag);
+
+ r = sd_bus_message_enter_container(m, 'a', "{sv}");
+ if (r <= 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
+ const char *key;
+
+ r = sd_bus_message_read(m, "s", &key);
+ if (r < 0)
+ return r;
+
+ r = agent_read_attribute(ag, key, m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
diff --git a/src/authority/authorityd-authentication.c b/src/authority/authorityd-authentication.c
new file mode 100644
index 000000000..b86427f0c
--- /dev/null
+++ b/src/authority/authorityd-authentication.c
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "util.h"
+
+int authentication_new(Authentication **out, Manager *m) {
+ _cleanup_(authentication_freep) Authentication *au = NULL;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ au = new0(Authentication, 1);
+ if (!au)
+ return -ENOMEM;
+
+ au->manager = m;
+ sprintf(au->id, "%" PRIu64, ++m->au_ids);
+
+ r = hashmap_ensure_allocated(&m->au_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->au_map, au->id, au);
+ if (r < 0)
+ return r;
+
+ *out = au;
+ au = NULL;
+ return 0;
+}
+
+Authentication *authentication_free(Authentication *au) {
+ if (!au)
+ return NULL;
+
+ authentication_stop(au, -ECANCELED);
+
+ au->owner_track = sd_bus_track_unref(au->owner_track);
+ free(au->owner_id);
+
+ hashmap_remove_value(au->manager->au_map, au->id, au);
+ free(au);
+
+ return NULL;
+}
+
+static int authentication_run_fn(sd_event_source *src, void *userdata) {
+ Authentication *au = userdata;
+
+ assert(au->started);
+ assert(!au->stopped);
+
+ authentication_stop(au, -EPERM);
+
+ return 0;
+}
+
+int authentication_start(Authentication *au) {
+ int r;
+
+ assert(au);
+
+ if (au->stopped)
+ return -ECANCELED;
+ if (au->started)
+ return 0;
+
+ r = sd_event_add_defer(au->manager->event, &au->run_src, authentication_run_fn, au);
+ if (r < 0)
+ return r;
+
+ au->started = true;
+
+ return 0;
+}
+
+void authentication_stop(Authentication *au, int ret) {
+ assert(au);
+
+ if (au->stopped)
+ return;
+
+ au->stopped = true;
+ au->run_src = sd_event_source_unref(au->run_src);
+}
diff --git a/src/authority/authorityd-authorization.c b/src/authority/authorityd-authorization.c
new file mode 100644
index 000000000..bde42f5b5
--- /dev/null
+++ b/src/authority/authorityd-authorization.c
@@ -0,0 +1,136 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "util.h"
+
+int authorization_new(Authorization **out, Manager *m) {
+ _cleanup_(authorization_freep) Authorization *az = NULL;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ az = new0(Authorization, 1);
+ if (!az)
+ return -ENOMEM;
+
+ az->manager = m;
+ sprintf(az->id, "%" PRIu64, ++m->az_ids);
+
+ r = hashmap_ensure_allocated(&m->az_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->az_map, az->id, az);
+ if (r < 0)
+ return r;
+
+ *out = az;
+ az = NULL;
+ return 0;
+}
+
+Authorization *authorization_free(Authorization *az) {
+ if (!az)
+ return NULL;
+
+ authorization_stop(az, -ECANCELED);
+
+ az->owner_track = sd_bus_track_unref(az->owner_track);
+ free(az->owner_id);
+
+ az->object = object_unref(az->object);
+ az->subject = subject_unref(az->subject);
+ hashmap_remove_value(az->manager->az_map, az->id, az);
+ free(az);
+
+ return NULL;
+}
+
+static int authorization_run_fn(sd_event_source *src, void *userdata) {
+ Authorization *az = userdata;
+
+ assert(az->started);
+ assert(!az->stopped);
+
+ /*
+ * TODO: Run Authorization
+ * - check temporary authorizations
+ * - check local rule files
+ * - run authentication
+ */
+
+ authorization_stop(az, -EPERM);
+
+ return 0;
+}
+
+int authorization_start(Authorization *az) {
+ int r;
+
+ assert(az);
+
+ if (az->stopped)
+ return -ECANCELED;
+ if (az->started)
+ return 0;
+
+ /* defer authorization so the dbus events are correctly ordered */
+ r = sd_event_add_defer(az->manager->event, &az->run_src, authorization_run_fn, az);
+ if (r < 0)
+ return r;
+
+ az->started = true;
+
+ return 0;
+}
+
+void authorization_stop(Authorization *az, int ret) {
+ assert(az);
+
+ if (az->stopped)
+ return;
+
+ az->stopped = true;
+ az->run_src = sd_event_source_unref(az->run_src);
+
+ if (az->owner_unique) {
+ if (ret < 0)
+ sd_bus_reply_method_errno(az->owner_msg, ret, NULL);
+ else
+ sd_bus_reply_method_return(az->owner_msg, NULL);
+
+ az->owner_msg = sd_bus_message_unref(az->owner_msg);
+
+ hashmap_remove_value(az->manager->az_by_unique, az->owner_unique, az);
+ free(az->owner_unique);
+ az->owner_unique = NULL;
+ }
+}
diff --git a/src/authority/authorityd-dbus.c b/src/authority/authorityd-dbus.c
new file mode 100644
index 000000000..aa96e2e33
--- /dev/null
+++ b/src/authority/authorityd-dbus.c
@@ -0,0 +1,758 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "strv.h"
+#include "util.h"
+
+static int authorization_bus_cancel(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ Authorization *az = userdata;
+
+ authorization_stop(az, -ECANCELED);
+
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static char *authorization_bus_path(Authorization *az) {
+ char *path;
+ int r;
+
+ assert(az);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/authorization", az->id, &path);
+ if (r < 0)
+ return NULL;
+
+ return path;
+}
+
+static int authorization_bus_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *id = NULL;
+ Manager *m = userdata;
+ Authorization *az;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/authorization", &id);
+ if (r <= 0)
+ return r;
+
+ az = hashmap_get(m->az_map, id);
+ if (!az)
+ return 0;
+
+ *found = az;
+ return 1;
+}
+
+static int authorization_bus_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Authorization *az;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(az, m->az_map, i) {
+ char *s;
+
+ s = authorization_bus_path(az);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(&l, s);
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = l;
+ l = NULL;
+ return 1;
+}
+
+static const sd_bus_vtable authorization_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ //SD_BUS_PROPERTY("Subject", "...", authorization_bus_get_subject, 0, SD_BUS_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("Object", "...", authorization_bus_get_object, 0, SD_BUS_PROPERTY_CONST),
+ SD_BUS_METHOD("Cancel", "i", NULL, authorization_bus_cancel, 0),
+ SD_BUS_SIGNAL("Done", "i", 0),
+ SD_BUS_VTABLE_END
+};
+
+static int authentication_bus_cancel(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static char *authentication_bus_path(Authentication *au) {
+ char *path;
+ int r;
+
+ assert(au);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/authentication", au->id, &path);
+ if (r < 0)
+ return NULL;
+
+ return path;
+}
+
+static int authentication_bus_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *id = NULL;
+ Manager *m = userdata;
+ Authentication *au;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/authentication", &id);
+ if (r <= 0)
+ return r;
+
+ au = hashmap_get(m->au_map, id);
+ if (!au)
+ return 0;
+
+ *found = au;
+ return 1;
+}
+
+static int authentication_bus_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Authentication *au;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(au, m->au_map, i) {
+ char *s;
+
+ s = authentication_bus_path(au);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(&l, s);
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = l;
+ l = NULL;
+ return 1;
+}
+
+static const sd_bus_vtable authentication_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ //SD_BUS_PROPERTY("Providers", "as", authorization_bus_get_providers, 0, SD_BUS_PROPERTY_CONST),
+ //SD_BUS_WRITABLE_PROPERTY("UID", "u", authentication_bus_get_uid, authentication_bus_set_uid, 0, 0),
+ SD_BUS_METHOD("Cancel", NULL, NULL, authentication_bus_cancel, 0),
+ SD_BUS_SIGNAL("Done", NULL, 0),
+ SD_BUS_VTABLE_END
+};
+
+static char *agent_bus_path(Agent *ag) {
+ char *path;
+ int r;
+
+ assert(ag);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/agent", ag->id, &path);
+ if (r < 0)
+ return NULL;
+
+ return path;
+}
+
+static int agent_bus_get_subjects(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ Agent *ag = userdata;
+ Iterator i;
+ Subject *s;
+ int r;
+
+ r = sd_bus_message_open_container(reply, 'a', "a{sv}");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(s, ag->subjects, i) {
+ r = subject_append(s, reply);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int agent_bus_set_language(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ Agent *ag = userdata;
+ const char *str;
+ int r;
+
+ r = sd_bus_message_read(m, "s", &str);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&ag->language, str);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int agent_bus_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *id = NULL;
+ Manager *m = userdata;
+ Agent *ag;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/agent", &id);
+ if (r <= 0)
+ return r;
+
+ ag = hashmap_get(m->ag_map, id);
+ if (!ag)
+ return 0;
+
+ *found = ag;
+ return 1;
+}
+
+static int agent_bus_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Iterator i;
+ Agent *ag;
+ int r;
+
+ HASHMAP_FOREACH(ag, m->ag_map, i) {
+ char *s;
+
+ s = agent_bus_path(ag);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(&l, s);
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = l;
+ l = NULL;
+ return 1;
+}
+
+static const sd_bus_vtable agent_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_WRITABLE_PROPERTY("Language", "s", NULL, agent_bus_set_language, offsetof(Agent, language), 0),
+ SD_BUS_PROPERTY("Subjects", "aa{sv}", agent_bus_get_subjects, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("Privileged", "b", agent_bus_get_privileged, 0, SD_BUS_PROPERTY_CONST),
+ SD_BUS_SIGNAL("StartAuthentication", "o", 0),
+ SD_BUS_SIGNAL("StopAuthentication", "o", 0),
+ SD_BUS_VTABLE_END
+};
+
+static char *provider_bus_path(Provider *p) {
+ char *path;
+ int r;
+
+ assert(p);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/provider", p->id, &path);
+ if (r < 0)
+ return NULL;
+
+ return path;
+}
+
+static int provider_bus_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *id = NULL;
+ Manager *m = userdata;
+ Provider *p;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/provider", &id);
+ if (r <= 0)
+ return r;
+
+ p = hashmap_get(m->provider_map, id);
+ if (!p)
+ return 0;
+
+ *found = p;
+ return 1;
+}
+
+static int provider_bus_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Provider *p;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(p, m->provider_map, i) {
+ char *s;
+
+ s = provider_bus_path(p);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(&l, s);
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = l;
+ l = NULL;
+ return 1;
+}
+
+static const sd_bus_vtable provider_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ //SD_BUS_PROPERTY("Name", "s", authentication_bus_get_name, 0, SD_BUS_PROPERTY_CONST),
+ //SD_BUS_METHOD("Attach", NULL, NULL, provider_bus_attach, 0),
+ //SD_BUS_METHOD("Detach", NULL, NULL, provider_bus_detach, 0),
+ //SD_BUS_METHOD("Respond", "osv", NULL, provider_bus_respond, 0),
+ SD_BUS_SIGNAL("ShowChallenge", "osv", 0),
+ SD_BUS_SIGNAL("ShowMessage", "osv", 0),
+ SD_BUS_VTABLE_END
+};
+
+static int manager_bus_lost_agent(sd_bus_track *track, void *userdata) {
+ Agent *ag = userdata;
+
+ agent_free(ag);
+ return 0;
+}
+
+static int manager_bus_new_agent(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(agent_freep) Agent *ag = NULL;
+ Manager *manager = userdata;
+ int r;
+
+ /* prevent unbound allocations so each peer is only allowed a single agent */
+ if (hashmap_get(manager->ag_by_owner, sd_bus_message_get_sender(m)))
+ return -EALREADY;
+
+ r = agent_new(&ag, manager);
+ if (r < 0)
+ return r;
+
+ /* track owner */
+
+ ag->owner_id = strdup(sd_bus_message_get_sender(m));
+ if (!ag->owner_id)
+ return -ENOMEM;
+
+ r = sd_bus_track_new(bus, &ag->owner_track, manager_bus_lost_agent, ag);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_sender(ag->owner_track, m);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&manager->ag_by_owner, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(manager->ag_by_owner, ag->owner_id, ag);
+ if (r < 0)
+ return r;
+
+ /* set initial attributes */
+
+ r = agent_read(ag, m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static int manager_bus_lost_authorization(sd_bus_track *track, void *userdata) {
+ Authorization *az = userdata;
+
+ authorization_stop(az, -ECANCELED);
+ return 0;
+}
+
+static int manager_bus_parse_authorization_request(Manager *manager, Authorization **out, sd_bus *bus, sd_bus_message *m) {
+ _cleanup_(authorization_freep) Authorization *az = NULL;
+ _cleanup_(subject_unrefp) Subject *s = NULL;
+ _cleanup_(object_unrefp) Object *o = NULL;
+ const char *flag;
+ int r;
+
+ /* parse subject */
+
+ r = subject_new(&s);
+ if (r < 0)
+ return r;
+
+ r = subject_read(s, m);
+ if (r < 0)
+ return r;
+
+ r = subject_complete(s, bus);
+ if (r < 0)
+ return r;
+
+ /* parse object */
+
+ r = object_new(&o);
+ if (r < 0)
+ return r;
+
+ r = object_read(o, m);
+ if (r < 0)
+ return r;
+
+ /* set authorization object */
+
+ r = authorization_new(&az, manager);
+ if (r < 0)
+ return r;
+
+ az->subject = s;
+ s = NULL;
+ az->object = o;
+ o = NULL;
+
+ /* parse flags */
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "s", &flag)) > 0) {
+ if (!strcmp(flag, "interactive")) {
+ az->interactive = true;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ /* track owner */
+
+ az->owner_id = strdup(sd_bus_message_get_sender(m));
+ if (!az->owner_id)
+ return -ENOMEM;
+
+ r = sd_bus_track_new(bus, &az->owner_track, manager_bus_lost_authorization, az);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_sender(az->owner_track, m);
+ if (r < 0)
+ return r;
+
+ *out = az;
+ az = NULL;
+ return 0;
+}
+
+static int manager_bus_request_authorization(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(authorization_freep) Authorization *az = NULL;
+ _cleanup_free_ char *path = NULL;
+ Manager *manager = userdata;
+ int r;
+
+ r = manager_bus_parse_authorization_request(manager, &az, bus, m);
+ if (r < 0)
+ return r;
+
+ path = authorization_bus_path(az);
+ if (!path)
+ return -ENOMEM;
+
+ r = authorization_start(az);
+ if (r < 0)
+ return r;
+
+ az = NULL;
+
+ return sd_bus_reply_method_return(m, "o", path);
+}
+
+static int manager_bus_request_blocking_authorization(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(authorization_freep) Authorization *az = NULL;
+ Manager *manager = userdata;
+ const char *unique;
+ int r;
+
+ r = manager_bus_parse_authorization_request(manager, &az, bus, m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(m, "s", &unique);
+ if (r < 0)
+ return r;
+ if (strlen(unique) > 255)
+ return -EINVAL;
+
+ assert(!az->owner_unique);
+ assert(!az->owner_msg);
+
+ az->owner_unique = strjoin(sd_bus_message_get_sender(m), "/", unique, NULL);
+ if (!az->owner_unique)
+ return -ENOMEM;
+
+ az->owner_msg = sd_bus_message_ref(m);
+
+ r = hashmap_ensure_allocated(&manager->az_by_unique, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(manager->az_by_unique, az->owner_unique, az);
+ if (r < 0)
+ return r;
+
+ r = authorization_start(az);
+ if (r < 0)
+ return r;
+
+ az = NULL;
+
+ /* reply is sent once the authorization finished */
+ return 1;
+}
+
+static int manager_bus_cancel_blocking_authorization(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ Manager *manager = userdata;
+ const char *unique;
+ Authorization *az;
+ char *key;
+ int r;
+
+ r = sd_bus_message_read(m, "s", &unique);
+ if (r < 0)
+ return r;
+ if (strlen(unique) > 255)
+ return -EINVAL;
+
+ key = strappenda(sd_bus_message_get_sender(m), "/", unique);
+ az = hashmap_get(manager->az_by_unique, key);
+ if (!az)
+ return -ENOENT;
+
+ authorization_stop(az, -ECANCELED);
+
+ return sd_bus_reply_method_return(m, NULL);
+}
+
+static int manager_bus_lost_authentication(sd_bus_track *track, void *userdata) {
+ Authentication *au = userdata;
+
+ authentication_stop(au, -ECANCELED);
+ return 0;
+}
+
+static int manager_bus_parse_authentication_request(Manager *manager, Authentication **out, sd_bus *bus, sd_bus_message *m) {
+ _cleanup_(authentication_freep) Authentication *au = NULL;
+ const char *flag;
+ int r;
+
+ /* TODO: define parameters */
+
+ /* set authorization object */
+
+ r = authentication_new(&au, manager);
+ if (r < 0)
+ return r;
+
+ /* parse flags */
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "s", &flag)) > 0) {
+ if (!strcmp(flag, "interactive")) {
+ au->interactive = true;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ /* track owner */
+
+ au->owner_id = strdup(sd_bus_message_get_sender(m));
+ if (!au->owner_id)
+ return -ENOMEM;
+
+ r = sd_bus_track_new(bus, &au->owner_track, manager_bus_lost_authentication, au);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_sender(au->owner_track, m);
+ if (r < 0)
+ return r;
+
+ *out = au;
+ au = NULL;
+ return 0;
+}
+
+static int manager_bus_request_authentication(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ _cleanup_(authentication_freep) Authentication *au = NULL;
+ _cleanup_free_ char *path = NULL;
+ Manager *manager = userdata;
+ int r;
+
+ r = manager_bus_parse_authentication_request(manager, &au, bus, m);
+ if (r < 0)
+ return r;
+
+ path = authentication_bus_path(au);
+ if (!path)
+ return -ENOMEM;
+
+ r = authentication_start(au);
+ if (r < 0)
+ return r;
+
+ au = NULL;
+
+ return sd_bus_reply_method_return(m, "o", path);
+}
+
+static const sd_bus_vtable manager_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ /* Agent */
+ SD_BUS_METHOD("NewAgent", "a{sv}", "o", manager_bus_new_agent, SD_BUS_VTABLE_UNPRIVILEGED),
+
+ /* Authentication */
+ SD_BUS_METHOD("RequestAuthentication", "a{sv}a{sv}as", "o", manager_bus_request_authentication, 0),
+
+ /* Authorization */
+ SD_BUS_METHOD("RequestAuthorization", "a{sv}a{sv}as", "o", manager_bus_request_authorization, 0),
+
+ /* Convenience API */
+ SD_BUS_METHOD("RequestBlockingAuthorization", "a{sv}a{sv}ass", NULL, manager_bus_request_blocking_authorization, 0),
+ SD_BUS_METHOD("CancelBlockingAuthorization", "s", NULL, manager_bus_cancel_blocking_authorization, 0),
+
+ SD_BUS_VTABLE_END
+};
+
+int manager_bus_init(Manager *m) {
+ int r;
+
+ /* manager */
+
+ r = sd_bus_add_object_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ manager_bus_vtable,
+ m);
+ if (r < 0)
+ return r;
+
+ /* provider */
+
+ r = sd_bus_add_fallback_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/provider",
+ "org.freedesktop.authority1.Provider",
+ provider_bus_vtable,
+ provider_bus_find,
+ m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_node_enumerator(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/provider",
+ provider_bus_enumerate,
+ m);
+ if (r < 0)
+ return r;
+
+ /* agent */
+
+ r = sd_bus_add_fallback_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/agent",
+ "org.freedesktop.authority1.Agent",
+ agent_bus_vtable,
+ agent_bus_find,
+ m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_node_enumerator(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/agent",
+ agent_bus_enumerate,
+ m);
+ if (r < 0)
+ return r;
+
+ /* authentication */
+
+ r = sd_bus_add_fallback_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/authentication",
+ "org.freedesktop.authority1.Authentication",
+ authentication_bus_vtable,
+ authentication_bus_find,
+ m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_node_enumerator(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/authentication",
+ authentication_bus_enumerate,
+ m);
+ if (r < 0)
+ return r;
+
+ /* authorization */
+
+ r = sd_bus_add_fallback_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/authorization",
+ "org.freedesktop.authority1.Authorization",
+ authorization_bus_vtable,
+ authorization_bus_find,
+ m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_node_enumerator(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/authorization",
+ authorization_bus_enumerate,
+ m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
diff --git a/src/authority/authorityd-manager.c b/src/authority/authorityd-manager.c
new file mode 100644
index 000000000..57713aad3
--- /dev/null
+++ b/src/authority/authorityd-manager.c
@@ -0,0 +1,163 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-path.h"
+#include "strv.h"
+#include "util.h"
+
+static const struct {
+ uint64_t type;
+ const char *suffix;
+} manager_dirs[] = {
+ { SD_PATH_USER_CONFIGURATION, "systemd/authority" },
+ { -1, "/etc/systemd/authority" },
+ { SD_PATH_USER_RUNTIME, "systemd/authority" },
+ { -1, "/run/systemd/authority" },
+ { SD_PATH_USER_LIBRARY_PRIVATE, "systemd/authority" },
+ { -1, "/usr/lib/systemd/authority" },
+};
+
+int manager_new(Manager **out, bool system_level) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ size_t i, pos;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ m = new0(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ m->system_level = system_level;
+
+ r = sd_event_default(&m->event);
+ if (r < 0)
+ return r;
+
+ r = sd_event_set_watchdog(m->event, true);
+ if (r < 0)
+ return r;
+
+ r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ m->dirs = new0(char*, ELEMENTSOF(manager_dirs) + 1);
+ if (!m->dirs)
+ return -ENOMEM;
+
+ for (i = 0, pos = 0; i < ELEMENTSOF(manager_dirs); ++i) {
+ if (manager_dirs[i].type == (uint64_t)-1) {
+ m->dirs[pos] = strdup(manager_dirs[i].suffix);
+ if (!m->dirs[pos++])
+ return -ENOMEM;
+ } else if (!m->system_level) {
+ r = sd_path_home(manager_dirs[i].type, manager_dirs[i].suffix, &m->dirs[pos++]);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (m->system_level)
+ r = sd_bus_default_system(&m->bus);
+ else
+ r = sd_bus_default_user(&m->bus);
+ if (r < 0)
+ return log_error_errno(r, "Cannot connect to bus: %m");
+
+ r = manager_bus_init(m);
+ if (r < 0)
+ return log_error_errno(r, "Cannot initialize bus API: %m");
+
+ r = sd_bus_request_name(m->bus, "org.freedesktop.authority1", 0);
+ if (r < 0)
+ return log_error_errno(r, "Cannot register name: %m");
+
+ r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return r;
+
+ *out = m;
+ m = NULL;
+ return 0;
+}
+
+Manager *manager_free(Manager *m) {
+ Authentication *au;
+ Authorization *az;
+ Provider *p;
+ Agent *ag;
+
+ if (!m)
+ return NULL;
+
+ while ((az = hashmap_first(m->az_map)))
+ authorization_free(az);
+
+ while ((au = hashmap_first(m->au_map)))
+ authentication_free(au);
+
+ while ((ag = hashmap_first(m->ag_map)))
+ agent_free(ag);
+
+ while ((p = hashmap_first(m->provider_map)))
+ provider_free(p);
+
+ assert(hashmap_isempty(m->az_by_unique));
+ assert(hashmap_isempty(m->ag_by_owner));
+
+ hashmap_free(m->az_by_unique);
+ hashmap_free(m->ag_by_owner);
+ hashmap_free(m->az_map);
+ hashmap_free(m->au_map);
+ hashmap_free(m->ag_map);
+ hashmap_free(m->provider_map);
+ strv_free(m->dirs);
+ m->dirs = NULL;
+ m->bus = sd_bus_unref(m->bus);
+ m->event = sd_event_unref(m->event);
+ free(m);
+
+ return NULL;
+}
+
+int manager_run(Manager *m) {
+ assert(m);
+
+ return 0;
+}
diff --git a/src/authority/authorityd-object.c b/src/authority/authorityd-object.c
new file mode 100644
index 000000000..0a049bb55
--- /dev/null
+++ b/src/authority/authorityd-object.c
@@ -0,0 +1,226 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "util.h"
+
+int object_new(Object **out) {
+ _cleanup_(object_unrefp) Object *o = NULL;
+
+ assert_return(out, -EINVAL);
+
+ o = new0(Object, 1);
+ if (!o)
+ return -ENOMEM;
+
+ o->ref = 1;
+
+ *out = o;
+ o = NULL;
+ return 0;
+}
+
+Object *object_ref(Object *o) {
+ if (o) {
+ assert(o->ref > 0);
+ ++o->ref;
+ }
+ return o;
+}
+
+Object *object_unref(Object *o) {
+ if (!o)
+ return NULL;
+
+ assert(o->ref > 0);
+
+ if (--o->ref > 0)
+ return NULL;
+
+ free(o->action_id);
+ free(o);
+
+ return NULL;
+}
+
+static int object_parse_file(Object *o, int fd) {
+ /* TODO: parse action file @fd */
+
+ return 0;
+}
+
+static int object_read_entry(Object *o, const char *key, sd_bus_message *m) {
+ int r;
+
+ assert(o);
+ assert(key);
+ assert(m);
+
+ if (!strcmp(key, "action-id")) {
+ const char *str;
+
+ r = sd_bus_message_read(m, "v", "s", &str);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&o->action_id, str);
+ if (r < 0)
+ return r;
+ } else if (!strcmp(key, "memfd")) {
+ int32_t fd;
+
+ r = sd_bus_message_read(m, "v", "h", &fd);
+ if (r < 0)
+ return r;
+
+ r = fcntl(fd, F_GET_SEALS);
+ if (r < 0)
+ return -errno;
+
+ if (r != (F_SEAL_SEAL | F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_GROW))
+ return -EINVAL;
+
+ r = object_parse_file(o, fd);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int object_read_attribute(Object *o, const char *key, sd_bus_message *m) {
+ int r;
+
+ assert(o);
+ assert(key);
+ assert(m);
+
+ if (!strcmp(key, "data")) {
+ r = sd_bus_message_enter_container(m, 'v', "a{sv}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
+ const char *str;
+
+ r = sd_bus_message_read(m, "s", &str);
+ if (r < 0)
+ return r;
+
+ r = object_read_entry(o, str, m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ } else if (!strcmp(key, "action-id")) {
+ const char *str;
+
+ r = sd_bus_message_read(m, "v", "s", &str);
+ if (r < 0)
+ return r;
+
+ /* TODO: open action-file for @str */
+ } else if (!strcmp(key, "memfd")) {
+ int32_t fd;
+
+ r = sd_bus_message_read(m, "v", "h", &fd);
+ if (r < 0)
+ return r;
+
+ r = fcntl(fd, F_GET_SEALS);
+ if (r < 0)
+ return -errno;
+
+ if (r != (F_SEAL_SEAL | F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_GROW))
+ return -EINVAL;
+
+ r = object_parse_file(o, fd);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int object_read(Object *o, sd_bus_message *m) {
+ int r;
+
+ assert(o);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'a', "{sv}");
+ if (r <= 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
+ const char *key;
+
+ r = sd_bus_message_read(m, "s", &key);
+ if (r < 0)
+ return r;
+
+ r = object_read_attribute(o, key, m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
diff --git a/src/authority/authorityd-provider.c b/src/authority/authorityd-provider.c
new file mode 100644
index 000000000..8ebc8e763
--- /dev/null
+++ b/src/authority/authorityd-provider.c
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "hashmap.h"
+#include "log.h"
+#include "util.h"
+
+int provider_new(Provider **out, Manager *m) {
+ _cleanup_(provider_freep) Provider *p = NULL;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ p = new0(Provider, 1);
+ if (!p)
+ return -ENOMEM;
+
+ p->manager = m;
+ sprintf(p->id, "%" PRIu64, ++m->provider_ids);
+
+ r = hashmap_ensure_allocated(&m->provider_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->provider_map, p->id, p);
+ if (r < 0)
+ return r;
+
+ *out = p;
+ p = NULL;
+ return 0;
+}
+
+Provider *provider_free(Provider *p) {
+ if (!p)
+ return NULL;
+
+ hashmap_remove_value(p->manager->provider_map, p->id, p);
+ free(p);
+
+ return NULL;
+}
diff --git a/src/authority/authorityd-subject.c b/src/authority/authorityd-subject.c
new file mode 100644
index 000000000..3290384b9
--- /dev/null
+++ b/src/authority/authorityd-subject.c
@@ -0,0 +1,510 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "bus-util.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "strv.h"
+#include "util.h"
+
+int subject_new(Subject **out) {
+ _cleanup_(subject_unrefp) Subject *s = NULL;
+
+ assert_return(out, -EINVAL);
+
+ s = new0(Subject, 1);
+ if (!s)
+ return -ENOMEM;
+
+ s->ref = 1;
+ s->euid = UID_INVALID;
+ s->fsuid = UID_INVALID;
+ s->egid = GID_INVALID;
+ s->fsgid = GID_INVALID;
+
+ *out = s;
+ s = NULL;
+ return 0;
+}
+
+Subject *subject_ref(Subject *s) {
+ if (s) {
+ assert(s->ref > 0);
+ ++s->ref;
+ }
+ return s;
+}
+
+Subject *subject_unref(Subject *s) {
+ if (!s)
+ return NULL;
+
+ assert(s->ref > 0);
+
+ if (--s->ref > 0)
+ return NULL;
+
+ strv_free(s->well_known_names);
+ free(s->unique_name);
+ free(s->session_id);
+ free(s->supplementary_gids);
+ free(s);
+
+ return NULL;
+}
+
+static int subject_read_attribute(Subject *s, const char *key, sd_bus_message *m) {
+ int r;
+
+ assert(s);
+ assert(key);
+ assert(m);
+
+ if (!strcmp(key, "pid")) {
+ int32_t pid;
+
+ r = sd_bus_message_read(m, "v", "i", &pid);
+ if (r < 0)
+ return r;
+
+ s->pid = pid;
+ } else if (!strcmp(key, "tid")) {
+ int32_t tid;
+
+ r = sd_bus_message_read(m, "v", "i", &tid);
+ if (r < 0)
+ return r;
+
+ s->tid = tid;
+ } else if (!strcmp(key, "euid")) {
+ uint32_t euid;
+
+ r = sd_bus_message_read(m, "v", "u", &euid);
+ if (r < 0)
+ return r;
+
+ s->euid = euid;
+ } else if (!strcmp(key, "fsuid")) {
+ uint32_t fsuid;
+
+ r = sd_bus_message_read(m, "v", "u", &fsuid);
+ if (r < 0)
+ return r;
+
+ s->fsuid = fsuid;
+ } else if (!strcmp(key, "egid")) {
+ uint32_t egid;
+
+ r = sd_bus_message_read(m, "v", "u", &egid);
+ if (r < 0)
+ return r;
+
+ s->egid = egid;
+ } else if (!strcmp(key, "fsgid")) {
+ uint32_t fsgid;
+
+ r = sd_bus_message_read(m, "v", "u", &fsgid);
+ if (r < 0)
+ return r;
+
+ s->fsgid = fsgid;
+ } else if (!strcmp(key, "supplementary-gids")) {
+ const uint32_t *gids;
+ uint32_t *t;
+ size_t size;
+
+ r = sd_bus_message_enter_container(m, 'v', "au");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(m, 'u', (const void**)&gids, &size);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ t = memdup(gids, size);
+ if (!t)
+ return -ENOMEM;
+
+ free(s->supplementary_gids);
+ s->supplementary_gids = t;
+ s->n_supplementary_gids = size / sizeof(*gids);
+ } else if (!strcmp(key, "session-id")) {
+ const char *sid;
+
+ r = sd_bus_message_read(m, "v", "s", &sid);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&s->session_id, sid);
+ if (r < 0)
+ return r;
+ } else if (!strcmp(key, "unique-name")) {
+ const char *str;
+
+ r = sd_bus_message_read(m, "v", "s", &str);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&s->unique_name, str);
+ if (r < 0)
+ return r;
+ } else if (!strcmp(key, "well-known-names")) {
+ _cleanup_strv_free_ char **strv = NULL;
+
+ r = sd_bus_message_enter_container(m, 'v', "as");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(m, &strv);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ strv_free(s->well_known_names);
+ s->well_known_names = strv;
+ strv = NULL;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int subject_read(Subject *s, sd_bus_message *m) {
+ int r;
+
+ assert(s);
+ assert(m);
+
+ r = sd_bus_message_enter_container(m, 'a', "{sv}");
+ if (r <= 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
+ const char *key;
+
+ r = sd_bus_message_read(m, "s", &key);
+ if (r < 0)
+ return r;
+
+ r = subject_read_attribute(s, key, m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int subject_append(Subject *s, sd_bus_message *m) {
+ int r;
+
+ assert(s);
+ assert(m);
+
+ r = sd_bus_message_open_container(m, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ if (s->pid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "pid", "i", s->pid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->tid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "tid", "i", s->tid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->euid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "euid", "u", s->euid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->fsuid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "fsuid", "u", s->fsuid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->egid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "egid", "u", s->egid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->fsgid > 0) {
+ r = sd_bus_message_append(m, "e", "sv", "fsgid", "u", s->fsgid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->supplementary_gids) {
+ r = sd_bus_message_open_container(m, 'e', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "s", "supplementary-gids");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'v', "au");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(m, 'u', s->supplementary_gids, s->n_supplementary_gids * sizeof(uint32_t));
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->session_id) {
+ r = sd_bus_message_append(m, "e", "sv", "session-id", "s", s->session_id);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->unique_name) {
+ r = sd_bus_message_append(m, "e", "sv", "unique-name", "s", s->unique_name);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->well_known_names) {
+ r = sd_bus_message_open_container(m, 'e', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "s", "well-known-names");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'v', "as");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(m, s->well_known_names);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(m);
+}
+
+int subject_complete(Subject *s, sd_bus *bus) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ int r;
+
+ assert(s);
+
+ if (s->unique_name) {
+ r = sd_bus_get_name_creds(bus,
+ s->unique_name,
+ SD_BUS_CREDS_PID |
+ SD_BUS_CREDS_EUID |
+ SD_BUS_CREDS_FSUID |
+ SD_BUS_CREDS_EGID |
+ SD_BUS_CREDS_FSGID |
+ SD_BUS_CREDS_WELL_KNOWN_NAMES |
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS,
+ &creds);
+ if (r < 0)
+ return r;
+
+ if (s->pid > 0) {
+ r = sd_bus_creds_get_pid(creds, &s->pid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (s->tid > 0) {
+ r = sd_bus_creds_get_tid(creds, &s->tid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (s->euid != UID_INVALID) {
+ r = sd_bus_creds_get_euid(creds, &s->euid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (s->fsuid != UID_INVALID) {
+ r = sd_bus_creds_get_fsuid(creds, &s->fsuid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (s->egid != GID_INVALID) {
+ r = sd_bus_creds_get_egid(creds, &s->egid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (s->fsgid != GID_INVALID) {
+ r = sd_bus_creds_get_fsgid(creds, &s->fsgid);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (!s->supplementary_gids) {
+ const gid_t *gids;
+ size_t i, num;
+ gid_t *t;
+
+ r = sd_bus_creds_get_supplementary_gids(creds, &gids);
+ if (r != -ENODATA) {
+ if (r < 0)
+ return r;
+
+ for (i = 0, num = 0; gids[i]; ++i)
+ ++num;
+
+ t = new0(uint32_t, num);
+ if (!t)
+ return -ENOMEM;
+
+ free(s->supplementary_gids);
+ s->supplementary_gids = t;
+ s->n_supplementary_gids = num;
+
+ for (i = 0; i < num; ++i)
+ s->supplementary_gids[i] = gids[i];
+ }
+ }
+
+ if (!s->well_known_names) {
+ char **strv;
+
+ r = sd_bus_creds_get_well_known_names(creds, &strv);
+ if (r != -ENODATA) {
+ if (r < 0)
+ return r;
+
+ strv_free(s->well_known_names);
+ s->well_known_names = strv;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool subject_is_superset(Subject *s, Subject *subset) {
+ size_t i, j;
+
+ assert(s);
+ assert(subset);
+
+ if (s->pid > 0 && s->pid != subset->pid)
+ return false;
+
+ if (s->tid > 0 && s->tid != subset->tid)
+ return false;
+
+ if (s->euid != UID_INVALID && s->euid != subset->euid)
+ return false;
+
+ if (s->fsuid != UID_INVALID && s->fsuid != subset->fsuid)
+ return false;
+
+ if (s->egid != GID_INVALID && s->egid != subset->egid)
+ return false;
+
+ if (s->fsgid != GID_INVALID && s->fsgid != subset->fsgid)
+ return false;
+
+ if (s->supplementary_gids) {
+ if (!subset->supplementary_gids)
+ return false;
+
+ for (i = 0; i < subset->n_supplementary_gids; ++i) {
+ for (j = 0; j < s->n_supplementary_gids; ++j)
+ if (s->supplementary_gids[j] == subset->supplementary_gids[i])
+ break;
+
+ if (j >= s->n_supplementary_gids)
+ return false;
+ }
+ }
+
+ if (s->session_id && !streq_ptr(s->session_id, subset->session_id))
+ return false;
+
+ if (s->unique_name && !streq_ptr(s->unique_name, subset->unique_name))
+ return false;
+
+ if (s->well_known_names) {
+ if (!subset->well_known_names)
+ return false;
+
+ for (i = 0; subset->well_known_names[i]; ++i) {
+ for (j = 0; s->well_known_names[j]; ++j)
+ if (streq(s->well_known_names[j], subset->well_known_names[i]))
+ break;
+
+ if (!s->well_known_names[j])
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/authority/authorityd.c b/src/authority/authorityd.c
new file mode 100644
index 000000000..687a572ad
--- /dev/null
+++ b/src/authority/authorityd.c
@@ -0,0 +1,67 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authorityd.h"
+#include "log.h"
+#include "sd-daemon.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if (argc != 1) {
+ log_error("This program takes no arguments.");
+ r = -EINVAL;
+ goto out;
+ }
+
+ r = manager_new(&m, true);
+ if (r < 0) {
+ log_error("Could not create manager: %s", strerror(-r));
+ goto out;
+ }
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing requests...");
+
+ r = manager_run(m);
+ if (r < 0) {
+ log_error("Cannot run manager: %s", strerror(-r));
+ goto out;
+ }
+
+out:
+ sd_notify(false,
+ "STATUS=Shutting down...");
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/authority/authorityd.h b/src/authority/authorityd.h
new file mode 100644
index 000000000..086be51b3
--- /dev/null
+++ b/src/authority/authorityd.h
@@ -0,0 +1,197 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
+
+ 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/>.
+***/
+
+#pragma once
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hashmap.h"
+#include "macro.h"
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "set.h"
+#include "util.h"
+
+typedef struct Manager Manager;
+typedef struct Provider Provider;
+typedef struct Agent Agent;
+typedef struct Authentication Authentication;
+typedef struct Authorization Authorization;
+typedef struct Subject Subject;
+typedef struct Object Object;
+
+struct Manager {
+ sd_event *event;
+ sd_bus *bus;
+ char **dirs;
+
+ Hashmap *provider_map;
+ Hashmap *ag_map;
+ Hashmap *au_map;
+ Hashmap *az_map;
+ uint64_t provider_ids;
+ uint64_t ag_ids;
+ uint64_t au_ids;
+ uint64_t az_ids;
+
+ Hashmap *ag_by_owner;
+ Hashmap *az_by_unique;
+
+ bool system_level : 1;
+};
+
+struct Provider {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+};
+
+struct Agent {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ char *owner_id;
+ sd_bus_track *owner_track;
+
+ char *language;
+ Set *subjects;
+};
+
+struct Authentication {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ char *owner_id;
+ sd_bus_track *owner_track;
+
+ sd_event_source *run_src;
+
+ bool interactive : 1;
+ bool started : 1;
+ bool stopped : 1;
+};
+
+struct Authorization {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ Subject *subject;
+ Object *object;
+
+ char *owner_id;
+ sd_bus_track *owner_track;
+ char *owner_unique;
+ sd_bus_message *owner_msg;
+
+ sd_event_source *run_src;
+
+ bool interactive : 1;
+ bool started : 1;
+ bool stopped : 1;
+};
+
+struct Subject {
+ unsigned long ref;
+
+ pid_t pid;
+ pid_t tid;
+ uid_t euid;
+ uid_t fsuid;
+ gid_t egid;
+ gid_t fsgid;
+ uint32_t *supplementary_gids;
+ size_t n_supplementary_gids;
+ char *session_id;
+ char *unique_name;
+ char **well_known_names;
+};
+
+struct Object {
+ unsigned long ref;
+
+ char *action_id;
+};
+
+/* manager */
+
+int manager_new(Manager **out, bool system_level);
+Manager *manager_free(Manager *m);
+int manager_run(Manager *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_bus_init(Manager *m);
+
+/* provider */
+
+int provider_new(Provider **out, Manager *m);
+Provider *provider_free(Provider *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Provider*, provider_free);
+
+/* agent */
+
+int agent_new(Agent **out, Manager *m);
+Agent *agent_free(Agent *ag);
+int agent_read(Agent *ag, sd_bus_message *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Agent*, agent_free);
+
+/* authentication */
+
+int authentication_new(Authentication **out, Manager *m);
+Authentication *authentication_free(Authentication *au);
+int authentication_start(Authentication *au);
+void authentication_stop(Authentication *au, int ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Authentication*, authentication_free);
+
+/* authorization */
+
+int authorization_new(Authorization **out, Manager *m);
+Authorization *authorization_free(Authorization *az);
+int authorization_start(Authorization *az);
+void authorization_stop(Authorization *az, int ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Authorization*, authorization_free);
+
+/* subject */
+
+int subject_new(Subject **out);
+Subject *subject_ref(Subject *s);
+Subject *subject_unref(Subject *s);
+int subject_read(Subject *s, sd_bus_message *m);
+int subject_append(Subject *s, sd_bus_message *m);
+int subject_complete(Subject *s, sd_bus *bus);
+bool subject_is_superset(Subject *s, Subject *subset);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Subject*, subject_unref);
+
+/* object */
+
+int object_new(Object **out);
+Object *object_ref(Object *o);
+Object *object_unref(Object *o);
+int object_read(Object *o, sd_bus_message *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Object*, object_unref);