]>
Commit | Line | Data |
---|---|---|
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ | |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #ifdef HAVE_VALGRIND_MEMCHECK_H | |
23 | #include <valgrind/memcheck.h> | |
24 | #endif | |
25 | ||
26 | #include <fcntl.h> | |
27 | #include <malloc.h> | |
28 | #include <sys/mman.h> | |
29 | ||
30 | #include "util.h" | |
31 | ||
32 | #include "bus-internal.h" | |
33 | #include "bus-message.h" | |
34 | #include "bus-kernel.h" | |
35 | #include "bus-bloom.h" | |
36 | #include "bus-util.h" | |
37 | ||
38 | #define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t)) | |
39 | ||
40 | int bus_kernel_parse_unique_name(const char *s, uint64_t *id) { | |
41 | int r; | |
42 | ||
43 | assert(s); | |
44 | assert(id); | |
45 | ||
46 | if (!startswith(s, ":1.")) | |
47 | return 0; | |
48 | ||
49 | r = safe_atou64(s + 3, id); | |
50 | if (r < 0) | |
51 | return r; | |
52 | ||
53 | return 1; | |
54 | } | |
55 | ||
56 | static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) { | |
57 | assert(d); | |
58 | assert(sz > 0); | |
59 | ||
60 | *d = ALIGN8_PTR(*d); | |
61 | ||
62 | /* Note that p can be NULL, which encodes a region full of | |
63 | * zeroes, which is useful to optimize certain padding | |
64 | * conditions */ | |
65 | ||
66 | (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec); | |
67 | (*d)->type = KDBUS_ITEM_PAYLOAD_VEC; | |
68 | (*d)->vec.address = PTR_TO_UINT64(p); | |
69 | (*d)->vec.size = sz; | |
70 | ||
71 | *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); | |
72 | } | |
73 | ||
74 | static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t sz) { | |
75 | assert(d); | |
76 | assert(memfd >= 0); | |
77 | assert(sz > 0); | |
78 | ||
79 | *d = ALIGN8_PTR(*d); | |
80 | (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd); | |
81 | (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD; | |
82 | (*d)->memfd.fd = memfd; | |
83 | (*d)->memfd.size = sz; | |
84 | ||
85 | *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); | |
86 | } | |
87 | ||
88 | static void append_destination(struct kdbus_item **d, const char *s, size_t length) { | |
89 | assert(d); | |
90 | assert(s); | |
91 | ||
92 | *d = ALIGN8_PTR(*d); | |
93 | ||
94 | (*d)->size = offsetof(struct kdbus_item, str) + length + 1; | |
95 | (*d)->type = KDBUS_ITEM_DST_NAME; | |
96 | memcpy((*d)->str, s, length + 1); | |
97 | ||
98 | *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); | |
99 | } | |
100 | ||
101 | static void* append_bloom(struct kdbus_item **d, size_t length) { | |
102 | void *r; | |
103 | ||
104 | assert(d); | |
105 | ||
106 | *d = ALIGN8_PTR(*d); | |
107 | ||
108 | (*d)->size = offsetof(struct kdbus_item, data) + length; | |
109 | (*d)->type = KDBUS_ITEM_BLOOM; | |
110 | r = (*d)->data; | |
111 | ||
112 | *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); | |
113 | ||
114 | return r; | |
115 | } | |
116 | ||
117 | static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) { | |
118 | assert(d); | |
119 | assert(fds); | |
120 | assert(n_fds > 0); | |
121 | ||
122 | *d = ALIGN8_PTR(*d); | |
123 | (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds; | |
124 | (*d)->type = KDBUS_ITEM_FDS; | |
125 | memcpy((*d)->fds, fds, sizeof(int) * n_fds); | |
126 | ||
127 | *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); | |
128 | } | |
129 | ||
130 | static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) { | |
131 | unsigned i; | |
132 | int r; | |
133 | ||
134 | assert(m); | |
135 | assert(bloom); | |
136 | ||
137 | memset(bloom, 0, BLOOM_SIZE); | |
138 | ||
139 | bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type)); | |
140 | ||
141 | if (m->interface) | |
142 | bloom_add_pair(bloom, "interface", m->interface); | |
143 | if (m->member) | |
144 | bloom_add_pair(bloom, "member", m->member); | |
145 | if (m->path) { | |
146 | bloom_add_pair(bloom, "path", m->path); | |
147 | bloom_add_pair(bloom, "path-slash-prefix", m->path); | |
148 | bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/'); | |
149 | } | |
150 | ||
151 | r = sd_bus_message_rewind(m, true); | |
152 | if (r < 0) | |
153 | return r; | |
154 | ||
155 | for (i = 0; i < 64; i++) { | |
156 | char type; | |
157 | const char *t; | |
158 | char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; | |
159 | char *e; | |
160 | ||
161 | r = sd_bus_message_peek_type(m, &type, NULL); | |
162 | if (r < 0) | |
163 | return r; | |
164 | ||
165 | if (type != SD_BUS_TYPE_STRING && | |
166 | type != SD_BUS_TYPE_OBJECT_PATH && | |
167 | type != SD_BUS_TYPE_SIGNATURE) | |
168 | break; | |
169 | ||
170 | r = sd_bus_message_read_basic(m, type, &t); | |
171 | if (r < 0) | |
172 | return r; | |
173 | ||
174 | e = stpcpy(buf, "arg"); | |
175 | if (i < 10) | |
176 | *(e++) = '0' + i; | |
177 | else { | |
178 | *(e++) = '0' + (i / 10); | |
179 | *(e++) = '0' + (i % 10); | |
180 | } | |
181 | ||
182 | *e = 0; | |
183 | bloom_add_pair(bloom, buf, t); | |
184 | ||
185 | strcpy(e, "-dot-prefix"); | |
186 | bloom_add_prefixes(bloom, buf, t, '.'); | |
187 | strcpy(e, "-slash-prefix"); | |
188 | bloom_add_prefixes(bloom, buf, t, '/'); | |
189 | } | |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
194 | static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { | |
195 | struct bus_body_part *part; | |
196 | struct kdbus_item *d; | |
197 | bool well_known; | |
198 | uint64_t unique; | |
199 | size_t sz, dl; | |
200 | unsigned i; | |
201 | int r; | |
202 | ||
203 | assert(b); | |
204 | assert(m); | |
205 | assert(m->sealed); | |
206 | ||
207 | if (m->kdbus) | |
208 | return 0; | |
209 | ||
210 | if (m->destination) { | |
211 | r = bus_kernel_parse_unique_name(m->destination, &unique); | |
212 | if (r < 0) | |
213 | return r; | |
214 | ||
215 | well_known = r == 0; | |
216 | } else | |
217 | well_known = false; | |
218 | ||
219 | sz = offsetof(struct kdbus_msg, items); | |
220 | ||
221 | assert_cc(ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)) == | |
222 | ALIGN8(offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd))); | |
223 | ||
224 | /* Add in fixed header, fields header and payload */ | |
225 | sz += (1 + m->n_body_parts) * | |
226 | ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)); | |
227 | ||
228 | /* Add space for bloom filter */ | |
229 | sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE); | |
230 | ||
231 | /* Add in well-known destination header */ | |
232 | if (well_known) { | |
233 | dl = strlen(m->destination); | |
234 | sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1); | |
235 | } | |
236 | ||
237 | /* Add space for unix fds */ | |
238 | if (m->n_fds > 0) | |
239 | sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds); | |
240 | ||
241 | m->kdbus = memalign(8, sz); | |
242 | if (!m->kdbus) { | |
243 | r = -ENOMEM; | |
244 | goto fail; | |
245 | } | |
246 | ||
247 | m->free_kdbus = true; | |
248 | memset(m->kdbus, 0, sz); | |
249 | ||
250 | m->kdbus->flags = | |
251 | ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) | | |
252 | ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0); | |
253 | m->kdbus->dst_id = | |
254 | well_known ? 0 : | |
255 | m->destination ? unique : KDBUS_DST_ID_BROADCAST; | |
256 | m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1; | |
257 | m->kdbus->cookie = m->header->serial; | |
258 | ||
259 | m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC; | |
260 | ||
261 | d = m->kdbus->items; | |
262 | ||
263 | if (well_known) | |
264 | append_destination(&d, m->destination, dl); | |
265 | ||
266 | append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m)); | |
267 | ||
268 | MESSAGE_FOREACH_PART(part, i, m) { | |
269 | if (part->is_zero) { | |
270 | /* If this is padding then simply send a | |
271 | * vector with a NULL data pointer which the | |
272 | * kernel will just pass through. This is the | |
273 | * most efficient way to encode zeroes */ | |
274 | ||
275 | append_payload_vec(&d, NULL, part->size); | |
276 | continue; | |
277 | } | |
278 | ||
279 | if (part->memfd >= 0 && part->sealed && m->destination) { | |
280 | /* Try to send a memfd, if the part is | |
281 | * sealed and this is not a broadcast. Since we can only */ | |
282 | ||
283 | append_payload_memfd(&d, part->memfd, part->size); | |
284 | continue; | |
285 | } | |
286 | ||
287 | /* Otherwise let's send a vector to the actual data, | |
288 | * for that we need to map it first. */ | |
289 | r = bus_body_part_map(part); | |
290 | if (r < 0) | |
291 | goto fail; | |
292 | ||
293 | append_payload_vec(&d, part->data, part->size); | |
294 | } | |
295 | ||
296 | if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) { | |
297 | void *p; | |
298 | ||
299 | p = append_bloom(&d, BLOOM_SIZE); | |
300 | r = bus_message_setup_bloom(m, p); | |
301 | if (r < 0) | |
302 | goto fail; | |
303 | } | |
304 | ||
305 | if (m->n_fds > 0) | |
306 | append_fds(&d, m->fds, m->n_fds); | |
307 | ||
308 | m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus; | |
309 | assert(m->kdbus->size <= sz); | |
310 | ||
311 | return 0; | |
312 | ||
313 | fail: | |
314 | m->poisoned = true; | |
315 | return r; | |
316 | } | |
317 | ||
318 | int bus_kernel_take_fd(sd_bus *b) { | |
319 | struct kdbus_cmd_hello hello; | |
320 | int r; | |
321 | ||
322 | assert(b); | |
323 | ||
324 | if (b->is_server) | |
325 | return -EINVAL; | |
326 | ||
327 | b->use_memfd = 1; | |
328 | ||
329 | zero(hello); | |
330 | hello.size = sizeof(hello); | |
331 | hello.conn_flags = b->hello_flags; | |
332 | hello.attach_flags = b->attach_flags; | |
333 | hello.pool_size = KDBUS_POOL_SIZE; | |
334 | ||
335 | r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello); | |
336 | if (r < 0) | |
337 | return -errno; | |
338 | ||
339 | if (!b->kdbus_buffer) { | |
340 | b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); | |
341 | if (b->kdbus_buffer == MAP_FAILED) { | |
342 | b->kdbus_buffer = NULL; | |
343 | return -errno; | |
344 | } | |
345 | } | |
346 | ||
347 | /* The higher 32bit of both flags fields are considered | |
348 | * 'incompatible flags'. Refuse them all for now. */ | |
349 | if (hello.bus_flags > 0xFFFFFFFFULL || | |
350 | hello.conn_flags > 0xFFFFFFFFULL) | |
351 | return -ENOTSUP; | |
352 | ||
353 | if (hello.bloom_size != BLOOM_SIZE) | |
354 | return -ENOTSUP; | |
355 | ||
356 | if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0) | |
357 | return -ENOMEM; | |
358 | ||
359 | b->is_kernel = true; | |
360 | b->bus_client = true; | |
361 | b->can_fds = !!(hello.conn_flags & KDBUS_HELLO_ACCEPT_FD); | |
362 | ||
363 | /* the kernel told us the UUID of the underlying bus */ | |
364 | memcpy(b->server_id.bytes, hello.id128, sizeof(b->server_id.bytes)); | |
365 | ||
366 | r = bus_start_running(b); | |
367 | if (r < 0) | |
368 | return r; | |
369 | ||
370 | return 1; | |
371 | } | |
372 | ||
373 | int bus_kernel_connect(sd_bus *b) { | |
374 | assert(b); | |
375 | assert(b->input_fd < 0); | |
376 | assert(b->output_fd < 0); | |
377 | assert(b->kernel); | |
378 | ||
379 | if (b->is_server) | |
380 | return -EINVAL; | |
381 | ||
382 | b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); | |
383 | if (b->input_fd < 0) | |
384 | return -errno; | |
385 | ||
386 | b->output_fd = b->input_fd; | |
387 | ||
388 | return bus_kernel_take_fd(b); | |
389 | } | |
390 | ||
391 | int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) { | |
392 | int r; | |
393 | ||
394 | assert(bus); | |
395 | assert(m); | |
396 | assert(bus->state == BUS_RUNNING); | |
397 | ||
398 | r = bus_message_setup_kmsg(bus, m); | |
399 | if (r < 0) | |
400 | return r; | |
401 | ||
402 | r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus); | |
403 | if (r < 0) | |
404 | return errno == EAGAIN ? 0 : -errno; | |
405 | ||
406 | return 1; | |
407 | } | |
408 | ||
409 | static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { | |
410 | uint64_t off; | |
411 | struct kdbus_item *d; | |
412 | ||
413 | assert(bus); | |
414 | assert(k); | |
415 | ||
416 | off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer; | |
417 | ioctl(bus->input_fd, KDBUS_CMD_MSG_RELEASE, &off); | |
418 | ||
419 | KDBUS_PART_FOREACH(d, k, items) { | |
420 | ||
421 | if (d->type == KDBUS_ITEM_FDS) | |
422 | close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); | |
423 | else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) | |
424 | close_nointr_nofail(d->memfd.fd); | |
425 | } | |
426 | } | |
427 | ||
428 | static int return_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner, sd_bus_message **ret) { | |
429 | _cleanup_bus_message_unref_ sd_bus_message *m = NULL; | |
430 | int r; | |
431 | ||
432 | assert(bus); | |
433 | assert(ret); | |
434 | ||
435 | r = sd_bus_message_new_signal( | |
436 | bus, | |
437 | "/org/freedesktop/DBus", | |
438 | "org.freedesktop.DBus", | |
439 | "NameOwnerChanged", | |
440 | &m); | |
441 | if (r < 0) | |
442 | return r; | |
443 | ||
444 | r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); | |
445 | if (r < 0) | |
446 | return r; | |
447 | ||
448 | m->sender = "org.freedesktop.DBus"; | |
449 | ||
450 | r = bus_seal_message(bus, m); | |
451 | if (r < 0) | |
452 | return r; | |
453 | ||
454 | *ret = m; | |
455 | m = NULL; | |
456 | ||
457 | return 1; | |
458 | } | |
459 | ||
460 | static int translate_name_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d, sd_bus_message **ret) { | |
461 | char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX]; | |
462 | ||
463 | assert(bus); | |
464 | assert(k); | |
465 | assert(d); | |
466 | assert(ret); | |
467 | ||
468 | if (d->name_change.flags != 0) | |
469 | return 0; | |
470 | ||
471 | if (d->type == KDBUS_ITEM_NAME_ADD) | |
472 | old_owner[0] = 0; | |
473 | else | |
474 | sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old_id); | |
475 | ||
476 | if (d->type == KDBUS_ITEM_NAME_REMOVE) | |
477 | new_owner[0] = 0; | |
478 | else | |
479 | sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new_id); | |
480 | ||
481 | return return_name_owner_changed(bus, d->name_change.name, old_owner, new_owner, ret); | |
482 | } | |
483 | ||
484 | static int translate_id_change(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d, sd_bus_message **ret) { | |
485 | char owner[UNIQUE_NAME_MAX]; | |
486 | ||
487 | assert(bus); | |
488 | assert(k); | |
489 | assert(d); | |
490 | assert(ret); | |
491 | ||
492 | sprintf(owner, ":1.%llu", d->id_change.id); | |
493 | ||
494 | return return_name_owner_changed( | |
495 | bus, owner, | |
496 | d->type == KDBUS_ITEM_ID_ADD ? NULL : owner, | |
497 | d->type == KDBUS_ITEM_ID_ADD ? owner : NULL, | |
498 | ret); | |
499 | } | |
500 | ||
501 | static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d, sd_bus_message **ret) { | |
502 | _cleanup_bus_message_unref_ sd_bus_message *m = NULL; | |
503 | int r; | |
504 | ||
505 | assert(bus); | |
506 | assert(k); | |
507 | assert(d); | |
508 | assert(ret); | |
509 | ||
510 | r = bus_message_new_synthetic_error( | |
511 | bus, | |
512 | k->cookie_reply, | |
513 | d->type == KDBUS_ITEM_REPLY_TIMEOUT ? | |
514 | &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") : | |
515 | &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"), | |
516 | &m); | |
517 | if (r < 0) | |
518 | return r; | |
519 | ||
520 | m->sender = "org.freedesktop.DBus"; | |
521 | ||
522 | r = bus_seal_message(bus, m); | |
523 | if (r < 0) | |
524 | return r; | |
525 | ||
526 | *ret = m; | |
527 | m = NULL; | |
528 | ||
529 | return 1; | |
530 | } | |
531 | ||
532 | static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) { | |
533 | struct kdbus_item *d, *found = NULL; | |
534 | ||
535 | static int (* const translate[])(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item *d, sd_bus_message **ret) = { | |
536 | [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, | |
537 | [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, | |
538 | [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, | |
539 | ||
540 | [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, | |
541 | [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, | |
542 | ||
543 | [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, | |
544 | [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, | |
545 | }; | |
546 | ||
547 | assert(bus); | |
548 | assert(k); | |
549 | assert(ret); | |
550 | assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); | |
551 | ||
552 | KDBUS_PART_FOREACH(d, k, items) { | |
553 | if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) { | |
554 | if (found) | |
555 | return -EBADMSG; | |
556 | found = d; | |
557 | } else | |
558 | log_debug("Got unknown field from kernel %llu", d->type); | |
559 | } | |
560 | ||
561 | if (!found) { | |
562 | log_debug("Didn't find a kernel message to translate."); | |
563 | return 0; | |
564 | } | |
565 | ||
566 | return translate[found->type](bus, k, d, ret); | |
567 | } | |
568 | ||
569 | int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) { | |
570 | ||
571 | uint64_t m = 0; | |
572 | ||
573 | SET_FLAG(m, KDBUS_ATTACH_CREDS, | |
574 | !!(mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID))); | |
575 | ||
576 | SET_FLAG(m, KDBUS_ATTACH_COMM, | |
577 | !!(mask & (SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))); | |
578 | ||
579 | SET_FLAG(m, KDBUS_ATTACH_EXE, | |
580 | !!(mask & SD_BUS_CREDS_EXE)); | |
581 | ||
582 | SET_FLAG(m, KDBUS_ATTACH_CMDLINE, | |
583 | !!(mask & SD_BUS_CREDS_CMDLINE)); | |
584 | ||
585 | SET_FLAG(m, KDBUS_ATTACH_CGROUP, | |
586 | !!(mask & (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))); | |
587 | ||
588 | SET_FLAG(m, KDBUS_ATTACH_CAPS, | |
589 | !!(mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS))); | |
590 | ||
591 | SET_FLAG(m, KDBUS_ATTACH_SECLABEL, | |
592 | !!(mask & SD_BUS_CREDS_SELINUX_CONTEXT)); | |
593 | ||
594 | SET_FLAG(m, KDBUS_ATTACH_AUDIT, | |
595 | !!(mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))); | |
596 | ||
597 | *kdbus_mask = m; | |
598 | ||
599 | return 0; | |
600 | } | |
601 | ||
602 | static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) { | |
603 | sd_bus_message *m = NULL; | |
604 | struct kdbus_item *d; | |
605 | unsigned n_fds = 0; | |
606 | _cleanup_free_ int *fds = NULL; | |
607 | struct bus_header *h = NULL; | |
608 | size_t total, n_bytes = 0, idx = 0; | |
609 | const char *destination = NULL, *seclabel = NULL; | |
610 | int r; | |
611 | ||
612 | assert(bus); | |
613 | assert(k); | |
614 | assert(ret); | |
615 | assert(k->payload_type == KDBUS_PAYLOAD_DBUS1); | |
616 | ||
617 | KDBUS_PART_FOREACH(d, k, items) { | |
618 | size_t l; | |
619 | ||
620 | l = d->size - offsetof(struct kdbus_item, data); | |
621 | ||
622 | switch (d->type) { | |
623 | ||
624 | case KDBUS_ITEM_PAYLOAD_OFF: | |
625 | if (!h) { | |
626 | h = (struct bus_header *)((uint8_t *)bus->kdbus_buffer + d->vec.offset); | |
627 | ||
628 | if (!bus_header_is_complete(h, d->vec.size)) | |
629 | return -EBADMSG; | |
630 | } | |
631 | ||
632 | n_bytes += d->vec.size; | |
633 | break; | |
634 | ||
635 | case KDBUS_ITEM_PAYLOAD_MEMFD: | |
636 | if (!h) | |
637 | return -EBADMSG; | |
638 | ||
639 | n_bytes += d->memfd.size; | |
640 | break; | |
641 | ||
642 | case KDBUS_ITEM_FDS: { | |
643 | int *f; | |
644 | unsigned j; | |
645 | ||
646 | j = l / sizeof(int); | |
647 | f = realloc(fds, sizeof(int) * (n_fds + j)); | |
648 | if (!f) | |
649 | return -ENOMEM; | |
650 | ||
651 | fds = f; | |
652 | memcpy(fds + n_fds, d->fds, sizeof(int) * j); | |
653 | n_fds += j; | |
654 | break; | |
655 | } | |
656 | ||
657 | case KDBUS_ITEM_SECLABEL: | |
658 | seclabel = d->str; | |
659 | break; | |
660 | } | |
661 | } | |
662 | ||
663 | if (!h) | |
664 | return -EBADMSG; | |
665 | ||
666 | r = bus_header_message_size(h, &total); | |
667 | if (r < 0) | |
668 | return r; | |
669 | ||
670 | if (n_bytes != total) | |
671 | return -EBADMSG; | |
672 | ||
673 | r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m); | |
674 | if (r < 0) | |
675 | return r; | |
676 | ||
677 | KDBUS_PART_FOREACH(d, k, items) { | |
678 | size_t l; | |
679 | ||
680 | l = d->size - offsetof(struct kdbus_item, data); | |
681 | ||
682 | switch (d->type) { | |
683 | ||
684 | case KDBUS_ITEM_PAYLOAD_OFF: { | |
685 | size_t begin_body; | |
686 | ||
687 | begin_body = BUS_MESSAGE_BODY_BEGIN(m); | |
688 | ||
689 | if (idx + d->vec.size > begin_body) { | |
690 | struct bus_body_part *part; | |
691 | ||
692 | /* Contains body material */ | |
693 | ||
694 | part = message_append_part(m); | |
695 | if (!part) { | |
696 | r = -ENOMEM; | |
697 | goto fail; | |
698 | } | |
699 | ||
700 | /* A -1 offset is NUL padding. */ | |
701 | part->is_zero = d->vec.offset == ~0ULL; | |
702 | ||
703 | if (idx >= begin_body) { | |
704 | if (!part->is_zero) | |
705 | part->data = (uint8_t *)bus->kdbus_buffer + d->vec.offset; | |
706 | part->size = d->vec.size; | |
707 | } else { | |
708 | if (!part->is_zero) | |
709 | part->data = (uint8_t *)bus->kdbus_buffer + d->vec.offset + (begin_body - idx); | |
710 | part->size = d->vec.size - (begin_body - idx); | |
711 | } | |
712 | ||
713 | part->sealed = true; | |
714 | } | |
715 | ||
716 | idx += d->vec.size; | |
717 | break; | |
718 | } | |
719 | ||
720 | case KDBUS_ITEM_PAYLOAD_MEMFD: { | |
721 | struct bus_body_part *part; | |
722 | ||
723 | if (idx < BUS_MESSAGE_BODY_BEGIN(m)) { | |
724 | r = -EBADMSG; | |
725 | goto fail; | |
726 | } | |
727 | ||
728 | part = message_append_part(m); | |
729 | if (!part) { | |
730 | r = -ENOMEM; | |
731 | goto fail; | |
732 | } | |
733 | ||
734 | part->memfd = d->memfd.fd; | |
735 | part->size = d->memfd.size; | |
736 | part->sealed = true; | |
737 | ||
738 | idx += d->memfd.size; | |
739 | break; | |
740 | } | |
741 | ||
742 | case KDBUS_ITEM_CREDS: | |
743 | m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC; | |
744 | m->creds.uid = d->creds.uid; | |
745 | m->creds.gid = d->creds.gid; | |
746 | m->creds.pid = d->creds.pid; | |
747 | m->creds.tid = d->creds.tid; | |
748 | m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID) & bus->creds_mask; | |
749 | break; | |
750 | ||
751 | case KDBUS_ITEM_TIMESTAMP: | |
752 | m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC; | |
753 | m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC; | |
754 | break; | |
755 | ||
756 | case KDBUS_ITEM_PID_COMM: | |
757 | m->creds.comm = d->str; | |
758 | m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask; | |
759 | break; | |
760 | ||
761 | case KDBUS_ITEM_TID_COMM: | |
762 | m->creds.tid_comm = d->str; | |
763 | m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask; | |
764 | break; | |
765 | ||
766 | case KDBUS_ITEM_EXE: | |
767 | m->creds.exe = d->str; | |
768 | m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask; | |
769 | break; | |
770 | ||
771 | case KDBUS_ITEM_CMDLINE: | |
772 | m->creds.cmdline = d->str; | |
773 | m->creds.cmdline_length = l; | |
774 | m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask; | |
775 | break; | |
776 | ||
777 | case KDBUS_ITEM_CGROUP: | |
778 | m->creds.cgroup = d->str; | |
779 | m->creds.mask |= (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) & bus->creds_mask; | |
780 | break; | |
781 | ||
782 | case KDBUS_ITEM_AUDIT: | |
783 | m->creds.audit_session_id = d->audit.sessionid; | |
784 | m->creds.audit_login_uid = d->audit.loginuid; | |
785 | m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask; | |
786 | break; | |
787 | ||
788 | case KDBUS_ITEM_CAPS: | |
789 | m->creds.capability = d->data; | |
790 | m->creds.capability_size = l; | |
791 | m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask; | |
792 | break; | |
793 | ||
794 | case KDBUS_ITEM_DST_NAME: | |
795 | destination = d->str; | |
796 | break; | |
797 | ||
798 | case KDBUS_ITEM_FDS: | |
799 | case KDBUS_ITEM_SECLABEL: | |
800 | case KDBUS_ITEM_NAMES: | |
801 | break; | |
802 | ||
803 | default: | |
804 | log_debug("Got unknown field from kernel %llu", d->type); | |
805 | } | |
806 | } | |
807 | ||
808 | r = bus_message_parse_fields(m); | |
809 | if (r < 0) | |
810 | goto fail; | |
811 | ||
812 | if (k->src_id == KDBUS_SRC_ID_KERNEL) | |
813 | m->sender = "org.freedesktop.DBus"; | |
814 | else { | |
815 | snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id); | |
816 | m->sender = m->sender_buffer; | |
817 | } | |
818 | ||
819 | if (!m->destination) { | |
820 | if (destination) | |
821 | m->destination = destination; | |
822 | else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME && | |
823 | k->dst_id != KDBUS_DST_ID_BROADCAST) { | |
824 | snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id); | |
825 | m->destination = m->destination_buffer; | |
826 | } | |
827 | } | |
828 | ||
829 | /* We take possession of the kmsg struct now */ | |
830 | m->kdbus = k; | |
831 | m->release_kdbus = true; | |
832 | m->free_fds = true; | |
833 | ||
834 | fds = NULL; | |
835 | ||
836 | *ret = m; | |
837 | return 1; | |
838 | ||
839 | fail: | |
840 | if (m) { | |
841 | struct bus_body_part *part; | |
842 | unsigned i; | |
843 | ||
844 | /* Make sure the memfds are not freed twice */ | |
845 | MESSAGE_FOREACH_PART(part, i, m) | |
846 | if (part->memfd >= 0) | |
847 | part->memfd = -1; | |
848 | ||
849 | sd_bus_message_unref(m); | |
850 | } | |
851 | ||
852 | return r; | |
853 | } | |
854 | ||
855 | int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) { | |
856 | uint64_t off; | |
857 | struct kdbus_msg *k; | |
858 | int r; | |
859 | ||
860 | assert(bus); | |
861 | assert(m); | |
862 | ||
863 | r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, &off); | |
864 | if (r < 0) { | |
865 | if (errno == EAGAIN) | |
866 | return 0; | |
867 | ||
868 | return -errno; | |
869 | } | |
870 | k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + off); | |
871 | ||
872 | if (k->payload_type == KDBUS_PAYLOAD_DBUS1) | |
873 | r = bus_kernel_make_message(bus, k, m); | |
874 | else if (k->payload_type == KDBUS_PAYLOAD_KERNEL) | |
875 | r = bus_kernel_translate_message(bus, k, m); | |
876 | else | |
877 | r = 0; | |
878 | ||
879 | if (r <= 0) | |
880 | close_kdbus_msg(bus, k); | |
881 | ||
882 | return r < 0 ? r : 1; | |
883 | } | |
884 | ||
885 | int bus_kernel_create(const char *name, char **s) { | |
886 | struct kdbus_cmd_bus_make *make; | |
887 | struct kdbus_item *n; | |
888 | size_t l; | |
889 | int fd; | |
890 | char *p; | |
891 | ||
892 | assert(name); | |
893 | assert(s); | |
894 | ||
895 | fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); | |
896 | if (fd < 0) | |
897 | return -errno; | |
898 | ||
899 | l = strlen(name); | |
900 | make = alloca0(offsetof(struct kdbus_cmd_bus_make, items) + | |
901 | KDBUS_PART_HEADER_SIZE + sizeof(uint64_t) + | |
902 | KDBUS_PART_HEADER_SIZE + DECIMAL_STR_MAX(uid_t) + 1 + l + 1); | |
903 | ||
904 | n = make->items; | |
905 | n->type = KDBUS_MAKE_NAME; | |
906 | sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name); | |
907 | n->size = KDBUS_PART_HEADER_SIZE + strlen(n->str) + 1; | |
908 | ||
909 | make->size = offsetof(struct kdbus_cmd_bus_make, items) + n->size; | |
910 | make->flags = KDBUS_MAKE_POLICY_OPEN; | |
911 | make->bus_flags = 0; | |
912 | make->bloom_size = BLOOM_SIZE; | |
913 | assert_cc(BLOOM_SIZE % 8 == 0); | |
914 | ||
915 | p = strjoin("/dev/kdbus/", n->str, "/bus", NULL); | |
916 | if (!p) | |
917 | return -ENOMEM; | |
918 | ||
919 | if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { | |
920 | close_nointr_nofail(fd); | |
921 | free(p); | |
922 | return -errno; | |
923 | } | |
924 | ||
925 | if (s) | |
926 | *s = p; | |
927 | ||
928 | return fd; | |
929 | } | |
930 | ||
931 | int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *size) { | |
932 | struct memfd_cache *c; | |
933 | int fd; | |
934 | ||
935 | assert(address); | |
936 | assert(size); | |
937 | ||
938 | if (!bus || !bus->is_kernel) | |
939 | return -ENOTSUP; | |
940 | ||
941 | assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0); | |
942 | ||
943 | if (bus->n_memfd_cache <= 0) { | |
944 | int r; | |
945 | ||
946 | assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0); | |
947 | ||
948 | r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, &fd); | |
949 | if (r < 0) | |
950 | return -errno; | |
951 | ||
952 | *address = NULL; | |
953 | *size = 0; | |
954 | return fd; | |
955 | } | |
956 | ||
957 | c = &bus->memfd_cache[--bus->n_memfd_cache]; | |
958 | ||
959 | assert(c->fd >= 0); | |
960 | assert(c->size == 0 || c->address); | |
961 | ||
962 | *address = c->address; | |
963 | *size = c->size; | |
964 | fd = c->fd; | |
965 | ||
966 | assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0); | |
967 | ||
968 | return fd; | |
969 | } | |
970 | ||
971 | static void close_and_munmap(int fd, void *address, size_t size) { | |
972 | if (size > 0) | |
973 | assert_se(munmap(address, PAGE_ALIGN(size)) >= 0); | |
974 | ||
975 | close_nointr_nofail(fd); | |
976 | } | |
977 | ||
978 | void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t size) { | |
979 | struct memfd_cache *c; | |
980 | uint64_t max_sz = PAGE_ALIGN(MEMFD_CACHE_ITEM_SIZE_MAX); | |
981 | ||
982 | assert(fd >= 0); | |
983 | assert(size == 0 || address); | |
984 | ||
985 | if (!bus || !bus->is_kernel) { | |
986 | close_and_munmap(fd, address, size); | |
987 | return; | |
988 | } | |
989 | ||
990 | assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0); | |
991 | ||
992 | if (bus->n_memfd_cache >= ELEMENTSOF(bus->memfd_cache)) { | |
993 | assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0); | |
994 | ||
995 | close_and_munmap(fd, address, size); | |
996 | return; | |
997 | } | |
998 | ||
999 | c = &bus->memfd_cache[bus->n_memfd_cache++]; | |
1000 | c->fd = fd; | |
1001 | c->address = address; | |
1002 | ||
1003 | /* If overly long, let's return a bit to the OS */ | |
1004 | if (size > max_sz) { | |
1005 | assert_se(ioctl(fd, KDBUS_CMD_MEMFD_SIZE_SET, &max_sz) >= 0); | |
1006 | assert_se(munmap((uint8_t*) address + max_sz, PAGE_ALIGN(size - max_sz)) >= 0); | |
1007 | c->size = max_sz; | |
1008 | } else | |
1009 | c->size = size; | |
1010 | ||
1011 | assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0); | |
1012 | } | |
1013 | ||
1014 | void bus_kernel_flush_memfd(sd_bus *b) { | |
1015 | unsigned i; | |
1016 | ||
1017 | assert(b); | |
1018 | ||
1019 | for (i = 0; i < b->n_memfd_cache; i++) | |
1020 | close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].size); | |
1021 | } | |
1022 | ||
1023 | int kdbus_translate_request_name_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags) { | |
1024 | ||
1025 | assert_return(kdbus_flags != NULL, -EINVAL); | |
1026 | ||
1027 | *kdbus_flags = 0; | |
1028 | ||
1029 | if (sd_bus_flags & SD_BUS_NAME_ALLOW_REPLACEMENT) | |
1030 | *kdbus_flags |= KDBUS_NAME_ALLOW_REPLACEMENT; | |
1031 | ||
1032 | if (sd_bus_flags & SD_BUS_NAME_REPLACE_EXISTING) | |
1033 | *kdbus_flags |= KDBUS_NAME_REPLACE_EXISTING; | |
1034 | ||
1035 | if (!(sd_bus_flags & SD_BUS_NAME_DO_NOT_QUEUE)) | |
1036 | *kdbus_flags |= KDBUS_NAME_QUEUE; | |
1037 | ||
1038 | return 0; | |
1039 | } |