chiark / gitweb /
resolved: add bus API for configuring per-link DNS settings
[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_(sd_bus_message_unrefp) 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 #if 0 /// UNNEEDED by elogind
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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_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_(sd_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 #if 0 /// UNNEEDED by elogind
261 _public_ int sd_bus_reply_method_errnof(
262                 sd_bus_message *call,
263                 int error,
264                 const char *format,
265                 ...) {
266
267         _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
268         va_list ap;
269
270         assert_return(call, -EINVAL);
271         assert_return(call->sealed, -EPERM);
272         assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
273         assert_return(call->bus, -EINVAL);
274         assert_return(!bus_pid_changed(call->bus), -ECHILD);
275
276         if (!BUS_IS_OPEN(call->bus->state))
277                 return -ENOTCONN;
278
279         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
280                 return 0;
281
282         va_start(ap, format);
283         sd_bus_error_set_errnofv(&berror, error, format, ap);
284         va_end(ap);
285
286         return sd_bus_reply_method_error(call, &berror);
287 }
288 #endif // 0
289
290 _public_ int sd_bus_get_property(
291                 sd_bus *bus,
292                 const char *destination,
293                 const char *path,
294                 const char *interface,
295                 const char *member,
296                 sd_bus_error *error,
297                 sd_bus_message **reply,
298                 const char *type) {
299
300         sd_bus_message *rep = NULL;
301         int r;
302
303         bus_assert_return(bus, -EINVAL, error);
304         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
305         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
306         bus_assert_return(reply, -EINVAL, error);
307         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
308         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
309
310         if (!BUS_IS_OPEN(bus->state)) {
311                 r = -ENOTCONN;
312                 goto fail;
313         }
314
315         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
316         if (r < 0)
317                 return r;
318
319         r = sd_bus_message_enter_container(rep, 'v', type);
320         if (r < 0) {
321                 sd_bus_message_unref(rep);
322                 goto fail;
323         }
324
325         *reply = rep;
326         return 0;
327
328 fail:
329         return sd_bus_error_set_errno(error, r);
330 }
331
332 #if 0 /// UNNEEDED by elogind
333 _public_ int sd_bus_get_property_trivial(
334                 sd_bus *bus,
335                 const char *destination,
336                 const char *path,
337                 const char *interface,
338                 const char *member,
339                 sd_bus_error *error,
340                 char type, void *ptr) {
341
342         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
343         int r;
344
345         bus_assert_return(bus, -EINVAL, error);
346         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
347         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
348         bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
349         bus_assert_return(ptr, -EINVAL, error);
350         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
351
352         if (!BUS_IS_OPEN(bus->state)) {
353                 r = -ENOTCONN;
354                 goto fail;
355         }
356
357         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
358         if (r < 0)
359                 return r;
360
361         r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
362         if (r < 0)
363                 goto fail;
364
365         r = sd_bus_message_read_basic(reply, type, ptr);
366         if (r < 0)
367                 goto fail;
368
369         return 0;
370
371 fail:
372         return sd_bus_error_set_errno(error, r);
373 }
374 #endif // 0
375
376 _public_ int sd_bus_get_property_string(
377                 sd_bus *bus,
378                 const char *destination,
379                 const char *path,
380                 const char *interface,
381                 const char *member,
382                 sd_bus_error *error,
383                 char **ret) {
384
385         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
386         const char *s;
387         char *n;
388         int r;
389
390         bus_assert_return(bus, -EINVAL, error);
391         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
392         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
393         bus_assert_return(ret, -EINVAL, error);
394         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
395
396         if (!BUS_IS_OPEN(bus->state)) {
397                 r = -ENOTCONN;
398                 goto fail;
399         }
400
401         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
402         if (r < 0)
403                 return r;
404
405         r = sd_bus_message_enter_container(reply, 'v', "s");
406         if (r < 0)
407                 goto fail;
408
409         r = sd_bus_message_read_basic(reply, 's', &s);
410         if (r < 0)
411                 goto fail;
412
413         n = strdup(s);
414         if (!n) {
415                 r = -ENOMEM;
416                 goto fail;
417         }
418
419         *ret = n;
420         return 0;
421
422 fail:
423         return sd_bus_error_set_errno(error, r);
424 }
425
426 #if 0 /// UNNEEDED by elogind
427 _public_ int sd_bus_get_property_strv(
428                 sd_bus *bus,
429                 const char *destination,
430                 const char *path,
431                 const char *interface,
432                 const char *member,
433                 sd_bus_error *error,
434                 char ***ret) {
435
436         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
437         int r;
438
439         bus_assert_return(bus, -EINVAL, error);
440         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
441         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
442         bus_assert_return(ret, -EINVAL, error);
443         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
444
445         if (!BUS_IS_OPEN(bus->state)) {
446                 r = -ENOTCONN;
447                 goto fail;
448         }
449
450         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
451         if (r < 0)
452                 return r;
453
454         r = sd_bus_message_enter_container(reply, 'v', NULL);
455         if (r < 0)
456                 goto fail;
457
458         r = sd_bus_message_read_strv(reply, ret);
459         if (r < 0)
460                 goto fail;
461
462         return 0;
463
464 fail:
465         return sd_bus_error_set_errno(error, r);
466 }
467
468 _public_ int sd_bus_set_property(
469                 sd_bus *bus,
470                 const char *destination,
471                 const char *path,
472                 const char *interface,
473                 const char *member,
474                 sd_bus_error *error,
475                 const char *type, ...) {
476
477         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
478         va_list ap;
479         int r;
480
481         bus_assert_return(bus, -EINVAL, error);
482         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
483         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
484         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
485         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
486
487         if (!BUS_IS_OPEN(bus->state)) {
488                 r = -ENOTCONN;
489                 goto fail;
490         }
491
492         r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
493         if (r < 0)
494                 goto fail;
495
496         r = sd_bus_message_append(m, "ss", strempty(interface), member);
497         if (r < 0)
498                 goto fail;
499
500         r = sd_bus_message_open_container(m, 'v', type);
501         if (r < 0)
502                 goto fail;
503
504         va_start(ap, type);
505         r = bus_message_append_ap(m, type, ap);
506         va_end(ap);
507         if (r < 0)
508                 goto fail;
509
510         r = sd_bus_message_close_container(m);
511         if (r < 0)
512                 goto fail;
513
514         return sd_bus_call(bus, m, 0, error, NULL);
515
516 fail:
517         return sd_bus_error_set_errno(error, r);
518 }
519 #endif // 0
520
521 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
522         sd_bus_creds *c;
523
524         assert_return(call, -EINVAL);
525         assert_return(call->sealed, -EPERM);
526         assert_return(call->bus, -EINVAL);
527         assert_return(!bus_pid_changed(call->bus), -ECHILD);
528
529         if (!BUS_IS_OPEN(call->bus->state))
530                 return -ENOTCONN;
531
532         c = sd_bus_message_get_creds(call);
533
534         /* All data we need? */
535         if (c && (mask & ~c->mask) == 0) {
536                 *creds = sd_bus_creds_ref(c);
537                 return 0;
538         }
539
540         /* No data passed? Or not enough data passed to retrieve the missing bits? */
541         if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
542                 /* We couldn't read anything from the call, let's try
543                  * to get it from the sender or peer. */
544
545                 if (call->sender)
546                         /* There's a sender, but the creds are
547                          * missing. This means we are talking via
548                          * dbus1, or are getting a message that was
549                          * sent to us via kdbus, but was converted
550                          * from a dbus1 message by the bus-proxy and
551                          * thus also lacks the creds. */
552                         return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
553                 else
554                         /* There's no sender, hence we are on a dbus1
555                          * direct connection. For direct connections
556                          * the credentials of the AF_UNIX peer matter,
557                          * which may be queried via
558                          * sd_bus_get_owner_creds(). */
559                         return sd_bus_get_owner_creds(call->bus, mask, creds);
560         }
561
562         return bus_creds_extend_by_pid(c, mask, creds);
563 }
564
565 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
566         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
567         uid_t our_uid;
568         bool know_caps = false;
569         int r;
570
571         assert_return(call, -EINVAL);
572         assert_return(call->sealed, -EPERM);
573         assert_return(call->bus, -EINVAL);
574         assert_return(!bus_pid_changed(call->bus), -ECHILD);
575
576         if (!BUS_IS_OPEN(call->bus->state))
577                 return -ENOTCONN;
578
579         if (capability >= 0) {
580
581                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
582                 if (r < 0)
583                         return r;
584
585                 /* We cannot use augmented caps for authorization,
586                  * since then data is acquired raceful from
587                  * /proc. This can never actually happen, but let's
588                  * better be safe than sorry, and do an extra check
589                  * here. */
590                 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
591
592                 /* Note that not even on kdbus we might have the caps
593                  * field, due to faked identities, or namespace
594                  * translation issues. */
595                 r = sd_bus_creds_has_effective_cap(creds, capability);
596                 if (r > 0)
597                         return 1;
598                 if (r == 0)
599                         know_caps = true;
600         } else {
601                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
602                 if (r < 0)
603                         return r;
604         }
605
606         /* Now, check the UID, but only if the capability check wasn't
607          * sufficient */
608         our_uid = getuid();
609         if (our_uid != 0 || !know_caps || capability < 0) {
610                 uid_t sender_uid;
611
612                 /* We cannot use augmented uid/euid for authorization,
613                  * since then data is acquired raceful from
614                  * /proc. This can never actually happen, but let's
615                  * better be safe than sorry, and do an extra check
616                  * here. */
617                 assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
618
619                 /* Try to use the EUID, if we have it. */
620                 r = sd_bus_creds_get_euid(creds, &sender_uid);
621                 if (r < 0)
622                         r = sd_bus_creds_get_uid(creds, &sender_uid);
623
624                 if (r >= 0) {
625                         /* Sender has same UID as us, then let's grant access */
626                         if (sender_uid == our_uid)
627                                 return 1;
628
629                         /* Sender is root, we are not root. */
630                         if (our_uid != 0 && sender_uid == 0)
631                                 return 1;
632                 }
633         }
634
635         return 0;
636 }