chiark / gitweb /
6a2fb04a777441cc2e6e5836823751997f5f6b65
[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         int fd;
384         struct ucred ucred;
385         socklen_t l;
386
387         assert(c);
388
389         fd = sd_bus_get_fd(c);
390
391         assert(fd >= 0);
392
393         l = sizeof(struct ucred);
394         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
395                 log_error("SO_PEERCRED failed: %m");
396                 return -errno;
397         }
398
399         if (l != sizeof(struct ucred)) {
400                 log_error("SO_PEERCRED returned wrong size.");
401                 return -E2BIG;
402         }
403
404         if (ucred.uid != 0 && ucred.uid != geteuid())
405                 return -EPERM;
406
407         return 1;
408 }
409
410 int bus_connect_system(sd_bus **_bus) {
411         sd_bus *bus = NULL;
412         int r;
413         bool private = true;
414
415         assert(_bus);
416
417         if (geteuid() == 0) {
418                 /* If we are root, then let's talk directly to the
419                  * system 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         } else {
434                 r = sd_bus_open_system(&bus);
435                 if (r < 0)
436                         return r;
437
438                 private = false;
439         }
440
441         if (private) {
442                 r = bus_check_peercred(bus);
443                 if (r < 0) {
444                         sd_bus_unref(bus);
445
446                         return -EACCES;
447                 }
448         }
449
450         *_bus = bus;
451         return 0;
452 }
453
454 int bus_connect_system_ssh(const char *host, sd_bus **_bus) {
455         sd_bus *bus;
456         char *p = NULL;
457         int r;
458
459         assert(_bus);
460         assert(host);
461
462         asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
463         if (!p)
464                 return -ENOMEM;
465
466         r = sd_bus_new(&bus);
467         if (r < 0)
468                 return r;
469
470         r = sd_bus_set_address(bus, p);
471         if (r < 0)
472                 return r;
473
474         r = sd_bus_set_bus_client(bus, true);
475         if (r < 0)
476                 return r;
477
478         r = sd_bus_start(bus);
479         if (r < 0)
480                 return r;
481
482         *_bus = bus;
483         return 0;
484 }
485
486 int bus_generic_print_property(const char *name, sd_bus_message *property, bool all) {
487         char type;
488         const char *contents;
489
490         assert(name);
491         assert(property);
492
493         sd_bus_message_peek_type(property, &type, &contents);
494
495         switch (type) {
496
497         case SD_BUS_TYPE_STRING: {
498                 const char *s;
499                 sd_bus_message_read_basic(property, type, &s);
500
501                 if (all || !isempty(s))
502                         printf("%s=%s\n", name, s);
503
504                 return 1;
505         }
506
507         case SD_BUS_TYPE_BOOLEAN: {
508                 bool b;
509
510                 sd_bus_message_read_basic(property, type, &b);
511                 printf("%s=%s\n", name, yes_no(b));
512
513                 return 1;
514         }
515
516         case SD_BUS_TYPE_UINT64: {
517                 uint64_t u;
518
519                 sd_bus_message_read_basic(property, type, &u);
520
521                 /* Yes, heuristics! But we can change this check
522                  * should it turn out to not be sufficient */
523
524                 if (endswith(name, "Timestamp")) {
525                         char timestamp[FORMAT_TIMESTAMP_MAX], *t;
526
527                         t = format_timestamp(timestamp, sizeof(timestamp), u);
528                         if (t || all)
529                                 printf("%s=%s\n", name, strempty(t));
530
531                 } else if (strstr(name, "USec")) {
532                         char timespan[FORMAT_TIMESPAN_MAX];
533
534                         printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0));
535                 } else
536                         printf("%s=%llu\n", name, (unsigned long long) u);
537
538                 return 1;
539         }
540
541         case SD_BUS_TYPE_UINT32: {
542                 uint32_t u;
543
544                 sd_bus_message_read_basic(property, type, &u);
545
546                 if (strstr(name, "UMask") || strstr(name, "Mode"))
547                         printf("%s=%04o\n", name, u);
548                 else
549                         printf("%s=%u\n", name, (unsigned) u);
550
551                 return 1;
552         }
553
554         case SD_BUS_TYPE_INT32: {
555                 int32_t i;
556
557                 sd_bus_message_read_basic(property, type, &i);
558
559                 printf("%s=%i\n", name, (int) i);
560                 return 1;
561         }
562
563         case SD_BUS_TYPE_DOUBLE: {
564                 double d;
565
566                 sd_bus_message_read_basic(property, type, &d);
567
568                 printf("%s=%g\n", name, d);
569                 return 1;
570         }
571
572         case SD_BUS_TYPE_ARRAY:
573
574                 if (streq(contents, "s")) {
575                         bool space = false;
576                         char tp;
577                         const char *cnt;
578
579                         sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents);
580
581                         sd_bus_message_peek_type(property, &tp, &cnt);
582                         if (all || cnt) {
583                                 const char *str;
584
585                                 printf("%s=", name);
586
587
588                                 while(sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) {
589                                         printf("%s%s", space ? " " : "", str);
590
591                                         space = true;
592                                 }
593
594                                 puts("");
595                         }
596
597                         sd_bus_message_exit_container(property);
598
599                         return 1;
600
601                 } else if (streq(contents, "y")) {
602                         const uint8_t *u;
603                         size_t n;
604
605                         sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
606                         if (all || n > 0) {
607                                 unsigned int i;
608
609                                 printf("%s=", name);
610
611                                 for (i = 0; i < n; i++)
612                                         printf("%02x", u[i]);
613
614                                 puts("");
615                         }
616
617                         return 1;
618
619                 } else if (streq(contents, "u")) {
620                         uint32_t *u;
621                         size_t n;
622
623                         sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
624                         if (all || n > 0) {
625                                 unsigned int i;
626
627                                 printf("%s=", name);
628
629                                 for (i = 0; i < n; i++)
630                                         printf("%08x", u[i]);
631
632                                 puts("");
633                         }
634
635                         return 1;
636                 }
637
638                 break;
639         }
640
641         return 0;
642 }