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