]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include "sd-bus.h" | |
4 | ||
5 | #include "alloc-util.h" | |
6 | #include "bus-control.h" | |
7 | #include "bus-internal.h" | |
8 | #include "bus-message.h" | |
9 | #include "fd-util.h" | |
10 | #include "log.h" | |
11 | #include "pidref.h" | |
12 | #include "process-util.h" | |
13 | #include "string-util.h" | |
14 | #include "strv.h" | |
15 | #include "user-util.h" | |
16 | ||
17 | _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { | |
18 | int r; | |
19 | ||
20 | assert_return(bus, -EINVAL); | |
21 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
22 | assert_return(unique, -EINVAL); | |
23 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
24 | ||
25 | if (!bus->bus_client) | |
26 | return -EINVAL; | |
27 | ||
28 | r = bus_ensure_running(bus); | |
29 | if (r < 0) | |
30 | return r; | |
31 | ||
32 | *unique = bus->unique_name; | |
33 | return 0; | |
34 | } | |
35 | ||
36 | static int validate_request_name_parameters( | |
37 | sd_bus *bus, | |
38 | const char *name, | |
39 | uint64_t flags, | |
40 | uint32_t *ret_param) { | |
41 | ||
42 | uint32_t param = 0; | |
43 | ||
44 | assert(bus); | |
45 | assert(name); | |
46 | assert(ret_param); | |
47 | ||
48 | assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); | |
49 | assert_return(service_name_is_valid(name), -EINVAL); | |
50 | assert_return(name[0] != ':', -EINVAL); | |
51 | ||
52 | if (!bus->bus_client) | |
53 | return -EINVAL; | |
54 | ||
55 | /* Don't allow requesting the special driver and local names */ | |
56 | if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) | |
57 | return -EINVAL; | |
58 | ||
59 | if (!BUS_IS_OPEN(bus->state)) | |
60 | return -ENOTCONN; | |
61 | ||
62 | if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) | |
63 | param |= BUS_NAME_ALLOW_REPLACEMENT; | |
64 | if (flags & SD_BUS_NAME_REPLACE_EXISTING) | |
65 | param |= BUS_NAME_REPLACE_EXISTING; | |
66 | if (!(flags & SD_BUS_NAME_QUEUE)) | |
67 | param |= BUS_NAME_DO_NOT_QUEUE; | |
68 | ||
69 | *ret_param = param; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | _public_ int sd_bus_request_name( | |
75 | sd_bus *bus, | |
76 | const char *name, | |
77 | uint64_t flags) { | |
78 | ||
79 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
80 | uint32_t ret, param = 0; | |
81 | int r; | |
82 | ||
83 | assert_return(bus, -EINVAL); | |
84 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
85 | assert_return(name, -EINVAL); | |
86 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
87 | ||
88 | r = validate_request_name_parameters(bus, name, flags, ¶m); | |
89 | if (r < 0) | |
90 | return r; | |
91 | ||
92 | r = sd_bus_call_method( | |
93 | bus, | |
94 | "org.freedesktop.DBus", | |
95 | "/org/freedesktop/DBus", | |
96 | "org.freedesktop.DBus", | |
97 | "RequestName", | |
98 | NULL, | |
99 | &reply, | |
100 | "su", | |
101 | name, | |
102 | param); | |
103 | if (r < 0) | |
104 | return r; | |
105 | ||
106 | r = sd_bus_message_read(reply, "u", &ret); | |
107 | if (r < 0) | |
108 | return r; | |
109 | ||
110 | switch (ret) { | |
111 | ||
112 | case BUS_NAME_ALREADY_OWNER: | |
113 | return -EALREADY; | |
114 | ||
115 | case BUS_NAME_EXISTS: | |
116 | return -EEXIST; | |
117 | ||
118 | case BUS_NAME_IN_QUEUE: | |
119 | return 0; | |
120 | ||
121 | case BUS_NAME_PRIMARY_OWNER: | |
122 | return 1; | |
123 | } | |
124 | ||
125 | return -EIO; | |
126 | } | |
127 | ||
128 | static int default_request_name_handler( | |
129 | sd_bus_message *m, | |
130 | void *userdata, | |
131 | sd_bus_error *ret_error) { | |
132 | ||
133 | uint32_t ret; | |
134 | int r; | |
135 | ||
136 | assert(m); | |
137 | ||
138 | if (sd_bus_message_is_method_error(m, NULL)) { | |
139 | log_debug_errno(sd_bus_message_get_errno(m), | |
140 | "Unable to request name, failing connection: %s", | |
141 | sd_bus_message_get_error(m)->message); | |
142 | ||
143 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
144 | return 1; | |
145 | } | |
146 | ||
147 | r = sd_bus_message_read(m, "u", &ret); | |
148 | if (r < 0) | |
149 | return r; | |
150 | ||
151 | switch (ret) { | |
152 | ||
153 | case BUS_NAME_ALREADY_OWNER: | |
154 | log_debug("Already owner of requested service name, ignoring."); | |
155 | return 1; | |
156 | ||
157 | case BUS_NAME_IN_QUEUE: | |
158 | log_debug("In queue for requested service name."); | |
159 | return 1; | |
160 | ||
161 | case BUS_NAME_PRIMARY_OWNER: | |
162 | log_debug("Successfully acquired requested service name."); | |
163 | return 1; | |
164 | ||
165 | case BUS_NAME_EXISTS: | |
166 | log_debug("Requested service name already owned, failing connection."); | |
167 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
168 | return 1; | |
169 | } | |
170 | ||
171 | log_debug("Unexpected response from RequestName(), failing connection."); | |
172 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
173 | return 1; | |
174 | } | |
175 | ||
176 | _public_ int sd_bus_request_name_async( | |
177 | sd_bus *bus, | |
178 | sd_bus_slot **ret_slot, | |
179 | const char *name, | |
180 | uint64_t flags, | |
181 | sd_bus_message_handler_t callback, | |
182 | void *userdata) { | |
183 | ||
184 | uint32_t param = 0; | |
185 | int r; | |
186 | ||
187 | assert_return(bus, -EINVAL); | |
188 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
189 | assert_return(name, -EINVAL); | |
190 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
191 | ||
192 | r = validate_request_name_parameters(bus, name, flags, ¶m); | |
193 | if (r < 0) | |
194 | return r; | |
195 | ||
196 | return sd_bus_call_method_async( | |
197 | bus, | |
198 | ret_slot, | |
199 | "org.freedesktop.DBus", | |
200 | "/org/freedesktop/DBus", | |
201 | "org.freedesktop.DBus", | |
202 | "RequestName", | |
203 | callback ?: default_request_name_handler, | |
204 | userdata, | |
205 | "su", | |
206 | name, | |
207 | param); | |
208 | } | |
209 | ||
210 | static int validate_release_name_parameters( | |
211 | sd_bus *bus, | |
212 | const char *name) { | |
213 | ||
214 | assert(bus); | |
215 | assert(name); | |
216 | ||
217 | assert_return(service_name_is_valid(name), -EINVAL); | |
218 | assert_return(name[0] != ':', -EINVAL); | |
219 | ||
220 | if (!bus->bus_client) | |
221 | return -EINVAL; | |
222 | ||
223 | /* Don't allow releasing the special driver and local names */ | |
224 | if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) | |
225 | return -EINVAL; | |
226 | ||
227 | if (!BUS_IS_OPEN(bus->state)) | |
228 | return -ENOTCONN; | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | _public_ int sd_bus_release_name( | |
234 | sd_bus *bus, | |
235 | const char *name) { | |
236 | ||
237 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
238 | uint32_t ret; | |
239 | int r; | |
240 | ||
241 | assert_return(bus, -EINVAL); | |
242 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
243 | assert_return(name, -EINVAL); | |
244 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
245 | ||
246 | r = validate_release_name_parameters(bus, name); | |
247 | if (r < 0) | |
248 | return r; | |
249 | ||
250 | r = sd_bus_call_method( | |
251 | bus, | |
252 | "org.freedesktop.DBus", | |
253 | "/org/freedesktop/DBus", | |
254 | "org.freedesktop.DBus", | |
255 | "ReleaseName", | |
256 | NULL, | |
257 | &reply, | |
258 | "s", | |
259 | name); | |
260 | if (r < 0) | |
261 | return r; | |
262 | ||
263 | r = sd_bus_message_read(reply, "u", &ret); | |
264 | if (r < 0) | |
265 | return r; | |
266 | ||
267 | switch (ret) { | |
268 | ||
269 | case BUS_NAME_NON_EXISTENT: | |
270 | return -ESRCH; | |
271 | ||
272 | case BUS_NAME_NOT_OWNER: | |
273 | return -EADDRINUSE; | |
274 | ||
275 | case BUS_NAME_RELEASED: | |
276 | return 0; | |
277 | } | |
278 | ||
279 | return -EIO; | |
280 | } | |
281 | ||
282 | static int default_release_name_handler( | |
283 | sd_bus_message *m, | |
284 | void *userdata, | |
285 | sd_bus_error *ret_error) { | |
286 | ||
287 | uint32_t ret; | |
288 | int r; | |
289 | ||
290 | assert(m); | |
291 | ||
292 | if (sd_bus_message_is_method_error(m, NULL)) { | |
293 | log_debug_errno(sd_bus_message_get_errno(m), | |
294 | "Unable to release name, failing connection: %s", | |
295 | sd_bus_message_get_error(m)->message); | |
296 | ||
297 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
298 | return 1; | |
299 | } | |
300 | ||
301 | r = sd_bus_message_read(m, "u", &ret); | |
302 | if (r < 0) | |
303 | return r; | |
304 | ||
305 | switch (ret) { | |
306 | ||
307 | case BUS_NAME_NON_EXISTENT: | |
308 | log_debug("Name asked to release is not taken currently, ignoring."); | |
309 | return 1; | |
310 | ||
311 | case BUS_NAME_NOT_OWNER: | |
312 | log_debug("Name asked to release is owned by somebody else, ignoring."); | |
313 | return 1; | |
314 | ||
315 | case BUS_NAME_RELEASED: | |
316 | log_debug("Name successfully released."); | |
317 | return 1; | |
318 | } | |
319 | ||
320 | log_debug("Unexpected response from ReleaseName(), failing connection."); | |
321 | bus_enter_closing(sd_bus_message_get_bus(m)); | |
322 | return 1; | |
323 | } | |
324 | ||
325 | _public_ int sd_bus_release_name_async( | |
326 | sd_bus *bus, | |
327 | sd_bus_slot **ret_slot, | |
328 | const char *name, | |
329 | sd_bus_message_handler_t callback, | |
330 | void *userdata) { | |
331 | ||
332 | int r; | |
333 | ||
334 | assert_return(bus, -EINVAL); | |
335 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
336 | assert_return(name, -EINVAL); | |
337 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
338 | ||
339 | r = validate_release_name_parameters(bus, name); | |
340 | if (r < 0) | |
341 | return r; | |
342 | ||
343 | return sd_bus_call_method_async( | |
344 | bus, | |
345 | ret_slot, | |
346 | "org.freedesktop.DBus", | |
347 | "/org/freedesktop/DBus", | |
348 | "org.freedesktop.DBus", | |
349 | "ReleaseName", | |
350 | callback ?: default_release_name_handler, | |
351 | userdata, | |
352 | "s", | |
353 | name); | |
354 | } | |
355 | ||
356 | _public_ int sd_bus_list_names(sd_bus *bus, char ***ret_acquired, char ***ret_activatable) { | |
357 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
358 | _cleanup_strv_free_ char **x = NULL, **y = NULL; | |
359 | int r; | |
360 | ||
361 | assert_return(bus, -EINVAL); | |
362 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
363 | assert_return(ret_acquired || ret_activatable, -EINVAL); | |
364 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
365 | ||
366 | if (!bus->bus_client) | |
367 | return -EINVAL; | |
368 | ||
369 | if (!BUS_IS_OPEN(bus->state)) | |
370 | return -ENOTCONN; | |
371 | ||
372 | if (ret_acquired) { | |
373 | r = sd_bus_call_method( | |
374 | bus, | |
375 | "org.freedesktop.DBus", | |
376 | "/org/freedesktop/DBus", | |
377 | "org.freedesktop.DBus", | |
378 | "ListNames", | |
379 | NULL, | |
380 | &reply, | |
381 | NULL); | |
382 | if (r < 0) | |
383 | return r; | |
384 | ||
385 | r = sd_bus_message_read_strv(reply, &x); | |
386 | if (r < 0) | |
387 | return r; | |
388 | ||
389 | reply = sd_bus_message_unref(reply); | |
390 | } | |
391 | ||
392 | if (ret_activatable) { | |
393 | r = sd_bus_call_method( | |
394 | bus, | |
395 | "org.freedesktop.DBus", | |
396 | "/org/freedesktop/DBus", | |
397 | "org.freedesktop.DBus", | |
398 | "ListActivatableNames", | |
399 | NULL, | |
400 | &reply, | |
401 | NULL); | |
402 | if (r < 0) | |
403 | return r; | |
404 | ||
405 | r = sd_bus_message_read_strv(reply, &y); | |
406 | if (r < 0) | |
407 | return r; | |
408 | ||
409 | *ret_activatable = TAKE_PTR(y); | |
410 | } | |
411 | ||
412 | if (ret_acquired) | |
413 | *ret_acquired = TAKE_PTR(x); | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | _public_ int sd_bus_get_name_creds( | |
419 | sd_bus *bus, | |
420 | const char *name, | |
421 | uint64_t mask, | |
422 | sd_bus_creds **ret) { | |
423 | ||
424 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL; | |
425 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; | |
426 | const char *unique; | |
427 | int r; | |
428 | ||
429 | assert_return(bus, -EINVAL); | |
430 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
431 | assert_return(name, -EINVAL); | |
432 | assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); | |
433 | assert_return(mask == 0 || ret, -EINVAL); | |
434 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
435 | assert_return(service_name_is_valid(name), -EINVAL); | |
436 | ||
437 | if (!bus->bus_client) | |
438 | return -EINVAL; | |
439 | ||
440 | /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not | |
441 | * going to match. */ | |
442 | if (!bus->is_local) | |
443 | mask &= ~SD_BUS_CREDS_AUGMENT; | |
444 | ||
445 | if (streq(name, "org.freedesktop.DBus.Local")) | |
446 | return -EINVAL; | |
447 | ||
448 | if (streq(name, "org.freedesktop.DBus")) | |
449 | return sd_bus_get_owner_creds(bus, mask, ret); | |
450 | ||
451 | if (!BUS_IS_OPEN(bus->state)) | |
452 | return -ENOTCONN; | |
453 | ||
454 | /* If the name is unique anyway, we can use it directly */ | |
455 | unique = name[0] == ':' ? name : NULL; | |
456 | ||
457 | /* Only query the owner if the caller wants to know it and the name is not unique anyway, or if the caller just | |
458 | * wants to check whether a name exists */ | |
459 | if ((FLAGS_SET(mask, SD_BUS_CREDS_UNIQUE_NAME) && !unique) || mask == 0) { | |
460 | r = sd_bus_call_method( | |
461 | bus, | |
462 | "org.freedesktop.DBus", | |
463 | "/org/freedesktop/DBus", | |
464 | "org.freedesktop.DBus", | |
465 | "GetNameOwner", | |
466 | NULL, | |
467 | &reply_unique, | |
468 | "s", | |
469 | name); | |
470 | if (r < 0) | |
471 | return r; | |
472 | ||
473 | r = sd_bus_message_read(reply_unique, "s", &unique); | |
474 | if (r < 0) | |
475 | return r; | |
476 | } | |
477 | ||
478 | if (mask != 0) { | |
479 | bool need_pid, need_uid, need_gids, need_selinux, need_separate_calls, need_pidfd, need_augment; | |
480 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
481 | _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; | |
482 | ||
483 | c = bus_creds_new(); | |
484 | if (!c) | |
485 | return -ENOMEM; | |
486 | ||
487 | if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) { | |
488 | c->unique_name = strdup(unique); | |
489 | if (!c->unique_name) | |
490 | return -ENOMEM; | |
491 | ||
492 | c->mask |= SD_BUS_CREDS_UNIQUE_NAME; | |
493 | } | |
494 | ||
495 | need_augment = | |
496 | (mask & SD_BUS_CREDS_AUGMENT) && | |
497 | (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| | |
498 | SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| | |
499 | SD_BUS_CREDS_SUPPLEMENTARY_GIDS| | |
500 | SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| | |
501 | 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| | |
502 | SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| | |
503 | SD_BUS_CREDS_SELINUX_CONTEXT| | |
504 | SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID| | |
505 | SD_BUS_CREDS_PIDFD)); | |
506 | ||
507 | need_pid = (mask & SD_BUS_CREDS_PID) || need_augment; | |
508 | need_uid = mask & SD_BUS_CREDS_EUID; | |
509 | need_gids = mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS; | |
510 | need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT; | |
511 | need_pidfd = (mask & SD_BUS_CREDS_PIDFD) || need_augment; | |
512 | ||
513 | if (need_pid + need_uid + need_selinux + need_pidfd + need_gids > 1) { | |
514 | ||
515 | /* If we need more than one of the credentials, then use GetConnectionCredentials() */ | |
516 | ||
517 | r = sd_bus_call_method( | |
518 | bus, | |
519 | "org.freedesktop.DBus", | |
520 | "/org/freedesktop/DBus", | |
521 | "org.freedesktop.DBus", | |
522 | "GetConnectionCredentials", | |
523 | &error, | |
524 | &reply, | |
525 | "s", | |
526 | unique ?: name); | |
527 | ||
528 | if (r < 0) { | |
529 | ||
530 | if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) | |
531 | return r; | |
532 | ||
533 | /* If we got an unknown method error, fall back to the individual calls... */ | |
534 | need_separate_calls = true; | |
535 | sd_bus_error_free(&error); | |
536 | ||
537 | } else { | |
538 | need_separate_calls = false; | |
539 | ||
540 | r = sd_bus_message_enter_container(reply, 'a', "{sv}"); | |
541 | if (r < 0) | |
542 | return r; | |
543 | ||
544 | for (;;) { | |
545 | const char *m; | |
546 | ||
547 | r = sd_bus_message_enter_container(reply, 'e', "sv"); | |
548 | if (r < 0) | |
549 | return r; | |
550 | if (r == 0) | |
551 | break; | |
552 | ||
553 | r = sd_bus_message_read(reply, "s", &m); | |
554 | if (r < 0) | |
555 | return r; | |
556 | ||
557 | if (need_uid && streq(m, "UnixUserID")) { | |
558 | uint32_t u; | |
559 | ||
560 | r = sd_bus_message_read(reply, "v", "u", &u); | |
561 | if (r < 0) | |
562 | return r; | |
563 | ||
564 | c->euid = u; | |
565 | c->mask |= SD_BUS_CREDS_EUID; | |
566 | ||
567 | } else if (need_pid && streq(m, "ProcessID")) { | |
568 | uint32_t p; | |
569 | ||
570 | r = sd_bus_message_read(reply, "v", "u", &p); | |
571 | if (r < 0) | |
572 | return r; | |
573 | ||
574 | if (!pidref_is_set(&pidref)) | |
575 | pidref = PIDREF_MAKE_FROM_PID(p); | |
576 | ||
577 | if (mask & SD_BUS_CREDS_PID) { | |
578 | c->pid = p; | |
579 | c->mask |= SD_BUS_CREDS_PID; | |
580 | } | |
581 | ||
582 | } else if (need_selinux && streq(m, "LinuxSecurityLabel")) { | |
583 | const void *p = NULL; | |
584 | size_t sz = 0; | |
585 | ||
586 | r = sd_bus_message_enter_container(reply, 'v', "ay"); | |
587 | if (r < 0) | |
588 | return r; | |
589 | ||
590 | r = sd_bus_message_read_array(reply, 'y', &p, &sz); | |
591 | if (r < 0) | |
592 | return r; | |
593 | ||
594 | r = free_and_strndup(&c->label, p, sz); | |
595 | if (r < 0) | |
596 | return r; | |
597 | ||
598 | c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; | |
599 | ||
600 | r = sd_bus_message_exit_container(reply); | |
601 | if (r < 0) | |
602 | return r; | |
603 | } else if (need_pidfd && streq(m, "ProcessFD")) { | |
604 | int fd; | |
605 | ||
606 | r = sd_bus_message_read(reply, "v", "h", &fd); | |
607 | if (r < 0) | |
608 | return r; | |
609 | ||
610 | pidref_done(&pidref); | |
611 | r = pidref_set_pidfd(&pidref, fd); | |
612 | if (r < 0) | |
613 | return r; | |
614 | ||
615 | if (mask & SD_BUS_CREDS_PIDFD) { | |
616 | fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); | |
617 | if (fd < 0) | |
618 | return -errno; | |
619 | ||
620 | close_and_replace(c->pidfd, fd); | |
621 | c->mask |= SD_BUS_CREDS_PIDFD; | |
622 | } | |
623 | } else if (need_gids && streq(m, "UnixGroupIDs")) { | |
624 | ||
625 | /* Note that D-Bus actually only gives us a combined list of | |
626 | * primary gid and supplementary gids. And we don't know | |
627 | * which one the primary one is. We'll take the whole shebang | |
628 | * hence and use it as the supplementary group list, and not | |
629 | * initialize the primary gid field. This is slightly | |
630 | * incorrect of course, but only slightly, as in effect if | |
631 | * the primary gid is also listed in the supplementary gid | |
632 | * it has zero effect. */ | |
633 | ||
634 | r = sd_bus_message_enter_container(reply, 'v', "au"); | |
635 | if (r < 0) | |
636 | return r; | |
637 | ||
638 | r = sd_bus_message_enter_container(reply, 'a', "u"); | |
639 | if (r < 0) | |
640 | return r; | |
641 | ||
642 | for (;;) { | |
643 | uint32_t u; | |
644 | ||
645 | r = sd_bus_message_read(reply, "u", &u); | |
646 | if (r < 0) | |
647 | return r; | |
648 | if (r == 0) | |
649 | break; | |
650 | ||
651 | if (!GREEDY_REALLOC(c->supplementary_gids, c->n_supplementary_gids+1)) | |
652 | return -ENOMEM; | |
653 | ||
654 | c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) u; | |
655 | } | |
656 | ||
657 | r = sd_bus_message_exit_container(reply); | |
658 | if (r < 0) | |
659 | return r; | |
660 | ||
661 | r = sd_bus_message_exit_container(reply); | |
662 | if (r < 0) | |
663 | return r; | |
664 | ||
665 | c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; | |
666 | } else { | |
667 | r = sd_bus_message_skip(reply, "v"); | |
668 | if (r < 0) | |
669 | return r; | |
670 | } | |
671 | ||
672 | r = sd_bus_message_exit_container(reply); | |
673 | if (r < 0) | |
674 | return r; | |
675 | } | |
676 | ||
677 | r = sd_bus_message_exit_container(reply); | |
678 | if (r < 0) | |
679 | return r; | |
680 | ||
681 | if (need_pid && !pidref_is_set(&pidref)) | |
682 | return -EPROTO; | |
683 | } | |
684 | ||
685 | } else /* When we only need a single field, then let's use separate calls */ | |
686 | need_separate_calls = true; | |
687 | ||
688 | if (need_separate_calls) { | |
689 | if (need_pid) { | |
690 | uint32_t u; | |
691 | ||
692 | r = sd_bus_call_method( | |
693 | bus, | |
694 | "org.freedesktop.DBus", | |
695 | "/org/freedesktop/DBus", | |
696 | "org.freedesktop.DBus", | |
697 | "GetConnectionUnixProcessID", | |
698 | NULL, | |
699 | &reply, | |
700 | "s", | |
701 | unique ?: name); | |
702 | if (r < 0) | |
703 | return r; | |
704 | ||
705 | r = sd_bus_message_read(reply, "u", &u); | |
706 | if (r < 0) | |
707 | return r; | |
708 | ||
709 | if (!pidref_is_set(&pidref)) | |
710 | pidref = PIDREF_MAKE_FROM_PID(u); | |
711 | ||
712 | if (mask & SD_BUS_CREDS_PID) { | |
713 | c->pid = u; | |
714 | c->mask |= SD_BUS_CREDS_PID; | |
715 | } | |
716 | ||
717 | reply = sd_bus_message_unref(reply); | |
718 | } | |
719 | ||
720 | if (need_uid) { | |
721 | uint32_t u; | |
722 | ||
723 | r = sd_bus_call_method( | |
724 | bus, | |
725 | "org.freedesktop.DBus", | |
726 | "/org/freedesktop/DBus", | |
727 | "org.freedesktop.DBus", | |
728 | "GetConnectionUnixUser", | |
729 | NULL, | |
730 | &reply, | |
731 | "s", | |
732 | unique ?: name); | |
733 | if (r < 0) | |
734 | return r; | |
735 | ||
736 | r = sd_bus_message_read(reply, "u", &u); | |
737 | if (r < 0) | |
738 | return r; | |
739 | ||
740 | c->euid = u; | |
741 | c->mask |= SD_BUS_CREDS_EUID; | |
742 | ||
743 | reply = sd_bus_message_unref(reply); | |
744 | } | |
745 | ||
746 | if (need_selinux) { | |
747 | const void *p = NULL; | |
748 | size_t sz = 0; | |
749 | ||
750 | r = sd_bus_call_method( | |
751 | bus, | |
752 | "org.freedesktop.DBus", | |
753 | "/org/freedesktop/DBus", | |
754 | "org.freedesktop.DBus", | |
755 | "GetConnectionSELinuxSecurityContext", | |
756 | &error, | |
757 | &reply, | |
758 | "s", | |
759 | unique ?: name); | |
760 | if (r < 0) { | |
761 | if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN)) | |
762 | return r; | |
763 | ||
764 | /* no data is fine */ | |
765 | } else { | |
766 | r = sd_bus_message_read_array(reply, 'y', &p, &sz); | |
767 | if (r < 0) | |
768 | return r; | |
769 | ||
770 | c->label = memdup_suffix0(p, sz); | |
771 | if (!c->label) | |
772 | return -ENOMEM; | |
773 | ||
774 | c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; | |
775 | } | |
776 | } | |
777 | } | |
778 | ||
779 | if (pidref_is_set(&pidref)) { | |
780 | r = bus_creds_add_more(c, mask, &pidref, 0); | |
781 | if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */ | |
782 | return r; | |
783 | } | |
784 | } | |
785 | ||
786 | if (ret) | |
787 | *ret = TAKE_PTR(c); | |
788 | ||
789 | return 0; | |
790 | } | |
791 | ||
792 | static int parse_sockaddr_string(const char *t, char **ret_comm, char **ret_description) { | |
793 | _cleanup_free_ char *comm = NULL, *description = NULL; | |
794 | const char *e, *sl; | |
795 | ||
796 | assert(t); | |
797 | assert(ret_comm); | |
798 | assert(ret_description); | |
799 | ||
800 | e = strstrafter(t, "/bus/"); | |
801 | if (!e) { | |
802 | log_debug("Didn't find /bus/ substring in peer socket address, ignoring."); | |
803 | goto not_found; | |
804 | } | |
805 | ||
806 | sl = strchr(e, '/'); | |
807 | if (!sl) { | |
808 | log_debug("Didn't find / substring after /bus/ in peer socket address, ignoring."); | |
809 | goto not_found; | |
810 | } | |
811 | ||
812 | if (sl - e > 0) { | |
813 | comm = strndup(e, sl - e); | |
814 | if (!comm) | |
815 | return -ENOMEM; | |
816 | } | |
817 | ||
818 | sl++; | |
819 | if (!isempty(sl)) { | |
820 | description = strdup(sl); | |
821 | if (!description) | |
822 | return -ENOMEM; | |
823 | } | |
824 | ||
825 | *ret_comm = TAKE_PTR(comm); | |
826 | *ret_description = TAKE_PTR(description); | |
827 | return 0; | |
828 | ||
829 | not_found: | |
830 | *ret_comm = *ret_description = NULL; | |
831 | return 0; | |
832 | } | |
833 | ||
834 | _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { | |
835 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; | |
836 | _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; | |
837 | bool do_label, do_groups, do_sockaddr_peer, do_pidfd; | |
838 | int r; | |
839 | ||
840 | assert_return(bus, -EINVAL); | |
841 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
842 | assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); | |
843 | assert_return(ret, -EINVAL); | |
844 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
845 | ||
846 | if (!BUS_IS_OPEN(bus->state)) | |
847 | return -ENOTCONN; | |
848 | ||
849 | if (!bus->is_local) | |
850 | mask &= ~SD_BUS_CREDS_AUGMENT; | |
851 | ||
852 | do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); | |
853 | do_groups = bus->n_groups != SIZE_MAX && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS); | |
854 | do_sockaddr_peer = bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1 && | |
855 | bus->sockaddr_peer.sa.sa_family == AF_UNIX && | |
856 | bus->sockaddr_peer.un.sun_path[0] == 0; | |
857 | do_pidfd = bus->pidfd >= 0 && (mask & SD_BUS_CREDS_PIDFD); | |
858 | ||
859 | /* Avoid allocating anything if we have no chance of returning useful data */ | |
860 | if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer && !do_pidfd) | |
861 | return -ENODATA; | |
862 | ||
863 | c = bus_creds_new(); | |
864 | if (!c) | |
865 | return -ENOMEM; | |
866 | ||
867 | if (bus->ucred_valid) { | |
868 | if (pid_is_valid(bus->ucred.pid)) { | |
869 | c->pid = bus->ucred.pid; | |
870 | c->mask |= SD_BUS_CREDS_PID & mask; | |
871 | ||
872 | pidref = PIDREF_MAKE_FROM_PID(c->pid); | |
873 | } | |
874 | ||
875 | if (uid_is_valid(bus->ucred.uid)) { | |
876 | c->euid = bus->ucred.uid; | |
877 | c->mask |= SD_BUS_CREDS_EUID & mask; | |
878 | } | |
879 | ||
880 | if (gid_is_valid(bus->ucred.gid)) { | |
881 | c->egid = bus->ucred.gid; | |
882 | c->mask |= SD_BUS_CREDS_EGID & mask; | |
883 | } | |
884 | } | |
885 | ||
886 | if (do_label) { | |
887 | c->label = strdup(bus->label); | |
888 | if (!c->label) | |
889 | return -ENOMEM; | |
890 | ||
891 | c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; | |
892 | } | |
893 | ||
894 | if (do_groups) { | |
895 | c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups); | |
896 | if (!c->supplementary_gids) | |
897 | return -ENOMEM; | |
898 | ||
899 | c->n_supplementary_gids = bus->n_groups; | |
900 | ||
901 | c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; | |
902 | } | |
903 | ||
904 | if (do_sockaddr_peer) { | |
905 | _cleanup_free_ char *t = NULL; | |
906 | ||
907 | assert(bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1); | |
908 | assert(bus->sockaddr_peer.sa.sa_family == AF_UNIX); | |
909 | assert(bus->sockaddr_peer.un.sun_path[0] == 0); | |
910 | ||
911 | /* So this is an abstract namespace socket, good. Now let's find the data we are interested in */ | |
912 | r = make_cstring(bus->sockaddr_peer.un.sun_path + 1, | |
913 | bus->sockaddr_size_peer - offsetof(struct sockaddr_un, sun_path) - 1, | |
914 | MAKE_CSTRING_ALLOW_TRAILING_NUL, | |
915 | &t); | |
916 | if (r == -ENOMEM) | |
917 | return r; | |
918 | if (r < 0) | |
919 | log_debug_errno(r, "Can't extract string from peer socket address, ignoring: %m"); | |
920 | else { | |
921 | r = parse_sockaddr_string(t, &c->comm, &c->description); | |
922 | if (r < 0) | |
923 | return r; | |
924 | ||
925 | if (c->comm) | |
926 | c->mask |= SD_BUS_CREDS_COMM & mask; | |
927 | ||
928 | if (c->description) | |
929 | c->mask |= SD_BUS_CREDS_DESCRIPTION & mask; | |
930 | } | |
931 | } | |
932 | ||
933 | if (do_pidfd) { | |
934 | c->pidfd = fcntl(bus->pidfd, F_DUPFD_CLOEXEC, 3); | |
935 | if (c->pidfd < 0) | |
936 | return -errno; | |
937 | ||
938 | pidref_done(&pidref); | |
939 | r = pidref_set_pidfd(&pidref, bus->pidfd); | |
940 | if (r < 0) | |
941 | return r; | |
942 | ||
943 | c->mask |= SD_BUS_CREDS_PIDFD; | |
944 | } | |
945 | ||
946 | r = bus_creds_add_more(c, mask, &pidref, 0); | |
947 | if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */ | |
948 | return r; | |
949 | ||
950 | *ret = TAKE_PTR(c); | |
951 | ||
952 | return 0; | |
953 | } | |
954 | ||
955 | #define append_eavesdrop(bus, m) \ | |
956 | ((bus)->is_monitor \ | |
957 | ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ | |
958 | : (m)) | |
959 | ||
960 | int bus_add_match_internal( | |
961 | sd_bus *bus, | |
962 | const char *match, | |
963 | uint64_t timeout_usec, | |
964 | uint64_t *ret_counter) { | |
965 | ||
966 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; | |
967 | const char *e; | |
968 | int r; | |
969 | ||
970 | assert(bus); | |
971 | ||
972 | if (!bus->bus_client) | |
973 | return -EINVAL; | |
974 | ||
975 | e = append_eavesdrop(bus, match); | |
976 | ||
977 | r = sd_bus_message_new_method_call( | |
978 | bus, | |
979 | &m, | |
980 | "org.freedesktop.DBus", | |
981 | "/org/freedesktop/DBus", | |
982 | "org.freedesktop.DBus", | |
983 | "AddMatch"); | |
984 | if (r < 0) | |
985 | return r; | |
986 | ||
987 | r = sd_bus_message_append(m, "s", e); | |
988 | if (r < 0) | |
989 | return r; | |
990 | ||
991 | r = sd_bus_call( | |
992 | bus, | |
993 | m, | |
994 | timeout_usec, | |
995 | NULL, | |
996 | &reply); | |
997 | if (r < 0) | |
998 | return r; | |
999 | ||
1000 | /* If the caller asked for it, return the read counter of the reply */ | |
1001 | if (ret_counter) | |
1002 | *ret_counter = reply->read_counter; | |
1003 | ||
1004 | return r; | |
1005 | } | |
1006 | ||
1007 | int bus_add_match_internal_async( | |
1008 | sd_bus *bus, | |
1009 | sd_bus_slot **ret_slot, | |
1010 | const char *match, | |
1011 | sd_bus_message_handler_t callback, | |
1012 | void *userdata, | |
1013 | uint64_t timeout_usec) { | |
1014 | ||
1015 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; | |
1016 | const char *e; | |
1017 | int r; | |
1018 | ||
1019 | assert(bus); | |
1020 | ||
1021 | if (!bus->bus_client) | |
1022 | return -EINVAL; | |
1023 | ||
1024 | e = append_eavesdrop(bus, match); | |
1025 | ||
1026 | r = sd_bus_message_new_method_call( | |
1027 | bus, | |
1028 | &m, | |
1029 | "org.freedesktop.DBus", | |
1030 | "/org/freedesktop/DBus", | |
1031 | "org.freedesktop.DBus", | |
1032 | "AddMatch"); | |
1033 | if (r < 0) | |
1034 | return r; | |
1035 | ||
1036 | r = sd_bus_message_append(m, "s", e); | |
1037 | if (r < 0) | |
1038 | return r; | |
1039 | ||
1040 | return sd_bus_call_async( | |
1041 | bus, | |
1042 | ret_slot, | |
1043 | m, | |
1044 | callback, | |
1045 | userdata, | |
1046 | timeout_usec); | |
1047 | } | |
1048 | ||
1049 | int bus_remove_match_internal( | |
1050 | sd_bus *bus, | |
1051 | const char *match) { | |
1052 | ||
1053 | const char *e; | |
1054 | ||
1055 | assert(bus); | |
1056 | assert(match); | |
1057 | ||
1058 | if (!bus->bus_client) | |
1059 | return -EINVAL; | |
1060 | ||
1061 | e = append_eavesdrop(bus, match); | |
1062 | ||
1063 | /* Fire and forget */ | |
1064 | ||
1065 | return sd_bus_call_method_async( | |
1066 | bus, | |
1067 | NULL, | |
1068 | "org.freedesktop.DBus", | |
1069 | "/org/freedesktop/DBus", | |
1070 | "org.freedesktop.DBus", | |
1071 | "RemoveMatch", | |
1072 | NULL, | |
1073 | NULL, | |
1074 | "s", | |
1075 | e); | |
1076 | } | |
1077 | ||
1078 | _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *ret) { | |
1079 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; | |
1080 | const char *mid; | |
1081 | int r; | |
1082 | ||
1083 | assert_return(bus, -EINVAL); | |
1084 | assert_return(bus = bus_resolve(bus), -ENOPKG); | |
1085 | assert_return(name, -EINVAL); | |
1086 | assert_return(ret, -EINVAL); | |
1087 | assert_return(!bus_origin_changed(bus), -ECHILD); | |
1088 | assert_return(service_name_is_valid(name), -EINVAL); | |
1089 | ||
1090 | if (!bus->bus_client) | |
1091 | return -EINVAL; | |
1092 | ||
1093 | if (!BUS_IS_OPEN(bus->state)) | |
1094 | return -ENOTCONN; | |
1095 | ||
1096 | if (streq_ptr(name, bus->unique_name)) | |
1097 | return sd_id128_get_machine(ret); | |
1098 | ||
1099 | r = sd_bus_message_new_method_call( | |
1100 | bus, | |
1101 | &m, | |
1102 | name, | |
1103 | "/", | |
1104 | "org.freedesktop.DBus.Peer", | |
1105 | "GetMachineId"); | |
1106 | if (r < 0) | |
1107 | return r; | |
1108 | ||
1109 | r = sd_bus_message_set_auto_start(m, false); | |
1110 | if (r < 0) | |
1111 | return r; | |
1112 | ||
1113 | r = sd_bus_call(bus, m, 0, NULL, &reply); | |
1114 | if (r < 0) | |
1115 | return r; | |
1116 | ||
1117 | r = sd_bus_message_read(reply, "s", &mid); | |
1118 | if (r < 0) | |
1119 | return r; | |
1120 | ||
1121 | return sd_id128_from_string(mid, ret); | |
1122 | } |