summaryrefslogtreecommitdiff
path: root/src/libsystemd-bus/bus-creds.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-11-28 17:50:02 +0100
committerLennart Poettering <lennart@poettering.net>2013-11-28 18:42:18 +0100
commit5b12334d35eadf1f45cc3d631fd1a2e72ffaea0a (patch)
tree55682fbecfeb705adfaf0f78fd76f5c8dc219b1b /src/libsystemd-bus/bus-creds.c
parent70f75a523b16ad495a7791d595ee3eececf75953 (diff)
bus: add new sd_bus_creds object to encapsulate process credentials
This way we can unify handling of credentials that are attached to messages, or can be queried for bus name owners or connection peers. This also adds the ability to extend incomplete credential information with data from /proc, Also, provide a convenience call that will automatically determine the most appropriate credential object for an incoming message, by using the the attached information if possible, the sending name information if available and otherwise the peer's credentials.
Diffstat (limited to 'src/libsystemd-bus/bus-creds.c')
-rw-r--r--src/libsystemd-bus/bus-creds.c796
1 files changed, 796 insertions, 0 deletions
diff --git a/src/libsystemd-bus/bus-creds.c b/src/libsystemd-bus/bus-creds.c
new file mode 100644
index 0000000000..5ca70ccafa
--- /dev/null
+++ b/src/libsystemd-bus/bus-creds.c
@@ -0,0 +1,796 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "util.h"
+#include "cgroup-util.h"
+#include "fileio.h"
+#include "audit.h"
+#include "bus-message.h"
+#include "bus-util.h"
+#include "time-util.h"
+#include "bus-creds.h"
+
+enum {
+ CAP_OFFSET_INHERITABLE = 0,
+ CAP_OFFSET_PERMITTED = 1,
+ CAP_OFFSET_EFFECTIVE = 2,
+ CAP_OFFSET_BOUNDING = 3
+};
+
+void bus_creds_done(sd_bus_creds *c) {
+ assert(c);
+
+ /* For internal bus cred structures that are allocated by
+ * something else */
+
+ free(c->session);
+ free(c->unit);
+ free(c->user_unit);
+ free(c->slice);
+
+ free(c->cmdline_array);
+}
+
+_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
+ assert_return(c, NULL);
+
+ if (c->allocated) {
+ assert(c->n_ref > 0);
+ c->n_ref++;
+ } else {
+ sd_bus_message *m;
+
+ /* If this is an embedded creds structure, then
+ * forward ref counting to the message */
+ m = container_of(c, sd_bus_message, creds);
+ sd_bus_message_ref(m);
+ }
+
+ return c;
+}
+
+_public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) {
+ assert_return(c, NULL);
+
+ if (c->allocated) {
+ assert(c->n_ref > 0);
+ c->n_ref--;
+
+ if (c->n_ref == 0) {
+ bus_creds_done(c);
+
+ free(c->comm);
+ free(c->tid_comm);
+ free(c->exe);
+ free(c->cmdline);
+ free(c->cgroup);
+ free(c->capability);
+ free(c->label);
+ free(c);
+ }
+ } else {
+ sd_bus_message *m;
+
+ m = container_of(c, sd_bus_message, creds);
+ sd_bus_message_unref(m);
+ }
+
+
+ return NULL;
+}
+
+_public_ uint64_t sd_bus_creds_get_mask(sd_bus_creds *c) {
+ assert_return(c, 0);
+
+ return c->mask;
+}
+
+sd_bus_creds* bus_creds_new(void) {
+ sd_bus_creds *c;
+
+ c = new0(sd_bus_creds, 1);
+ if (!c)
+ return NULL;
+
+ c->allocated = true;
+ c->n_ref = 1;
+ return c;
+}
+
+_public_ int sd_bus_creds_new_from_pid(pid_t pid, uint64_t mask, sd_bus_creds **ret) {
+ sd_bus_creds *c;
+ int r;
+
+ assert_return(pid >= 0, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
+ assert_return(ret, -EINVAL);
+
+ if (pid == 0)
+ pid = getpid();
+
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0) {
+ free(c);
+ return r;
+ }
+
+ /* Check if the process existed at all, in case we haven't
+ * figured that out already */
+ if (kill(pid, 0) < 0 && errno == ESRCH) {
+ sd_bus_creds_unref(c);
+ return -ESRCH;
+ }
+
+ *ret = c;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) {
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_UID, -ENODATA);
+
+ *uid = c->uid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
+ assert_return(c, -EINVAL);
+ assert_return(gid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_UID, -ENODATA);
+
+ *gid = c->gid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
+ assert_return(c, -EINVAL);
+ assert_return(pid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_PID, -ENODATA);
+
+ assert(c->pid > 0);
+ *pid = c->pid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) {
+ assert_return(c, -EINVAL);
+ assert_return(tid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_TID, -ENODATA);
+
+ assert(c->tid > 0);
+ *tid = c->tid;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec) {
+ assert_return(c, -EINVAL);
+ assert_return(usec, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_PID_STARTTIME, -ENODATA);
+
+ assert(c->pid_starttime > 0);
+ *usec = c->pid_starttime;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT, -ENODATA);
+
+ assert(c->label);
+ *ret = c->label;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_COMM, -ENODATA);
+
+ assert(c->comm);
+ *ret = c->comm;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_TID_COMM, -ENODATA);
+
+ assert(c->tid_comm);
+ *ret = c->tid_comm;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_EXE, -ENODATA);
+
+ assert(c->exe);
+ *ret = c->exe;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) {
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_CGROUP, -ENODATA);
+
+ assert(c->cgroup);
+ *ret = c->cgroup;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_UNIT, -ENODATA);
+
+ assert(c->cgroup);
+
+ if (!c->unit) {
+ r = cg_path_get_unit(c->cgroup, (char**) &c->unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->unit;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_USER_UNIT, -ENODATA);
+
+ assert(c->cgroup);
+
+ if (!c->user_unit) {
+ r = cg_path_get_user_unit(c->cgroup, (char**) &c->user_unit);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->user_unit;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_SLICE, -ENODATA);
+
+ assert(c->cgroup);
+
+ if (!c->slice) {
+ r = cg_path_get_slice(c->cgroup, (char**) &c->slice);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->slice;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) {
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_SESSION, -ENODATA);
+
+ assert(c->cgroup);
+
+ if (!c->session) {
+ r = cg_path_get_session(c->cgroup, (char**) &c->session);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = c->session;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) {
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_OWNER_UID, -ENODATA);
+
+ assert(c->cgroup);
+
+ return cg_path_get_owner_uid(c->cgroup, uid);
+}
+
+_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
+ size_t n, i;
+ const char *p;
+ bool first;
+
+ assert_return(c, -EINVAL);
+ assert_return(c->cmdline, -ESRCH);
+ assert_return(c->mask & SD_BUS_CREDS_CMDLINE, -ENODATA);
+
+ assert(c->cmdline);
+
+ for (p = c->cmdline, n = 0; p < c->cmdline + c->cmdline_length; p++)
+ if (*p == 0)
+ n++;
+
+ *(char***) &c->cmdline_array = new(char*, n + 1);
+ if (!c->cmdline_array)
+ return -ENOMEM;
+
+ for (p = c->cmdline, i = 0, first = true; p < c->cmdline + c->cmdline_length; p++) {
+ if (first)
+ c->cmdline_array[i++] = (char*) p;
+
+ first = *p == 0;
+ }
+
+ c->cmdline_array[i] = NULL;
+ *cmdline = c->cmdline_array;
+
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) {
+ assert_return(c, -EINVAL);
+ assert_return(sessionid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID, -ENODATA);
+
+ *sessionid = c->audit_session_id;
+ return 0;
+}
+
+_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
+ assert_return(c, -EINVAL);
+ assert_return(uid, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID, -ENODATA);
+
+ *uid = c->audit_login_uid;
+ return 0;
+}
+
+static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
+ size_t sz;
+
+ assert(c);
+ assert(c->capability);
+
+ sz = c->capability_size / 4;
+ if ((size_t) capability >= sz*8)
+ return 0;
+
+ return !!(c->capability[offset * sz + (capability / 8)] & (1 << (capability % 8)));
+}
+
+_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS, -ENODATA);
+
+ return has_cap(c, CAP_OFFSET_EFFECTIVE, capability);
+}
+
+_public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_PERMITTED_CAPS, -ENODATA);
+
+ return has_cap(c, CAP_OFFSET_PERMITTED, capability);
+}
+
+_public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS, -ENODATA);
+
+ return has_cap(c, CAP_OFFSET_INHERITABLE, capability);
+}
+
+_public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) {
+ assert_return(c, -EINVAL);
+ assert_return(capability >= 0, -EINVAL);
+ assert_return(c->mask & SD_BUS_CREDS_BOUNDING_CAPS, -ENODATA);
+
+ return has_cap(c, CAP_OFFSET_BOUNDING, capability);
+}
+
+static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
+ size_t sz;
+ unsigned i;
+
+ assert(c);
+ assert(p);
+
+ p += strspn(p, WHITESPACE);
+
+ sz = strlen(p);
+ if (sz % 2 != 0)
+ return -EINVAL;
+
+ sz /= 2;
+ if (!c->capability) {
+ c->capability = new0(uint8_t, sz * 4);
+ if (!c->capability)
+ return -ENOMEM;
+
+ c->capability_size = sz * 4;
+ }
+
+ for (i = 0; i < sz; i ++) {
+ int x, y;
+
+ x = unhexchar(p[i*2]);
+ y = unhexchar(p[i*2+1]);
+
+ if (x < 0 || y < 0)
+ return -EINVAL;
+
+ c->capability[offset * sz + (sz - i - 1)] = (uint8_t) x << 4 | (uint8_t) y;
+ }
+
+ return 0;
+}
+
+int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
+ uint64_t missing;
+ int r;
+
+ assert(c);
+ assert(c->allocated);
+
+ missing = mask & ~c->mask;
+ if (missing == 0)
+ return 0;
+
+ /* Try to retrieve PID from creds if it wasn't passed to us */
+ if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID))
+ pid = c->pid;
+
+ if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
+ tid = c->pid;
+
+ /* Without pid we cannot do much... */
+ if (pid <= 0)
+ return 0;
+
+ if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID |
+ SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS |
+ SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ const char *p;
+
+ p = procfs_file_alloca(pid, "status");
+
+ f = fopen(p, "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ FOREACH_LINE(line, f, return -errno) {
+ truncate_nl(line);
+
+ if (missing & SD_BUS_CREDS_UID) {
+ p = startswith(line, "Uid:");
+ if (p) {
+ unsigned long uid;
+
+ p += strspn(p, WHITESPACE);
+ if (sscanf(p, "%lu", &uid) != 1)
+ return -EIO;
+
+ c->uid = (uid_t) uid;
+ c->mask |= SD_BUS_CREDS_UID;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_GID) {
+ p = startswith(line, "Gid:");
+ if (p) {
+ unsigned long gid;
+
+ p += strspn(p, WHITESPACE);
+ if (sscanf(p, "%lu", &gid) != 1)
+ return -EIO;
+
+ c->gid = (uid_t) gid;
+ c->mask |= SD_BUS_CREDS_GID;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) {
+ p = startswith(line, "CapEff:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_PERMITTED_CAPS) {
+ p = startswith(line, "CapPrm:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_PERMITTED, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_PERMITTED_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) {
+ p = startswith(line, "CapInh:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_INHERITABLE, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS;
+ continue;
+ }
+ }
+
+ if (missing & SD_BUS_CREDS_BOUNDING_CAPS) {
+ p = startswith(line, "CapBnd:");
+ if (p) {
+ r = parse_caps(c, CAP_OFFSET_BOUNDING, p);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_BOUNDING_CAPS;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (missing & (SD_BUS_CREDS_PID_STARTTIME)) {
+ unsigned long long st;
+
+ r = get_starttime_of_pid(pid, &st);
+ if (r < 0)
+ return r;
+
+ c->pid_starttime = ((usec_t) st * USEC_PER_SEC) / (usec_t) sysconf(_SC_CLK_TCK);
+ c->mask |= SD_BUS_CREDS_PID_STARTTIME;
+ }
+
+ if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ const char *p;
+
+ p = procfs_file_alloca(pid, "attr/current");
+ r = read_one_line_file(p, &c->label);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ if (missing & SD_BUS_CREDS_COMM) {
+ r = get_process_comm(pid, &c->comm);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_COMM;
+ }
+
+ if (missing & SD_BUS_CREDS_EXE) {
+ r = get_process_exe(pid, &c->exe);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_EXE;
+ }
+
+ if (missing & SD_BUS_CREDS_CMDLINE) {
+ const char *p;
+
+ p = procfs_file_alloca(pid, "cmdline");
+ r = read_full_file(p, &c->cmdline, &c->cmdline_length);
+ if (r < 0)
+ return r;
+
+ if (c->cmdline_length == 0) {
+ free(c->cmdline);
+ c->cmdline = NULL;
+ } else
+ c->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+
+ if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) {
+ _cleanup_free_ char *p = NULL;
+
+ if (asprintf(&p, "/proc/%lu/task/%lu/comm", (unsigned long) pid, (unsigned long) tid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &c->tid_comm);
+ if (r < 0)
+ return r == -ENOENT ? -ESRCH : r;
+
+ c->mask |= SD_BUS_CREDS_TID_COMM;
+ }
+
+ if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
+
+ r = cg_pid_get_path(NULL, pid, &c->cgroup);
+ if (r < 0)
+ return r;
+
+ c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
+ }
+
+ if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
+ r = audit_session_from_pid(pid, &c->audit_session_id);
+ if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != ENOENT)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
+ }
+
+ if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
+ r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
+ if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != ENOENT)
+ return r;
+ else if (r >= 0)
+ c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_creds_extend(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
+ _cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if ((mask & ~c->mask) == 0) {
+ /* There's already all data we need. */
+
+ *ret = sd_bus_creds_ref(c);
+ return 0;
+ }
+
+ n = bus_creds_new();
+ if (!n)
+ return -ENOMEM;
+
+ /* Copy the original data over */
+
+ if (c->mask & mask & SD_BUS_CREDS_UID) {
+ n->uid = c->uid;
+ n->mask |= SD_BUS_CREDS_UID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_GID) {
+ n->gid = c->gid;
+ n->mask |= SD_BUS_CREDS_GID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_PID) {
+ n->pid = c->pid;
+ n->mask |= SD_BUS_CREDS_PID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_TID) {
+ n->tid = c->tid;
+ n->mask |= SD_BUS_CREDS_TID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_PID_STARTTIME) {
+ n->pid_starttime = c->pid_starttime;
+ n->mask |= SD_BUS_CREDS_PID_STARTTIME;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_COMM) {
+ n->comm = strdup(c->comm);
+ if (!n->comm)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_COMM;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_TID_COMM) {
+ n->tid_comm = strdup(c->tid_comm);
+ if (!n->tid_comm)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_TID_COMM;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_EXE) {
+ n->exe = strdup(c->exe);
+ if (!n->exe)
+ return -ENOMEM;
+
+ n->mask |= SD_BUS_CREDS_EXE;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_CMDLINE) {
+ n->cmdline = memdup(c->cmdline, c->cmdline_length);
+ if (!n->cmdline)
+ return -ENOMEM;
+
+ n->cmdline_length = c->cmdline_length;
+ n->mask |= SD_BUS_CREDS_CMDLINE;
+ }
+
+ if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) {
+ n->cgroup = strdup(c->cgroup);
+ if (!n->cgroup)
+ return -ENOMEM;
+
+ n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID);
+ }
+
+ if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
+ n->capability = memdup(c->capability, c->capability_size);
+ if (!n->capability)
+ return -ENOMEM;
+
+ n->capability_size = c->capability_size;
+ n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS);
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
+ n->audit_session_id = c->audit_session_id;
+ n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
+ }
+
+ if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
+ n->audit_login_uid = c->audit_login_uid;
+ n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
+ }
+
+ /* Get more data */
+
+ r = bus_creds_add_more(n, mask,
+ c->mask & SD_BUS_CREDS_PID ? c->pid : 0,
+ c->mask & SD_BUS_CREDS_TID ? c->tid : 0);
+ if (r < 0)
+ return r;
+
+ *ret = n;
+ n = NULL;
+ return 0;
+}