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