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>2015-04-10 18:04:10 +0200
commit14684f858e4e5b0c00bc5878dd85cf2e0c68e053 (patch)
tree7da1c577ad4608005babf6476ca74b3d9982f9b6
parenta813b30b68dd57a3d6b93778ee95b43dee1cc9fd (diff)
authority: add authorization and authentication daemonwip
WIP
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am32
l---------src/authority/Makefile1
-rw-r--r--src/authority/authorityctl.c242
-rw-r--r--src/authority/authorityd-action.c153
-rw-r--r--src/authority/authorityd-agent.c223
-rw-r--r--src/authority/authorityd-authorization.c365
-rw-r--r--src/authority/authorityd-dbus.c492
-rw-r--r--src/authority/authorityd-factor.c73
-rw-r--r--src/authority/authorityd-manager.c162
-rw-r--r--src/authority/authorityd-subject.c451
-rw-r--r--src/authority/authorityd.c67
-rw-r--r--src/authority/authorityd.h197
-rw-r--r--src/shared/authority-agent.c954
-rw-r--r--src/shared/authority-agent.h34
15 files changed, 3448 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 875ada5eb..e05ee3066 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
/TAGS
/accelerometer
/ata_id
+/authorityctl
/bootctl
/build-aux
/busctl
@@ -56,6 +57,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 0a5738944..ee44f0bbf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -212,6 +212,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 \
@@ -909,6 +910,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/build.h \
src/shared/import-util.c \
src/shared/import-util.h \
+ src/shared/authority-agent.c \
+ src/shared/authority-agent.h \
src/shared/sysctl-util.c \
src/shared/sysctl-util.h
@@ -5566,6 +5569,35 @@ endif
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-authorization.c \
+ src/authority/authorityd-factor.c \
+ src/authority/authorityd-agent.c \
+ src/authority/authorityd-subject.c \
+ src/authority/authorityd-action.c
+
+systemd_authorityd_LDADD = \
+ libsystemd-internal.la \
+ libsystemd-shared.la
+
+rootlibexec_PROGRAMS += \
+ systemd-authorityd
+
+authorityctl_SOURCES = \
+ src/authority/authorityctl.c
+
+authorityctl_LDADD = \
+ libsystemd-internal.la \
+ libsystemd-shared.la
+
+rootbin_PROGRAMS += \
+ authorityctl
+
+# ------------------------------------------------------------------------------
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/authorityctl.c b/src/authority/authorityctl.c
new file mode 100644
index 000000000..7b2e49df5
--- /dev/null
+++ b/src/authority/authorityctl.c
@@ -0,0 +1,242 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 <getopt.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "authority-agent.h"
+#include "build.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "pager.h"
+#include "sd-bus.h"
+#include "util.h"
+#include "verbs.h"
+
+static bool arg_pager = true;
+static bool arg_legend = true;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+static char *arg_host = NULL;
+static bool arg_user = false;
+
+static void pager_open_if_enabled(void) {
+ if (arg_pager)
+ pager_open(false);
+}
+
+static int list_factors(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ const char *factor, *busname;
+ sd_bus *bus = userdata;
+ unsigned int k = 0;
+ int r;
+
+ pager_open_if_enabled();
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.authority1",
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "ListFactors",
+ &error, &reply,
+ "");
+ if (r < 0) {
+ log_error("Cannot list authentication factors: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'a', "(s)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (arg_legend)
+ printf("%10s %-16s\n", "FACTOR", "BUSNAME");
+
+ while ((r = sd_bus_message_read(reply, "(s)", &factor, &busname)) > 0) {
+ printf("%10s %-16s\n", factor, busname);
+ ++k;
+ }
+
+ if (arg_legend)
+ printf("\n%i factors listed.\n", k);
+
+ return 0;
+}
+
+static int list_agents(int argc, char *argv[], void *userdata) {
+ pager_open_if_enabled();
+
+ return 0;
+}
+
+static int request_authorization(int argc, char *argv[], void *userdata) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ sd_bus *bus = userdata;
+ int r;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.authority1",
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "RequestBlockingAuthorization",
+ &error, NULL,
+ "a{sv}a{sv}ass",
+ /* subject */
+ 2,
+ "pid", "i", getpid(),
+ "euid", "u", geteuid(),
+ /* action */
+ 1,
+ "action-id", "s", "example.action",
+ /* flags */
+ 1, "interactive",
+ /* unique */
+ "unique");
+ if (r < 0) {
+ log_error("Authorization request failed: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ return 0;
+}
+
+static void help(void) {
+ printf("%s [OPTIONS...]\n\n"
+ "Query and control the authority daemon.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n\n"
+ "Commands:\n"
+ " list-factors List factors\n"
+ " list-agents List agents\n"
+ , program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_NO_LEGEND,
+ ARG_USER,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "user", no_argument, NULL, ARG_USER },
+ {}
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+hH:M:", options, NULL)) >= 0) {
+ switch (c) {
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_NO_PAGER:
+ arg_pager = false;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_legend = false;
+ break;
+
+ case 'H':
+ arg_transport = BUS_TRANSPORT_REMOTE;
+ arg_host = optarg;
+ break;
+
+ case 'M':
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case ARG_USER:
+ arg_user = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+ }
+
+ return 1;
+}
+
+static int authorityctl_main(int argc, char *argv[], sd_bus *bus) {
+ const Verb verbs[] = {
+ { "list-factors", VERB_ANY, 1, 0, list_factors },
+ { "list-agents", VERB_ANY, 1, 0, list_agents },
+ { "request-authorization", VERB_ANY, 1, 0, request_authorization },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, bus);
+}
+
+int main(int argc, char* argv[]) {
+ _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+ int r;
+
+ setlocale(LC_ALL, "");
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
+ if (r < 0) {
+ log_error_errno(r, "Cannot open bus connection: %m");
+ goto finish;
+ }
+
+ r = authorityctl_main(argc, argv, bus);
+
+finish:
+ pager_close();
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/authority/authorityd-action.c b/src/authority/authorityd-action.c
new file mode 100644
index 000000000..37f7fcfb9
--- /dev/null
+++ b/src/authority/authorityd-action.c
@@ -0,0 +1,153 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 action_new(Action **out) {
+ _cleanup_(action_unrefp) Action *a = NULL;
+
+ assert_return(out, -EINVAL);
+
+ a = new0(Action, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->ref = 1;
+
+ *out = a;
+ a = NULL;
+ return 0;
+}
+
+Action *action_ref(Action *a) {
+ if (a) {
+ assert(a->ref > 0);
+ ++a->ref;
+ }
+ return a;
+}
+
+Action *action_unref(Action *a) {
+ if (!a)
+ return NULL;
+
+ assert(a->ref > 0);
+
+ if (--a->ref > 0)
+ return NULL;
+
+ free(a->action_id);
+ free(a);
+
+ return NULL;
+}
+
+static int action_parse_file(Action *a, int fd) {
+ /* TODO: parse action file @fd */
+
+ return 0;
+}
+
+static int action_read_attribute(Action *a, const char *key, sd_bus_message *m) {
+ int r;
+
+ assert(a);
+ 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;
+
+ /* TODO: parse file */
+
+ r = free_and_strdup(&a->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 = action_parse_file(a, fd);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "v");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int action_read(Action *a, sd_bus_message *m) {
+ int r;
+
+ assert(a);
+ 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 = action_read_attribute(a, 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-agent.c b/src/authority/authorityd-agent.c
new file mode 100644
index 000000000..fa5c68221
--- /dev/null
+++ b/src/authority/authorityd-agent.c
@@ -0,0 +1,223 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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);
+
+static int agent_lost_fn(sd_bus_track *track, void *userdata) {
+ Agent *a = userdata;
+
+ agent_free(a);
+
+ return 0;
+}
+
+int agent_new(Agent **out, Manager *m, const char *sender) {
+ _cleanup_(agent_freep) Agent *a = NULL;
+ int r;
+
+ assert(out);
+ assert(m);
+ assert(sender);
+
+ a = new0(Agent, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->manager = m;
+ sprintf(a->id, "%" PRIu64, ++m->agent_ids);
+
+ r = sd_bus_track_new(m->bus, &a->owner_track, agent_lost_fn, a);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_name(a->owner_track, sender);
+ if (r < 0)
+ return r;
+
+ a->owner_busid = strdup(sender);
+ if (!a->owner_busid)
+ return -ENOMEM;
+
+ r = hashmap_ensure_allocated(&m->agent_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->agent_map, a->id, a);
+ if (r < 0)
+ return r;
+
+ *out = a;
+ a = NULL;
+ return 0;
+}
+
+Agent *agent_free(Agent *a) {
+ if (!a)
+ return NULL;
+
+ a->subjects = free_subject_set(a->subjects);
+ hashmap_remove_value(a->manager->agent_map, a->id, a);
+ free(a->owner_busid);
+ a->owner_track = sd_bus_track_unref(a->owner_track);
+ free(a);
+
+ return NULL;
+}
+
+#if 0
+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, "Fallback")) {
+ int v;
+
+ r = sd_bus_message_read(m, "v", "b", &v);
+ if (r < 0)
+ return r;
+
+ ag->fallback = !!v;
+ } 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;
+}
+#endif
diff --git a/src/authority/authorityd-authorization.c b/src/authority/authorityd-authorization.c
new file mode 100644
index 000000000..91da4151c
--- /dev/null
+++ b/src/authority/authorityd-authorization.c
@@ -0,0 +1,365 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 *authorization = NULL;
+ int r;
+
+ assert(out);
+ assert(m);
+
+ authorization = new0(Authorization, 1);
+ if (!authorization)
+ return -ENOMEM;
+
+ authorization->manager = m;
+ authorization->uid = UID_INVALID;
+ sprintf(authorization->id, "%" PRIu64, ++m->authorization_ids);
+
+ r = hashmap_ensure_allocated(&m->authorization_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->authorization_map, authorization->id, authorization);
+ if (r < 0)
+ return r;
+
+ *out = authorization;
+ authorization = NULL;
+ return 0;
+}
+
+Authorization *authorization_free(Authorization *authorization) {
+ if (!authorization)
+ return NULL;
+
+ authorization_stop(authorization, -ECANCELED);
+
+ hashmap_remove_value(authorization->manager->authorization_map, authorization->id, authorization);
+
+ assert(hashmap_isempty(authorization->transaction_map));
+
+ authorization->action = action_unref(authorization->action);
+ authorization->subject = subject_unref(authorization->subject);
+ authorization->transaction_map = hashmap_free(authorization->transaction_map);
+ free(authorization);
+
+ return NULL;
+}
+
+static int authorization_schedule_interactive(Authorization *authorization)
+{
+ unsigned int k = 0;
+ Transaction *trans;
+ Iterator i;
+ Factor *factor;
+ int r;
+
+ /* SELECT AGENT */
+
+ HASHMAP_FOREACH(factor, authorization->manager->factor_map, i) {
+ /*r = factor_start(factor, &trans, authorization, true);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ++k;*/
+ }
+
+ return k > 0;
+}
+
+static int authorization_schedule_non_interactive(Authorization *authorization)
+{
+ unsigned int k = 0;
+
+ /* skip if UID is selectable */
+ if (authorization->uid == UID_INVALID)
+ return 0;
+
+/*
+ Iterator i;
+ Factor *factor;
+ int r;
+
+ HASHMAP_FOREACH(factor, auth->manager->factor_map, i) {
+ r = factor_attach(factor, auth, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ ++k;
+ }
+*/
+ return k > 0;
+}
+
+static int authorization_run_rules(Authorization *authorization)
+{
+ /* skip if UID is selectable */
+ if (authorization->uid == UID_INVALID)
+ return 0;
+
+ /* no static rules are supported right now */
+ return 0;
+}
+
+static int authorization_run_temporary(Authorization *authorization)
+{
+ /* skip if UID is selectable */
+ if (authorization->uid == UID_INVALID)
+ return 0;
+
+ /* no temporary authorizations are supported right now */
+ return 0;
+}
+
+static int authorization_run_fn(sd_event_source *src, void *userdata) {
+ Authorization *authorization = userdata;
+ int r;
+
+ assert(authorization->started);
+
+ if (authorization->stopped)
+ return 0;
+
+ /*
+ * Authorization
+ * When we run an authorization, we have the subject and action fully
+ * initialized and will go through the following steps (in order):
+ * - Check for any temporary authorizations that have been granted by
+ * a previous request. No environment changes are taken into
+ * account. This means, if the temporary authorization was granted
+ * by a different action, then we will *not* run through the rules
+ * again. If an action allows temporary authorizations, it must be
+ * aware of that.
+ * - If no temporary authorization is granted, we run through the
+ * configured rules to see whether a static authorization is
+ * granted.
+ * - If no rule matches, we ask the authentication factors to run
+ * non-interactive authentication against the subject.
+ * - If non-interactive authentication didn't succeed, we run
+ * interactive authentication, iff the request allows it.
+ *
+ * If, at any step, authorization is granted, we immediately bail out
+ * and return success. This means, you must never rely on negative
+ * results in any of these steps. For instance, a non-interactive
+ * authentication factor cannot prevent an authentication, if a static
+ * rule already granted the authorization. Same is true for negative
+ * results: We always continue with the next step, if all previous
+ * steps denied authorization.
+ */
+
+ switch (authorization->run_state) {
+ case AUTHORIZATION_RUN_TEMPORARY:
+ r = authorization_run_temporary(authorization);
+ if (r != 0)
+ break;
+
+ ++authorization->run_state;
+ /* fallthrough */
+ case AUTHORIZATION_RUN_RULES:
+ r = authorization_run_rules(authorization);
+ if (r != 0)
+ break;
+
+ ++authorization->run_state;
+ /* fallthrough */
+ case AUTHORIZATION_RUN_NON_INTERACTIVE:
+ r = authorization_schedule_non_interactive(authorization);
+ if (r < 0)
+ break;
+ if (r > 0) {
+ r = 0;
+ break;
+ }
+
+ ++authorization->run_state;
+ /* fallthrough */
+ case AUTHORIZATION_RUN_INTERACTIVE:
+ r = authorization_schedule_interactive(authorization);
+ if (r < 0)
+ break;
+ if (r > 0) {
+ r = 0;
+ break;
+ }
+
+ ++authorization->run_state;
+ /* fallthrough */
+ default:
+ /* no authorization was granted */
+ r = -EPERM;
+ break;
+ }
+
+ if (r != 0)
+ authorization_stop(authorization, r);
+
+ return 0;
+}
+
+static int authorization_lost_fn(sd_bus_track *track, void *userdata) {
+ authorization_stop(userdata, -ECANCELED);
+ return 0;
+}
+
+int authorization_start(Authorization *authorization, sd_bus_message *m, const char *block_unique) {
+ const char *sender;
+ int r;
+
+ assert(authorization);
+ assert(m);
+
+ if (authorization->stopped)
+ return -ECANCELED;
+ if (authorization->started)
+ return -EALREADY;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ /* track sender so we don't leak authorization objects on disconnect */
+ r = sd_bus_track_new(authorization->manager->bus, &authorization->owner_track, authorization_lost_fn, authorization);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_add_name(authorization->owner_track, sender);
+ if (r < 0)
+ return r;
+
+ authorization->owner_busid = strdup(sender);
+ if (!authorization->owner_busid)
+ return -ENOMEM;
+
+ /* on blocking requests, we track the message so we can send a reply */
+ if (block_unique) {
+ authorization->owner_unique = strjoin(sender, "/", block_unique, NULL);
+ if (!authorization->owner_unique)
+ return -ENOMEM;
+
+ authorization->owner_msg = sd_bus_message_ref(m);
+
+ r = hashmap_ensure_allocated(&authorization->manager->authorization_by_unique, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(authorization->manager->authorization_by_unique, authorization->owner_unique, authorization);
+ if (r < 0)
+ return r;
+ }
+
+ /*
+ * Now that the authorization object is fully prepared, we schedule the
+ * execution function and notify dbus listeners. We defer the execution
+ * so dbus events are always sent in correct order for non-blocking and
+ * blocking calls.
+ */
+
+ /* defer authorization so the dbus events are correctly ordered */
+ r = sd_event_add_defer(authorization->manager->event, &authorization->run_src, authorization_run_fn, authorization);
+ if (r < 0)
+ return r;
+
+ authorization->started = true;
+
+ r = manager_bus_emit_authorization_new(authorization->manager, authorization);
+ if (r < 0) {
+ authorization_stop(authorization, r);
+ return r;
+ }
+
+ authorization->live = true;
+
+ return 0;
+}
+
+void authorization_stop(Authorization *authorization, int ret) {
+ Transaction *transaction;
+ int r;
+
+ assert(authorization);
+
+ if (authorization->stopped)
+ return;
+
+ authorization->stopped = true;
+
+ /* drop transactions */
+ while ((transaction = hashmap_first(authorization->transaction_map)))
+ ;//transaction_free(transaction);
+
+ /* notify bus listeners */
+ if (authorization->live) {
+ r = manager_bus_emit_authorization_removed(authorization->manager, authorization);
+ if (r < 0)
+ log_warning_errno(r, "Cannot emit AuthorizationRemoved signal: %m");
+ }
+
+ /* send reply for blocking requests */
+ if (authorization->owner_unique) {
+ if (authorization->live) {
+ if (ret < 0)
+ sd_bus_reply_method_errno(authorization->owner_msg, ret, NULL);
+ else
+ sd_bus_reply_method_return(authorization->owner_msg, NULL);
+ }
+
+ authorization->owner_msg = sd_bus_message_unref(authorization->owner_msg);
+
+ hashmap_remove_value(authorization->manager->authorization_by_unique, authorization->owner_unique, authorization);
+ free(authorization->owner_unique);
+ authorization->owner_unique = NULL;
+ }
+
+ authorization->run_src = sd_event_source_unref(authorization->run_src);
+ free(authorization->owner_busid);
+ authorization->owner_busid = NULL;
+ authorization->owner_track = sd_bus_track_unref(authorization->owner_track);
+ authorization->agent = NULL;
+
+ authorization->live = false;
+}
+
+Authorization *authorization_lookup(Manager *m, const char *sender, const char *block_unique) {
+ _cleanup_free_ char *key = NULL;
+
+ assert(m);
+ assert(sender);
+ assert(block_unique);
+
+ key = strjoin(sender, "/", block_unique, NULL);
+ if (!key)
+ return NULL;
+
+ return hashmap_get(m->authorization_by_unique, key);
+}
diff --git a/src/authority/authorityd-dbus.c b/src/authority/authorityd-dbus.c
new file mode 100644
index 000000000..e923ab462
--- /dev/null
+++ b/src/authority/authorityd-dbus.c
@@ -0,0 +1,492 @@
+/*-*- 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 char *factor_bus_path(Factor *factor) {
+ char *path;
+ int r;
+
+ assert(factor);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/factor", factor->id, &path);
+ if (r < 0)
+ return NULL;
+
+ return path;
+}
+
+static int factor_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;
+ Factor *factor;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/factor", &id);
+ if (r <= 0)
+ return r;
+
+ factor = hashmap_get(m->factor_map, id);
+ if (!factor)
+ return 0;
+
+ *found = factor;
+ return 1;
+}
+
+static int factor_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;
+ Factor *factor;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(factor, m->factor_map, i) {
+ char *s;
+
+ s = factor_bus_path(factor);
+ 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 factor_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ //SD_BUS_PROPERTY("UID", "u", authorization_bus_get_uid, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Subject", "...", authorization_bus_get_subject, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("Action", "...", authorization_bus_get_action, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("PrimaryAgent", "o", authorization_bus_get_primary_agent, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Agents", "ao", authorization_bus_get_agents, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Factors", "ao", authorization_bus_get_factors, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_SIGNAL("Done", "i", 0),
+ SD_BUS_VTABLE_END
+};
+
+static int authorization_bus_cancel(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ Authorization *authorization = userdata;
+ const char *sender;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender || !streq_ptr(sender, authorization->owner_busid))
+ return -EPERM;
+
+ authorization_stop(authorization, -ECANCELED);
+ authorization_free(authorization);
+
+ return 0;
+}
+
+static char *authorization_bus_path(Authorization *authorization) {
+ char *path;
+ int r;
+
+ assert(authorization);
+
+ r = sd_bus_path_encode("/org/freedesktop/authority1/authorization", authorization->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 *authorization;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/authority1/authorization", &id);
+ if (r <= 0)
+ return r;
+
+ authorization = hashmap_get(m->authorization_map, id);
+ if (!authorization)
+ return 0;
+
+ *found = authorization;
+ 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 *authorization;
+ Iterator i;
+ int r;
+
+ HASHMAP_FOREACH(authorization, m->authorization_map, i) {
+ char *s;
+
+ s = authorization_bus_path(authorization);
+ 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("UID", "u", authorization_bus_get_uid, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Subject", "...", authorization_bus_get_subject, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("Action", "...", authorization_bus_get_action, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ //SD_BUS_PROPERTY("PrimaryAgent", "o", authorization_bus_get_primary_agent, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Agents", "ao", authorization_bus_get_agents, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ //SD_BUS_PROPERTY("Factors", "ao", authorization_bus_get_factors, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_METHOD("Cancel", "i", NULL, authorization_bus_cancel, 0),
+ //SD_BUS_SIGNAL("Done", "i", 0),
+ SD_BUS_VTABLE_END
+};
+
+#if 0
+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);
+}
+#endif
+
+static int manager_bus_parse_authorization_request(Manager *manager, Authorization **out, sd_bus *bus, sd_bus_message *m) {
+ _cleanup_(authorization_freep) Authorization *authorization = NULL;
+ _cleanup_(subject_unrefp) Subject *subject = NULL;
+ _cleanup_(action_unrefp) Action *action = NULL;
+ const char *flag;
+ int r;
+
+ /* parse subject */
+
+ r = subject_new(&subject);
+ if (r < 0)
+ return r;
+
+ r = subject_read(subject, m);
+ if (r < 0)
+ return r;
+
+ /* parse action */
+
+ r = action_new(&action);
+ if (r < 0)
+ return r;
+
+ r = action_read(action, m);
+ if (r < 0)
+ return r;
+
+ /* create authorization object */
+
+ r = authorization_new(&authorization, manager);
+ if (r < 0)
+ return r;
+
+ authorization->subject = subject;
+ subject = NULL;
+ authorization->action = action;
+ action = 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"))
+ authorization->interactive = true;
+ else
+ return -EOPNOTSUPP;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ *out = authorization;
+ authorization = 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 *authorization = NULL;
+ _cleanup_free_ char *path = NULL;
+ Manager *manager = userdata;
+ int r;
+
+ r = manager_bus_parse_authorization_request(manager, &authorization, bus, m);
+ if (r < 0)
+ return r;
+
+ path = authorization_bus_path(authorization);
+ if (!path)
+ return -ENOMEM;
+
+ r = authorization_start(authorization, m, NULL);
+ if (r < 0)
+ return r;
+
+ authorization = NULL;
+
+ return sd_bus_reply_method_return(m, "o", path);
+}
+
+int manager_bus_emit_authorization_new(Manager *m, Authorization *authorization)
+{
+ _cleanup_free_ char *path = NULL;
+
+ assert(m);
+ assert(authorization);
+
+ path = authorization_bus_path(authorization);
+ if (!path)
+ return -ENOMEM;
+
+ return sd_bus_emit_signal(m->bus,
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "AuthorizationNew",
+ "o",
+ path);
+}
+
+int manager_bus_emit_authorization_removed(Manager *m, Authorization *authorization)
+{
+ _cleanup_free_ char *path = NULL;
+
+ assert(m);
+ assert(authorization);
+
+ path = authorization_bus_path(authorization);
+ if (!path)
+ return -ENOMEM;
+
+ return sd_bus_emit_signal(m->bus,
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "AuthorizationRemoved",
+ "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 *authorization = NULL;
+ Manager *manager = userdata;
+ const char *unique;
+ int r;
+
+ r = manager_bus_parse_authorization_request(manager, &authorization, 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;
+
+ r = authorization_start(authorization, m, unique);
+ if (r < 0)
+ return r;
+
+ authorization = 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 *sender, *unique;
+ Authorization *authorization;
+ int r;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ r = sd_bus_message_read(m, "s", &unique);
+ if (r < 0)
+ return r;
+ if (strlen(unique) > 255)
+ return -EINVAL;
+
+ authorization = authorization_lookup(manager, sender, unique);
+ if (!authorization)
+ return -ENOENT;
+
+ authorization_stop(authorization, -ECANCELED);
+ authorization_free(authorization);
+
+ return 0;
+}
+
+static const sd_bus_vtable manager_bus_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ /* Factor */
+ //SD_BUS_METHOD("NewFactor", "a{sv}", "o", manager_bus_new_factor, 0),
+ //SD_BUS_METHOD("RemoveFactor", "o", NULL, manager_bus_remove_factor, 0),
+ //SD_BUS_METHOD("ListFactors", NULL, "a(s)", manager_bus_list_factors, SD_BUS_VTABLE_UNPRIVILEGED),
+
+ /* Agent */
+ //SD_BUS_METHOD("NewAgent", "a{sv}", "o", manager_bus_new_agent, SD_BUS_VTABLE_UNPRIVILEGED),
+ //SD_BUS_METHOD("RemoveAgent", "o", NULL, manager_bus_remove_agent, SD_BUS_VTABLE_UNPRIVILEGED),
+
+ /* Authorization */
+ SD_BUS_METHOD("RequestAuthorization", "a{sv}a{sv}ass", NULL, 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;
+
+ r = sd_bus_add_object_manager(m->bus,
+ NULL,
+ "/org/freedesktop/authority1");
+ 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;
+
+ /* factor */
+
+ r = sd_bus_add_fallback_vtable(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/factor",
+ "org.freedesktop.authority1.Factor",
+ factor_bus_vtable,
+ factor_bus_find,
+ m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_node_enumerator(m->bus,
+ NULL,
+ "/org/freedesktop/authority1/factor",
+ factor_bus_enumerate,
+ m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
diff --git a/src/authority/authorityd-factor.c b/src/authority/authorityd-factor.c
new file mode 100644
index 000000000..d440b5671
--- /dev/null
+++ b/src/authority/authorityd-factor.c
@@ -0,0 +1,73 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 factor_new(Factor **out, Manager *m) {
+ _cleanup_(factor_freep) Factor *f = NULL;
+ int r;
+
+ assert_return(out, -EINVAL);
+
+ f = new0(Factor, 1);
+ if (!f)
+ return -ENOMEM;
+
+ f->manager = m;
+ sprintf(f->id, "%" PRIu64, ++m->factor_ids);
+
+ r = hashmap_ensure_allocated(&m->factor_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->factor_map, f->id, f);
+ if (r < 0)
+ return r;
+
+ *out = f;
+ f = NULL;
+ return 0;
+}
+
+Factor *factor_free(Factor *f) {
+ if (!f)
+ return NULL;
+
+ hashmap_remove_value(f->manager->factor_map, f->id, f);
+ free(f);
+
+ return NULL;
+}
+
+int factor_attach(Factor *f, Authorization *auth, bool interactive) {
+ return -EINVAL;
+}
+
+void factor_detach(Factor *f, Authorization *auth) {
+}
diff --git a/src/authority/authorityd-manager.c b/src/authority/authorityd-manager.c
new file mode 100644
index 000000000..b8da984dd
--- /dev/null
+++ b/src/authority/authorityd-manager.c
@@ -0,0 +1,162 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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(out);
+
+ m = new0(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ 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, SIGQUIT, 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, SIGQUIT, 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 (!system_level) {
+ r = sd_path_home(manager_dirs[i].type, manager_dirs[i].suffix, &m->dirs[pos++]);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (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) {
+ Authorization *authorization;
+ Factor *factor;
+ Agent *agent;
+
+ if (!m)
+ return NULL;
+
+ while ((authorization = hashmap_first(m->authorization_map)))
+ authorization_free(authorization);
+
+ while ((factor = hashmap_first(m->factor_map)))
+ factor_free(factor);
+
+ while ((agent = hashmap_first(m->agent_map)))
+ agent_free(agent);
+
+ assert(hashmap_isempty(m->authorization_by_unique));
+ assert(hashmap_isempty(m->agent_by_owner));
+ assert(hashmap_isempty(m->authorization_map));
+ assert(hashmap_isempty(m->factor_map));
+ assert(hashmap_isempty(m->agent_map));
+
+ m->authorization_by_unique = hashmap_free(m->authorization_by_unique);
+ m->agent_by_owner = hashmap_free(m->agent_by_owner);
+ m->authorization_map = hashmap_free(m->authorization_map);
+ m->factor_map = hashmap_free(m->factor_map);
+ m->agent_map = hashmap_free(m->agent_map);
+ m->dirs = strv_free(m->dirs);
+ 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 sd_event_loop(m->event);
+}
diff --git a/src/authority/authorityd-subject.c b/src/authority/authorityd-subject.c
new file mode 100644
index 000000000..337d7eeab
--- /dev/null
+++ b/src/authority/authorityd-subject.c
@@ -0,0 +1,451 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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"
+
+assert_cc(sizeof(uid_t) >= 4);
+assert_cc(sizeof(gid_t) >= 4);
+assert_cc(sizeof(pid_t) >= 4);
+
+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;
+ if (pid <= 0)
+ return -EINVAL;
+
+ 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;
+ if (tid <= 0)
+ return -EINVAL;
+
+ 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;
+ if (euid == UID_INVALID)
+ return -EINVAL;
+
+ 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;
+ if (fsuid == UID_INVALID)
+ return -EINVAL;
+
+ 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;
+ if (egid == GID_INVALID)
+ return -EINVAL;
+
+ 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;
+ if (fsgid == GID_INVALID)
+ return -EINVAL;
+
+ s->fsgid = fsgid;
+ } else if (!strcmp(key, "supplementary-gids")) {
+ const uint32_t *gids;
+ size_t i, n, size;
+ gid_t *t;
+
+ 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;
+
+ n = size / sizeof(*gids);
+ for (i = 0; i < n; ++i)
+ if (gids[i] == GID_INVALID)
+ return -EINVAL;
+
+ t = new(gid_t, n);
+ if (!t)
+ return -ENOMEM;
+
+ for (i = 0; i < n; ++i)
+ t[i] = gids[i];
+
+ free(s->supplementary_gids);
+ s->supplementary_gids = t;
+ s->n_supplementary_gids = n;
+ } 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 1;
+}
+
+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 && s->pid <= INT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "pid", "i", s->pid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->tid > 0 && s->tid <= INT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "tid", "i", s->tid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->euid != UID_INVALID && s->euid <= UINT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "euid", "u", s->euid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->fsuid != UID_INVALID && s->fsuid <= UINT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "fsuid", "u", s->fsuid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->egid != GID_INVALID && s->egid <= UINT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "egid", "u", s->egid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->fsgid != GID_INVALID && s->fsgid <= UINT32_MAX) {
+ r = sd_bus_message_append(m, "e", "sv", "fsgid", "u", s->fsgid);
+ if (r < 0)
+ return r;
+ }
+
+ if (s->supplementary_gids) {
+ size_t i;
+
+ 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_open_container(m, 'a', "u");
+ if (r < 0)
+ return r;
+
+ for (i = 0; i < s->n_supplementary_gids; ++i) {
+ gid_t g = s->supplementary_gids[i];
+
+ if (g != GID_INVALID && g <= UINT32_MAX) {
+ r = sd_bus_message_append(m, "u", g);
+ 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;
+
+ 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);
+}
+
+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..17cba2570
--- /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-2015 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, false);
+ if (r < 0) {
+ log_error_errno(r, "Could not create manager: %m");
+ goto out;
+ }
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing requests...");
+
+ r = manager_run(m);
+ if (r < 0) {
+ log_error_errno(r, "Cannot run manager: %m");
+ 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..686ee57c5
--- /dev/null
+++ b/src/authority/authorityd.h
@@ -0,0 +1,197 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 <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 Action Action;
+typedef struct Subject Subject;
+typedef struct Agent Agent;
+typedef struct Transaction Transaction;
+typedef struct Factor Factor;
+typedef struct Authorization Authorization;
+typedef struct Manager Manager;
+
+/* action */
+
+struct Action {
+ unsigned long ref;
+
+ char *action_id;
+};
+
+int action_new(Action **out);
+Action *action_ref(Action *action);
+Action *action_unref(Action *action);
+int action_read(Action *action, sd_bus_message *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Action*, action_unref);
+
+/* subject */
+
+struct Subject {
+ unsigned long ref;
+
+ pid_t pid;
+ pid_t tid;
+ uid_t euid;
+ uid_t fsuid;
+ gid_t egid;
+ gid_t fsgid;
+ gid_t *supplementary_gids;
+ size_t n_supplementary_gids;
+ char *session_id;
+ char *unique_name;
+ char **well_known_names;
+};
+
+int subject_new(Subject **out);
+Subject *subject_ref(Subject *subject);
+Subject *subject_unref(Subject *subject);
+int subject_read(Subject *subject, sd_bus_message *m);
+int subject_append(Subject *subject, sd_bus_message *m);
+bool subject_is_superset(Subject *subject, Subject *subset);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Subject*, subject_unref);
+
+/* agent */
+
+struct Agent {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ sd_bus_track *owner_track;
+ char *owner_busid;
+
+ Set *subjects;
+};
+
+int agent_new(Agent **out, Manager *m, const char *sender);
+Agent *agent_free(Agent *agent);
+int agent_read(Agent *agent, sd_bus_message *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Agent*, agent_free);
+
+/* transaction */
+
+struct Transaction {
+ Authorization *authorization;
+ Factor *factor;
+};
+
+int transaction_new(Transaction **out, Authorization *authorization, Factor *factor);
+Transaction *transaction_free(Transaction *transaction);
+
+/* factor */
+
+struct Factor {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ Hashmap *transaction_map;
+
+ bool interactive : 1;
+ bool non_interactive : 1;
+};
+
+int factor_new(Factor **out, Manager *m);
+Factor *factor_free(Factor *factor);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Factor*, factor_free);
+
+/* authorization */
+
+enum {
+ AUTHORIZATION_RUN_TEMPORARY,
+ AUTHORIZATION_RUN_RULES,
+ AUTHORIZATION_RUN_NON_INTERACTIVE,
+ AUTHORIZATION_RUN_INTERACTIVE,
+};
+
+struct Authorization {
+ Manager *manager;
+ char id[DECIMAL_STR_MAX(uint64_t)];
+
+ Hashmap *transaction_map;
+
+ uid_t uid;
+ Subject *subject;
+ Action *action;
+
+ sd_bus_track *owner_track;
+ char *owner_busid;
+ char *owner_unique;
+ sd_bus_message *owner_msg;
+ sd_event_source *run_src;
+ unsigned int run_state;
+ Agent *agent;
+
+ bool interactive : 1;
+ bool started : 1;
+ bool stopped : 1;
+ bool live : 1;
+};
+
+int authorization_new(Authorization **out, Manager *m);
+Authorization *authorization_free(Authorization *auth);
+int authorization_start(Authorization *auth, sd_bus_message *m, const char *block_unique);
+void authorization_stop(Authorization *auth, int ret);
+Authorization *authorization_lookup(Manager *m, const char *sender, const char *block_unique);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Authorization*, authorization_free);
+
+/* manager */
+
+struct Manager {
+ sd_event *event;
+ sd_bus *bus;
+ char **dirs;
+
+ Hashmap *agent_map;
+ Hashmap *factor_map;
+ Hashmap *authorization_map;
+ uint64_t agent_ids;
+ uint64_t factor_ids;
+ uint64_t authorization_ids;
+
+ Hashmap *agent_by_owner;
+ Hashmap *authorization_by_unique;
+};
+
+int manager_new(Manager **out, bool system_level);
+Manager *manager_free(Manager *m);
+int manager_bus_init(Manager *m);
+int manager_bus_emit_authorization_new(Manager *m, Authorization *authorization);
+int manager_bus_emit_authorization_removed(Manager *m, Authorization *authorization);
+int manager_run(Manager *m);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
diff --git a/src/shared/authority-agent.c b/src/shared/authority-agent.c
new file mode 100644
index 000000000..80fa2fb48
--- /dev/null
+++ b/src/shared/authority-agent.c
@@ -0,0 +1,954 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "authority-agent.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "sd-bus.h"
+#include "util.h"
+
+typedef struct AuthorityPrompt AuthorityPrompt;
+typedef struct AuthorityTransaction AuthorityTransaction;
+typedef struct AuthorityFactor AuthorityFactor;
+typedef struct AuthorityAuthorization AuthorityAuthorization;
+
+struct AuthorityPrompt {
+ char prompt[LINE_MAX];
+ char input[LINE_MAX];
+ size_t length;
+ bool hidden : 1;
+ bool valid : 1;
+};
+
+struct AuthorityTransaction {
+ AuthorityAuthorization *authorization;
+ AuthorityFactor *factor;
+
+ AuthorityPrompt prompt;
+
+ bool stray : 1;
+ bool shown : 1;
+};
+
+struct AuthorityFactor {
+ AuthorityAgent *agent;
+ char *path;
+
+ Hashmap *transaction_map;
+
+ char *name;
+};
+
+struct AuthorityAuthorization {
+ AuthorityAgent *agent;
+ char *path;
+
+ OrderedHashmap *transaction_map;
+ AuthorityTransaction *active;
+};
+
+struct AuthorityAgent {
+ int ttyin;
+ int ttyout;
+ sd_bus *bus;
+ sd_event_source *src_tty;
+ sd_bus_slot *slot_interfaces_added;
+ sd_bus_slot *slot_interfaces_removed;
+ sd_bus_slot *slot_properties_changed;
+ sd_bus_slot *slot_prompt;
+ sd_bus_slot *slot_get_managed_objects;
+ sd_bus_slot *slot_new_agent;
+
+ OrderedHashmap *authorization_map;
+ Hashmap *factor_map;
+ AuthorityAuthorization *active;
+ AuthorityPrompt *prompt;
+ char *agent_path;
+
+ bool allow_draft : 1;
+};
+
+static bool authority_transaction_is_active(AuthorityTransaction *transaction);
+static void authority_transaction_show(AuthorityTransaction *transaction);
+static void authority_transaction_hide(AuthorityTransaction *transaction);
+static bool authority_authorization_is_active(AuthorityAuthorization *authorization);
+static void authority_agent_draft(AuthorityAgent *agent);
+
+static bool ui_has_prompt(AuthorityAgent *agent) {
+ return agent->prompt;
+}
+
+static void ui_show_prompt(AuthorityAgent *agent) {
+ static const char asterisk[] = "****************";
+
+ if (!ui_has_prompt(agent))
+ return;
+
+ loop_write(agent->ttyout, agent->prompt->prompt, strlen(agent->prompt->prompt), true);
+
+ if (agent->prompt->hidden) {
+ size_t i, t;
+
+ for (i = 0; i < agent->prompt->length; i += t) {
+ t = MIN(agent->prompt->length - i, sizeof(asterisk - 1));
+ loop_write(agent->ttyout, asterisk, t, true);
+ }
+ } else {
+ loop_write(agent->ttyout, agent->prompt->input, agent->prompt->length, true);
+ }
+}
+
+static void ui_hide_prompt(AuthorityAgent *agent) {
+ /* erase line and carriage return */
+ if (ui_has_prompt(agent))
+ loop_write(agent->ttyout, "\e[2K\r", 5, true);
+}
+
+static void ui_print(AuthorityAgent *agent, const char *format, ...) {
+ char message[LINE_MAX + 1];
+ va_list list;
+ int len;
+
+ ui_hide_prompt(agent);
+
+ va_start(list, format);
+ len = vsnprintf(message, LINE_MAX, format, list);
+ va_end(list);
+
+ if (len >= LINE_MAX)
+ len = LINE_MAX - 1;
+
+ if (len == 0 || message[len - 1] != '\n') {
+ message[len++] = '\n';
+ message[len] = 0;
+ }
+
+ loop_write(agent->ttyout, message, len, true);
+
+ ui_show_prompt(agent);
+}
+
+static bool authority_transaction_is_active(AuthorityTransaction *transaction) {
+ return transaction && transaction->authorization->active == transaction;
+}
+
+static AuthorityTransaction *authority_transaction_free(AuthorityTransaction *transaction) {
+ if (!transaction)
+ return NULL;
+
+ hashmap_remove_value(transaction->factor->transaction_map, transaction->authorization->path, transaction);
+ ordered_hashmap_remove_value(transaction->authorization->transaction_map, transaction->factor->path, transaction);
+
+ /* if active, deselect ourself and draft next */
+ if (authority_transaction_is_active(transaction)) {
+ if (authority_authorization_is_active(transaction->authorization))
+ authority_transaction_hide(transaction);
+ transaction->authorization->active = NULL;
+ authority_agent_draft(transaction->authorization->agent);
+ }
+
+ assert(!transaction->shown);
+
+ free(transaction);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(AuthorityTransaction*, authority_transaction_free);
+
+static int authority_transaction_new(AuthorityTransaction **out, AuthorityAuthorization *authorization, AuthorityFactor *factor) {
+ _cleanup_(authority_transaction_freep) AuthorityTransaction *transaction = NULL;
+ int r;
+
+ assert(out);
+ assert(authorization);
+ assert(factor);
+
+ transaction = new0(AuthorityTransaction, 1);
+ if (!transaction)
+ return -ENOMEM;
+
+ transaction->authorization = authorization;
+ transaction->factor = factor;
+
+ r = ordered_hashmap_ensure_allocated(&authorization->transaction_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&factor->transaction_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(authorization->transaction_map, factor->path, transaction);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(factor->transaction_map, authorization->path, transaction);
+ if (r < 0)
+ return r;
+
+ *out = transaction;
+ transaction = NULL;
+ return 0;
+}
+
+static void authority_transaction_show(AuthorityTransaction *transaction) {
+ assert(transaction);
+ assert(authority_transaction_is_active(transaction));
+ assert(authority_authorization_is_active(transaction->authorization));
+ assert(!transaction->shown);
+
+ transaction->shown = true;
+
+ if (transaction->prompt.valid) {
+ AuthorityAgent *agent = transaction->authorization->agent;
+
+ agent->prompt = &transaction->prompt;
+ ui_show_prompt(agent);
+ }
+}
+
+static void authority_transaction_hide(AuthorityTransaction *transaction) {
+ assert(transaction);
+ assert(authority_transaction_is_active(transaction));
+ assert(authority_authorization_is_active(transaction->authorization));
+ assert(transaction->shown);
+
+ transaction->shown = false;
+
+ if (transaction->prompt.valid) {
+ AuthorityAgent *agent = transaction->authorization->agent;
+
+ ui_hide_prompt(agent);
+ agent->prompt = NULL;
+ }
+}
+
+static void authority_transaction_prompt(AuthorityTransaction *transaction, sd_bus_message *m) {
+ AuthorityPrompt *prompt;
+ AuthorityAgent *agent;
+
+ assert(transaction);
+
+ agent = transaction->authorization->agent;
+ prompt = &transaction->prompt;
+ prompt->valid = true;
+ prompt->hidden = false;
+ prompt->input[0] = 0;
+ strcpy(prompt->prompt, "Prompt: ");
+
+ if (transaction->shown) {
+ ui_hide_prompt(agent);
+ agent->prompt = &transaction->prompt;
+ ui_show_prompt(agent);
+ } else {
+ /* we gained a prompt and thus might get drafted */
+ authority_agent_draft(transaction->authorization->agent);
+ }
+}
+
+static void authority_transaction_feed(AuthorityTransaction *transaction, char c) {
+ AuthorityAgent *agent;
+
+ assert(transaction);
+
+ agent = transaction->authorization->agent;
+
+ switch (c) {
+ case '\n':
+ if (!transaction->prompt.valid)
+ break;
+
+ if (transaction->shown)
+ ui_hide_prompt(agent);
+
+ agent->prompt = NULL;
+ transaction->prompt.valid = false;
+
+ break;
+ default:
+ if (!transaction->prompt.valid)
+ break;
+
+ break;
+ }
+}
+
+static AuthorityFactor *authority_factor_free(AuthorityFactor *factor) {
+ AuthorityTransaction *transaction;
+
+ if (!factor)
+ return NULL;
+
+ hashmap_remove_value(factor->agent->factor_map, factor->path, factor);
+
+ while ((transaction = hashmap_first(factor->transaction_map)))
+ authority_transaction_free(transaction);
+
+ hashmap_free(factor->transaction_map);
+ free(factor->name);
+ free(factor->path);
+ free(factor);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(AuthorityFactor*, authority_factor_free);
+
+static int authority_factor_new(AuthorityFactor **out, AuthorityAgent *agent, const char *path) {
+ _cleanup_(authority_factor_freep) AuthorityFactor *factor = NULL;
+ int r;
+
+ assert(out);
+ assert(agent);
+ assert(path);
+
+ factor = new0(AuthorityFactor, 1);
+ if (!factor)
+ return -ENOMEM;
+
+ factor->agent = agent;
+
+ factor->path = strdup(path);
+ if (!factor->path)
+ return -ENOMEM;
+
+ r = hashmap_ensure_allocated(&agent->factor_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(agent->factor_map, factor->path, factor);
+ if (r < 0)
+ return r;
+
+ *out = factor;
+ factor = NULL;
+ return 0;
+}
+
+static int authority_factor_properties_changed(AuthorityFactor *factor, sd_bus_message *m) {
+ static const struct bus_properties_map map[] = {
+ { "Name", "s", NULL, offsetof(AuthorityFactor, name) },
+ { },
+ };
+
+ return bus_message_map_all_properties(factor->agent->bus, m, map, factor);
+}
+
+static bool authority_authorization_is_active(AuthorityAuthorization *authorization) {
+ return authorization && authorization->agent->active == authorization;
+}
+
+static AuthorityAuthorization *authority_authorization_free(AuthorityAuthorization *authorization) {
+ AuthorityTransaction *transaction;
+
+ if (!authorization)
+ return NULL;
+
+ ordered_hashmap_remove_value(authorization->agent->authorization_map, authorization->path, authorization);
+
+ /* hide and deselect active transaction */
+ if (authority_authorization_is_active(authorization) && authorization->active)
+ authority_transaction_hide(authorization->active);
+ authorization->active = NULL;
+
+ /* if active, deselect authorization and draft */
+ if (authority_authorization_is_active(authorization)) {
+ authorization->agent->active = NULL;
+ authority_agent_draft(authorization->agent);
+ }
+
+ while ((transaction = ordered_hashmap_first(authorization->transaction_map)))
+ authority_transaction_free(transaction);
+
+ ordered_hashmap_free(authorization->transaction_map);
+ free(authorization->path);
+ free(authorization);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(AuthorityAuthorization*, authority_authorization_free);
+
+static int authority_authorization_new(AuthorityAuthorization **out, AuthorityAgent *agent, const char *path) {
+ _cleanup_(authority_authorization_freep) AuthorityAuthorization *authorization = NULL;
+ int r;
+
+ assert(out);
+ assert(agent);
+ assert(path);
+
+ authorization = new0(AuthorityAuthorization, 1);
+ if (!authorization)
+ return -ENOMEM;
+
+ authorization->agent = agent;
+
+ authorization->path = strdup(path);
+ if (!authorization->path)
+ return -ENOMEM;
+
+ r = ordered_hashmap_ensure_allocated(&agent->authorization_map, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(agent->authorization_map, authorization->path, authorization);
+ if (r < 0)
+ return r;
+
+ *out = authorization;
+ authorization = NULL;
+ return 0;
+}
+
+static int authority_authorization_get_factors_fn(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ AuthorityAuthorization *authorization = userdata;
+ AuthorityTransaction *transaction;
+ const char *object;
+ Iterator iter;
+ int r;
+
+ ORDERED_HASHMAP_FOREACH(transaction, authorization->transaction_map, iter)
+ transaction->stray = true;
+
+ r = sd_bus_message_enter_container(m, 'a', "o");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "o", &object)) > 0) {
+ AuthorityFactor *factor;
+
+ factor = hashmap_get(authorization->agent->factor_map, object);
+ if (!factor) {
+ r = authority_factor_new(&factor, authorization->agent, object);
+ if (r < 0)
+ return r;
+ }
+
+ transaction = hashmap_get(factor->transaction_map, authorization->path);
+ if (transaction) {
+ transaction->stray = false;
+ } else {
+ r = authority_transaction_new(&transaction, authorization, factor);
+ if (r < 0)
+ return r;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ /* disable drafting temporarily to 'batch-remove' old transactions */
+ authorization->agent->allow_draft = false;
+
+ ORDERED_HASHMAP_FOREACH(transaction, authorization->transaction_map, iter)
+ if (transaction->stray)
+ authority_transaction_free(transaction);
+
+ authorization->agent->allow_draft = true;
+ authority_agent_draft(authorization->agent);
+
+ return 0;
+}
+
+static int authority_authorization_properties_changed(AuthorityAuthorization *authorization, sd_bus_message *m) {
+ static const struct bus_properties_map map[] = {
+ { "Factors", "ao", authority_authorization_get_factors_fn, 0 },
+ { },
+ };
+
+ return bus_message_map_all_properties(authorization->agent->bus, m, map, authorization);
+}
+
+static void authority_authorization_next(AuthorityAuthorization *authorization) {
+ AuthorityTransaction *next = NULL;
+
+ assert(authorization);
+
+ if (authorization->active)
+ next = ordered_hashmap_next(authorization->transaction_map, authorization->active->factor->path);
+ if (!next)
+ next = ordered_hashmap_first(authorization->transaction_map);
+
+ /* TODO: only select transactions with prompts */
+
+ /* bail out if nothing to do */
+ if (!next || next == authorization->active)
+ return;
+
+ if (authority_authorization_is_active(authorization) && authorization->active)
+ authority_transaction_hide(authorization->active);
+
+ authorization->active = next;
+
+ if (authority_authorization_is_active(authorization))
+ authority_transaction_show(authorization->active);
+}
+
+static void authority_agent_draft(AuthorityAgent *agent) {
+ assert(agent);
+
+ if (!agent->allow_draft)
+ return;
+
+ if (!agent->active)
+ agent->active = ordered_hashmap_first(agent->authorization_map);
+
+ if (agent->active) {
+ if (!agent->active->active)
+ agent->active->active = ordered_hashmap_first(agent->active->transaction_map);
+ if (agent->active->active && !agent->active->active->shown)
+ authority_transaction_show(agent->active->active);
+ }
+}
+
+static void authority_agent_feed(AuthorityAgent *agent, char *input, size_t len) {
+ size_t i;
+
+ assert(agent);
+
+ for (i = 0; i < len; ++i) {
+ switch (input[i]) {
+ case '\t':
+ if (agent->active)
+ authority_authorization_next(agent->active);
+ break;
+ default:
+ if (agent->active && agent->active->active)
+ authority_transaction_feed(agent->active->active, input[i]);
+ break;
+ }
+ }
+}
+
+static int authority_agent_tty_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ AuthorityAgent *agent = userdata;
+ int r = 0;
+
+ if (revents & EPOLLIN) {
+ char buf[LINE_MAX];
+ ssize_t l;
+
+ l = read(fd, buf, sizeof(buf));
+ if (l < 0) {
+ if (errno != EAGAIN)
+ r = -errno;
+ } else {
+ authority_agent_feed(agent, buf, l);
+ return 0;
+ }
+ }
+
+ if (r < 0 || revents & (EPOLLHUP | EPOLLERR)) {
+ sd_event_source_set_enabled(source, SD_EVENT_OFF);
+ errno = r < 0 ? -r : EPIPE;
+ ui_print(agent, "HUP/ERR on TTY input: %m");
+ }
+
+ return 0;
+}
+
+static int authority_agent_properties_changed(AuthorityAgent *agent, const char *object, sd_bus_message *m) {
+ const char *interface;
+ int r;
+
+ assert(agent);
+ assert(object);
+ assert(m);
+
+ r = sd_bus_message_read(m, "s", &interface);
+ if (r < 0)
+ return r;
+
+ if (streq(interface, "org.freedesktop.authority1.Authorization")) {
+ AuthorityAuthorization *authorization;
+
+ authorization = ordered_hashmap_get(agent->authorization_map, object);
+ if (!authorization) {
+ r = authority_authorization_new(&authorization, agent, object);
+ if (r < 0)
+ return r;
+ }
+
+ r = authority_authorization_properties_changed(authorization, m);
+ if (r < 0)
+ return r;
+ } else if (streq(interface, "org.freedesktop.authority1.Factor")) {
+ AuthorityFactor *factor;
+
+ factor = hashmap_get(agent->factor_map, object);
+ if (!factor) {
+ r = authority_factor_new(&factor, agent, object);
+ if (r < 0)
+ return r;
+ }
+
+ r = authority_factor_properties_changed(factor, m);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_bus_message_skip(m, "a{sv}");
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int authority_agent_interfaces_added_fn(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ AuthorityAgent *agent = userdata;
+ const char *object;
+ int r;
+
+ r = sd_bus_message_read(m, "o", &object);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, 'a', "{sa{sv}}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(m, 'e', "sa{sv}")) > 0) {
+ r = authority_agent_properties_changed(agent, object, 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 0;
+}
+
+static int authority_agent_interfaces_removed_fn(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ AuthorityAgent *agent = userdata;
+ const char *object, *interface;
+ int r;
+
+ r = sd_bus_message_read(m, "s", &object);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(m, 'a', "s");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(m, "s", &interface)) > 0) {
+ if (streq(interface, "org.freedesktop.authority1.Authorization"))
+ authority_authorization_free(ordered_hashmap_get(agent->authorization_map, object));
+ else if (streq(interface, "org.freedesktop.authority1.Factor"))
+ authority_factor_free(hashmap_get(agent->factor_map, object));
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int authority_agent_properties_changed_fn(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ AuthorityAgent *agent = userdata;
+ const char *object;
+
+ object = sd_bus_message_get_path(m);
+ if (!object)
+ return 0;
+
+ return authority_agent_properties_changed(agent, object, m);
+}
+
+static int authority_agent_prompt_fn(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ AuthorityAuthorization *authorization;
+ AuthorityTransaction *transaction;
+ AuthorityAgent *agent = userdata;
+ const char *object;
+ int r;
+
+ object = sd_bus_message_get_path(m);
+ if (!object)
+ return 0;
+
+ authorization = ordered_hashmap_get(agent->authorization_map, object);
+ if (!authorization)
+ return 0;
+
+ r = sd_bus_message_read(m, "o", &object);
+ if (r < 0)
+ return r;
+
+ transaction = ordered_hashmap_get(authorization->transaction_map, object);
+ if (transaction)
+ authority_transaction_prompt(transaction, m);
+
+ return 0;
+}
+
+static int authority_agent_get_managed_objects_fn(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+ const sd_bus_error *error = NULL;
+ AuthorityAgent *agent = userdata;
+ int r;
+
+ agent->slot_get_managed_objects = sd_bus_slot_unref(agent->slot_get_managed_objects);
+
+ if (sd_bus_message_is_method_error(reply, NULL)) {
+ error = sd_bus_message_get_error(reply);
+ return -sd_bus_error_get_errno(error);
+ }
+
+ r = sd_bus_message_enter_container(reply, 'a', "{oa{sa{sv}}}");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_enter_container(reply, 'e', "oa{sa{sv}}")) > 0) {
+ r = authority_agent_interfaces_added_fn(bus, reply, userdata, ret_error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int authority_agent_new_agent_fn(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+ const sd_bus_error *error = NULL;
+ AuthorityAgent *agent = userdata;
+ const char *path;
+ int r;
+
+ agent->slot_new_agent = sd_bus_slot_unref(agent->slot_new_agent);
+
+ if (sd_bus_message_is_method_error(reply, NULL)) {
+ error = sd_bus_message_get_error(reply);
+ r = -sd_bus_error_get_errno(error);
+ goto error;
+ }
+
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ goto error;
+
+ agent->agent_path = strdup(path);
+ if (!agent->agent_path) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ agent->allow_draft = true;
+ authority_agent_draft(agent);
+ return 0;
+
+error:
+ ui_print(agent, "Cannot register agent: %s", bus_error_message(error, r));
+ return r;
+}
+
+int authority_agent_new(AuthorityAgent **out, int ttyin, int ttyout, sd_bus *bus) {
+ _cleanup_(authority_agent_freep) AuthorityAgent *agent = NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ sd_event *event;
+ int r;
+
+ assert(out);
+ assert(ttyin >= 0);
+ assert(ttyout >= 0);
+ assert(bus);
+
+ if (!isatty(ttyin) || !isatty(ttyout))
+ return -ENOTTY;
+
+ event = sd_bus_get_event(bus);
+ if (!event)
+ return -EINVAL;
+
+ agent = new0(AuthorityAgent, 1);
+ if (!agent)
+ return -ENOMEM;
+
+ agent->ttyin = ttyin;
+ agent->ttyout = ttyout;
+ agent->bus = sd_bus_ref(bus);
+
+ r = sd_event_add_io(event, &agent->src_tty, ttyin, EPOLLIN, authority_agent_tty_fn, agent);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(agent->bus,
+ &agent->slot_interfaces_added,
+ "type='signal',"
+ "sender='org.freedesktop.authority1',"
+ "interface='org.freedesktop.DBus.ObjectManager',"
+ "member='InterfacesAdded'",
+ authority_agent_interfaces_added_fn,
+ agent);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(agent->bus,
+ &agent->slot_interfaces_removed,
+ "type='signal',"
+ "sender='org.freedesktop.authority1',"
+ "interface='org.freedesktop.DBus.ObjectManager',"
+ "member='InterfacesRemoved'",
+ authority_agent_interfaces_removed_fn,
+ agent);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(agent->bus,
+ &agent->slot_properties_changed,
+ "type='signal',"
+ "sender='org.freedesktop.authority1',"
+ "interface='org.freedesktop.DBus.Properties',"
+ "member='PropertiesChanged',"
+ "arg1='org.freedesktop.authority1.Authorization'",
+ authority_agent_properties_changed_fn,
+ agent);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_add_match(agent->bus,
+ &agent->slot_prompt,
+ "type='signal',"
+ "sender='org.freedesktop.authority1',"
+ "interface='org.freedesktop.authority1.Authorization',"
+ "member='Prompt'",
+ authority_agent_prompt_fn,
+ agent);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(agent->bus,
+ &m,
+ "org.freedesktop.authority1",
+ "/org/freedesktop/authority1",
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_async(agent->bus, &agent->slot_get_managed_objects, m, authority_agent_get_managed_objects_fn, agent, 0);
+ if (r < 0)
+ return r;
+
+ m = sd_bus_message_unref(m);
+
+ r = sd_bus_message_new_method_call(agent->bus,
+ &m,
+ "org.freedesktop.authority1",
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "NewAgent");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "sas", "<WIP>", 2, "self", "queue");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_async(agent->bus, &agent->slot_new_agent, m, authority_agent_new_agent_fn, agent, 0);
+ if (r < 0)
+ return r;
+
+ *out = agent;
+ agent = NULL;
+ return 0;
+}
+
+AuthorityAgent *authority_agent_free(AuthorityAgent *agent) {
+ AuthorityAuthorization *authorization;
+ AuthorityFactor *factor;
+ int r;
+
+ if (!agent)
+ return NULL;
+
+ /* If we have a registered agent or NewAgent is pending, remove it */
+ if (agent->agent_path || agent->slot_new_agent) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(agent->bus, &m,
+ "org.freedesktop.authority1",
+ "/org/freedesktop/authority1",
+ "org.freedesktop.authority1.Manager",
+ "RemoveAgent");
+ if (r >= 0) {
+ r = sd_bus_message_append(m, "o", "/org/freedesktop/authority1/agent/self");
+ if (r >= 0)
+ sd_bus_send(agent->bus, m, NULL);
+ }
+
+ if (r < 0)
+ ui_print(agent, "Cannot unregister agent: %s", bus_error_message(NULL, r));
+ }
+
+ /* disable drafting for silent shutdown */
+ agent->allow_draft = false;
+
+ /* drop all factors and authorizations */
+ while ((factor = hashmap_first(agent->factor_map)))
+ authority_factor_free(factor);
+ while ((authorization = ordered_hashmap_first(agent->authorization_map)))
+ authority_authorization_free(authorization);
+
+ assert(!agent->active);
+ assert(!agent->prompt);
+ assert(ordered_hashmap_isempty(agent->authorization_map));
+ assert(hashmap_isempty(agent->factor_map));
+
+ free(agent->agent_path);
+ hashmap_free(agent->factor_map);
+ ordered_hashmap_free(agent->authorization_map);
+ agent->slot_new_agent = sd_bus_slot_unref(agent->slot_new_agent);
+ agent->slot_get_managed_objects = sd_bus_slot_unref(agent->slot_get_managed_objects);
+ agent->slot_prompt = sd_bus_slot_unref(agent->slot_prompt);
+ agent->slot_properties_changed = sd_bus_slot_unref(agent->slot_properties_changed);
+ agent->slot_interfaces_removed = sd_bus_slot_unref(agent->slot_interfaces_removed);
+ agent->slot_interfaces_added = sd_bus_slot_unref(agent->slot_interfaces_added);
+ agent->src_tty = sd_event_source_unref(agent->src_tty);
+ agent->bus = sd_bus_unref(agent->bus);
+ free(agent);
+
+ return NULL;
+}
diff --git a/src/shared/authority-agent.h b/src/shared/authority-agent.h
new file mode 100644
index 000000000..723c514b4
--- /dev/null
+++ b/src/shared/authority-agent.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014-2015 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 <stdlib.h>
+#include "macro.h"
+#include "sd-bus.h"
+#include "util.h"
+
+typedef struct AuthorityAgent AuthorityAgent;
+
+int authority_agent_new(AuthorityAgent **out, int ttyin, int ttyout, sd_bus *bus);
+AuthorityAgent *authority_agent_free(AuthorityAgent *agent);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(AuthorityAgent*, authority_agent_free);