fix the closing session bug, even if next user authentication is failed.
[platform/core/system/tlm.git] / src / daemon / tlm-seat.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm (Tiny Login Manager)
5  *
6  * Copyright (C) 2013-2014 Intel Corporation.
7  *
8  * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9  *          Jussi Laako <jussi.laako@linux.intel.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  */
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31
32 #include "config.h"
33
34 #include "tlm-seat.h"
35 #include "tlm-session-remote.h"
36 #include "tlm-log.h"
37 #include "tlm-error.h"
38 #include "tlm-utils.h"
39 #include "tlm-config-general.h"
40 #include "tlm-dbus-observer.h"
41
42 G_DEFINE_TYPE (TlmSeat, tlm_seat, G_TYPE_OBJECT);
43
44 #define TLM_SEAT_PRIV(obj) \
45     G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SEAT, TlmSeatPrivate)
46
47 enum {
48     PROP_0,
49     PROP_CONFIG,
50     PROP_ID,
51     PROP_PATH,
52     N_PROPERTIES
53 };
54 static GParamSpec *pspecs[N_PROPERTIES];
55
56 enum {
57     SIG_PREPARE_USER_LOGIN,
58     SIG_PREPARE_USER_LOGOUT,
59     SIG_SESSION_CREATED,
60     SIG_SESSION_TERMINATED,
61     SIG_SESSION_ERROR,
62     SIG_MAX
63 };
64 static guint signals[SIG_MAX];
65
66 struct _TlmSeatPrivate
67 {
68     TlmConfig *config;
69     gchar *id;
70     gchar *default_user;
71     gchar *path;
72     gchar *next_service;
73     gchar *next_user;
74     gchar *next_password;
75     GHashTable *next_environment;
76     gint64 prev_time;
77     gint32 prev_count;
78     gboolean default_active;
79     TlmSessionRemote *session;
80     TlmDbusObserver *dbus_observer; /* dbus server accessed only by user who has
81     active session */
82     TlmDbusObserver *prev_dbus_observer;
83 };
84
85 typedef struct _DelayClosure
86 {
87     TlmSeat *seat;
88     gchar *service;
89     gchar *username;
90     gchar *password;
91     GHashTable *environment;
92 } DelayClosure;
93
94 static void
95 _disconnect_session_signals (
96         TlmSeat *seat);
97
98 static void
99 _reset_next (TlmSeatPrivate *priv)
100 {
101     g_clear_string (&priv->next_service);
102     g_clear_string (&priv->next_user);
103     g_clear_string (&priv->next_password);
104     if (priv->next_environment) {
105         g_hash_table_unref (priv->next_environment);
106         priv->next_environment = NULL;
107     }
108 }
109
110 static void
111 _handle_session_created (
112         TlmSeat *self,
113         const gchar *sessionid,
114         gpointer user_data)
115 {
116     g_return_if_fail (self && TLM_IS_SEAT (self));
117
118     DBG ("sessionid: %s", sessionid);
119
120     g_signal_emit (self, signals[SIG_SESSION_CREATED], 0, self->priv->id);
121
122     g_clear_object (&self->priv->prev_dbus_observer);
123 }
124
125 static void
126 _close_active_session (TlmSeat *self)
127 {
128     TlmSeatPrivate *priv = TLM_SEAT_PRIV (self);
129     _disconnect_session_signals (self);
130     if (priv->session)
131         g_clear_object (&priv->session);
132 }
133
134 static void
135 _handle_session_terminated (
136         TlmSeat *self,
137         gpointer user_data)
138 {
139     g_return_if_fail (self && TLM_IS_SEAT (self));
140
141     TlmSeat *seat = TLM_SEAT(self);
142     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
143     gboolean stop = FALSE;
144
145     DBG ("seat %p session %p", self, priv->session);
146     _close_active_session (seat);
147
148     g_signal_emit (seat,
149             signals[SIG_SESSION_TERMINATED],
150             0,
151             priv->id,
152             &stop);
153     if (stop) {
154         DBG ("no relogin or switch user");
155         return;
156     }
157     g_clear_object (&priv->dbus_observer);
158
159     if (tlm_config_get_boolean (priv->config,
160                                 TLM_CONFIG_GENERAL,
161                                 TLM_CONFIG_GENERAL_X11_SESSION,
162                                 FALSE)) {
163         DBG ("X11 session termination");
164         if (kill (0, SIGTERM))
165             WARN ("Failed to send TERM signal to process tree");
166         return;
167     }
168
169     if (tlm_config_get_boolean (priv->config,
170                                 TLM_CONFIG_GENERAL,
171                                 TLM_CONFIG_GENERAL_AUTO_LOGIN,
172                                 TRUE) ||
173                                 seat->priv->next_user) {
174         DBG ("auto re-login with '%s'", seat->priv->next_user);
175         tlm_seat_create_session (seat,
176                 seat->priv->next_service,
177                 seat->priv->next_user,
178                 seat->priv->next_password,
179                 seat->priv->next_environment);
180         _reset_next (priv);
181     }
182 }
183
184 static void
185 _handle_error (
186         TlmSeat *self,
187         GError *error,
188         gpointer user_data)
189 {
190     g_return_if_fail (self && TLM_IS_SEAT (self));
191
192     DBG ("Error : %d:%s", error->code, error->message);
193     g_signal_emit (self, signals[SIG_SESSION_ERROR],  0, error->code);
194
195     if (error->code == TLM_ERROR_PAM_AUTH_FAILURE ||
196         error->code == TLM_ERROR_SESSION_CREATION_FAILURE ||
197         error->code == TLM_ERROR_SESSION_TERMINATION_FAILURE) {
198         DBG ("Destroy the session in case of creation/termination failure");
199         _close_active_session (self);
200         g_clear_object (&self->priv->dbus_observer);
201     }
202 }
203
204 static void
205 _disconnect_session_signals (
206         TlmSeat *seat)
207 {
208     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
209     if (!priv->session) return;
210     DBG ("seat %p session %p", seat, priv->session);
211
212     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
213             _handle_session_created, seat);
214     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
215             _handle_session_terminated, seat);
216     g_signal_handlers_disconnect_by_func (G_OBJECT (priv->session),
217             _handle_error, seat);
218 }
219
220 static void
221 _connect_session_signals (
222         TlmSeat *seat)
223 {
224     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
225     DBG ("seat %p", seat);
226     /* Connect session signals to handlers */
227     g_signal_connect_swapped (priv->session, "session-created",
228             G_CALLBACK (_handle_session_created), seat);
229     g_signal_connect_swapped (priv->session, "session-terminated",
230             G_CALLBACK(_handle_session_terminated), seat);
231     g_signal_connect_swapped (priv->session, "session-error",
232             G_CALLBACK(_handle_error), seat);
233 }
234
235 static gboolean
236 _create_dbus_observer (
237         TlmSeat *seat,
238         const gchar *username)
239 {
240     gchar *address = NULL;
241     uid_t uid = 0;
242
243     if (!username) return FALSE;
244
245     uid = tlm_user_get_uid (username);
246     if (uid == -1) return FALSE;
247
248     address = g_strdup_printf ("unix:path=%s/%s-%d", TLM_DBUS_SOCKET_PATH,
249             seat->priv->id, uid);
250     seat->priv->dbus_observer = TLM_DBUS_OBSERVER (tlm_dbus_observer_new (
251             NULL, seat, address, uid,
252             DBUS_OBSERVER_ENABLE_LOGOUT_USER |
253             DBUS_OBSERVER_ENABLE_SWITCH_USER));
254     g_free (address);
255     DBG ("created dbus obs: %p", seat->priv->dbus_observer);
256     return (seat->priv->dbus_observer != NULL);
257 }
258
259 static void
260 tlm_seat_dispose (GObject *self)
261 {
262     TlmSeat *seat = TLM_SEAT(self);
263
264     DBG("disposing seat: %s", seat->priv->id);
265
266     g_clear_object (&seat->priv->dbus_observer);
267     g_clear_object (&seat->priv->prev_dbus_observer);
268
269     _disconnect_session_signals (seat);
270     if (seat->priv->session)
271         g_clear_object (&seat->priv->session);
272     if (seat->priv->config) {
273         g_object_unref (seat->priv->config);
274         seat->priv->config = NULL;
275     }
276
277     G_OBJECT_CLASS (tlm_seat_parent_class)->dispose (self);
278 }
279
280 static void
281 tlm_seat_finalize (GObject *self)
282 {
283     TlmSeat *seat = TLM_SEAT(self);
284     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
285
286     g_clear_string (&priv->id);
287     g_clear_string (&priv->default_user);
288     g_clear_string (&priv->path);
289
290     _reset_next (priv);
291
292     G_OBJECT_CLASS (tlm_seat_parent_class)->finalize (self);
293 }
294
295 static void
296 _seat_set_property (GObject *obj,
297                     guint property_id,
298                     const GValue *value,
299                     GParamSpec *pspec)
300 {
301     TlmSeat *seat = TLM_SEAT(obj);
302     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
303
304     switch (property_id) {
305         case PROP_CONFIG:
306             priv->config = g_value_dup_object (value);
307             break;
308         case PROP_ID: 
309             priv->id = g_value_dup_string (value);
310             break;
311         case PROP_PATH:
312             priv->path = g_value_dup_string (value);
313             break;
314         default:
315             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
316     }
317 }
318
319 static void
320 _seat_get_property (GObject *obj,
321                     guint property_id,
322                     GValue *value,
323                     GParamSpec *pspec)
324 {
325     TlmSeat *seat = TLM_SEAT(obj);
326     TlmSeatPrivate *priv = TLM_SEAT_PRIV(seat);
327
328     switch (property_id) {
329         case PROP_CONFIG:
330             g_value_set_object (value, priv->config);
331             break;
332         case PROP_ID: 
333             g_value_set_string (value, priv->id);
334             break;
335         case PROP_PATH:
336             g_value_set_string (value, priv->path);
337             break;
338         default:
339             G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
340     }
341 }
342
343 static void
344 tlm_seat_class_init (TlmSeatClass *klass)
345 {
346     GObjectClass *g_klass = G_OBJECT_CLASS (klass);
347
348     g_type_class_add_private (klass, sizeof (TlmSeatPrivate));
349
350     g_klass->dispose = tlm_seat_dispose ;
351     g_klass->finalize = tlm_seat_finalize;
352     g_klass->set_property = _seat_set_property;
353     g_klass->get_property = _seat_get_property;
354
355     pspecs[PROP_CONFIG] =
356         g_param_spec_object ("config",
357                              "config object",
358                              "Configuration object",
359                              TLM_TYPE_CONFIG,
360                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
361                              G_PARAM_STATIC_STRINGS);
362     pspecs[PROP_ID] =
363         g_param_spec_string ("id",
364                              "seat id",
365                              "Seat Id",
366                              NULL,
367                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
368                              G_PARAM_STATIC_STRINGS);
369     pspecs[PROP_PATH] =
370         g_param_spec_string ("path",
371                              "object path",
372                              "Seat Object path at logind",
373                              NULL,
374                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|
375                              G_PARAM_STATIC_STRINGS);
376     g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
377
378     signals[SIG_PREPARE_USER_LOGIN] = g_signal_new ("prepare-user-login",
379                                               TLM_TYPE_SEAT,
380                                               G_SIGNAL_RUN_LAST,
381                                               0,
382                                               NULL,
383                                               NULL,
384                                               NULL,
385                                               G_TYPE_NONE,
386                                               1,
387                                               G_TYPE_STRING);
388     signals[SIG_PREPARE_USER_LOGOUT] = g_signal_new ("prepare-user-logout",
389                                               TLM_TYPE_SEAT,
390                                               G_SIGNAL_RUN_LAST,
391                                               0,
392                                               NULL,
393                                               NULL,
394                                               NULL,
395                                               G_TYPE_NONE,
396                                               1,
397                                               G_TYPE_STRING);
398     signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
399                                                     TLM_TYPE_SEAT,
400                                                     G_SIGNAL_RUN_LAST,
401                                                     0,
402                                                     NULL,
403                                                     NULL,
404                                                     NULL,
405                                                     G_TYPE_NONE,
406                                                     1,
407                                                     G_TYPE_STRING);
408     signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
409                                                     TLM_TYPE_SEAT,
410                                                     G_SIGNAL_RUN_LAST,
411                                                     0,
412                                                     NULL,
413                                                     NULL,
414                                                     NULL,
415                                                     G_TYPE_BOOLEAN,
416                                                     1,
417                                                     G_TYPE_STRING);
418     signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
419                                                     TLM_TYPE_SEAT,
420                                                     G_SIGNAL_RUN_LAST,
421                                                     0,
422                                                     NULL,
423                                                     NULL,
424                                                     NULL,
425                                                     G_TYPE_NONE,
426                                                     1,
427                                                     G_TYPE_UINT);
428 }
429
430 static void
431 tlm_seat_init (TlmSeat *seat)
432 {
433     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
434     
435     priv->id = priv->path = priv->default_user = NULL;
436     priv->dbus_observer = priv->prev_dbus_observer = NULL;
437     priv->default_active = FALSE;
438     seat->priv = priv;
439 }
440
441 const gchar *
442 tlm_seat_get_id (TlmSeat *seat)
443 {
444     g_return_val_if_fail (seat && TLM_IS_SEAT (seat), NULL);
445
446     return (const gchar*) seat->priv->id;
447 }
448
449 gboolean
450 tlm_seat_switch_user (TlmSeat *seat,
451                       const gchar *service,
452                       const gchar *username,
453                       const gchar *password,
454                       GHashTable *environment)
455 {
456     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
457
458     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
459
460     // If username & its password is not authenticated, immediately return FALSE
461     // so that current session is not terminated.
462     if (!tlm_authenticate_user (priv->config, username, password)) {
463         WARN("fail to tlm_authenticate_user");
464         return FALSE;
465     }
466
467     if (!priv->session) {
468         return tlm_seat_create_session (seat, service, username, password,
469                 environment);
470     }
471
472     _reset_next (priv);
473     priv->next_service = g_strdup (service);
474     priv->next_user = g_strdup (username);
475     priv->next_password = g_strdup (password);
476     priv->next_environment = g_hash_table_ref (environment);
477
478     return tlm_seat_terminate_session (seat);
479 }
480
481 static gchar *
482 _build_user_name (const gchar *template, const gchar *seat_id)
483 {
484     int seat_num = 0;
485     const char *pptr;
486     gchar *out;
487     GString *str;
488
489     if (strncmp (seat_id, "seat", 4) == 0)
490         seat_num = atoi (seat_id + 4);
491     else
492         WARN ("Unrecognized seat id format");
493     pptr = template;
494     str = g_string_sized_new (16);
495     while (*pptr != '\0') {
496         if (*pptr == '%') {
497             pptr++;
498             switch (*pptr) {
499                 case 'S':
500                     g_string_append_printf (str, "%d", seat_num);
501                     break;
502                 case 'I':
503                     g_string_append (str, seat_id);
504                     break;
505                 default:
506                     ;
507             }
508         } else {
509             g_string_append_c (str, *pptr);
510         }
511         pptr++;
512     }
513     out = g_string_free (str, FALSE);
514     return out;
515 }
516
517 static gboolean
518 _delayed_session (gpointer user_data)
519 {
520     DelayClosure *delay_closure = (DelayClosure *) user_data;
521
522     DBG ("delayed relogin for closure %p", delay_closure);
523     g_return_val_if_fail (user_data, G_SOURCE_REMOVE);
524
525     DBG ("delayed relogin for seat=%s, service=%s, user=%s",
526          delay_closure->seat->priv->id,
527          delay_closure->service,
528          delay_closure->username);
529     tlm_seat_create_session (delay_closure->seat,
530                              delay_closure->service,
531                              delay_closure->username,
532                              delay_closure->password,
533                              delay_closure->environment);
534     g_object_unref (delay_closure->seat);
535     g_free (delay_closure->service);
536     g_free (delay_closure->username);
537     g_free (delay_closure->password);
538     if (delay_closure->environment)
539         g_hash_table_unref (delay_closure->environment);
540     g_slice_free (DelayClosure, delay_closure);
541     return G_SOURCE_REMOVE;
542 }
543
544 gboolean
545 tlm_seat_create_session (TlmSeat *seat,
546                          const gchar *service,
547                          const gchar *username,
548                          const gchar *password,
549                          GHashTable *environment)
550 {
551     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
552     TlmSeatPrivate *priv = TLM_SEAT_PRIV (seat);
553
554     if (priv->session != NULL) {
555         g_signal_emit (seat, signals[SIG_SESSION_ERROR],  0,
556                 TLM_ERROR_SESSION_ALREADY_EXISTS);
557         return FALSE;
558     }
559
560     if (g_get_monotonic_time () - priv->prev_time < 1000000) {
561         DBG ("short time relogin");
562         priv->prev_time = g_get_monotonic_time ();
563         priv->prev_count++;
564         if (priv->prev_count > 3) {
565             WARN ("relogins spinning too fast, delay...");
566             DelayClosure *delay_closure = g_slice_new0 (DelayClosure);
567             delay_closure->seat = g_object_ref (seat);
568             delay_closure->service = g_strdup (service);
569             delay_closure->username = g_strdup (username);
570             delay_closure->password = g_strdup (password);
571             if (environment)
572                 delay_closure->environment = g_hash_table_ref (environment);
573             g_timeout_add_seconds (10, _delayed_session, delay_closure);
574             return TRUE;
575         }
576     } else {
577         priv->prev_time = g_get_monotonic_time ();
578         priv->prev_count = 1;
579     }
580
581     if (!service) {
582         DBG ("PAM service not defined, looking up configuration");
583         service = tlm_config_get_string (priv->config,
584                                          priv->id,
585                                          username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
586         if (!service)
587             service = tlm_config_get_string (priv->config,
588                                              TLM_CONFIG_GENERAL,
589                                              username ? TLM_CONFIG_GENERAL_PAM_SERVICE : TLM_CONFIG_GENERAL_DEFAULT_PAM_SERVICE);
590         if (!service)
591             service = username ? "tlm-login" : "tlm-default-login";
592     }
593     DBG ("using PAM service %s for seat %s", service, priv->id);
594
595     if (!username) {
596         if (!priv->default_user) {
597             const gchar *name_tmpl =
598                     tlm_config_get_string_default (priv->config,
599                             priv->id,
600                             TLM_CONFIG_GENERAL_DEFAULT_USER,
601                             "guest");
602             if (!name_tmpl)
603                 name_tmpl = tlm_config_get_string_default (priv->config,
604                         TLM_CONFIG_GENERAL,
605                         TLM_CONFIG_GENERAL_DEFAULT_USER,
606                         "guest");
607             if (name_tmpl)
608                 priv->default_user = _build_user_name (name_tmpl, priv->id);
609         }
610         if (priv->default_user) {
611             priv->default_active = TRUE;
612             g_signal_emit (seat,
613                            signals[SIG_PREPARE_USER_LOGIN],
614                            0,
615                            priv->default_user);
616         }
617     }
618
619     priv->session = tlm_session_remote_new (priv->config,
620             priv->id,
621             service,
622             priv->default_active ? priv->default_user : username);
623     if (!priv->session) {
624         g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
625                 TLM_ERROR_SESSION_CREATION_FAILURE);
626         return FALSE;
627     }
628
629     /*It is needed to handle switch user case which completes after new session
630      *is created */
631     seat->priv->prev_dbus_observer = seat->priv->dbus_observer;
632     seat->priv->dbus_observer = NULL;
633     if (!_create_dbus_observer (seat,
634             priv->default_active ? priv->default_user : username)) {
635         g_object_unref (priv->session);
636         g_signal_emit (seat, signals[SIG_SESSION_ERROR],  0,
637                 TLM_ERROR_DBUS_SERVER_START_FAILURE);
638         return FALSE;
639     }
640
641     _connect_session_signals (seat);
642     tlm_session_remote_create (priv->session, password, environment);
643     return TRUE;
644 }
645
646 gboolean
647 tlm_seat_terminate_session (TlmSeat *seat)
648 {
649     g_return_val_if_fail (seat && TLM_IS_SEAT(seat), FALSE);
650     g_return_val_if_fail (seat->priv, FALSE);
651
652     if (seat->priv->default_active) {
653         seat->priv->default_active = FALSE;
654         g_signal_emit (seat,
655                 signals[SIG_PREPARE_USER_LOGOUT],
656                 0,
657                 seat->priv->default_user);
658     }
659
660     if (!seat->priv->session ||
661         !tlm_session_remote_terminate (seat->priv->session)) {
662         WARN ("No active session to terminate");
663         g_signal_emit (seat, signals[SIG_SESSION_ERROR], 0,
664                 TLM_ERROR_SESSION_NOT_VALID);
665         return FALSE;
666     }
667
668     return TRUE;
669 }
670
671 TlmSeat *
672 tlm_seat_new (TlmConfig *config,
673               const gchar *id,
674               const gchar *path)
675 {
676     TlmSeat *seat = g_object_new (TLM_TYPE_SEAT,
677                          "config", config,
678                          "id", id,
679                          "path", path,
680                          NULL);
681     return seat;
682 }
683