Line data Source code
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 : #include "bus-internal.h"
23 : #include "bus-message.h"
24 : #include "bus-signature.h"
25 : #include "bus-util.h"
26 : #include "bus-type.h"
27 :
28 32 : _public_ int sd_bus_emit_signal(
29 : sd_bus *bus,
30 : const char *path,
31 : const char *interface,
32 : const char *member,
33 : const char *types, ...) {
34 :
35 64 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
36 : int r;
37 :
38 32 : assert_return(bus, -EINVAL);
39 32 : assert_return(!bus_pid_changed(bus), -ECHILD);
40 :
41 32 : if (!BUS_IS_OPEN(bus->state))
42 0 : return -ENOTCONN;
43 :
44 32 : r = sd_bus_message_new_signal(bus, &m, path, interface, member);
45 32 : if (r < 0)
46 0 : return r;
47 :
48 32 : if (!isempty(types)) {
49 : va_list ap;
50 :
51 32 : va_start(ap, types);
52 32 : r = bus_message_append_ap(m, types, ap);
53 32 : va_end(ap);
54 32 : if (r < 0)
55 0 : return r;
56 : }
57 :
58 32 : return sd_bus_send(bus, m, NULL);
59 : }
60 :
61 0 : _public_ int sd_bus_call_method_async(
62 : sd_bus *bus,
63 : sd_bus_slot **slot,
64 : const char *destination,
65 : const char *path,
66 : const char *interface,
67 : const char *member,
68 : sd_bus_message_handler_t callback,
69 : void *userdata,
70 : const char *types, ...) {
71 :
72 0 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
73 : int r;
74 :
75 0 : assert_return(bus, -EINVAL);
76 0 : assert_return(!bus_pid_changed(bus), -ECHILD);
77 :
78 0 : if (!BUS_IS_OPEN(bus->state))
79 0 : return -ENOTCONN;
80 :
81 0 : r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
82 0 : if (r < 0)
83 0 : return r;
84 :
85 0 : if (!isempty(types)) {
86 : va_list ap;
87 :
88 0 : va_start(ap, types);
89 0 : r = bus_message_append_ap(m, types, ap);
90 0 : va_end(ap);
91 0 : if (r < 0)
92 0 : return r;
93 : }
94 :
95 0 : return sd_bus_call_async(bus, slot, m, callback, userdata, 0);
96 : }
97 :
98 28 : _public_ int sd_bus_call_method(
99 : sd_bus *bus,
100 : const char *destination,
101 : const char *path,
102 : const char *interface,
103 : const char *member,
104 : sd_bus_error *error,
105 : sd_bus_message **reply,
106 : const char *types, ...) {
107 :
108 56 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
109 : int r;
110 :
111 28 : assert_return(bus, -EINVAL);
112 28 : assert_return(!bus_pid_changed(bus), -ECHILD);
113 :
114 28 : if (!BUS_IS_OPEN(bus->state))
115 1 : return -ENOTCONN;
116 :
117 27 : r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
118 27 : if (r < 0)
119 0 : return r;
120 :
121 27 : if (!isempty(types)) {
122 : va_list ap;
123 :
124 11 : va_start(ap, types);
125 11 : r = bus_message_append_ap(m, types, ap);
126 11 : va_end(ap);
127 11 : if (r < 0)
128 0 : return r;
129 : }
130 :
131 27 : return sd_bus_call(bus, m, 0, error, reply);
132 : }
133 :
134 15499 : _public_ int sd_bus_reply_method_return(
135 : sd_bus_message *call,
136 : const char *types, ...) {
137 :
138 30998 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
139 : int r;
140 :
141 15499 : assert_return(call, -EINVAL);
142 15499 : assert_return(call->sealed, -EPERM);
143 15499 : assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
144 15499 : assert_return(call->bus, -EINVAL);
145 15499 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
146 :
147 15499 : if (!BUS_IS_OPEN(call->bus->state))
148 0 : return -ENOTCONN;
149 :
150 15499 : if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
151 3 : return 0;
152 :
153 15496 : r = sd_bus_message_new_method_return(call, &m);
154 15496 : if (r < 0)
155 0 : return r;
156 :
157 15496 : if (!isempty(types)) {
158 : va_list ap;
159 :
160 3 : va_start(ap, types);
161 3 : r = bus_message_append_ap(m, types, ap);
162 3 : va_end(ap);
163 3 : if (r < 0)
164 0 : return r;
165 : }
166 :
167 15496 : return sd_bus_send(call->bus, m, NULL);
168 : }
169 :
170 4 : _public_ int sd_bus_reply_method_error(
171 : sd_bus_message *call,
172 : const sd_bus_error *e) {
173 :
174 8 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
175 : int r;
176 :
177 4 : assert_return(call, -EINVAL);
178 4 : assert_return(call->sealed, -EPERM);
179 4 : assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
180 4 : assert_return(sd_bus_error_is_set(e), -EINVAL);
181 4 : assert_return(call->bus, -EINVAL);
182 4 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
183 :
184 4 : if (!BUS_IS_OPEN(call->bus->state))
185 0 : return -ENOTCONN;
186 :
187 4 : if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
188 0 : return 0;
189 :
190 4 : r = sd_bus_message_new_method_error(call, &m, e);
191 4 : if (r < 0)
192 0 : return r;
193 :
194 4 : return sd_bus_send(call->bus, m, NULL);
195 : }
196 :
197 4 : _public_ int sd_bus_reply_method_errorf(
198 : sd_bus_message *call,
199 : const char *name,
200 : const char *format,
201 : ...) {
202 :
203 8 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
204 : va_list ap;
205 :
206 4 : assert_return(call, -EINVAL);
207 4 : assert_return(call->sealed, -EPERM);
208 4 : assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
209 4 : assert_return(call->bus, -EINVAL);
210 4 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
211 :
212 4 : if (!BUS_IS_OPEN(call->bus->state))
213 0 : return -ENOTCONN;
214 :
215 4 : if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
216 0 : return 0;
217 :
218 4 : va_start(ap, format);
219 4 : bus_error_setfv(&error, name, format, ap);
220 4 : va_end(ap);
221 :
222 4 : return sd_bus_reply_method_error(call, &error);
223 : }
224 :
225 0 : _public_ int sd_bus_reply_method_errno(
226 : sd_bus_message *call,
227 : int error,
228 : const sd_bus_error *p) {
229 :
230 0 : _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
231 :
232 0 : assert_return(call, -EINVAL);
233 0 : assert_return(call->sealed, -EPERM);
234 0 : assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
235 0 : assert_return(call->bus, -EINVAL);
236 0 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
237 :
238 0 : if (!BUS_IS_OPEN(call->bus->state))
239 0 : return -ENOTCONN;
240 :
241 0 : if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
242 0 : return 0;
243 :
244 0 : if (sd_bus_error_is_set(p))
245 0 : return sd_bus_reply_method_error(call, p);
246 :
247 0 : sd_bus_error_set_errno(&berror, error);
248 :
249 0 : return sd_bus_reply_method_error(call, &berror);
250 : }
251 :
252 0 : _public_ int sd_bus_reply_method_errnof(
253 : sd_bus_message *call,
254 : int error,
255 : const char *format,
256 : ...) {
257 :
258 0 : _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
259 : va_list ap;
260 :
261 0 : assert_return(call, -EINVAL);
262 0 : assert_return(call->sealed, -EPERM);
263 0 : assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
264 0 : assert_return(call->bus, -EINVAL);
265 0 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
266 :
267 0 : if (!BUS_IS_OPEN(call->bus->state))
268 0 : return -ENOTCONN;
269 :
270 0 : if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
271 0 : return 0;
272 :
273 0 : va_start(ap, format);
274 0 : sd_bus_error_set_errnofv(&berror, error, format, ap);
275 0 : va_end(ap);
276 :
277 0 : return sd_bus_reply_method_error(call, &berror);
278 : }
279 :
280 3 : _public_ int sd_bus_get_property(
281 : sd_bus *bus,
282 : const char *destination,
283 : const char *path,
284 : const char *interface,
285 : const char *member,
286 : sd_bus_error *error,
287 : sd_bus_message **reply,
288 : const char *type) {
289 :
290 3 : sd_bus_message *rep = NULL;
291 : int r;
292 :
293 3 : assert_return(bus, -EINVAL);
294 3 : assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
295 3 : assert_return(member_name_is_valid(member), -EINVAL);
296 3 : assert_return(reply, -EINVAL);
297 3 : assert_return(signature_is_single(type, false), -EINVAL);
298 3 : assert_return(!bus_pid_changed(bus), -ECHILD);
299 :
300 3 : if (!BUS_IS_OPEN(bus->state))
301 0 : return -ENOTCONN;
302 :
303 3 : r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
304 3 : if (r < 0)
305 0 : return r;
306 :
307 3 : r = sd_bus_message_enter_container(rep, 'v', type);
308 3 : if (r < 0) {
309 0 : sd_bus_message_unref(rep);
310 0 : return r;
311 : }
312 :
313 3 : *reply = rep;
314 3 : return 0;
315 : }
316 :
317 0 : _public_ int sd_bus_get_property_trivial(
318 : sd_bus *bus,
319 : const char *destination,
320 : const char *path,
321 : const char *interface,
322 : const char *member,
323 : sd_bus_error *error,
324 : char type, void *ptr) {
325 :
326 0 : _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
327 : int r;
328 :
329 0 : assert_return(bus, -EINVAL);
330 0 : assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
331 0 : assert_return(member_name_is_valid(member), -EINVAL);
332 0 : assert_return(bus_type_is_trivial(type), -EINVAL);
333 0 : assert_return(ptr, -EINVAL);
334 0 : assert_return(!bus_pid_changed(bus), -ECHILD);
335 :
336 0 : if (!BUS_IS_OPEN(bus->state))
337 0 : return -ENOTCONN;
338 :
339 0 : r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
340 0 : if (r < 0)
341 0 : return r;
342 :
343 0 : r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
344 0 : if (r < 0)
345 0 : return r;
346 :
347 0 : r = sd_bus_message_read_basic(reply, type, ptr);
348 0 : if (r < 0)
349 0 : return r;
350 :
351 0 : return 0;
352 : }
353 :
354 0 : _public_ int sd_bus_get_property_string(
355 : sd_bus *bus,
356 : const char *destination,
357 : const char *path,
358 : const char *interface,
359 : const char *member,
360 : sd_bus_error *error,
361 : char **ret) {
362 :
363 0 : _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
364 : const char *s;
365 : char *n;
366 : int r;
367 :
368 0 : assert_return(bus, -EINVAL);
369 0 : assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
370 0 : assert_return(member_name_is_valid(member), -EINVAL);
371 0 : assert_return(ret, -EINVAL);
372 0 : assert_return(!bus_pid_changed(bus), -ECHILD);
373 :
374 0 : if (!BUS_IS_OPEN(bus->state))
375 0 : return -ENOTCONN;
376 :
377 0 : r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
378 0 : if (r < 0)
379 0 : return r;
380 :
381 0 : r = sd_bus_message_enter_container(reply, 'v', "s");
382 0 : if (r < 0)
383 0 : return r;
384 :
385 0 : r = sd_bus_message_read_basic(reply, 's', &s);
386 0 : if (r < 0)
387 0 : return r;
388 :
389 0 : n = strdup(s);
390 0 : if (!n)
391 0 : return -ENOMEM;
392 :
393 0 : *ret = n;
394 0 : return 0;
395 : }
396 :
397 0 : _public_ int sd_bus_get_property_strv(
398 : sd_bus *bus,
399 : const char *destination,
400 : const char *path,
401 : const char *interface,
402 : const char *member,
403 : sd_bus_error *error,
404 : char ***ret) {
405 :
406 0 : _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
407 : int r;
408 :
409 0 : assert_return(bus, -EINVAL);
410 0 : assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
411 0 : assert_return(member_name_is_valid(member), -EINVAL);
412 0 : assert_return(ret, -EINVAL);
413 0 : assert_return(!bus_pid_changed(bus), -ECHILD);
414 :
415 0 : if (!BUS_IS_OPEN(bus->state))
416 0 : return -ENOTCONN;
417 :
418 0 : r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
419 0 : if (r < 0)
420 0 : return r;
421 :
422 0 : r = sd_bus_message_enter_container(reply, 'v', NULL);
423 0 : if (r < 0)
424 0 : return r;
425 :
426 0 : r = sd_bus_message_read_strv(reply, ret);
427 0 : if (r < 0)
428 0 : return r;
429 :
430 0 : return 0;
431 : }
432 :
433 3 : _public_ int sd_bus_set_property(
434 : sd_bus *bus,
435 : const char *destination,
436 : const char *path,
437 : const char *interface,
438 : const char *member,
439 : sd_bus_error *error,
440 : const char *type, ...) {
441 :
442 6 : _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
443 : va_list ap;
444 : int r;
445 :
446 3 : assert_return(bus, -EINVAL);
447 3 : assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
448 3 : assert_return(member_name_is_valid(member), -EINVAL);
449 3 : assert_return(signature_is_single(type, false), -EINVAL);
450 3 : assert_return(!bus_pid_changed(bus), -ECHILD);
451 :
452 3 : if (!BUS_IS_OPEN(bus->state))
453 0 : return -ENOTCONN;
454 :
455 3 : r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
456 3 : if (r < 0)
457 0 : return r;
458 :
459 3 : r = sd_bus_message_append(m, "ss", strempty(interface), member);
460 3 : if (r < 0)
461 0 : return r;
462 :
463 3 : r = sd_bus_message_open_container(m, 'v', type);
464 3 : if (r < 0)
465 0 : return r;
466 :
467 3 : va_start(ap, type);
468 3 : r = bus_message_append_ap(m, type, ap);
469 3 : va_end(ap);
470 3 : if (r < 0)
471 0 : return r;
472 :
473 3 : r = sd_bus_message_close_container(m);
474 3 : if (r < 0)
475 0 : return r;
476 :
477 3 : return sd_bus_call(bus, m, 0, error, NULL);
478 : }
479 :
480 13 : _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
481 : sd_bus_creds *c;
482 :
483 13 : assert_return(call, -EINVAL);
484 13 : assert_return(call->sealed, -EPERM);
485 13 : assert_return(call->bus, -EINVAL);
486 13 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
487 :
488 13 : if (!BUS_IS_OPEN(call->bus->state))
489 0 : return -ENOTCONN;
490 :
491 13 : c = sd_bus_message_get_creds(call);
492 :
493 : /* All data we need? */
494 13 : if (c && (mask & ~c->mask) == 0) {
495 0 : *creds = sd_bus_creds_ref(c);
496 0 : return 0;
497 : }
498 :
499 : /* No data passed? Or not enough data passed to retrieve the missing bits? */
500 13 : if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
501 : /* We couldn't read anything from the call, let's try
502 : * to get it from the sender or peer. */
503 :
504 13 : if (call->sender)
505 : /* There's a sender, but the creds are
506 : * missing. This means we are talking via
507 : * dbus1, or are getting a message that was
508 : * sent to us via kdbus, but was converted
509 : * from a dbus1 message by the bus-proxy and
510 : * thus also lacks the creds. */
511 0 : return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
512 : else
513 : /* There's no sender, hence we are on a dbus1
514 : * direct connection. For direct connections
515 : * the credentials of the AF_UNIX peer matter,
516 : * which may be queried via
517 : * sd_bus_get_owner_creds(). */
518 13 : return sd_bus_get_owner_creds(call->bus, mask, creds);
519 : }
520 :
521 0 : return bus_creds_extend_by_pid(c, mask, creds);
522 : }
523 :
524 13 : _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
525 26 : _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
526 : uid_t our_uid;
527 13 : bool know_caps = false;
528 : int r;
529 :
530 13 : assert_return(call, -EINVAL);
531 13 : assert_return(call->sealed, -EPERM);
532 13 : assert_return(call->bus, -EINVAL);
533 13 : assert_return(!bus_pid_changed(call->bus), -ECHILD);
534 :
535 13 : if (!BUS_IS_OPEN(call->bus->state))
536 0 : return -ENOTCONN;
537 :
538 13 : if (capability >= 0) {
539 :
540 13 : r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
541 13 : if (r < 0)
542 0 : return r;
543 :
544 : /* We cannot use augmented caps for authorization,
545 : * since then data is acquired raceful from
546 : * /proc. This can never actually happen, but let's
547 : * better be safe than sorry, and do an extra check
548 : * here. */
549 13 : assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
550 :
551 : /* Note that not even on kdbus we might have the caps
552 : * field, due to faked identities, or namespace
553 : * translation issues. */
554 13 : r = sd_bus_creds_has_effective_cap(creds, capability);
555 13 : if (r > 0)
556 0 : return 1;
557 13 : if (r == 0)
558 0 : know_caps = true;
559 : } else {
560 0 : r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
561 0 : if (r < 0)
562 0 : return r;
563 : }
564 :
565 : /* Now, check the UID, but only if the capability check wasn't
566 : * sufficient */
567 13 : our_uid = getuid();
568 13 : if (our_uid != 0 || !know_caps || capability < 0) {
569 : uid_t sender_uid;
570 :
571 : /* We cannot use augmented uid/euid for authorization,
572 : * since then data is acquired raceful from
573 : * /proc. This can never actually happen, but let's
574 : * better be safe than sorry, and do an extra check
575 : * here. */
576 26 : assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
577 :
578 : /* Try to use the EUID, if we have it. */
579 13 : r = sd_bus_creds_get_euid(creds, &sender_uid);
580 13 : if (r < 0)
581 0 : r = sd_bus_creds_get_uid(creds, &sender_uid);
582 :
583 13 : if (r >= 0) {
584 : /* Sender has same UID as us, then let's grant access */
585 13 : if (sender_uid == our_uid)
586 13 : return 1;
587 :
588 : /* Sender is root, we are not root. */
589 0 : if (our_uid != 0 && sender_uid == 0)
590 0 : return 1;
591 : }
592 : }
593 :
594 0 : return 0;
595 : }
|