/* * Copyright (C) 2013-2015 Daniel Mack * Copyright (C) 2013-2015 Kay Sievers * Copyright (C) 2014-2015 Djalal Harouni * * kdbus 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __NR_memfd_create #ifdef __x86_64__ #define __NR_memfd_create 319 #elif defined __arm__ #define __NR_memfd_create 385 #else #define __NR_memfd_create 356 #endif #endif #include "kdbus-api.h" #include "kdbus-util.h" #include "kdbus-enum.h" #ifndef F_ADD_SEALS #define F_LINUX_SPECIFIC_BASE 1024 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) #define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ #define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ #define F_SEAL_GROW 0x0004 /* prevent file from growing */ #define F_SEAL_WRITE 0x0008 /* prevent writes */ #endif int kdbus_util_verbose = true; int kdbus_sysfs_get_parameter_mask(const char *path, uint64_t *mask) { int ret; FILE *file; unsigned long long value; file = fopen(path, "r"); if (!file) { ret = -errno; kdbus_printf("--- error fopen(): %d (%m)\n", ret); return ret; } ret = fscanf(file, "%llu", &value); if (ret != 1) { if (ferror(file)) ret = -errno; else ret = -EIO; kdbus_printf("--- error fscanf(): %d\n", ret); fclose(file); return ret; } *mask = (uint64_t)value; fclose(file); return 0; } int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask) { int ret; FILE *file; file = fopen(path, "w"); if (!file) { ret = -errno; kdbus_printf("--- error open(): %d (%m)\n", ret); return ret; } ret = fprintf(file, "%llu", (unsigned long long)mask); if (ret <= 0) { ret = -EIO; kdbus_printf("--- error fprintf(): %d\n", ret); } fclose(file); return ret > 0 ? 0 : ret; } int kdbus_create_bus(int control_fd, const char *name, uint64_t owner_meta, char **path) { struct { struct kdbus_cmd cmd; /* bloom size item */ struct { uint64_t size; uint64_t type; struct kdbus_bloom_parameter bloom; } bp; /* owner metadata items */ struct { uint64_t size; uint64_t type; uint64_t flags; } attach; /* name item */ struct { uint64_t size; uint64_t type; char str[64]; } name; } bus_make; int ret; memset(&bus_make, 0, sizeof(bus_make)); bus_make.bp.size = sizeof(bus_make.bp); bus_make.bp.type = KDBUS_ITEM_BLOOM_PARAMETER; bus_make.bp.bloom.size = 64; bus_make.bp.bloom.n_hash = 1; snprintf(bus_make.name.str, sizeof(bus_make.name.str), "%u-%s", getuid(), name); bus_make.attach.type = KDBUS_ITEM_ATTACH_FLAGS_SEND; bus_make.attach.size = sizeof(bus_make.attach); bus_make.attach.flags = owner_meta; bus_make.name.type = KDBUS_ITEM_MAKE_NAME; bus_make.name.size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name.str) + 1; bus_make.cmd.flags = KDBUS_MAKE_ACCESS_WORLD; bus_make.cmd.size = sizeof(bus_make.cmd) + bus_make.bp.size + bus_make.attach.size + bus_make.name.size; kdbus_printf("Creating bus with name >%s< on control fd %d ...\n", name, control_fd); ret = kdbus_cmd_bus_make(control_fd, &bus_make.cmd); if (ret < 0) { kdbus_printf("--- error when making bus: %d (%m)\n", ret); return ret; } if (ret == 0 && path) *path = strdup(bus_make.name.str); return ret; } struct kdbus_conn * kdbus_hello(const char *path, uint64_t flags, const struct kdbus_item *item, size_t item_size) { struct kdbus_cmd_free cmd_free = {}; int fd, ret; struct { struct kdbus_cmd_hello hello; struct { uint64_t size; uint64_t type; char str[16]; } conn_name; uint8_t extra_items[item_size]; } h; struct kdbus_conn *conn; memset(&h, 0, sizeof(h)); if (item_size > 0) memcpy(h.extra_items, item, item_size); kdbus_printf("-- opening bus connection %s\n", path); fd = open(path, O_RDWR|O_CLOEXEC); if (fd < 0) { kdbus_printf("--- error %d (%m)\n", fd); return NULL; } h.hello.flags = flags | KDBUS_HELLO_ACCEPT_FD; h.hello.attach_flags_send = _KDBUS_ATTACH_ALL; h.hello.attach_flags_recv = _KDBUS_ATTACH_ALL; h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION; strcpy(h.conn_name.str, "this-is-my-name"); h.conn_name.size = KDBUS_ITEM_HEADER_SIZE + strlen(h.conn_name.str) + 1; h.hello.size = sizeof(h); h.hello.pool_size = POOL_SIZE; ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) &h.hello); if (ret < 0) { kdbus_printf("--- error when saying hello: %d (%m)\n", ret); return NULL; } kdbus_printf("-- Our peer ID for %s: %llu -- bus uuid: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n", path, (unsigned long long)h.hello.id, h.hello.id128[0], h.hello.id128[1], h.hello.id128[2], h.hello.id128[3], h.hello.id128[4], h.hello.id128[5], h.hello.id128[6], h.hello.id128[7], h.hello.id128[8], h.hello.id128[9], h.hello.id128[10], h.hello.id128[11], h.hello.id128[12], h.hello.id128[13], h.hello.id128[14], h.hello.id128[15]); cmd_free.size = sizeof(cmd_free); cmd_free.offset = h.hello.offset; kdbus_cmd_free(fd, &cmd_free); conn = malloc(sizeof(*conn)); if (!conn) { kdbus_printf("unable to malloc()!?\n"); return NULL; } conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); if (conn->buf == MAP_FAILED) { free(conn); close(fd); kdbus_printf("--- error mmap (%m)\n"); return NULL; } conn->fd = fd; conn->id = h.hello.id; return conn; } struct kdbus_conn * kdbus_hello_registrar(const char *path, const char *name, const struct kdbus_policy_access *access, size_t num_access, uint64_t flags) { struct kdbus_item *item, *items; size_t i, size; size = KDBUS_ITEM_SIZE(strlen(name) + 1) + num_access * KDBUS_ITEM_SIZE(sizeof(*access)); items = alloca(size); item = items; item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; item->type = KDBUS_ITEM_NAME; strcpy(item->str, name); item = KDBUS_ITEM_NEXT(item); for (i = 0; i < num_access; i++) { item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_policy_access); item->type = KDBUS_ITEM_POLICY_ACCESS; item->policy_access.type = access[i].type; item->policy_access.access = access[i].access; item->policy_access.id = access[i].id; item = KDBUS_ITEM_NEXT(item); } return kdbus_hello(path, flags, items, size); } struct kdbus_conn *kdbus_hello_activator(const char *path, const char *name, const struct kdbus_policy_access *access, size_t num_access) { return kdbus_hello_registrar(path, name, access, num_access, KDBUS_HELLO_ACTIVATOR); } bool kdbus_item_in_message(struct kdbus_msg *msg, uint64_t type) { const struct kdbus_item *item; KDBUS_ITEM_FOREACH(item, msg, items) if (item->type == type) return true; return false; } int kdbus_bus_creator_info(struct kdbus_conn *conn, uint64_t flags, uint64_t *offset) { struct kdbus_cmd_info *cmd; size_t size = sizeof(*cmd); int ret; cmd = alloca(size); memset(cmd, 0, size); cmd->size = size; cmd->attach_flags = flags; ret = kdbus_cmd_bus_creator_info(conn->fd, cmd); if (ret < 0) { kdbus_printf("--- error when requesting info: %d (%m)\n", ret); return ret; } if (offset) *offset = cmd->offset; else kdbus_free(conn, cmd->offset); return 0; } int kdbus_conn_info(struct kdbus_conn *conn, uint64_t id, const char *name, uint64_t flags, uint64_t *offset) { struct kdbus_cmd_info *cmd; size_t size = sizeof(*cmd); struct kdbus_info *info; int ret; if (name) size += KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; cmd = alloca(size); memset(cmd, 0, size); cmd->size = size; cmd->attach_flags = flags; if (name) { cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; cmd->items[0].type = KDBUS_ITEM_NAME; strcpy(cmd->items[0].str, name); } else { cmd->id = id; } ret = kdbus_cmd_conn_info(conn->fd, cmd); if (ret < 0) { kdbus_printf("--- error when requesting info: %d (%m)\n", ret); return ret; } info = (struct kdbus_info *) (conn->buf + cmd->offset); if (info->size != cmd->info_size) { kdbus_printf("%s(): size mismatch: %d != %d\n", __func__, (int) info->size, (int) cmd->info_size); return -EIO; } if (offset) *offset = cmd->offset; else kdbus_free(conn, cmd->offset); return 0; } void kdbus_conn_free(struct kdbus_conn *conn) { if (!conn) return; if (conn->buf) munmap(conn->buf, POOL_SIZE); if (conn->fd >= 0) close(conn->fd); free(conn); } int sys_memfd_create(const char *name, __u64 size) { int ret, fd; fd = syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING); if (fd < 0) return fd; ret = ftruncate(fd, size); if (ret < 0) { close(fd); return ret; } return fd; } int sys_memfd_seal_set(int fd) { return fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); } off_t sys_memfd_get_size(int fd, off_t *size) { struct stat stat; int ret; ret = fstat(fd, &stat); if (ret < 0) { kdbus_printf("stat() failed: %m\n"); return ret; } *size = stat.st_size; return 0; } static int __kdbus_msg_send(const struct kdbus_conn *conn, const char *name, uint64_t cookie, uint64_t flags, uint64_t timeout, int64_t priority, uint64_t dst_id, uint64_t cmd_flags, int cancel_fd) { struct kdbus_cmd_send *cmd = NULL; struct kdbus_msg *msg = NULL; const char ref1[1024 * 128 + 3] = "0123456789_0"; const char ref2[] = "0123456789_1"; struct kdbus_item *item; struct timespec now; uint64_t size; int memfd = -1; int ret; size = sizeof(*msg) + 3 * KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); if (dst_id == KDBUS_DST_ID_BROADCAST) size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; else { memfd = sys_memfd_create("my-name-is-nice", 1024 * 1024); if (memfd < 0) { kdbus_printf("failed to create memfd: %m\n"); return memfd; } if (write(memfd, "kdbus memfd 1234567", 19) != 19) { ret = -errno; kdbus_printf("writing to memfd failed: %m\n"); goto out; } ret = sys_memfd_seal_set(memfd); if (ret < 0) { ret = -errno; kdbus_printf("memfd sealing failed: %m\n"); goto out; } size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); } if (name) size += KDBUS_ITEM_SIZE(strlen(name) + 1); msg = malloc(size); if (!msg) { ret = -errno; kdbus_printf("unable to malloc()!?\n"); goto out; } if (dst_id == KDBUS_DST_ID_BROADCAST) flags |= KDBUS_MSG_SIGNAL; memset(msg, 0, size); msg->flags = flags; msg->priority = priority; msg->size = size; msg->src_id = conn->id; msg->dst_id = name ? 0 : dst_id; msg->cookie = cookie; msg->payload_type = KDBUS_PAYLOAD_DBUS; if (timeout) { ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now); if (ret < 0) goto out; msg->timeout_ns = now.tv_sec * 1000000000ULL + now.tv_nsec + timeout; } item = msg->items; if (name) { item->type = KDBUS_ITEM_DST_NAME; item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; strcpy(item->str, name); item = KDBUS_ITEM_NEXT(item); } item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)&ref1; item->vec.size = sizeof(ref1); item = KDBUS_ITEM_NEXT(item); /* data padding for ref1 */ item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)NULL; item->vec.size = KDBUS_ALIGN8(sizeof(ref1)) - sizeof(ref1); item = KDBUS_ITEM_NEXT(item); item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)&ref2; item->vec.size = sizeof(ref2); item = KDBUS_ITEM_NEXT(item); if (dst_id == KDBUS_DST_ID_BROADCAST) { item->type = KDBUS_ITEM_BLOOM_FILTER; item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; item->bloom_filter.generation = 0; } else { item->type = KDBUS_ITEM_PAYLOAD_MEMFD; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd); item->memfd.size = 16; item->memfd.fd = memfd; } item = KDBUS_ITEM_NEXT(item); size = sizeof(*cmd); if (cancel_fd != -1) size += KDBUS_ITEM_SIZE(sizeof(cancel_fd)); cmd = malloc(size); if (!cmd) { ret = -errno; kdbus_printf("unable to malloc()!?\n"); goto out; } cmd->size = size; cmd->flags = cmd_flags; cmd->msg_address = (uintptr_t)msg; item = cmd->items; if (cancel_fd != -1) { item->type = KDBUS_ITEM_CANCEL_FD; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(cancel_fd); item->fds[0] = cancel_fd; item = KDBUS_ITEM_NEXT(item); } ret = kdbus_cmd_send(conn->fd, cmd); if (ret < 0) { kdbus_printf("error sending message: %d (%m)\n", ret); goto out; } if (cmd_flags & KDBUS_SEND_SYNC_REPLY) { struct kdbus_msg *reply; kdbus_printf("SYNC REPLY @offset %llu:\n", cmd->reply.offset); reply = (struct kdbus_msg *)(conn->buf + cmd->reply.offset); kdbus_msg_dump(conn, reply); kdbus_msg_free(reply); ret = kdbus_free(conn, cmd->reply.offset); if (ret < 0) goto out; } out: free(msg); free(cmd); if (memfd >= 0) close(memfd); return ret < 0 ? ret : 0; } int kdbus_msg_send(const struct kdbus_conn *conn, const char *name, uint64_t cookie, uint64_t flags, uint64_t timeout, int64_t priority, uint64_t dst_id) { return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority, dst_id, 0, -1); } int kdbus_msg_send_sync(const struct kdbus_conn *conn, const char *name, uint64_t cookie, uint64_t flags, uint64_t timeout, int64_t priority, uint64_t dst_id, int cancel_fd) { return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority, dst_id, KDBUS_SEND_SYNC_REPLY, cancel_fd); } int kdbus_msg_send_reply(const struct kdbus_conn *conn, uint64_t reply_cookie, uint64_t dst_id) { struct kdbus_cmd_send cmd = {}; struct kdbus_msg *msg; const char ref1[1024 * 128 + 3] = "0123456789_0"; struct kdbus_item *item; uint64_t size; int ret; size = sizeof(struct kdbus_msg); size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); msg = malloc(size); if (!msg) { kdbus_printf("unable to malloc()!?\n"); return -ENOMEM; } memset(msg, 0, size); msg->size = size; msg->src_id = conn->id; msg->dst_id = dst_id; msg->cookie_reply = reply_cookie; msg->payload_type = KDBUS_PAYLOAD_DBUS; item = msg->items; item->type = KDBUS_ITEM_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); item->vec.address = (uintptr_t)&ref1; item->vec.size = sizeof(ref1); item = KDBUS_ITEM_NEXT(item); cmd.size = sizeof(cmd); cmd.msg_address = (uintptr_t)msg; ret = kdbus_cmd_send(conn->fd, &cmd); if (ret < 0) kdbus_printf("error sending message: %d (%m)\n", ret); free(msg); return ret; } static char *msg_id(uint64_t id, char *buf) { if (id == 0) return "KERNEL"; if (id == ~0ULL) return "BROADCAST"; sprintf(buf, "%llu", (unsigned long long)id); return buf; } int kdbus_msg_dump(const struct kdbus_conn *conn, const struct kdbus_msg *msg) { const struct kdbus_item *item = msg->items; char buf_src[32]; char buf_dst[32]; uint64_t timeout = 0; uint64_t cookie_reply = 0; int ret = 0; if (msg->flags & KDBUS_MSG_EXPECT_REPLY) timeout = msg->timeout_ns; else cookie_reply = msg->cookie_reply; kdbus_printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s → %s, " "cookie=%llu, timeout=%llu cookie_reply=%llu priority=%lli\n", enum_PAYLOAD(msg->payload_type), (unsigned long long)msg->size, (unsigned long long)msg->flags, msg_id(msg->src_id, buf_src), msg_id(msg->dst_id, buf_dst), (unsigned long long)msg->cookie, (unsigned long long)timeout, (unsigned long long)cookie_reply, (long long)msg->priority); KDBUS_ITEM_FOREACH(item, msg, items) { if (item->size < KDBUS_ITEM_HEADER_SIZE) { kdbus_printf(" +%s (%llu bytes) invalid data record\n", enum_MSG(item->type), item->size); ret = -EINVAL; break; } switch (item->type) { case KDBUS_ITEM_PAYLOAD_OFF: { char *s; if (item->vec.offset == ~0ULL) s = "[\\0-bytes]"; else s = (char *)msg + item->vec.offset; kdbus_printf(" +%s (%llu bytes) off=%llu size=%llu '%s'\n", enum_MSG(item->type), item->size, (unsigned long long)item->vec.offset, (unsigned long long)item->vec.size, s); break; } case KDBUS_ITEM_FDS: { int i, n = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(int); kdbus_printf(" +%s (%llu bytes, %d fds)\n", enum_MSG(item->type), item->size, n); for (i = 0; i < n; i++) kdbus_printf(" fd[%d] = %d\n", i, item->fds[i]); break; } case KDBUS_ITEM_PAYLOAD_MEMFD: { char *buf; off_t size; buf = mmap(NULL, item->memfd.size, PROT_READ, MAP_PRIVATE, item->memfd.fd, 0); if (buf == MAP_FAILED) { kdbus_printf("mmap() fd=%i size=%llu failed: %m\n", item->memfd.fd, item->memfd.size); break; } if (sys_memfd_get_size(item->memfd.fd, &size) < 0) { kdbus_printf("KDBUS_CMD_MEMFD_SIZE_GET failed: %m\n"); break; } kdbus_printf(" +%s (%llu bytes) fd=%i size=%llu filesize=%llu '%s'\n", enum_MSG(item->type), item->size, item->memfd.fd, (unsigned long long)item->memfd.size, (unsigned long long)size, buf); munmap(buf, item->memfd.size); break; } case KDBUS_ITEM_CREDS: kdbus_printf(" +%s (%llu bytes) uid=%lld, euid=%lld, suid=%lld, fsuid=%lld, " "gid=%lld, egid=%lld, sgid=%lld, fsgid=%lld\n", enum_MSG(item->type), item->size, item->creds.uid, item->creds.euid, item->creds.suid, item->creds.fsuid, item->creds.gid, item->creds.egid, item->creds.sgid, item->creds.fsgid); break; case KDBUS_ITEM_PIDS: kdbus_printf(" +%s (%llu bytes) pid=%lld, tid=%lld, ppid=%lld\n", enum_MSG(item->type), item->size, item->pids.pid, item->pids.tid, item->pids.ppid); break; case KDBUS_ITEM_AUXGROUPS: { int i, n; kdbus_printf(" +%s (%llu bytes)\n", enum_MSG(item->type), item->size); n = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(uint64_t); for (i = 0; i < n; i++) kdbus_printf(" gid[%d] = %lld\n", i, item->data64[i]); break; } case KDBUS_ITEM_NAME: case KDBUS_ITEM_PID_COMM: case KDBUS_ITEM_TID_COMM: case KDBUS_ITEM_EXE: case KDBUS_ITEM_CGROUP: case KDBUS_ITEM_SECLABEL: case KDBUS_ITEM_DST_NAME: case KDBUS_ITEM_CONN_DESCRIPTION: kdbus_printf(" +%s (%llu bytes) '%s' (%zu)\n", enum_MSG(item->type), item->size, item->str, strlen(item->str)); break; case KDBUS_ITEM_OWNED_NAME: { kdbus_printf(" +%s (%llu bytes) '%s' (%zu) flags=0x%08llx\n", enum_MSG(item->type), item->size, item->name.name, strlen(item->name.name), item->name.flags); break; } case KDBUS_ITEM_CMDLINE: { size_t size = item->size - KDBUS_ITEM_HEADER_SIZE; const char *str = item->str; int count = 0; kdbus_printf(" +%s (%llu bytes) ", enum_MSG(item->type), item->size); while (size) { kdbus_printf("'%s' ", str); size -= strlen(str) + 1; str += strlen(str) + 1; count++; } kdbus_printf("(%d string%s)\n", count, (count == 1) ? "" : "s"); break; } case KDBUS_ITEM_AUDIT: kdbus_printf(" +%s (%llu bytes) loginuid=%u sessionid=%u\n", enum_MSG(item->type), item->size, item->audit.loginuid, item->audit.sessionid); break; case KDBUS_ITEM_CAPS: { const uint32_t *cap; int n, i; kdbus_printf(" +%s (%llu bytes) len=%llu bytes, last_cap %d\n", enum_MSG(item->type), item->size, (unsigned long long)item->size - KDBUS_ITEM_HEADER_SIZE, (int) item->caps.last_cap); cap = item->caps.caps; n = (item->size - offsetof(struct kdbus_item, caps.caps)) / 4 / sizeof(uint32_t); kdbus_printf(" CapInh="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(0 * n) + (n - i - 1)]); kdbus_printf(" CapPrm="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(1 * n) + (n - i - 1)]); kdbus_printf(" CapEff="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(2 * n) + (n - i - 1)]); kdbus_printf(" CapBnd="); for (i = 0; i < n; i++) kdbus_printf("%08x", cap[(3 * n) + (n - i - 1)]); kdbus_printf("\n"); break; } case KDBUS_ITEM_TIMESTAMP: kdbus_printf(" +%s (%llu bytes) seq=%llu realtime=%lluns monotonic=%lluns\n", enum_MSG(item->type), item->size, (unsigned long long)item->timestamp.seqnum, (unsigned long long)item->timestamp.realtime_ns, (unsigned long long)item->timestamp.monotonic_ns); break; case KDBUS_ITEM_REPLY_TIMEOUT: kdbus_printf(" +%s (%llu bytes) cookie=%llu\n", enum_MSG(item->type), item->size, msg->cookie_reply); break; case KDBUS_ITEM_NAME_ADD: case KDBUS_ITEM_NAME_REMOVE: case KDBUS_ITEM_NAME_CHANGE: kdbus_printf(" +%s (%llu bytes) '%s', old id=%lld, now id=%lld, old_flags=0x%llx new_flags=0x%llx\n", enum_MSG(item->type), (unsigned long long) item->size, item->name_change.name, item->name_change.old_id.id, item->name_change.new_id.id, item->name_change.old_id.flags, item->name_change.new_id.flags); break; case KDBUS_ITEM_ID_ADD: case KDBUS_ITEM_ID_REMOVE: kdbus_printf(" +%s (%llu bytes) id=%llu flags=%llu\n", enum_MSG(item->type), (unsigned long long) item->size, (unsigned long long) item->id_change.id, (unsigned long long) item->id_change.flags); break; default: kdbus_printf(" +%s (%llu bytes)\n", enum_MSG(item->type), item->size); break; } } if ((char *)item - ((char *)msg + msg->size) >= 8) { kdbus_printf("invalid padding at end of message\n"); ret = -EINVAL; } kdbus_printf("\n"); return ret; } void kdbus_msg_free(struct kdbus_msg *msg) { const struct kdbus_item *item; int nfds, i; if (!msg) return; KDBUS_ITEM_FOREACH(item, msg, items) { switch (item->type) { /* close all memfds */ case KDBUS_ITEM_PAYLOAD_MEMFD: close(item->memfd.fd); break; case KDBUS_ITEM_FDS: nfds = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(int); for (i = 0; i < nfds; i++) close(item->fds[i]); break; } } } int kdbus_msg_recv(struct kdbus_conn *conn, struct kdbus_msg **msg_out, uint64_t *offset) { struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; struct kdbus_msg *msg; int ret; ret = kdbus_cmd_recv(conn->fd, &recv); if (ret < 0) return ret; msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset); ret = kdbus_msg_dump(conn, msg); if (ret < 0) { kdbus_msg_free(msg); return ret; } if (msg_out) { *msg_out = msg; if (offset) *offset = recv.msg.offset; } else { kdbus_msg_free(msg); ret = kdbus_free(conn, recv.msg.offset); if (ret < 0) return ret; } return 0; } /* * Returns: 0 on success, negative errno on failure. * * We must return -ETIMEDOUT, -ECONNREST, -EAGAIN and other errors. * We must return the result of kdbus_msg_recv() */ int kdbus_msg_recv_poll(struct kdbus_conn *conn, int timeout_ms, struct kdbus_msg **msg_out, uint64_t *offset) { int ret; do { struct timeval before, after, diff; struct pollfd fd; fd.fd = conn->fd; fd.events = POLLIN | POLLPRI | POLLHUP; fd.revents = 0; gettimeofday(&before, NULL); ret = poll(&fd, 1, timeout_ms); gettimeofday(&after, NULL); if (ret == 0) { ret = -ETIMEDOUT; break; } if (ret > 0) { if (fd.revents & POLLIN) ret = kdbus_msg_recv(conn, msg_out, offset); if (fd.revents & (POLLHUP | POLLERR)) ret = -ECONNRESET; } if (ret == 0 || ret != -EAGAIN) break; timersub(&after, &before, &diff); timeout_ms -= diff.tv_sec * 1000UL + diff.tv_usec / 1000UL; } while (timeout_ms > 0); return ret; } int kdbus_free(const struct kdbus_conn *conn, uint64_t offset) { struct kdbus_cmd_free cmd_free = {}; int ret; cmd_free.size = sizeof(cmd_free); cmd_free.offset = offset; cmd_free.flags = 0; ret = kdbus_cmd_free(conn->fd, &cmd_free); if (ret < 0) { kdbus_printf("KDBUS_CMD_FREE failed: %d (%m)\n", ret); return ret; } return 0; } int kdbus_name_acquire(struct kdbus_conn *conn, const char *name, uint64_t *flags) { struct kdbus_cmd *cmd_name; size_t name_len = strlen(name) + 1; uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len); struct kdbus_item *item; int ret; cmd_name = alloca(size); memset(cmd_name, 0, size); item = cmd_name->items; item->size = KDBUS_ITEM_HEADER_SIZE + name_len; item->type = KDBUS_ITEM_NAME; strcpy(item->str, name); cmd_name->size = size; if (flags) cmd_name->flags = *flags; ret = kdbus_cmd_name_acquire(conn->fd, cmd_name); if (ret < 0) { kdbus_printf("error aquiring name: %s\n", strerror(-ret)); return ret; } kdbus_printf("%s(): flags after call: 0x%llx\n", __func__, cmd_name->return_flags); if (flags) *flags = cmd_name->return_flags; return 0; } int kdbus_name_release(struct kdbus_conn *conn, const char *name) { struct kdbus_cmd *cmd_name; size_t name_len = strlen(name) + 1; uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len); struct kdbus_item *item; int ret; cmd_name = alloca(size); memset(cmd_name, 0, size); item = cmd_name->items; item->size = KDBUS_ITEM_HEADER_SIZE + name_len; item->type = KDBUS_ITEM_NAME; strcpy(item->str, name); cmd_name->size = size; kdbus_printf("conn %lld giving up name '%s'\n", (unsigned long long) conn->id, name); ret = kdbus_cmd_name_release(conn->fd, cmd_name); if (ret < 0) { kdbus_printf("error releasing name: %s\n", strerror(-ret)); return ret; } return 0; } int kdbus_list(struct kdbus_conn *conn, uint64_t flags) { struct kdbus_cmd_list cmd_list = {}; struct kdbus_info *list, *name; int ret; cmd_list.size = sizeof(cmd_list); cmd_list.flags = flags; ret = kdbus_cmd_list(conn->fd, &cmd_list); if (ret < 0) { kdbus_printf("error listing names: %d (%m)\n", ret); return ret; } kdbus_printf("REGISTRY:\n"); list = (struct kdbus_info *)(conn->buf + cmd_list.offset); KDBUS_FOREACH(name, list, cmd_list.list_size) { uint64_t flags = 0; struct kdbus_item *item; const char *n = "MISSING-NAME"; if (name->size == sizeof(struct kdbus_cmd)) continue; KDBUS_ITEM_FOREACH(item, name, items) if (item->type == KDBUS_ITEM_OWNED_NAME) { n = item->name.name; flags = item->name.flags; kdbus_printf("%8llu flags=0x%08llx conn=0x%08llx '%s'\n", name->id, (unsigned long long) flags, name->flags, n); } } kdbus_printf("\n"); ret = kdbus_free(conn, cmd_list.offset); return ret; } int kdbus_conn_update_attach_flags(struct kdbus_conn *conn, uint64_t attach_flags_send, uint64_t attach_flags_recv) { int ret; size_t size; struct kdbus_cmd *update; struct kdbus_item *item; size = sizeof(struct kdbus_cmd); size += KDBUS_ITEM_SIZE(sizeof(uint64_t)) * 2; update = malloc(size); if (!update) { kdbus_printf("error malloc: %m\n"); return -ENOMEM; } memset(update, 0, size); update->size = size; item = update->items; item->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); item->data64[0] = attach_flags_send; item = KDBUS_ITEM_NEXT(item); item->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); item->data64[0] = attach_flags_recv; item = KDBUS_ITEM_NEXT(item); ret = kdbus_cmd_update(conn->fd, update); if (ret < 0) kdbus_printf("error conn update: %d (%m)\n", ret); free(update); return ret; } int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name, const struct kdbus_policy_access *access, size_t num_access) { struct kdbus_cmd *update; struct kdbus_item *item; size_t i, size; int ret; size = sizeof(struct kdbus_cmd); size += KDBUS_ITEM_SIZE(strlen(name) + 1); size += num_access * KDBUS_ITEM_SIZE(sizeof(struct kdbus_policy_access)); update = malloc(size); if (!update) { kdbus_printf("error malloc: %m\n"); return -ENOMEM; } memset(update, 0, size); update->size = size; item = update->items; item->type = KDBUS_ITEM_NAME; item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; strcpy(item->str, name); item = KDBUS_ITEM_NEXT(item); for (i = 0; i < num_access; i++) { item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_policy_access); item->type = KDBUS_ITEM_POLICY_ACCESS; item->policy_access.type = access[i].type; item->policy_access.access = access[i].access; item->policy_access.id = access[i].id; item = KDBUS_ITEM_NEXT(item); } ret = kdbus_cmd_update(conn->fd, update); if (ret < 0) kdbus_printf("error conn update: %d (%m)\n", ret); free(update); return ret; } int kdbus_add_match_id(struct kdbus_conn *conn, uint64_t cookie, uint64_t type, uint64_t id) { struct { struct kdbus_cmd_match cmd; struct { uint64_t size; uint64_t type; struct kdbus_notify_id_change chg; } item; } buf; int ret; memset(&buf, 0, sizeof(buf)); buf.cmd.size = sizeof(buf); buf.cmd.cookie = cookie; buf.item.size = sizeof(buf.item); buf.item.type = type; buf.item.chg.id = id; ret = kdbus_cmd_match_add(conn->fd, &buf.cmd); if (ret < 0) kdbus_printf("--- error adding conn match: %d (%m)\n", ret); return ret; } int kdbus_add_match_empty(struct kdbus_conn *conn) { struct { struct kdbus_cmd_match cmd; struct kdbus_item item; } buf; int ret; memset(&buf, 0, sizeof(buf)); buf.item.size = sizeof(uint64_t) * 3; buf.item.type = KDBUS_ITEM_ID; buf.item.id = KDBUS_MATCH_ID_ANY; buf.cmd.size = sizeof(buf.cmd) + buf.item.size; ret = kdbus_cmd_match_add(conn->fd, &buf.cmd); if (ret < 0) kdbus_printf("--- error adding conn match: %d (%m)\n", ret); return ret; } static int all_ids_are_mapped(const char *path) { int ret; FILE *file; uint32_t inside_id, length; file = fopen(path, "r"); if (!file) { ret = -errno; kdbus_printf("error fopen() %s: %d (%m)\n", path, ret); return ret; } ret = fscanf(file, "%u\t%*u\t%u", &inside_id, &length); if (ret != 2) { if (ferror(file)) ret = -errno; else ret = -EIO; kdbus_printf("--- error fscanf(): %d\n", ret); fclose(file); return ret; } fclose(file); /* * If length is 4294967295 which means the invalid uid * (uid_t) -1 then we are able to map all uid/gids */ if (inside_id == 0 && length == (uid_t) -1) return 1; return 0; } int all_uids_gids_are_mapped(void) { int ret; ret = all_ids_are_mapped("/proc/self/uid_map"); if (ret <= 0) { kdbus_printf("--- error not all uids are mapped\n"); return 0; } ret = all_ids_are_mapped("/proc/self/gid_map"); if (ret <= 0) { kdbus_printf("--- error not all gids are mapped\n"); return 0; } return 1; } int drop_privileges(uid_t uid, gid_t gid) { int ret; ret = setgroups(0, NULL); if (ret < 0) { ret = -errno; kdbus_printf("error setgroups: %d (%m)\n", ret); return ret; } ret = setresgid(gid, gid, gid); if (ret < 0) { ret = -errno; kdbus_printf("error setresgid: %d (%m)\n", ret); return ret; } ret = setresuid(uid, uid, uid); if (ret < 0) { ret = -errno; kdbus_printf("error setresuid: %d (%m)\n", ret); return ret; } return ret; } uint64_t now(clockid_t clock) { struct timespec spec; clock_gettime(clock, &spec); return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec; } char *unique_name(const char *prefix) { unsigned int i; uint64_t u_now; char n[17]; char *str; int r; /* * This returns a random string which is guaranteed to be * globally unique across all calls to unique_name(). We * compose the string as: * --