Systemd/src/libsystemd/sd-bus/bus-creds.c
Lennart Poettering 0f51442056 sd-bus: when augmenting creds, remember which ones were augmented
Also, when we do permissions checks using creds, verify that we don't do
so based on augmented creds, as extra safety check.
2015-04-21 00:58:56 +02:00

1170 lines
37 KiB
C

/*-*- 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 <linux/capability.h>
#include "util.h"
#include "formats-util.h"
#include "process-util.h"
#include "capability.h"
#include "cgroup-util.h"
#include "fileio.h"
#include "audit.h"
#include "bus-message.h"
#include "bus-util.h"
#include "strv.h"
#include "bus-creds.h"
#include "bus-label.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->unescaped_description);
free(c->supplementary_gids);
free(c->well_known_names); /* note that this is an strv, but
* we only free the array, not the
* strings the array points to. The
* full strv we only free if
* c->allocated is set, see
* below. */
strv_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) {
if (!c)
return NULL;
if (c->allocated) {
assert(c->n_ref > 0);
c->n_ref--;
if (c->n_ref == 0) {
free(c->comm);
free(c->tid_comm);
free(c->exe);
free(c->cmdline);
free(c->cgroup);
free(c->capability);
free(c->label);
free(c->unique_name);
free(c->cgroup_root);
free(c->description);
free(c->supplementary_gids);
c->supplementary_gids = NULL;
strv_free(c->well_known_names);
c->well_known_names = NULL;
bus_creds_done(c);
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(const sd_bus_creds *c) {
assert_return(c, 0);
return c->mask;
}
_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) {
assert_return(c, 0);
return c->augmented;
}
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(sd_bus_creds **ret, pid_t pid, uint64_t mask) {
sd_bus_creds *c;
int r;
assert_return(pid >= 0, -EINVAL);
assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
assert_return(ret, -EINVAL);
if (pid == 0)
pid = getpid();
c = bus_creds_new();
if (!c)
return -ENOMEM;
r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0);
if (r < 0) {
sd_bus_creds_unref(c);
return r;
}
/* Check if the process existed at all, in case we haven't
* figured that out already */
if (!pid_is_alive(pid)) {
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);
if (!(c->mask & SD_BUS_CREDS_UID))
return -ENODATA;
*uid = c->uid;
return 0;
}
_public_ int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) {
assert_return(c, -EINVAL);
assert_return(euid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_EUID))
return -ENODATA;
*euid = c->euid;
return 0;
}
_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) {
assert_return(c, -EINVAL);
assert_return(suid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_SUID))
return -ENODATA;
*suid = c->suid;
return 0;
}
_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) {
assert_return(c, -EINVAL);
assert_return(fsuid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_FSUID))
return -ENODATA;
*fsuid = c->fsuid;
return 0;
}
_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
assert_return(c, -EINVAL);
assert_return(gid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_GID))
return -ENODATA;
*gid = c->gid;
return 0;
}
_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) {
assert_return(c, -EINVAL);
assert_return(egid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_EGID))
return -ENODATA;
*egid = c->egid;
return 0;
}
_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) {
assert_return(c, -EINVAL);
assert_return(sgid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_SGID))
return -ENODATA;
*sgid = c->sgid;
return 0;
}
_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) {
assert_return(c, -EINVAL);
assert_return(fsgid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_FSGID))
return -ENODATA;
*fsgid = c->fsgid;
return 0;
}
_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) {
assert_return(c, -EINVAL);
assert_return(gids, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS))
return -ENODATA;
*gids = c->supplementary_gids;
return (int) c->n_supplementary_gids;
}
_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
assert_return(c, -EINVAL);
assert_return(pid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_PID))
return -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);
if (!(c->mask & SD_BUS_CREDS_TID))
return -ENODATA;
assert(c->tid > 0);
*tid = c->tid;
return 0;
}
_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) {
assert_return(c, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
return -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);
if (!(c->mask & SD_BUS_CREDS_COMM))
return -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);
if (!(c->mask & SD_BUS_CREDS_TID_COMM))
return -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);
if (!(c->mask & SD_BUS_CREDS_EXE))
return -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);
if (!(c->mask & SD_BUS_CREDS_CGROUP))
return -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);
if (!(c->mask & SD_BUS_CREDS_UNIT))
return -ENODATA;
assert(c->cgroup);
if (!c->unit) {
const char *shifted;
r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
if (r < 0)
return r;
r = cg_path_get_unit(shifted, (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);
if (!(c->mask & SD_BUS_CREDS_USER_UNIT))
return -ENODATA;
assert(c->cgroup);
if (!c->user_unit) {
const char *shifted;
r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
if (r < 0)
return r;
r = cg_path_get_user_unit(shifted, (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);
if (!(c->mask & SD_BUS_CREDS_SLICE))
return -ENODATA;
assert(c->cgroup);
if (!c->slice) {
const char *shifted;
r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
if (r < 0)
return r;
r = cg_path_get_slice(shifted, (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);
if (!(c->mask & SD_BUS_CREDS_SESSION))
return -ENODATA;
assert(c->cgroup);
if (!c->session) {
const char *shifted;
r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
if (r < 0)
return r;
r = cg_path_get_session(shifted, (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) {
const char *shifted;
int r;
assert_return(c, -EINVAL);
assert_return(uid, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_OWNER_UID))
return -ENODATA;
assert(c->cgroup);
r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted);
if (r < 0)
return r;
return cg_path_get_owner_uid(shifted, uid);
}
_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
assert_return(c, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_CMDLINE))
return -ENODATA;
assert_return(c->cmdline, -ESRCH);
assert(c->cmdline);
if (!c->cmdline_array) {
c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size);
if (!c->cmdline_array)
return -ENOMEM;
}
*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);
if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID))
return -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);
if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID))
return -ENODATA;
*uid = c->audit_login_uid;
return 0;
}
_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) {
assert_return(c, -EINVAL);
assert_return(unique_name, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_UNIQUE_NAME))
return -ENODATA;
*unique_name = c->unique_name;
return 0;
}
_public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) {
assert_return(c, -EINVAL);
assert_return(well_known_names, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES))
return -ENODATA;
/* As a special hack we return the bus driver as well-known
* names list when this is requested. */
if (c->well_known_names_driver) {
static const char* const wkn[] = {
"org.freedesktop.DBus",
NULL
};
*well_known_names = (char**) wkn;
return 0;
}
if (c->well_known_names_local) {
static const char* const wkn[] = {
"org.freedesktop.DBus.Local",
NULL
};
*well_known_names = (char**) wkn;
return 0;
}
*well_known_names = c->well_known_names;
return 0;
}
_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) {
assert_return(c, -EINVAL);
assert_return(ret, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_DESCRIPTION))
return -ENODATA;
assert(c->description);
if (!c->unescaped_description) {
c->unescaped_description = bus_label_unescape(c->description);
if (!c->unescaped_description)
return -ENOMEM;
}
*ret = c->unescaped_description;
return 0;
}
static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
size_t sz;
assert(c);
assert(capability >= 0);
assert(c->capability);
if ((unsigned) capability > cap_last_cap())
return 0;
sz = DIV_ROUND_UP(cap_last_cap(), 32U);
return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability));
}
_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
assert_return(c, -EINVAL);
assert_return(capability >= 0, -EINVAL);
if (!(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS))
return -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);
if (!(c->mask & SD_BUS_CREDS_PERMITTED_CAPS))
return -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);
if (!(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS))
return -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);
if (!(c->mask & SD_BUS_CREDS_BOUNDING_CAPS))
return -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, max;
unsigned i, j;
assert(c);
assert(p);
max = DIV_ROUND_UP(cap_last_cap(), 32U);
p += strspn(p, WHITESPACE);
sz = strlen(p);
if (sz % 8 != 0)
return -EINVAL;
sz /= 8;
if (sz > max)
return -EINVAL;
if (!c->capability) {
c->capability = new0(uint32_t, max * 4);
if (!c->capability)
return -ENOMEM;
}
for (i = 0; i < sz; i ++) {
uint32_t v = 0;
for (j = 0; j < 8; ++j) {
int t;
t = unhexchar(*p++);
if (t < 0)
return -EINVAL;
v = (v << 4) | t;
}
c->capability[offset * max + (sz - i - 1)] = v;
}
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);
if (!(mask & SD_BUS_CREDS_AUGMENT))
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;
/* Without pid we cannot do much... */
if (pid <= 0)
return 0;
/* Try to retrieve TID from creds if it wasn't passed to us */
if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
tid = c->tid;
/* Calculate what we shall and can add */
missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT);
if (missing == 0)
return 0;
c->pid = pid;
c->mask |= SD_BUS_CREDS_PID;
if (tid > 0) {
c->tid = tid;
c->mask |= SD_BUS_CREDS_TID;
}
if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID |
SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID |
SD_BUS_CREDS_SUPPLEMENTARY_GIDS |
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;
const char *p;
p = procfs_file_alloca(pid, "status");
f = fopen(p, "re");
if (!f) {
if (errno == ENOENT)
return -ESRCH;
else if (errno != EPERM && errno != EACCES)
return -errno;
} else {
char line[LINE_MAX];
FOREACH_LINE(line, f, return -errno) {
truncate_nl(line);
if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) {
p = startswith(line, "Uid:");
if (p) {
unsigned long uid, euid, suid, fsuid;
p += strspn(p, WHITESPACE);
if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4)
return -EIO;
if (missing & SD_BUS_CREDS_UID)
c->uid = (uid_t) uid;
if (missing & SD_BUS_CREDS_EUID)
c->euid = (uid_t) euid;
if (missing & SD_BUS_CREDS_SUID)
c->suid = (uid_t) suid;
if (missing & SD_BUS_CREDS_FSUID)
c->fsuid = (uid_t) fsuid;
c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID);
continue;
}
}
if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) {
p = startswith(line, "Gid:");
if (p) {
unsigned long gid, egid, sgid, fsgid;
p += strspn(p, WHITESPACE);
if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4)
return -EIO;
if (missing & SD_BUS_CREDS_GID)
c->gid = (gid_t) gid;
if (missing & SD_BUS_CREDS_EGID)
c->egid = (gid_t) egid;
if (missing & SD_BUS_CREDS_SGID)
c->sgid = (gid_t) sgid;
if (missing & SD_BUS_CREDS_FSGID)
c->fsgid = (gid_t) fsgid;
c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID);
continue;
}
}
if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
p = startswith(line, "Groups:");
if (p) {
size_t allocated = 0;
for (;;) {
unsigned long g;
int n = 0;
p += strspn(p, WHITESPACE);
if (*p == 0)
break;
if (sscanf(p, "%lu%n", &g, &n) != 1)
return -EIO;
if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1))
return -ENOMEM;
c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g;
p += n;
}
c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
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_SELINUX_CONTEXT) {
const char *p;
p = procfs_file_alloca(pid, "attr/current");
r = read_one_line_file(p, &c->label);
if (r < 0) {
if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
if (missing & SD_BUS_CREDS_COMM) {
r = get_process_comm(pid, &c->comm);
if (r < 0) {
if (r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_COMM;
}
if (missing & SD_BUS_CREDS_EXE) {
r = get_process_exe(pid, &c->exe);
if (r < 0) {
if (r != -EPERM && r != -EACCES)
return r;
} else
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_size);
if (r < 0) {
if (r == -ENOENT)
return -ESRCH;
if (r != -EPERM && r != -EACCES)
return r;
} else {
if (c->cmdline_size == 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/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0)
return -ENOMEM;
r = read_one_line_file(p, &c->tid_comm);
if (r < 0) {
if (r == -ENOENT)
return -ESRCH;
if (r != -EPERM && r != -EACCES)
return r;
} else
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)) {
if (!c->cgroup) {
r = cg_pid_get_path(NULL, pid, &c->cgroup);
if (r < 0) {
if (r != -EPERM && r != -EACCES)
return r;
}
}
if (!c->cgroup_root) {
r = cg_get_root_path(&c->cgroup_root);
if (r < 0)
return r;
}
if (c->cgroup)
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) {
if (r != -EOPNOTSUPP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES)
return r;
} else
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) {
if (r != -EOPNOTSUPP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES)
return r;
} else
c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
}
c->augmented = missing & c->mask;
return 0;
}
int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
_cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
int r;
assert(c);
assert(ret);
if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) {
/* There's already all data we need, or augmentation
* wasn't turned on. */
*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_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_UID) {
n->uid = c->uid;
n->mask |= SD_BUS_CREDS_UID;
}
if (c->mask & mask & SD_BUS_CREDS_EUID) {
n->euid = c->euid;
n->mask |= SD_BUS_CREDS_EUID;
}
if (c->mask & mask & SD_BUS_CREDS_SUID) {
n->suid = c->suid;
n->mask |= SD_BUS_CREDS_SUID;
}
if (c->mask & mask & SD_BUS_CREDS_FSUID) {
n->fsuid = c->fsuid;
n->mask |= SD_BUS_CREDS_FSUID;
}
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_EGID) {
n->egid = c->egid;
n->mask |= SD_BUS_CREDS_EGID;
}
if (c->mask & mask & SD_BUS_CREDS_SGID) {
n->sgid = c->sgid;
n->mask |= SD_BUS_CREDS_SGID;
}
if (c->mask & mask & SD_BUS_CREDS_FSGID) {
n->fsgid = c->fsgid;
n->mask |= SD_BUS_CREDS_FSGID;
}
if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids);
if (!n->supplementary_gids)
return -ENOMEM;
n->n_supplementary_gids = c->n_supplementary_gids;
n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
}
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_size);
if (!n->cmdline)
return -ENOMEM;
n->cmdline_size = c->cmdline_size;
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->cgroup_root = strdup(c->cgroup_root);
if (!n->cgroup_root)
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, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4);
if (!n->capability)
return -ENOMEM;
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_SELINUX_CONTEXT) {
n->label = strdup(c->label);
if (!n->label)
return -ENOMEM;
n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
}
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;
}
if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) {
n->unique_name = strdup(c->unique_name);
if (!n->unique_name)
return -ENOMEM;
n->mask |= SD_BUS_CREDS_UNIQUE_NAME;
}
if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
n->well_known_names = strv_copy(c->well_known_names);
if (!n->well_known_names)
return -ENOMEM;
n->well_known_names_driver = c->well_known_names_driver;
n->well_known_names_local = c->well_known_names_local;
n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES;
}
if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) {
n->description = strdup(c->description);
if (!n->description)
return -ENOMEM;
n->mask |= SD_BUS_CREDS_DESCRIPTION;
}
n->augmented = c->augmented & n->mask;
/* Get more data */
r = bus_creds_add_more(n, mask, 0, 0);
if (r < 0)
return r;
*ret = n;
n = NULL;
return 0;
}