chiark / gitweb /
bus: add new sd_bus_creds object to encapsulate process credentials
[elogind.git] / src / login / logind-session-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 "util.h"
26 #include "strv.h"
27 #include "bus-util.h"
28 #include "bus-errors.h"
29
30 #include "logind.h"
31 #include "logind-session.h"
32 #include "logind-session-device.h"
33
34 static int property_get_user(
35                 sd_bus *bus,
36                 const char *path,
37                 const char *interface,
38                 const char *property,
39                 sd_bus_message *reply,
40                 void *userdata,
41                 sd_bus_error *error) {
42
43         _cleanup_free_ char *p = NULL;
44         Session *s = userdata;
45
46         assert(bus);
47         assert(reply);
48         assert(s);
49
50         p = user_bus_path(s->user);
51         if (!p)
52                 return -ENOMEM;
53
54         return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
55 }
56
57 static int property_get_name(
58                 sd_bus *bus,
59                 const char *path,
60                 const char *interface,
61                 const char *property,
62                 sd_bus_message *reply,
63                 void *userdata,
64                 sd_bus_error *error) {
65
66         Session *s = userdata;
67
68         assert(bus);
69         assert(reply);
70         assert(s);
71
72         return sd_bus_message_append(reply, "s", s->user->name);
73 }
74
75 static int property_get_seat(
76                 sd_bus *bus,
77                 const char *path,
78                 const char *interface,
79                 const char *property,
80                 sd_bus_message *reply,
81                 void *userdata,
82                 sd_bus_error *error) {
83
84         _cleanup_free_ char *p = NULL;
85         Session *s = userdata;
86
87         assert(bus);
88         assert(reply);
89         assert(s);
90
91         p = s->seat ? seat_bus_path(s->seat) : strdup("/");
92         if (!p)
93                 return -ENOMEM;
94
95         return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p);
96 }
97
98 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType);
99 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass);
100
101 static int property_get_active(
102                 sd_bus *bus,
103                 const char *path,
104                 const char *interface,
105                 const char *property,
106                 sd_bus_message *reply,
107                 void *userdata,
108                 sd_bus_error *error) {
109
110         Session *s = userdata;
111
112         assert(bus);
113         assert(reply);
114         assert(s);
115
116         return sd_bus_message_append(reply, "b", session_is_active(s));
117 }
118
119 static int property_get_state(
120                 sd_bus *bus,
121                 const char *path,
122                 const char *interface,
123                 const char *property,
124                 sd_bus_message *reply,
125                 void *userdata,
126                 sd_bus_error *error) {
127
128         Session *s = userdata;
129
130         assert(bus);
131         assert(reply);
132         assert(s);
133
134         return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s)));
135 }
136
137 static int property_get_idle_hint(
138                 sd_bus *bus,
139                 const char *path,
140                 const char *interface,
141                 const char *property,
142                 sd_bus_message *reply,
143                 void *userdata,
144                 sd_bus_error *error) {
145
146         Session *s = userdata;
147
148         assert(bus);
149         assert(reply);
150         assert(s);
151
152         return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0);
153 }
154
155 static int property_get_idle_since_hint(
156                 sd_bus *bus,
157                 const char *path,
158                 const char *interface,
159                 const char *property,
160                 sd_bus_message *reply,
161                 void *userdata,
162                 sd_bus_error *error) {
163
164         Session *s = userdata;
165         dual_timestamp t;
166         uint64_t u;
167         int r;
168
169         assert(bus);
170         assert(reply);
171         assert(s);
172
173         r = session_get_idle_hint(s, &t);
174         if (r < 0)
175                 return r;
176
177         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
178
179         return sd_bus_message_append(reply, "t", u);
180 }
181
182 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
183         Session *s = userdata;
184         int r;
185
186         assert(bus);
187         assert(message);
188         assert(s);
189
190         r = session_stop(s);
191         if (r < 0)
192                 return r;
193
194         return sd_bus_reply_method_return(message, NULL);
195 }
196
197 static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
198         Session *s = userdata;
199         int r;
200
201         assert(bus);
202         assert(message);
203         assert(s);
204
205         r = session_activate(s);
206         if (r < 0)
207                 return r;
208
209         return sd_bus_reply_method_return(message, NULL);
210 }
211
212 static int method_lock(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
213         Session *s = userdata;
214         int r;
215
216         assert(bus);
217         assert(message);
218         assert(s);
219
220         r = session_send_lock(s, streq(sd_bus_message_get_member(message), "Lock"));
221         if (r < 0)
222                 return r;
223
224         return sd_bus_reply_method_return(message, NULL);
225 }
226
227 static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
228         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
229         Session *s = userdata;
230         uid_t uid;
231         int r, b;
232
233         assert(bus);
234         assert(message);
235         assert(s);
236
237         r = sd_bus_message_read(message, "b", &b);
238         if (r < 0)
239                 return r;
240
241         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
242         if (r < 0)
243                 return r;
244
245         r = sd_bus_creds_get_uid(creds, &uid);
246         if (r < 0)
247                 return r;
248
249         if (uid != 0 && uid != s->user->uid)
250                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session my set idle hint");
251
252         session_set_idle_hint(s, b);
253
254         return sd_bus_reply_method_return(message, NULL);
255 }
256
257 static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
258         Session *s = userdata;
259         const char *swho;
260         int32_t signo;
261         KillWho who;
262         int r;
263
264         assert(bus);
265         assert(message);
266         assert(s);
267
268         r = sd_bus_message_read(message, "si", &swho, &signo);
269         if (r < 0)
270                 return r;
271
272         if (isempty(swho))
273                 who = KILL_ALL;
274         else {
275                 who = kill_who_from_string(swho);
276                 if (who < 0)
277                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
278         }
279
280         if (signo <= 0 || signo >= _NSIG)
281                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
282
283         r = session_kill(s, who, signo);
284         if (r < 0)
285                 return r;
286
287         return sd_bus_reply_method_return(message, NULL);
288 }
289
290 static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
291         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
292         Session *s = userdata;
293         int r, force;
294         uid_t uid;
295
296         assert(bus);
297         assert(message);
298         assert(s);
299
300         r = sd_bus_message_read(message, "b", &force);
301         if (r < 0)
302                 return r;
303
304         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_UID, &creds);
305         if (r < 0)
306                 return r;
307
308         r = sd_bus_creds_get_uid(creds, &uid);
309         if (r < 0)
310                 return r;
311
312         if (uid != 0 && (force || uid != s->user->uid))
313                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
314
315         r = session_set_controller(s, sd_bus_message_get_sender(message), force);
316         if (r < 0)
317                 return r;
318
319         return sd_bus_reply_method_return(message, NULL);
320 }
321
322 static int method_release_control(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
323         Session *s = userdata;
324
325         assert(bus);
326         assert(message);
327         assert(s);
328
329         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
330                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
331
332         session_drop_controller(s);
333
334         return sd_bus_reply_method_return(message, NULL);
335 }
336
337 static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
338         Session *s = userdata;
339         uint32_t major, minor;
340         SessionDevice *sd;
341         dev_t dev;
342         int r;
343
344         assert(bus);
345         assert(message);
346         assert(s);
347
348         r = sd_bus_message_read(message, "uu", &major, &minor);
349         if (r < 0)
350                 return r;
351
352         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
353                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
354
355         dev = makedev(major, minor);
356         sd = hashmap_get(s->devices, &dev);
357         if (sd)
358                 /* We don't allow retrieving a device multiple times.
359                  * The related ReleaseDevice call is not ref-counted.
360                  * The caller should use dup() if it requires more
361                  * than one fd (it would be functionally
362                  * equivalent). */
363                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
364
365         r = session_device_new(s, dev, &sd);
366         if (r < 0)
367                 return r;
368
369         r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active);
370         if (r < 0)
371                 session_device_free(sd);
372
373         return r;
374 }
375
376 static int method_release_device(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
377         Session *s = userdata;
378         uint32_t major, minor;
379         SessionDevice *sd;
380         dev_t dev;
381         int r;
382
383         assert(bus);
384         assert(message);
385         assert(s);
386
387         r = sd_bus_message_read(message, "uu", &major, &minor);
388         if (r < 0)
389                 return r;
390
391         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
392                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
393
394         dev = makedev(major, minor);
395         sd = hashmap_get(s->devices, &dev);
396         if (!sd)
397                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
398
399         session_device_free(sd);
400         return sd_bus_reply_method_return(message, NULL);
401 }
402
403 static int method_pause_device_complete(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
404         Session *s = userdata;
405         uint32_t major, minor;
406         SessionDevice *sd;
407         dev_t dev;
408         int r;
409
410         assert(bus);
411         assert(message);
412         assert(s);
413
414         r = sd_bus_message_read(message, "uu", &major, &minor);
415         if (r < 0)
416                 return r;
417
418         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
419                 return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
420
421         dev = makedev(major, minor);
422         sd = hashmap_get(s->devices, &dev);
423         if (!sd)
424                 return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
425
426         session_device_complete_pause(sd);
427
428         return sd_bus_reply_method_return(message, NULL);
429 }
430
431 const sd_bus_vtable session_vtable[] = {
432         SD_BUS_VTABLE_START(0),
433
434         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), 0),
435         SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, 0),
436         SD_BUS_PROPERTY("Name", "s", property_get_name, 0, 0),
437         SD_BUS_PROPERTY("Timestamp", "t", NULL, offsetof(Session, timestamp.realtime), 0),
438         SD_BUS_PROPERTY("TimestampMonotonic", "t", NULL, offsetof(Session, timestamp.monotonic), 0),
439         SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), 0),
440         SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, 0),
441         SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), 0),
442         SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), 0),
443         SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), 0),
444         SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), 0),
445         SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), 0),
446         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), 0),
447         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), 0),
448         SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), 0),
449         SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), 0),
450         SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), 0),
451         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), 0),
452         SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
453         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
454         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
455         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
456         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
457
458         SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, 0),
459         SD_BUS_METHOD("Activate", NULL, NULL, method_activate, 0),
460         SD_BUS_METHOD("Lock", NULL, NULL, method_lock, 0),
461         SD_BUS_METHOD("Unlock", NULL, NULL, method_lock, 0),
462         SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, 0),
463         SD_BUS_METHOD("Kill", "si", NULL, method_kill, 0),
464         SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, 0),
465         SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, 0),
466         SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, 0),
467         SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, 0),
468         SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, 0),
469
470         SD_BUS_SIGNAL("PauseDevice", "uus", 0),
471         SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
472         SD_BUS_SIGNAL("Lock", NULL, 0),
473         SD_BUS_SIGNAL("Unlock", NULL, 0),
474
475         SD_BUS_VTABLE_END
476 };
477
478 int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
479         Manager *m = userdata;
480         Session *session;
481         int r;
482
483         assert(bus);
484         assert(path);
485         assert(interface);
486         assert(found);
487         assert(m);
488
489         if (streq(path, "/org/freedesktop/login1/session/self")) {
490                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
491                 sd_bus_message *message;
492                 pid_t pid;
493
494                 message = sd_bus_get_current(bus);
495                 if (!message)
496                         return 0;
497
498                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
499                 if (r < 0)
500                         return r;
501
502                 r = sd_bus_creds_get_pid(creds, &pid);
503                 if (r < 0)
504                         return r;
505
506                 r = manager_get_session_by_pid(m, pid, &session);
507                 if (r <= 0)
508                         return 0;
509         } else {
510                 _cleanup_free_ char *e = NULL;
511                 const char *p;
512
513                 p = startswith(path, "/org/freedesktop/login1/session/");
514                 if (!p)
515                         return 0;
516
517                 e = sd_bus_label_unescape(p);
518                 if (!e)
519                         return -ENOMEM;
520
521                 session = hashmap_get(m->sessions, e);
522                 if (!session)
523                         return 0;
524         }
525
526         *found = session;
527         return 1;
528 }
529
530 char *session_bus_path(Session *s) {
531         _cleanup_free_ char *t = NULL;
532
533         assert(s);
534
535         t = sd_bus_label_escape(s->id);
536         if (!t)
537                 return NULL;
538
539         return strappend("/org/freedesktop/login1/session/", t);
540 }
541
542 int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
543         _cleanup_strv_free_ char **l = NULL;
544         Manager *m = userdata;
545         Session *session;
546         Iterator i;
547         int r;
548
549         assert(bus);
550         assert(path);
551         assert(nodes);
552
553         HASHMAP_FOREACH(session, m->sessions, i) {
554                 char *p;
555
556                 p = session_bus_path(session);
557                 if (!p)
558                         return -ENOMEM;
559
560                 r = strv_push(&l, p);
561                 if (r < 0) {
562                         free(p);
563                         return r;
564                 }
565         }
566
567         *nodes = l;
568         l = NULL;
569
570         return 1;
571 }
572
573 int session_send_signal(Session *s, bool new_session) {
574         _cleanup_free_ char *p = NULL;
575
576         assert(s);
577
578         p = session_bus_path(s);
579         if (!p)
580                 return -ENOMEM;
581
582         return sd_bus_emit_signal(
583                         s->manager->bus,
584                         "/org/freedesktop/login1",
585                         "org.freedesktop.login1.Manager",
586                         new_session ? "SessionNew" : "SessionRemoved",
587                         "so", s->id, p);
588 }
589
590 int session_send_changed(Session *s, const char *properties, ...) {
591         _cleanup_free_ char *p = NULL;
592         char **l;
593
594         assert(s);
595
596         if (!s->started)
597                 return 0;
598
599         p = session_bus_path(s);
600         if (!p)
601                 return -ENOMEM;
602
603         l = strv_from_stdarg_alloca(properties);
604
605         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l);
606 }
607
608 int session_send_lock(Session *s, bool lock) {
609         _cleanup_free_ char *p = NULL;
610
611         assert(s);
612
613         p = session_bus_path(s);
614         if (!p)
615                 return -ENOMEM;
616
617         return sd_bus_emit_signal(
618                         s->manager->bus,
619                         p,
620                         "org.freedesktop.login1.Session",
621                         lock ? "Lock" : "Unlock",
622                         NULL);
623 }
624
625 int session_send_lock_all(Manager *m, bool lock) {
626         Session *session;
627         Iterator i;
628         int r = 0;
629
630         assert(m);
631
632         HASHMAP_FOREACH(session, m->sessions, i) {
633                 int k;
634
635                 k = session_send_lock(session, lock);
636                 if (k < 0)
637                         r = k;
638         }
639
640         return r;
641 }
642
643 int session_send_create_reply(Session *s, sd_bus_error *error) {
644         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
645         _cleanup_close_ int fifo_fd = -1;
646         _cleanup_free_ char *p = NULL;
647
648         assert(s);
649
650         /* This is called after the session scope was successfully
651          * created, and finishes where bus_manager_create_session()
652          * left off. */
653
654         if (!s->create_message)
655                 return 0;
656
657         c = s->create_message;
658         s->create_message = NULL;
659
660         if (error)
661                 return sd_bus_reply_method_error(c, error);
662
663         fifo_fd = session_create_fifo(s);
664         if (fifo_fd < 0)
665                 return fifo_fd;
666
667         /* Update the session state file before we notify the client
668          * about the result. */
669         session_save(s);
670
671         p = session_bus_path(s);
672         if (!p)
673                 return -ENOMEM;
674
675         log_debug("Sending reply about created session: "
676                   "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
677                   s->id,
678                   p,
679                   s->user->runtime_path,
680                   fifo_fd,
681                   s->seat ? s->seat->id : "",
682                   (uint32_t) s->vtnr);
683
684         return sd_bus_reply_method_return(
685                         c, "soshusub",
686                         s->id,
687                         p,
688                         s->user->runtime_path,
689                         fifo_fd,
690                         (uint32_t) s->user->uid,
691                         s->seat ? s->seat->id : "",
692                         (uint32_t) s->vtnr,
693                         false);
694 }