chiark / gitweb /
bb5dd29a5782c626cb9689e83225b7c32622d54b
[elogind.git] / src / libsystemd-bus / bus-util.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 <sys/socket.h>
23
24 #include "util.h"
25 #include "strv.h"
26 #include "macro.h"
27 #include "def.h"
28
29 #include "sd-event.h"
30 #include "sd-bus.h"
31 #include "bus-error.h"
32 #include "bus-message.h"
33
34 #include "bus-util.h"
35
36 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
37         sd_event *e = userdata;
38
39         assert(bus);
40         assert(m);
41         assert(e);
42
43         sd_event_request_quit(e);
44         return 1;
45 }
46
47 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
48         _cleanup_free_ char *match = NULL;
49         int r;
50
51         assert(e);
52         assert(bus);
53         assert(name);
54
55         r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
56         if (r < 0)
57                 return r;
58
59         r = sd_bus_add_match(bus, match, quit_callback, e);
60         if (r < 0)
61                 return r;
62
63         r = sd_bus_release_name(bus, name);
64         if (r < 0)
65                 return r;
66
67         if (r != SD_BUS_NAME_RELEASED)
68                 return -EIO;
69
70         return 0;
71 }
72
73 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
74         bool exiting = false;
75         int r;
76
77         assert(e);
78         assert(bus);
79         assert(name);
80
81         for (;;) {
82                 r = sd_event_get_state(e);
83                 if (r < 0)
84                         return r;
85
86                 if (r == SD_EVENT_FINISHED)
87                         break;
88
89                 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
90                 if (r < 0)
91                         return r;
92
93                 if (r == 0 && !exiting) {
94                         r = bus_async_unregister_and_quit(e, bus, name);
95                         if (r < 0)
96                                 return r;
97
98                         exiting = true;
99                 }
100         }
101
102         return 0;
103 }
104
105 int bus_property_get_tristate(
106                 sd_bus *bus,
107                 const char *path,
108                 const char *interface,
109                 const char *property,
110                 sd_bus_message *reply,
111                 sd_bus_error *error,
112                 void *userdata) {
113
114         int *tristate = userdata;
115         int r;
116
117         r = sd_bus_message_append(reply, "b", *tristate > 0);
118         if (r < 0)
119                 return r;
120
121         return 1;
122 }
123
124 int bus_verify_polkit(
125                 sd_bus *bus,
126                 sd_bus_message *m,
127                 const char *action,
128                 bool interactive,
129                 bool *_challenge,
130                 sd_bus_error *e) {
131
132         const char *sender;
133         uid_t uid;
134         int r;
135
136         assert(bus);
137         assert(m);
138         assert(action);
139
140         sender = sd_bus_message_get_sender(m);
141         if (!sender)
142                 return -EBADMSG;
143
144         r = sd_bus_get_owner_uid(bus, sender, &uid);
145         if (r < 0)
146                 return r;
147
148         if (uid == 0)
149                 return 1;
150
151 #ifdef ENABLE_POLKIT
152         else {
153                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
154                 unsigned authorized = false, challenge = false;
155
156                 r = sd_bus_call_method(
157                                 bus,
158                                 "org.freedesktop.PolicyKit1",
159                                 "/org/freedesktop/PolicyKit1/Authority",
160                                 "org.freedesktop.PolicyKit1.Authority",
161                                 "CheckAuthorization",
162                                 e,
163                                 &reply,
164                                 "(sa{sv})sa{ss}us",
165                                 "system-bus-name", 1, "name", "s", sender,
166                                 action,
167                                 0,
168                                 interactive ? 1 : 0,
169                                 "");
170
171                 if (r < 0) {
172                         /* Treat no PK available as access denied */
173                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
174                                 sd_bus_error_free(e);
175                                 return -EACCES;
176                         }
177
178                         return r;
179                 }
180
181                 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
182                 if (r < 0)
183                         return r;
184
185                 if (authorized)
186                         return 1;
187
188                 if (_challenge) {
189                         *_challenge = challenge;
190                         return 0;
191                 }
192         }
193 #endif
194
195         return -EACCES;
196 }
197
198 #ifdef ENABLE_POLKIT
199
200 typedef struct AsyncPolkitQuery {
201         sd_bus_message *request, *reply;
202         sd_bus_message_handler_t callback;
203         void *userdata;
204         uint64_t serial;
205 } AsyncPolkitQuery;
206
207 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
208         AsyncPolkitQuery *q = userdata;
209         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
210         int r;
211
212         assert(bus);
213         assert(reply);
214         assert(q);
215
216         q->reply = sd_bus_message_ref(reply);
217         q->serial = 0;
218
219         m = sd_bus_message_ref(q->request);
220
221         r = sd_bus_message_rewind(m, true);
222         if (r < 0)
223                 return r;
224
225         r = q->callback(bus, m, q->userdata);
226         if (r < 0)
227                 return r;
228
229         return 1;
230 }
231
232 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
233
234         if (!q)
235                 return;
236
237         if (q->serial >  0 && b)
238                 sd_bus_send_with_reply_cancel(b, q->serial);
239
240         sd_bus_message_unref(q->request);
241         sd_bus_message_unref(q->reply);
242         free(q);
243 }
244
245 #endif
246
247 int bus_verify_polkit_async(
248                 sd_bus *bus,
249                 Hashmap **registry,
250                 sd_bus_message *m,
251                 const char *action,
252                 bool interactive,
253                 sd_bus_error *error,
254                 sd_bus_message_handler_t callback,
255                 void *userdata) {
256
257 #ifdef ENABLE_POLKIT
258         _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
259         AsyncPolkitQuery *q;
260 #endif
261         const char *sender;
262         uid_t uid;
263         int r;
264
265         assert(bus);
266         assert(registry);
267         assert(m);
268         assert(action);
269
270 #ifdef ENABLE_POLKIT
271         q = hashmap_remove(*registry, m);
272         if (q) {
273                 unsigned authorized, challenge;
274
275                 /* This is the second invocation of this function, and
276                  * there's already a response from polkit, let's
277                  * process it */
278                 assert(q->reply);
279
280                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
281                         const sd_bus_error *e;
282
283                         /* Treat no PK available as access denied */
284                         if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
285                                 async_polkit_query_free(bus, q);
286                                 return -EACCES;
287                         }
288
289                         e = sd_bus_message_get_error(q->reply);
290                         sd_bus_error_copy(error, e);
291                         r = sd_bus_error_get_errno(e);
292
293                         async_polkit_query_free(bus, q);
294                         return r;
295                 }
296
297                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
298                 if (r >= 0)
299                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
300
301                 async_polkit_query_free(bus, q);
302
303                 if (r < 0)
304                         return r;
305
306                 if (authorized)
307                         return 1;
308
309                 return -EACCES;
310         }
311 #endif
312
313         sender = sd_bus_message_get_sender(m);
314         if (!sender)
315                 return -EBADMSG;
316
317         r = sd_bus_get_owner_uid(bus, sender, &uid);
318         if (r < 0)
319                 return r;
320
321         if (uid == 0)
322                 return 1;
323 #ifdef ENABLE_POLKIT
324
325         r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
326         if (r < 0)
327                 return r;
328
329         r = sd_bus_message_new_method_call(
330                         bus,
331                         "org.freedesktop.PolicyKit1",
332                         "/org/freedesktop/PolicyKit1/Authority",
333                         "org.freedesktop.PolicyKit1.Authority",
334                         "CheckAuthorization",
335                         &pk);
336         if (r < 0)
337                 return r;
338
339         r = sd_bus_message_append(
340                         pk,
341                         "(sa{sv})sa{ss}us",
342                         "system-bus-name", 1, "name", "s", sender,
343                         action,
344                         0,
345                         interactive ? 1 : 0,
346                         "");
347         if (r < 0)
348                 return r;
349
350         q = new0(AsyncPolkitQuery, 1);
351         if (!q)
352                 return -ENOMEM;
353
354         q->request = sd_bus_message_ref(m);
355         q->callback = callback;
356         q->userdata = userdata;
357
358         r = hashmap_put(*registry, m, q);
359         if (r < 0) {
360                 async_polkit_query_free(bus, q);
361                 return r;
362         }
363
364         r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
365         if (r < 0)
366                 return r;
367
368         return 0;
369 #endif
370
371         return -EACCES;
372 }
373
374 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
375 #ifdef ENABLE_POLKIT
376         AsyncPolkitQuery *q;
377
378         while ((q = hashmap_steal_first(registry)))
379                 async_polkit_query_free(bus, q);
380
381         hashmap_free(registry);
382 #endif
383 }
384
385 static int bus_check_peercred(sd_bus *c) {
386         struct ucred ucred;
387         socklen_t l;
388         int fd;
389
390         assert(c);
391
392         fd = sd_bus_get_fd(c);
393         if (fd < 0)
394                 return fd;
395
396         l = sizeof(struct ucred);
397         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
398                 return -errno;
399
400         if (l != sizeof(struct ucred))
401                 return -E2BIG;
402
403         if (ucred.uid != 0 && ucred.uid != geteuid())
404                 return -EPERM;
405
406         return 1;
407 }
408
409 int bus_open_system_systemd(sd_bus **_bus) {
410         _cleanup_bus_unref_ sd_bus *bus = NULL;
411         int r;
412
413         assert(_bus);
414
415         if (geteuid() != 0)
416                 return sd_bus_open_system(_bus);
417
418         /* If we are root, then let's talk directly to the system
419          * instance, instead of going via the bus */
420
421         r = sd_bus_new(&bus);
422         if (r < 0)
423                 return r;
424
425         r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
426         if (r < 0)
427                 return r;
428
429         r = sd_bus_start(bus);
430         if (r < 0)
431                 return r;
432
433         r = bus_check_peercred(bus);
434         if (r < 0)
435                 return r;
436
437         *_bus = bus;
438         bus = NULL;
439
440         return 0;
441 }
442
443 int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
444         char type;
445         const char *contents;
446
447         assert(name);
448         assert(property);
449
450         sd_bus_message_peek_type(property, &type, &contents);
451
452         switch (type) {
453
454         case SD_BUS_TYPE_STRING: {
455                 const char *s;
456                 sd_bus_message_read_basic(property, type, &s);
457
458                 if (all || !isempty(s))
459                         printf("%s=%s\n", name, s);
460
461                 return 1;
462         }
463
464         case SD_BUS_TYPE_BOOLEAN: {
465                 bool b;
466
467                 sd_bus_message_read_basic(property, type, &b);
468                 printf("%s=%s\n", name, yes_no(b));
469
470                 return 1;
471         }
472
473         case SD_BUS_TYPE_UINT64: {
474                 uint64_t u;
475
476                 sd_bus_message_read_basic(property, type, &u);
477
478                 /* Yes, heuristics! But we can change this check
479                  * should it turn out to not be sufficient */
480
481                 if (endswith(name, "Timestamp")) {
482                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
483
484                         t = format_timestamp(timestamp, sizeof(timestamp), u);
485                         if (t || all)
486                                 printf("%s=%s\n", name, strempty(t));
487
488                 } else if (strstr(name, "USec")) {
489                         char timespan[FORMAT_TIMESPAN_MAX];
490
491                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
492                 } else
493                         printf("%s=%llu\n", name, (unsigned long long) u);
494
495                 return 1;
496         }
497
498         case SD_BUS_TYPE_UINT32: {
499                 uint32_t u;
500
501                 sd_bus_message_read_basic(property, type, &u);
502
503                 if (strstr(name, "UMask") || strstr(name, "Mode"))
504                         printf("%s=%04o\n", name, u);
505                 else
506                         printf("%s=%u\n", name, (unsigned) u);
507
508                 return 1;
509         }
510
511         case SD_BUS_TYPE_INT32: {
512                 int32_t i;
513
514                 sd_bus_message_read_basic(property, type, &i);
515
516                 printf("%s=%i\n", name, (int) i);
517                 return 1;
518         }
519
520         case SD_BUS_TYPE_DOUBLE: {
521                 double d;
522
523                 sd_bus_message_read_basic(property, type, &d);
524
525                 printf("%s=%g\n", name, d);
526                 return 1;
527         }
528
529         case SD_BUS_TYPE_ARRAY:
530
531                 if (streq(contents, "s")) {
532                         bool space = false;
533                         char tp;
534                         const char *cnt;
535
536                         sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
537
538                         sd_bus_message_peek_type(property, &tp, &cnt);
539                         if (all || cnt) {
540                                 const char *str;
541
542                                 printf("%s=", name);
543
544
545                                 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str) > 0) {
546                                         printf("%s%s", space ? " " : "", str);
547
548                                         space = true;
549                                 }
550
551                                 puts("");
552                         }
553
554                         sd_bus_message_exit_container(property);
555
556                         return 1;
557
558                 } else if (streq(contents, "y")) {
559                         const uint8_t *u;
560                         size_t n;
561
562                         sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
563                         if (all || n > 0) {
564                                 unsigned int i;
565
566                                 printf("%s=", name);
567
568                                 for (i = 0; i < n; i++)
569                                         printf("%02x", u[i]);
570
571                                 puts("");
572                         }
573
574                         return 1;
575
576                 } else if (streq(contents, "u")) {
577                         uint32_t *u;
578                         size_t n;
579
580                         sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
581                         if (all || n > 0) {
582                                 unsigned int i;
583
584                                 printf("%s=", name);
585
586                                 for (i = 0; i < n; i++)
587                                         printf("%08x", u[i]);
588
589                                 puts("");
590                         }
591
592                         return 1;
593                 }
594
595                 break;
596         }
597
598         return 0;
599 }
600
601 int bus_map_all_properties(sd_bus *bus,
602                            const char *destination,
603                            const char *path,
604                            const struct bus_properties_map *map) {
605         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
606         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
607         int r;
608
609         r = sd_bus_call_method( bus,
610                         destination,
611                         path,
612                         "org.freedesktop.DBus.Properties",
613                         "GetAll",
614                         &error,
615                         &m,
616                         "s", "");
617         if (r < 0) {
618                 log_error("Could not get properties: %s", bus_error_message(&error, -r));
619                 return r;
620         }
621
622         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
623         if (r < 0)
624                 return r;
625
626         while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
627                 const char *name;
628                 char type;
629                 const char *contents;
630                 unsigned i;
631
632                 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
633                 if (r < 0)
634                         return r;
635
636                 r = sd_bus_message_peek_type(m, NULL, &contents);
637                 if (r < 0)
638                         return r;
639
640                 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
641                 if (r < 0)
642                         return r;
643
644                 r = sd_bus_message_peek_type(m, &type, &contents);
645                 if (r < 0) {
646                         log_error("Could not determine type of message: %s", strerror(-r));
647                         return r;
648                 }
649
650                 switch (type) {
651                 case SD_BUS_TYPE_STRING: {
652                         const char *s;
653
654                         sd_bus_message_read_basic(m, type, &s);
655                         if (isempty(s))
656                                 break;
657
658                         for (i = 0; map[i].type; i++) {
659                                 char **p;
660
661                                 if (!streq(map[i].type, "s"))
662                                         continue;
663                                 if (!streq(map[i].name, name))
664                                         continue;
665
666                                 p = map[i].ptr;
667                                 free(*p);
668                                 *p = strdup(s);
669                                 if (!*p) {
670                                         r = -ENOMEM;
671                                         goto fail;
672                                 }
673                         }
674                         break;
675                 }
676
677                 case SD_BUS_TYPE_ARRAY: {
678                         _cleanup_strv_free_ char **l = NULL;
679
680                         if (!streq(contents, "s"))
681                                 break;
682
683                         for (i = 0; map[i].type; i++) {
684                                 char ***p;
685
686                                 if (!streq(map[i].type, "as"))
687                                         continue;
688                                 if (!streq(map[i].name, name))
689                                         continue;
690
691                                 r = bus_message_read_strv_extend(m, &l);
692                                 if (r < 0)
693                                         break;
694
695                                 p = map[i].ptr;
696                                 strv_free(*p);
697                                 *p = l;
698                                 l = NULL;
699                         }
700                         break;
701                 }
702
703                 case SD_BUS_TYPE_BOOLEAN: {
704                         unsigned b;
705
706                         sd_bus_message_read_basic(m, type, &b);
707
708                         for (i = 0; map[i].type; i++) {
709                                 bool *p;
710
711                                 if (!streq(map[i].type, "b"))
712                                         continue;
713                                 if (!streq(map[i].name, name))
714                                         continue;
715
716                                 p = map[i].ptr;
717                                 *p = b;
718                         }
719                         break;
720                 }
721
722                 case SD_BUS_TYPE_UINT64: {
723                         uint64_t t;
724
725                         sd_bus_message_read_basic(m, type, &t);
726
727                         for (i = 0; map[i].type; i++) {
728                                 uint64_t *p;
729
730                                 if (!streq(map[i].type, "t"))
731                                         continue;
732                                 if (!streq(map[i].name, name))
733                                         continue;
734
735                                 p = map[i].ptr;
736                                 *p = t;
737                         }
738                         break;
739                 }
740
741                 default:
742                         break;
743                 }
744
745                 r = sd_bus_message_exit_container(m);
746                 if (r < 0)
747                         return r;
748
749                 r = sd_bus_message_exit_container(m);
750                 if (r < 0)
751                         return r;
752         }
753
754 fail:
755         return r;
756 }
757
758 int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
759         int r;
760
761         assert(transport >= 0);
762         assert(transport < _BUS_TRANSPORT_MAX);
763         assert(bus);
764
765         assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
766         assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -ENOTSUP);
767
768         switch (transport) {
769
770         case BUS_TRANSPORT_LOCAL:
771                 if (user)
772                         r = sd_bus_open_user(bus);
773                 else
774                         r = sd_bus_open_system(bus);
775
776                 break;
777
778         case BUS_TRANSPORT_REMOTE:
779                 r = sd_bus_open_system_remote(host, bus);
780                 break;
781
782         case BUS_TRANSPORT_CONTAINER:
783                 r = sd_bus_open_system_container(host, bus);
784                 break;
785
786         default:
787                 assert_not_reached("Hmm, unknown transport type.");
788         }
789
790         return r;
791 }