chiark / gitweb /
bus: disable debug output in test-bus-kernel-benchmark
[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 }