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