]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-control.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-control.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #if HAVE_VALGRIND_MEMCHECK_H
22 #include <valgrind/memcheck.h>
23 #endif
24
25 #include <errno.h>
26 #include <stddef.h>
27
28 #include "sd-bus.h"
29
30 #include "alloc-util.h"
31 #include "bus-bloom.h"
32 #include "bus-control.h"
33 #include "bus-internal.h"
34 #include "bus-message.h"
35 #include "bus-util.h"
36 #include "capability-util.h"
37 #include "stdio-util.h"
38 #include "string-util.h"
39 #include "strv.h"
40 #include "user-util.h"
41
42 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
43 int r;
44
45 assert_return(bus, -EINVAL);
46 assert_return(unique, -EINVAL);
47 assert_return(!bus_pid_changed(bus), -ECHILD);
48
49 if (!bus->bus_client)
50 return -EINVAL;
51
52 r = bus_ensure_running(bus);
53 if (r < 0)
54 return r;
55
56 *unique = bus->unique_name;
57 return 0;
58 }
59
60 static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) {
61 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
62 uint32_t ret, param = 0;
63 int r;
64
65 assert(bus);
66 assert(name);
67
68 if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT)
69 param |= BUS_NAME_ALLOW_REPLACEMENT;
70 if (flags & SD_BUS_NAME_REPLACE_EXISTING)
71 param |= BUS_NAME_REPLACE_EXISTING;
72 if (!(flags & SD_BUS_NAME_QUEUE))
73 param |= BUS_NAME_DO_NOT_QUEUE;
74
75 r = sd_bus_call_method(
76 bus,
77 "org.freedesktop.DBus",
78 "/org/freedesktop/DBus",
79 "org.freedesktop.DBus",
80 "RequestName",
81 NULL,
82 &reply,
83 "su",
84 name,
85 param);
86 if (r < 0)
87 return r;
88
89 r = sd_bus_message_read(reply, "u", &ret);
90 if (r < 0)
91 return r;
92
93 if (ret == BUS_NAME_ALREADY_OWNER)
94 return -EALREADY;
95 else if (ret == BUS_NAME_EXISTS)
96 return -EEXIST;
97 else if (ret == BUS_NAME_IN_QUEUE)
98 return 0;
99 else if (ret == BUS_NAME_PRIMARY_OWNER)
100 return 1;
101
102 return -EIO;
103 }
104
105 _public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) {
106 assert_return(bus, -EINVAL);
107 assert_return(name, -EINVAL);
108 assert_return(!bus_pid_changed(bus), -ECHILD);
109 assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL);
110 assert_return(service_name_is_valid(name), -EINVAL);
111 assert_return(name[0] != ':', -EINVAL);
112
113 if (!bus->bus_client)
114 return -EINVAL;
115
116 /* Don't allow requesting the special driver and local names */
117 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
118 return -EINVAL;
119
120 if (!BUS_IS_OPEN(bus->state))
121 return -ENOTCONN;
122
123 return bus_request_name_dbus1(bus, name, flags);
124 }
125
126 static int bus_release_name_dbus1(sd_bus *bus, const char *name) {
127 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
128 uint32_t ret;
129 int r;
130
131 assert(bus);
132 assert(name);
133
134 r = sd_bus_call_method(
135 bus,
136 "org.freedesktop.DBus",
137 "/org/freedesktop/DBus",
138 "org.freedesktop.DBus",
139 "ReleaseName",
140 NULL,
141 &reply,
142 "s",
143 name);
144 if (r < 0)
145 return r;
146
147 r = sd_bus_message_read(reply, "u", &ret);
148 if (r < 0)
149 return r;
150 if (ret == BUS_NAME_NON_EXISTENT)
151 return -ESRCH;
152 if (ret == BUS_NAME_NOT_OWNER)
153 return -EADDRINUSE;
154 if (ret == BUS_NAME_RELEASED)
155 return 0;
156
157 return -EINVAL;
158 }
159
160 _public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
161 assert_return(bus, -EINVAL);
162 assert_return(name, -EINVAL);
163 assert_return(!bus_pid_changed(bus), -ECHILD);
164 assert_return(service_name_is_valid(name), -EINVAL);
165 assert_return(name[0] != ':', -EINVAL);
166
167 if (!bus->bus_client)
168 return -EINVAL;
169
170 /* Don't allow releasing the special driver and local names */
171 if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local"))
172 return -EINVAL;
173
174 if (!BUS_IS_OPEN(bus->state))
175 return -ENOTCONN;
176
177 return bus_release_name_dbus1(bus, name);
178 }
179
180 static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) {
181 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
182 _cleanup_strv_free_ char **x = NULL, **y = NULL;
183 int r;
184
185 if (acquired) {
186 r = sd_bus_call_method(
187 bus,
188 "org.freedesktop.DBus",
189 "/org/freedesktop/DBus",
190 "org.freedesktop.DBus",
191 "ListNames",
192 NULL,
193 &reply,
194 NULL);
195 if (r < 0)
196 return r;
197
198 r = sd_bus_message_read_strv(reply, &x);
199 if (r < 0)
200 return r;
201
202 reply = sd_bus_message_unref(reply);
203 }
204
205 if (activatable) {
206 r = sd_bus_call_method(
207 bus,
208 "org.freedesktop.DBus",
209 "/org/freedesktop/DBus",
210 "org.freedesktop.DBus",
211 "ListActivatableNames",
212 NULL,
213 &reply,
214 NULL);
215 if (r < 0)
216 return r;
217
218 r = sd_bus_message_read_strv(reply, &y);
219 if (r < 0)
220 return r;
221
222 *activatable = y;
223 y = NULL;
224 }
225
226 if (acquired) {
227 *acquired = x;
228 x = NULL;
229 }
230
231 return 0;
232 }
233
234 _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) {
235 assert_return(bus, -EINVAL);
236 assert_return(acquired || activatable, -EINVAL);
237 assert_return(!bus_pid_changed(bus), -ECHILD);
238
239 if (!bus->bus_client)
240 return -EINVAL;
241
242 if (!BUS_IS_OPEN(bus->state))
243 return -ENOTCONN;
244
245 return bus_list_names_dbus1(bus, acquired, activatable);
246 }
247
248 static int bus_get_name_creds_dbus1(
249 sd_bus *bus,
250 const char *name,
251 uint64_t mask,
252 sd_bus_creds **creds) {
253
254 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
255 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
256 const char *unique = NULL;
257 pid_t pid = 0;
258 int r;
259
260 /* Only query the owner if the caller wants to know it or if
261 * the caller just wants to check whether a name exists */
262 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
263 r = sd_bus_call_method(
264 bus,
265 "org.freedesktop.DBus",
266 "/org/freedesktop/DBus",
267 "org.freedesktop.DBus",
268 "GetNameOwner",
269 NULL,
270 &reply_unique,
271 "s",
272 name);
273 if (r < 0)
274 return r;
275
276 r = sd_bus_message_read(reply_unique, "s", &unique);
277 if (r < 0)
278 return r;
279 }
280
281 if (mask != 0) {
282 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
283 bool need_pid, need_uid, need_selinux, need_separate_calls;
284 c = bus_creds_new();
285 if (!c)
286 return -ENOMEM;
287
288 if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
289 c->unique_name = strdup(unique);
290 if (!c->unique_name)
291 return -ENOMEM;
292
293 c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
294 }
295
296 need_pid = (mask & SD_BUS_CREDS_PID) ||
297 ((mask & SD_BUS_CREDS_AUGMENT) &&
298 (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
299 SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
300 SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
301 SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
302 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|
303 SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
304 SD_BUS_CREDS_SELINUX_CONTEXT|
305 SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)));
306 need_uid = mask & SD_BUS_CREDS_EUID;
307 need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT;
308
309 if (need_pid + need_uid + need_selinux > 1) {
310
311 /* If we need more than one of the credentials, then use GetConnectionCredentials() */
312
313 r = sd_bus_call_method(
314 bus,
315 "org.freedesktop.DBus",
316 "/org/freedesktop/DBus",
317 "org.freedesktop.DBus",
318 "GetConnectionCredentials",
319 &error,
320 &reply,
321 "s",
322 unique ?: name);
323
324 if (r < 0) {
325
326 if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
327 return r;
328
329 /* If we got an unknown method error, fall back to the invidual calls... */
330 need_separate_calls = true;
331 sd_bus_error_free(&error);
332
333 } else {
334 need_separate_calls = false;
335
336 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
337 if (r < 0)
338 return r;
339
340 for (;;) {
341 const char *m;
342
343 r = sd_bus_message_enter_container(reply, 'e', "sv");
344 if (r < 0)
345 return r;
346 if (r == 0)
347 break;
348
349 r = sd_bus_message_read(reply, "s", &m);
350 if (r < 0)
351 return r;
352
353 if (need_uid && streq(m, "UnixUserID")) {
354 uint32_t u;
355
356 r = sd_bus_message_read(reply, "v", "u", &u);
357 if (r < 0)
358 return r;
359
360 c->euid = u;
361 c->mask |= SD_BUS_CREDS_EUID;
362
363 } else if (need_pid && streq(m, "ProcessID")) {
364 uint32_t p;
365
366 r = sd_bus_message_read(reply, "v", "u", &p);
367 if (r < 0)
368 return r;
369
370 pid = p;
371 if (mask & SD_BUS_CREDS_PID) {
372 c->pid = p;
373 c->mask |= SD_BUS_CREDS_PID;
374 }
375
376 } else if (need_selinux && streq(m, "LinuxSecurityLabel")) {
377 const void *p = NULL;
378 size_t sz = 0;
379
380 r = sd_bus_message_enter_container(reply, 'v', "ay");
381 if (r < 0)
382 return r;
383
384 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
385 if (r < 0)
386 return r;
387
388 free(c->label);
389 c->label = strndup(p, sz);
390 if (!c->label)
391 return -ENOMEM;
392
393 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
394
395 r = sd_bus_message_exit_container(reply);
396 if (r < 0)
397 return r;
398 } else {
399 r = sd_bus_message_skip(reply, "v");
400 if (r < 0)
401 return r;
402 }
403
404 r = sd_bus_message_exit_container(reply);
405 if (r < 0)
406 return r;
407 }
408
409 r = sd_bus_message_exit_container(reply);
410 if (r < 0)
411 return r;
412
413 if (need_pid && pid == 0)
414 return -EPROTO;
415 }
416
417 } else /* When we only need a single field, then let's use separate calls */
418 need_separate_calls = true;
419
420 if (need_separate_calls) {
421 if (need_pid) {
422 uint32_t u;
423
424 r = sd_bus_call_method(
425 bus,
426 "org.freedesktop.DBus",
427 "/org/freedesktop/DBus",
428 "org.freedesktop.DBus",
429 "GetConnectionUnixProcessID",
430 NULL,
431 &reply,
432 "s",
433 unique ?: name);
434 if (r < 0)
435 return r;
436
437 r = sd_bus_message_read(reply, "u", &u);
438 if (r < 0)
439 return r;
440
441 pid = u;
442 if (mask & SD_BUS_CREDS_PID) {
443 c->pid = u;
444 c->mask |= SD_BUS_CREDS_PID;
445 }
446
447 reply = sd_bus_message_unref(reply);
448 }
449
450 if (need_uid) {
451 uint32_t u;
452
453 r = sd_bus_call_method(
454 bus,
455 "org.freedesktop.DBus",
456 "/org/freedesktop/DBus",
457 "org.freedesktop.DBus",
458 "GetConnectionUnixUser",
459 NULL,
460 &reply,
461 "s",
462 unique ? unique : name);
463 if (r < 0)
464 return r;
465
466 r = sd_bus_message_read(reply, "u", &u);
467 if (r < 0)
468 return r;
469
470 c->euid = u;
471 c->mask |= SD_BUS_CREDS_EUID;
472
473 reply = sd_bus_message_unref(reply);
474 }
475
476 if (need_selinux) {
477 const void *p = NULL;
478 size_t sz = 0;
479
480 r = sd_bus_call_method(
481 bus,
482 "org.freedesktop.DBus",
483 "/org/freedesktop/DBus",
484 "org.freedesktop.DBus",
485 "GetConnectionSELinuxSecurityContext",
486 &error,
487 &reply,
488 "s",
489 unique ? unique : name);
490 if (r < 0) {
491 if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
492 return r;
493
494 /* no data is fine */
495 } else {
496 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
497 if (r < 0)
498 return r;
499
500 c->label = strndup(p, sz);
501 if (!c->label)
502 return -ENOMEM;
503
504 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
505 }
506 }
507 }
508
509 r = bus_creds_add_more(c, mask, pid, 0);
510 if (r < 0)
511 return r;
512 }
513
514 if (creds) {
515 *creds = c;
516 c = NULL;
517 }
518
519 return 0;
520 }
521
522 _public_ int sd_bus_get_name_creds(
523 sd_bus *bus,
524 const char *name,
525 uint64_t mask,
526 sd_bus_creds **creds) {
527
528 assert_return(bus, -EINVAL);
529 assert_return(name, -EINVAL);
530 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
531 assert_return(mask == 0 || creds, -EINVAL);
532 assert_return(!bus_pid_changed(bus), -ECHILD);
533 assert_return(service_name_is_valid(name), -EINVAL);
534
535 if (!bus->bus_client)
536 return -EINVAL;
537
538 /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not
539 * going to match. */
540 if (!bus->is_local)
541 mask &= ~SD_BUS_CREDS_AUGMENT;
542
543 if (streq(name, "org.freedesktop.DBus.Local"))
544 return -EINVAL;
545
546 if (streq(name, "org.freedesktop.DBus"))
547 return sd_bus_get_owner_creds(bus, mask, creds);
548
549 if (!BUS_IS_OPEN(bus->state))
550 return -ENOTCONN;
551
552 return bus_get_name_creds_dbus1(bus, name, mask, creds);
553 }
554
555 static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
556 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
557 pid_t pid = 0;
558 bool do_label;
559 int r;
560
561 assert(bus);
562
563 do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT);
564
565 /* Avoid allocating anything if we have no chance of returning useful data */
566 if (!bus->ucred_valid && !do_label)
567 return -ENODATA;
568
569 c = bus_creds_new();
570 if (!c)
571 return -ENOMEM;
572
573 if (bus->ucred_valid) {
574 if (bus->ucred.pid > 0) {
575 pid = c->pid = bus->ucred.pid;
576 c->mask |= SD_BUS_CREDS_PID & mask;
577 }
578
579 if (bus->ucred.uid != UID_INVALID) {
580 c->euid = bus->ucred.uid;
581 c->mask |= SD_BUS_CREDS_EUID & mask;
582 }
583
584 if (bus->ucred.gid != GID_INVALID) {
585 c->egid = bus->ucred.gid;
586 c->mask |= SD_BUS_CREDS_EGID & mask;
587 }
588 }
589
590 if (do_label) {
591 c->label = strdup(bus->label);
592 if (!c->label)
593 return -ENOMEM;
594
595 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
596 }
597
598 r = bus_creds_add_more(c, mask, pid, 0);
599 if (r < 0)
600 return r;
601
602 *ret = c;
603 c = NULL;
604 return 0;
605 }
606
607 _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
608 assert_return(bus, -EINVAL);
609 assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
610 assert_return(ret, -EINVAL);
611 assert_return(!bus_pid_changed(bus), -ECHILD);
612
613 if (!BUS_IS_OPEN(bus->state))
614 return -ENOTCONN;
615
616 if (!bus->is_local)
617 mask &= ~SD_BUS_CREDS_AUGMENT;
618
619 return bus_get_owner_creds_dbus1(bus, mask, ret);
620 }
621
622 #define internal_match(bus, m) \
623 ((bus)->hello_flags & KDBUS_HELLO_MONITOR \
624 ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \
625 : (m))
626
627 static int bus_add_match_internal_dbus1(
628 sd_bus *bus,
629 const char *match) {
630
631 const char *e;
632
633 assert(bus);
634 assert(match);
635
636 e = internal_match(bus, match);
637
638 return sd_bus_call_method(
639 bus,
640 "org.freedesktop.DBus",
641 "/org/freedesktop/DBus",
642 "org.freedesktop.DBus",
643 "AddMatch",
644 NULL,
645 NULL,
646 "s",
647 e);
648 }
649
650 int bus_add_match_internal(
651 sd_bus *bus,
652 const char *match,
653 struct bus_match_component *components,
654 unsigned n_components) {
655
656 assert(bus);
657
658 if (!bus->bus_client)
659 return -EINVAL;
660
661 return bus_add_match_internal_dbus1(bus, match);
662 }
663
664 static int bus_remove_match_internal_dbus1(
665 sd_bus *bus,
666 const char *match) {
667
668 const char *e;
669
670 assert(bus);
671 assert(match);
672
673 e = internal_match(bus, match);
674
675 return sd_bus_call_method(
676 bus,
677 "org.freedesktop.DBus",
678 "/org/freedesktop/DBus",
679 "org.freedesktop.DBus",
680 "RemoveMatch",
681 NULL,
682 NULL,
683 "s",
684 e);
685 }
686
687 int bus_remove_match_internal(
688 sd_bus *bus,
689 const char *match) {
690
691 assert(bus);
692
693 if (!bus->bus_client)
694 return -EINVAL;
695
696 return bus_remove_match_internal_dbus1(bus, match);
697 }
698
699 _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
700 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
701 const char *mid;
702 int r;
703
704 assert_return(bus, -EINVAL);
705 assert_return(name, -EINVAL);
706 assert_return(machine, -EINVAL);
707 assert_return(!bus_pid_changed(bus), -ECHILD);
708 assert_return(service_name_is_valid(name), -EINVAL);
709
710 if (!bus->bus_client)
711 return -EINVAL;
712
713 if (!BUS_IS_OPEN(bus->state))
714 return -ENOTCONN;
715
716 if (streq_ptr(name, bus->unique_name))
717 return sd_id128_get_machine(machine);
718
719 r = sd_bus_message_new_method_call(
720 bus,
721 &m,
722 name,
723 "/",
724 "org.freedesktop.DBus.Peer",
725 "GetMachineId");
726 if (r < 0)
727 return r;
728
729 r = sd_bus_message_set_auto_start(m, false);
730 if (r < 0)
731 return r;
732
733 r = sd_bus_call(bus, m, 0, NULL, &reply);
734 if (r < 0)
735 return r;
736
737 r = sd_bus_message_read(reply, "s", &mid);
738 if (r < 0)
739 return r;
740
741 return sd_id128_from_string(mid, machine);
742 }