diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2014-12-04 18:32:55 +0100 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2014-12-04 18:32:55 +0100 |
commit | 57c72e1667ed24ae46763d69232ffd0700cf86b5 (patch) | |
tree | e79b0a20270278d09f69a28067bd6e6dd155e0cf | |
parent | 0a86c1a9d8066267b878dfeddc5e0087dda6a37b (diff) |
authority: add authorization and authentication daemonauthority
WIP
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 21 | ||||
l--------- | src/authority/Makefile | 1 | ||||
-rw-r--r-- | src/authority/authorityd-agent.c | 197 | ||||
-rw-r--r-- | src/authority/authorityd-authentication.c | 111 | ||||
-rw-r--r-- | src/authority/authorityd-authorization.c | 136 | ||||
-rw-r--r-- | src/authority/authorityd-dbus.c | 758 | ||||
-rw-r--r-- | src/authority/authorityd-manager.c | 163 | ||||
-rw-r--r-- | src/authority/authorityd-object.c | 226 | ||||
-rw-r--r-- | src/authority/authorityd-provider.c | 66 | ||||
-rw-r--r-- | src/authority/authorityd-subject.c | 510 | ||||
-rw-r--r-- | src/authority/authorityd.c | 67 | ||||
-rw-r--r-- | src/authority/authorityd.h | 197 |
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); |