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