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