1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/socket.h>
33 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
34 sd_event *e = userdata;
40 sd_event_request_quit(e);
44 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
45 _cleanup_free_ char *match = NULL;
52 r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
56 r = sd_bus_add_match(bus, match, quit_callback, e);
60 r = sd_bus_release_name(bus, name);
64 if (r != SD_BUS_NAME_RELEASED)
70 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
79 r = sd_event_get_state(e);
83 if (r == SD_EVENT_FINISHED)
86 r = sd_event_run(e, exiting ? (uint64_t) -1 : timeout);
90 if (r == 0 && !exiting) {
91 r = bus_async_unregister_and_quit(e, bus, name);
102 int bus_property_get_tristate(
105 const char *interface,
106 const char *property,
107 sd_bus_message *reply,
111 int *tristate = userdata;
114 r = sd_bus_message_append(reply, "b", *tristate > 0);
121 int bus_verify_polkit(
137 sender = sd_bus_message_get_sender(m);
141 r = sd_bus_get_owner_uid(bus, sender, &uid);
150 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
151 unsigned authorized = false, challenge = false;
153 r = sd_bus_call_method(
155 "org.freedesktop.PolicyKit1",
156 "/org/freedesktop/PolicyKit1/Authority",
157 "org.freedesktop.PolicyKit1.Authority",
158 "CheckAuthorization",
162 "system-bus-name", 1, "name", "s", sender,
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);
178 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
186 *_challenge = challenge;
197 typedef struct AsyncPolkitQuery {
198 sd_bus_message *request, *reply;
199 sd_bus_message_handler_t callback;
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;
213 q->reply = sd_bus_message_ref(reply);
216 m = sd_bus_message_ref(q->request);
218 r = sd_bus_message_rewind(m, true);
222 r = q->callback(bus, m, q->userdata);
229 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
234 if (q->serial > 0 && b)
235 sd_bus_send_with_reply_cancel(b, q->serial);
237 sd_bus_message_unref(q->request);
238 sd_bus_message_unref(q->reply);
244 int bus_verify_polkit_async(
251 sd_bus_message_handler_t callback,
255 _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
268 q = hashmap_remove(*registry, m);
270 unsigned authorized, challenge;
272 /* This is the second invocation of this function, and
273 * there's already a response from polkit, let's
277 if (sd_bus_message_is_method_error(q->reply, NULL)) {
278 const sd_bus_error *e;
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);
286 e = sd_bus_message_get_error(q->reply);
287 sd_bus_error_copy(error, e);
288 r = sd_bus_error_get_errno(e);
290 async_polkit_query_free(bus, q);
294 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
296 r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
298 async_polkit_query_free(bus, q);
310 sender = sd_bus_message_get_sender(m);
314 r = sd_bus_get_owner_uid(bus, sender, &uid);
322 r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
326 r = sd_bus_message_new_method_call(
328 "org.freedesktop.PolicyKit1",
329 "/org/freedesktop/PolicyKit1/Authority",
330 "org.freedesktop.PolicyKit1.Authority",
331 "CheckAuthorization",
336 r = sd_bus_message_append(
339 "system-bus-name", 1, "name", "s", sender,
347 q = new0(AsyncPolkitQuery, 1);
351 q->request = sd_bus_message_ref(m);
352 q->callback = callback;
353 q->userdata = userdata;
355 r = hashmap_put(*registry, m, q);
357 async_polkit_query_free(bus, q);
361 r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
371 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
375 while ((q = hashmap_steal_first(registry)))
376 async_polkit_query_free(bus, q);
378 hashmap_free(registry);
382 static int bus_check_peercred(sd_bus *c) {
389 fd = sd_bus_get_fd(c);
393 l = sizeof(struct ucred);
394 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
395 log_error("SO_PEERCRED failed: %m");
399 if (l != sizeof(struct ucred)) {
400 log_error("SO_PEERCRED returned wrong size.");
404 if (ucred.uid != 0 && ucred.uid != geteuid())
410 int bus_connect_system(sd_bus **_bus) {
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 */
421 r = sd_bus_new(&bus);
425 r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
429 r = sd_bus_start(bus);
434 r = sd_bus_open_system(&bus);
442 r = bus_check_peercred(bus);