chiark / gitweb /
Prep v236 : Add missing SPDX-License-Identifier (4/9) src/libelogind
[elogind.git] / src / libelogind / sd-bus / bus-convenience.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2013 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "bus-internal.h"
22 #include "bus-message.h"
23 #include "bus-signature.h"
24 #include "bus-type.h"
25 #include "bus-util.h"
26 #include "string-util.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_(sd_bus_message_unrefp) 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 = sd_bus_message_appendv(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 _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         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
73         int r;
74
75         assert_return(bus, -EINVAL);
76         assert_return(!bus_pid_changed(bus), -ECHILD);
77
78         if (!BUS_IS_OPEN(bus->state))
79                 return -ENOTCONN;
80
81         r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
82         if (r < 0)
83                 return r;
84
85         if (!isempty(types)) {
86                 va_list ap;
87
88                 va_start(ap, types);
89                 r = sd_bus_message_appendv(m, types, ap);
90                 va_end(ap);
91                 if (r < 0)
92                         return r;
93         }
94
95         return sd_bus_call_async(bus, slot, m, callback, userdata, 0);
96 }
97
98 _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         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
109         int r;
110
111         bus_assert_return(bus, -EINVAL, error);
112         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
113
114         if (!BUS_IS_OPEN(bus->state)) {
115                 r = -ENOTCONN;
116                 goto fail;
117         }
118
119         r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
120         if (r < 0)
121                 goto fail;
122
123         if (!isempty(types)) {
124                 va_list ap;
125
126                 va_start(ap, types);
127                 r = sd_bus_message_appendv(m, types, ap);
128                 va_end(ap);
129                 if (r < 0)
130                         goto fail;
131         }
132
133         return sd_bus_call(bus, m, 0, error, reply);
134
135 fail:
136         return sd_bus_error_set_errno(error, r);
137 }
138
139 _public_ int sd_bus_reply_method_return(
140                 sd_bus_message *call,
141                 const char *types, ...) {
142
143         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
144         int r;
145
146         assert_return(call, -EINVAL);
147         assert_return(call->sealed, -EPERM);
148         assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
149         assert_return(call->bus, -EINVAL);
150         assert_return(!bus_pid_changed(call->bus), -ECHILD);
151
152         if (!BUS_IS_OPEN(call->bus->state))
153                 return -ENOTCONN;
154
155         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
156                 return 0;
157
158         r = sd_bus_message_new_method_return(call, &m);
159         if (r < 0)
160                 return r;
161
162         if (!isempty(types)) {
163                 va_list ap;
164
165                 va_start(ap, types);
166                 r = sd_bus_message_appendv(m, types, ap);
167                 va_end(ap);
168                 if (r < 0)
169                         return r;
170         }
171
172         return sd_bus_send(call->bus, m, NULL);
173 }
174
175 _public_ int sd_bus_reply_method_error(
176                 sd_bus_message *call,
177                 const sd_bus_error *e) {
178
179         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
180         int r;
181
182         assert_return(call, -EINVAL);
183         assert_return(call->sealed, -EPERM);
184         assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
185         assert_return(sd_bus_error_is_set(e), -EINVAL);
186         assert_return(call->bus, -EINVAL);
187         assert_return(!bus_pid_changed(call->bus), -ECHILD);
188
189         if (!BUS_IS_OPEN(call->bus->state))
190                 return -ENOTCONN;
191
192         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
193                 return 0;
194
195         r = sd_bus_message_new_method_error(call, &m, e);
196         if (r < 0)
197                 return r;
198
199         return sd_bus_send(call->bus, m, NULL);
200 }
201
202 _public_ int sd_bus_reply_method_errorf(
203                 sd_bus_message *call,
204                 const char *name,
205                 const char *format,
206                 ...) {
207
208         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
209         va_list ap;
210
211         assert_return(call, -EINVAL);
212         assert_return(call->sealed, -EPERM);
213         assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
214         assert_return(call->bus, -EINVAL);
215         assert_return(!bus_pid_changed(call->bus), -ECHILD);
216
217         if (!BUS_IS_OPEN(call->bus->state))
218                 return -ENOTCONN;
219
220         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
221                 return 0;
222
223         va_start(ap, format);
224         bus_error_setfv(&error, name, format, ap);
225         va_end(ap);
226
227         return sd_bus_reply_method_error(call, &error);
228 }
229
230 _public_ int sd_bus_reply_method_errno(
231                 sd_bus_message *call,
232                 int error,
233                 const sd_bus_error *p) {
234
235         _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
236
237         assert_return(call, -EINVAL);
238         assert_return(call->sealed, -EPERM);
239         assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
240         assert_return(call->bus, -EINVAL);
241         assert_return(!bus_pid_changed(call->bus), -ECHILD);
242
243         if (!BUS_IS_OPEN(call->bus->state))
244                 return -ENOTCONN;
245
246         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
247                 return 0;
248
249         if (sd_bus_error_is_set(p))
250                 return sd_bus_reply_method_error(call, p);
251
252         sd_bus_error_set_errno(&berror, error);
253
254         return sd_bus_reply_method_error(call, &berror);
255 }
256
257 _public_ int sd_bus_reply_method_errnof(
258                 sd_bus_message *call,
259                 int error,
260                 const char *format,
261                 ...) {
262
263         _cleanup_(sd_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
285 _public_ int sd_bus_get_property(
286                 sd_bus *bus,
287                 const char *destination,
288                 const char *path,
289                 const char *interface,
290                 const char *member,
291                 sd_bus_error *error,
292                 sd_bus_message **reply,
293                 const char *type) {
294
295         sd_bus_message *rep = NULL;
296         int r;
297
298         bus_assert_return(bus, -EINVAL, error);
299         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
300         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
301         bus_assert_return(reply, -EINVAL, error);
302         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
303         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
304
305         if (!BUS_IS_OPEN(bus->state)) {
306                 r = -ENOTCONN;
307                 goto fail;
308         }
309
310         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
311         if (r < 0)
312                 return r;
313
314         r = sd_bus_message_enter_container(rep, 'v', type);
315         if (r < 0) {
316                 sd_bus_message_unref(rep);
317                 goto fail;
318         }
319
320         *reply = rep;
321         return 0;
322
323 fail:
324         return sd_bus_error_set_errno(error, r);
325 }
326
327 _public_ int sd_bus_get_property_trivial(
328                 sd_bus *bus,
329                 const char *destination,
330                 const char *path,
331                 const char *interface,
332                 const char *member,
333                 sd_bus_error *error,
334                 char type, void *ptr) {
335
336         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
337         int r;
338
339         bus_assert_return(bus, -EINVAL, error);
340         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
341         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
342         bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
343         bus_assert_return(ptr, -EINVAL, error);
344         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
345
346         if (!BUS_IS_OPEN(bus->state)) {
347                 r = -ENOTCONN;
348                 goto fail;
349         }
350
351         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
352         if (r < 0)
353                 return r;
354
355         r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
356         if (r < 0)
357                 goto fail;
358
359         r = sd_bus_message_read_basic(reply, type, ptr);
360         if (r < 0)
361                 goto fail;
362
363         return 0;
364
365 fail:
366         return sd_bus_error_set_errno(error, r);
367 }
368
369 _public_ int sd_bus_get_property_string(
370                 sd_bus *bus,
371                 const char *destination,
372                 const char *path,
373                 const char *interface,
374                 const char *member,
375                 sd_bus_error *error,
376                 char **ret) {
377
378         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
379         const char *s;
380         char *n;
381         int r;
382
383         bus_assert_return(bus, -EINVAL, error);
384         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
385         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
386         bus_assert_return(ret, -EINVAL, error);
387         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
388
389         if (!BUS_IS_OPEN(bus->state)) {
390                 r = -ENOTCONN;
391                 goto fail;
392         }
393
394         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
395         if (r < 0)
396                 return r;
397
398         r = sd_bus_message_enter_container(reply, 'v', "s");
399         if (r < 0)
400                 goto fail;
401
402         r = sd_bus_message_read_basic(reply, 's', &s);
403         if (r < 0)
404                 goto fail;
405
406         n = strdup(s);
407         if (!n) {
408                 r = -ENOMEM;
409                 goto fail;
410         }
411
412         *ret = n;
413         return 0;
414
415 fail:
416         return sd_bus_error_set_errno(error, r);
417 }
418
419 _public_ int sd_bus_get_property_strv(
420                 sd_bus *bus,
421                 const char *destination,
422                 const char *path,
423                 const char *interface,
424                 const char *member,
425                 sd_bus_error *error,
426                 char ***ret) {
427
428         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
429         int r;
430
431         bus_assert_return(bus, -EINVAL, error);
432         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
433         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
434         bus_assert_return(ret, -EINVAL, error);
435         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
436
437         if (!BUS_IS_OPEN(bus->state)) {
438                 r = -ENOTCONN;
439                 goto fail;
440         }
441
442         r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
443         if (r < 0)
444                 return r;
445
446         r = sd_bus_message_enter_container(reply, 'v', NULL);
447         if (r < 0)
448                 goto fail;
449
450         r = sd_bus_message_read_strv(reply, ret);
451         if (r < 0)
452                 goto fail;
453
454         return 0;
455
456 fail:
457         return sd_bus_error_set_errno(error, r);
458 }
459
460 _public_ int sd_bus_set_property(
461                 sd_bus *bus,
462                 const char *destination,
463                 const char *path,
464                 const char *interface,
465                 const char *member,
466                 sd_bus_error *error,
467                 const char *type, ...) {
468
469         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
470         va_list ap;
471         int r;
472
473         bus_assert_return(bus, -EINVAL, error);
474         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
475         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
476         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
477         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
478
479         if (!BUS_IS_OPEN(bus->state)) {
480                 r = -ENOTCONN;
481                 goto fail;
482         }
483
484         r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
485         if (r < 0)
486                 goto fail;
487
488         r = sd_bus_message_append(m, "ss", strempty(interface), member);
489         if (r < 0)
490                 goto fail;
491
492         r = sd_bus_message_open_container(m, 'v', type);
493         if (r < 0)
494                 goto fail;
495
496         va_start(ap, type);
497         r = sd_bus_message_appendv(m, type, ap);
498         va_end(ap);
499         if (r < 0)
500                 goto fail;
501
502         r = sd_bus_message_close_container(m);
503         if (r < 0)
504                 goto fail;
505
506         return sd_bus_call(bus, m, 0, error, NULL);
507
508 fail:
509         return sd_bus_error_set_errno(error, r);
510 }
511
512 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
513         sd_bus_creds *c;
514
515         assert_return(call, -EINVAL);
516         assert_return(call->sealed, -EPERM);
517         assert_return(call->bus, -EINVAL);
518         assert_return(!bus_pid_changed(call->bus), -ECHILD);
519
520         if (!BUS_IS_OPEN(call->bus->state))
521                 return -ENOTCONN;
522
523         c = sd_bus_message_get_creds(call);
524
525         /* All data we need? */
526         if (c && (mask & ~c->mask) == 0) {
527                 *creds = sd_bus_creds_ref(c);
528                 return 0;
529         }
530
531         /* No data passed? Or not enough data passed to retrieve the missing bits? */
532         if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
533                 /* We couldn't read anything from the call, let's try
534                  * to get it from the sender or peer. */
535
536                 if (call->sender)
537                         /* There's a sender, but the creds are missing. */
538                         return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
539                 else
540                         /* There's no sender. For direct connections
541                          * the credentials of the AF_UNIX peer matter,
542                          * which may be queried via sd_bus_get_owner_creds(). */
543                         return sd_bus_get_owner_creds(call->bus, mask, creds);
544         }
545
546         return bus_creds_extend_by_pid(c, mask, creds);
547 }
548
549 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
550         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
551         uid_t our_uid;
552         bool know_caps = false;
553         int r;
554
555         assert_return(call, -EINVAL);
556         assert_return(call->sealed, -EPERM);
557         assert_return(call->bus, -EINVAL);
558         assert_return(!bus_pid_changed(call->bus), -ECHILD);
559
560         if (!BUS_IS_OPEN(call->bus->state))
561                 return -ENOTCONN;
562
563         if (capability >= 0) {
564
565                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
566                 if (r < 0)
567                         return r;
568
569                 /* We cannot use augmented caps for authorization,
570                  * since then data is acquired raceful from
571                  * /proc. This can never actually happen, but let's
572                  * better be safe than sorry, and do an extra check
573                  * here. */
574                 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
575
576                 r = sd_bus_creds_has_effective_cap(creds, capability);
577                 if (r > 0)
578                         return 1;
579                 if (r == 0)
580                         know_caps = true;
581         } else {
582                 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
583                 if (r < 0)
584                         return r;
585         }
586
587         /* Now, check the UID, but only if the capability check wasn't
588          * sufficient */
589         our_uid = getuid();
590         if (our_uid != 0 || !know_caps || capability < 0) {
591                 uid_t sender_uid;
592
593                 /* We cannot use augmented uid/euid for authorization,
594                  * since then data is acquired raceful from
595                  * /proc. This can never actually happen, but let's
596                  * better be safe than sorry, and do an extra check
597                  * here. */
598                 assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
599
600                 /* Try to use the EUID, if we have it. */
601                 r = sd_bus_creds_get_euid(creds, &sender_uid);
602                 if (r < 0)
603                         r = sd_bus_creds_get_uid(creds, &sender_uid);
604
605                 if (r >= 0) {
606                         /* Sender has same UID as us, then let's grant access */
607                         if (sender_uid == our_uid)
608                                 return 1;
609
610                         /* Sender is root, we are not root. */
611                         if (our_uid != 0 && sender_uid == 0)
612                                 return 1;
613                 }
614         }
615
616         return 0;
617 }