chiark / gitweb /
logind: open up most bus calls for unpriviliged processes, using PolicyKit
[elogind.git] / src / login / logind-user-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <errno.h>
23 #include <string.h>
24
25 #include "strv.h"
26 #include "bus-util.h"
27 #include "logind.h"
28 #include "logind-user.h"
29
30 static int property_get_display(
31                 sd_bus *bus,
32                 const char *path,
33                 const char *interface,
34                 const char *property,
35                 sd_bus_message *reply,
36                 void *userdata,
37                 sd_bus_error *error) {
38
39         _cleanup_free_ char *p = NULL;
40         User *u = userdata;
41
42         assert(bus);
43         assert(reply);
44         assert(u);
45
46         p = u->display ? session_bus_path(u->display) : strdup("/");
47         if (!p)
48                 return -ENOMEM;
49
50         return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p);
51 }
52
53 static int property_get_state(
54                 sd_bus *bus,
55                 const char *path,
56                 const char *interface,
57                 const char *property,
58                 sd_bus_message *reply,
59                 void *userdata,
60                 sd_bus_error *error) {
61
62         User *u = userdata;
63
64         assert(bus);
65         assert(reply);
66         assert(u);
67
68         return sd_bus_message_append(reply, "s", user_state_to_string(user_get_state(u)));
69 }
70
71 static int property_get_sessions(
72                 sd_bus *bus,
73                 const char *path,
74                 const char *interface,
75                 const char *property,
76                 sd_bus_message *reply,
77                 void *userdata,
78                 sd_bus_error *error) {
79
80         User *u = userdata;
81         Session *session;
82         int r;
83
84         assert(bus);
85         assert(reply);
86         assert(u);
87
88         r = sd_bus_message_open_container(reply, 'a', "(so)");
89         if (r < 0)
90                 return r;
91
92         LIST_FOREACH(sessions_by_user, session, u->sessions) {
93                 _cleanup_free_ char *p = NULL;
94
95                 p = session_bus_path(session);
96                 if (!p)
97                         return -ENOMEM;
98
99                 r = sd_bus_message_append(reply, "(so)", session->id, p);
100                 if (r < 0)
101                         return r;
102
103         }
104
105         r = sd_bus_message_close_container(reply);
106         if (r < 0)
107                 return r;
108
109         return 1;
110 }
111
112 static int property_get_idle_hint(
113                 sd_bus *bus,
114                 const char *path,
115                 const char *interface,
116                 const char *property,
117                 sd_bus_message *reply,
118                 void *userdata,
119                 sd_bus_error *error) {
120
121         User *u = userdata;
122
123         assert(bus);
124         assert(reply);
125         assert(u);
126
127         return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0);
128 }
129
130 static int property_get_idle_since_hint(
131                 sd_bus *bus,
132                 const char *path,
133                 const char *interface,
134                 const char *property,
135                 sd_bus_message *reply,
136                 void *userdata,
137                 sd_bus_error *error) {
138
139         User *u = userdata;
140         dual_timestamp t;
141         uint64_t k;
142
143         assert(bus);
144         assert(reply);
145         assert(u);
146
147         user_get_idle_hint(u, &t);
148         k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
149
150         return sd_bus_message_append(reply, "t", k);
151 }
152
153 static int property_get_linger(
154                 sd_bus *bus,
155                 const char *path,
156                 const char *interface,
157                 const char *property,
158                 sd_bus_message *reply,
159                 void *userdata,
160                 sd_bus_error *error) {
161
162         User *u = userdata;
163         int r;
164
165         assert(bus);
166         assert(reply);
167         assert(u);
168
169         r = user_check_linger_file(u);
170
171         return sd_bus_message_append(reply, "b", r > 0);
172 }
173
174 int bus_user_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
175         User *u = userdata;
176         int r;
177
178         assert(bus);
179         assert(message);
180         assert(u);
181
182         r = bus_verify_polkit_async(
183                         message,
184                         CAP_KILL,
185                         "org.freedesktop.login1.manage",
186                         false,
187                         u->uid,
188                         &u->manager->polkit_registry,
189                         error);
190         if (r < 0)
191                 return r;
192         if (r == 0)
193                 return 1; /* Will call us back */
194
195         r = user_stop(u, true);
196         if (r < 0)
197                 return r;
198
199         return sd_bus_reply_method_return(message, NULL);
200 }
201
202 int bus_user_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
203         User *u = userdata;
204         int32_t signo;
205         int r;
206
207         assert(bus);
208         assert(message);
209         assert(u);
210
211         r = bus_verify_polkit_async(
212                         message,
213                         CAP_KILL,
214                         "org.freedesktop.login1.manage",
215                         false,
216                         u->uid,
217                         &u->manager->polkit_registry,
218                         error);
219         if (r < 0)
220                 return r;
221         if (r == 0)
222                 return 1; /* Will call us back */
223
224         r = sd_bus_message_read(message, "i", &signo);
225         if (r < 0)
226                 return r;
227
228         if (signo <= 0 || signo >= _NSIG)
229                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
230
231         r = user_kill(u, signo);
232         if (r < 0)
233                 return r;
234
235         return sd_bus_reply_method_return(message, NULL);
236 }
237
238 const sd_bus_vtable user_vtable[] = {
239         SD_BUS_VTABLE_START(0),
240
241         SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST),
242         SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST),
243         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST),
244         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
245         SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
246         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
247         SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST),
248         SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
249         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
250         SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
251         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
252         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
253         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
254         SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
255
256         SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
257         SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
258
259         SD_BUS_VTABLE_END
260 };
261
262 int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
263         Manager *m = userdata;
264         uid_t uid;
265         User *user;
266         int r;
267
268         assert(bus);
269         assert(path);
270         assert(interface);
271         assert(found);
272         assert(m);
273
274         if (streq(path, "/org/freedesktop/login1/user/self")) {
275                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
276                 sd_bus_message *message;
277
278                 message = sd_bus_get_current_message(bus);
279                 if (!message)
280                         return 0;
281
282                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
283                 if (r < 0)
284                         return r;
285
286                 r = sd_bus_creds_get_owner_uid(creds, &uid);
287         } else {
288                 const char *p;
289
290                 p = startswith(path, "/org/freedesktop/login1/user/_");
291                 if (!p)
292                         return 0;
293
294                 r = parse_uid(p, &uid);
295         }
296         if (r < 0)
297                 return 0;
298
299         user = hashmap_get(m->users, UID_TO_PTR(uid));
300         if (!user)
301                 return 0;
302
303         *found = user;
304         return 1;
305 }
306
307 char *user_bus_path(User *u) {
308         char *s;
309
310         assert(u);
311
312         if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0)
313                 return NULL;
314
315         return s;
316 }
317
318 int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
319         _cleanup_strv_free_ char **l = NULL;
320         sd_bus_message *message;
321         Manager *m = userdata;
322         User *user;
323         Iterator i;
324         int r;
325
326         assert(bus);
327         assert(path);
328         assert(nodes);
329
330         HASHMAP_FOREACH(user, m->users, i) {
331                 char *p;
332
333                 p = user_bus_path(user);
334                 if (!p)
335                         return -ENOMEM;
336
337                 r = strv_consume(&l, p);
338                 if (r < 0)
339                         return r;
340         }
341
342         message = sd_bus_get_current_message(bus);
343         if (message) {
344                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
345                 uid_t uid;
346
347                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
348                 if (r >= 0) {
349                         r = sd_bus_creds_get_owner_uid(creds, &uid);
350                         if (r >= 0) {
351                                 user = hashmap_get(m->users, UID_TO_PTR(uid));
352                                 if (user) {
353                                         r = strv_extend(&l, "/org/freedesktop/login1/user/self");
354                                         if (r < 0)
355                                                 return r;
356                                 }
357                         }
358                 }
359         }
360
361         *nodes = l;
362         l = NULL;
363
364         return 1;
365 }
366
367 int user_send_signal(User *u, bool new_user) {
368         _cleanup_free_ char *p = NULL;
369
370         assert(u);
371
372         p = user_bus_path(u);
373         if (!p)
374                 return -ENOMEM;
375
376         return sd_bus_emit_signal(
377                         u->manager->bus,
378                         "/org/freedesktop/login1",
379                         "org.freedesktop.login1.Manager",
380                         new_user ? "UserNew" : "UserRemoved",
381                         "uo", (uint32_t) u->uid, p);
382 }
383
384 int user_send_changed(User *u, const char *properties, ...) {
385         _cleanup_free_ char *p = NULL;
386         char **l;
387
388         assert(u);
389
390         if (!u->started)
391                 return 0;
392
393         p = user_bus_path(u);
394         if (!p)
395                 return -ENOMEM;
396
397         l = strv_from_stdarg_alloca(properties);
398
399         return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l);
400 }