| /*** |
| This file is part of systemd. |
| |
| Copyright 2014 Daniel Mack |
| |
| 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 "alloc-util.h" |
| #include "bus-kernel.h" |
| #include "bus-policy.h" |
| #include "kdbus.h" |
| #include "string-table.h" |
| #include "user-util.h" |
| #include "util.h" |
| |
| int bus_kernel_translate_access(BusPolicyAccess access) { |
| assert(access >= 0); |
| assert(access < _BUS_POLICY_ACCESS_MAX); |
| |
| switch (access) { |
| |
| case BUS_POLICY_ACCESS_SEE: |
| return KDBUS_POLICY_SEE; |
| |
| case BUS_POLICY_ACCESS_TALK: |
| return KDBUS_POLICY_TALK; |
| |
| case BUS_POLICY_ACCESS_OWN: |
| return KDBUS_POLICY_OWN; |
| |
| default: |
| assert_not_reached("Unknown policy access"); |
| } |
| } |
| |
| int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item) { |
| int r; |
| |
| assert(policy); |
| assert(item); |
| |
| switch (policy->type) { |
| |
| case BUSNAME_POLICY_TYPE_USER: { |
| const char *user = policy->name; |
| uid_t uid; |
| |
| r = get_user_creds(&user, &uid, NULL, NULL, NULL); |
| if (r < 0) |
| return r; |
| |
| item->policy_access.type = KDBUS_POLICY_ACCESS_USER; |
| item->policy_access.id = uid; |
| break; |
| } |
| |
| case BUSNAME_POLICY_TYPE_GROUP: { |
| const char *group = policy->name; |
| gid_t gid; |
| |
| r = get_group_creds(&group, &gid); |
| if (r < 0) |
| return r; |
| |
| item->policy_access.type = KDBUS_POLICY_ACCESS_GROUP; |
| item->policy_access.id = gid; |
| break; |
| } |
| |
| default: |
| assert_not_reached("Unknown policy type"); |
| } |
| |
| item->policy_access.access = bus_kernel_translate_access(policy->access); |
| |
| return 0; |
| } |
| |
| int bus_kernel_make_starter( |
| int fd, |
| const char *name, |
| bool activating, |
| bool accept_fd, |
| BusNamePolicy *policy, |
| BusPolicyAccess world_policy) { |
| |
| struct kdbus_cmd_free cmd_free = { .size = sizeof(cmd_free) }; |
| struct kdbus_cmd_hello *hello; |
| struct kdbus_item *n; |
| size_t policy_cnt = 0; |
| BusNamePolicy *po; |
| size_t size; |
| int r; |
| |
| assert(fd >= 0); |
| assert(name); |
| |
| LIST_FOREACH(policy, po, policy) |
| policy_cnt++; |
| |
| if (world_policy >= 0) |
| policy_cnt++; |
| |
| size = offsetof(struct kdbus_cmd_hello, items) + |
| ALIGN8(offsetof(struct kdbus_item, str) + strlen(name) + 1) + |
| policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); |
| |
| hello = alloca0_align(size, 8); |
| |
| n = hello->items; |
| strcpy(n->str, name); |
| n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; |
| n->type = KDBUS_ITEM_NAME; |
| n = KDBUS_ITEM_NEXT(n); |
| |
| LIST_FOREACH(policy, po, policy) { |
| n->type = KDBUS_ITEM_POLICY_ACCESS; |
| n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); |
| |
| r = bus_kernel_translate_policy(po, n); |
| if (r < 0) |
| return r; |
| |
| n = KDBUS_ITEM_NEXT(n); |
| } |
| |
| if (world_policy >= 0) { |
| n->type = KDBUS_ITEM_POLICY_ACCESS; |
| n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); |
| n->policy_access.type = KDBUS_POLICY_ACCESS_WORLD; |
| n->policy_access.access = bus_kernel_translate_access(world_policy); |
| } |
| |
| hello->size = size; |
| hello->flags = |
| (activating ? KDBUS_HELLO_ACTIVATOR : KDBUS_HELLO_POLICY_HOLDER) | |
| (accept_fd ? KDBUS_HELLO_ACCEPT_FD : 0); |
| hello->pool_size = KDBUS_POOL_SIZE; |
| hello->attach_flags_send = _KDBUS_ATTACH_ANY; |
| hello->attach_flags_recv = _KDBUS_ATTACH_ANY; |
| |
| if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) { |
| if (errno == ENOTTY) /* Major API change */ |
| return -ESOCKTNOSUPPORT; |
| return -errno; |
| } |
| |
| /* not interested in any output values */ |
| cmd_free.offset = hello->offset; |
| (void) ioctl(fd, KDBUS_CMD_FREE, &cmd_free); |
| |
| /* The higher 32bit of the bus_flags fields are considered |
| * 'incompatible flags'. Refuse them all for now. */ |
| if (hello->bus_flags > 0xFFFFFFFFULL) |
| return -ESOCKTNOSUPPORT; |
| |
| return fd; |
| } |
| |
| static const char* const bus_policy_access_table[_BUS_POLICY_ACCESS_MAX] = { |
| [BUS_POLICY_ACCESS_SEE] = "see", |
| [BUS_POLICY_ACCESS_TALK] = "talk", |
| [BUS_POLICY_ACCESS_OWN] = "own", |
| }; |
| |
| DEFINE_STRING_TABLE_LOOKUP(bus_policy_access, BusPolicyAccess); |