1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
4 * This file is part of tlm (Tizen Login Manager)
6 * Copyright (C) 2013-2014 Intel Corporation.
8 * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9 * Jussi Laako <jussi.laako@linux.intel.com>
10 * Imran Zaman <imran.zaman@intel.com>
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
38 #include <sys/types.h>
41 #include <sys/ioctl.h>
43 #include <sys/socket.h>
46 #include "tlm-session.h"
47 #include "tlm-auth-session.h"
48 #include "common/tlm-log.h"
49 #include "common/tlm-utils.h"
50 #include "common/tlm-error.h"
51 #include "common/tlm-config-general.h"
53 G_DEFINE_TYPE (TlmSession, tlm_session, G_TYPE_OBJECT);
55 #define TLM_SESSION_PRIV(obj) \
56 G_TYPE_INSTANCE_GET_PRIVATE ((obj), TLM_TYPE_SESSION, TlmSessionPrivate)
67 static GParamSpec *pspecs[N_PROPERTIES];
71 SIG_SESSION_TERMINATED,
76 static guint signals[SIG_MAX];
78 struct _TlmSessionPrivate
84 struct termios stdin_state, stdout_state, stderr_state;
89 TlmAuthSession *auth_session;
94 gboolean can_emit_signal;
96 gboolean session_pause;
100 _clear_session (TlmSession *session)
102 tlm_session_reset_tty (session);
104 if (session->priv->timer_id) {
105 g_source_remove (session->priv->timer_id);
106 session->priv->timer_id = 0;
109 if (session->priv->child_watch_id) {
110 g_source_remove (session->priv->child_watch_id);
111 session->priv->child_watch_id = 0;
114 if (session->priv->auth_session)
115 g_clear_object (&session->priv->auth_session);
117 if (session->priv->env_hash) {
118 g_hash_table_unref (session->priv->env_hash);
119 session->priv->env_hash = NULL;
121 g_clear_string (&session->priv->seat_id);
122 g_clear_string (&session->priv->service);
123 g_clear_string (&session->priv->username);
124 g_clear_string (&session->priv->sessionid);
128 tlm_session_dispose (GObject *self)
130 TlmSession *session = TLM_SESSION(self);
131 TlmSessionPrivate *priv = session->priv;
132 DBG("disposing session: %s", priv->service);
133 priv->can_emit_signal = FALSE;
135 tlm_session_terminate (session);
136 while (priv->is_child_up)
137 g_main_context_iteration(NULL, TRUE);
139 g_clear_object (&session->priv->config);
141 G_OBJECT_CLASS (tlm_session_parent_class)->dispose (self);
145 tlm_session_finalize (GObject *self)
147 //TlmSession *session = TLM_SESSION(self);
148 G_OBJECT_CLASS (tlm_session_parent_class)->finalize (self);
152 _session_set_property (GObject *obj,
157 TlmSession *session = TLM_SESSION(obj);
158 TlmSessionPrivate *priv = TLM_SESSION_PRIV (session);
160 switch (property_id) {
162 priv->config = g_value_dup_object (value);
165 g_free (priv->seat_id);
166 priv->seat_id = g_value_dup_string (value);
169 priv->service = g_value_dup_string (value);
172 priv->username = g_value_dup_string (value);
174 case PROP_ENVIRONMENT:
175 priv->env_hash = (GHashTable *) g_value_get_pointer (value);
177 g_hash_table_ref (priv->env_hash);
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
185 _session_get_property (GObject *obj,
190 TlmSession *session = TLM_SESSION(obj);
191 TlmSessionPrivate *priv = TLM_SESSION_PRIV (session);
193 switch (property_id) {
195 g_value_set_object (value, priv->config);
198 g_value_set_string (value, priv->seat_id);
201 g_value_set_string (value, priv->service);
204 g_value_set_string (value, priv->username);
206 case PROP_ENVIRONMENT:
207 g_value_set_pointer (value, priv->env_hash);
210 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
215 tlm_session_class_init (TlmSessionClass *klass)
217 GObjectClass *g_klass = G_OBJECT_CLASS (klass);
219 g_type_class_add_private (klass, sizeof (TlmSessionPrivate));
221 g_klass->dispose = tlm_session_dispose ;
222 g_klass->finalize = tlm_session_finalize;
223 g_klass->set_property = _session_set_property;
224 g_klass->get_property = _session_get_property;
226 pspecs[PROP_CONFIG] =
227 g_param_spec_object ("config",
229 "Configuration object",
232 G_PARAM_STATIC_STRINGS);
234 g_param_spec_string ("seat",
238 G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
239 pspecs[PROP_SERVICE] =
240 g_param_spec_string ("service",
241 "authentication service",
244 G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
245 pspecs[PROP_USERNAME] =
246 g_param_spec_string ("username",
248 "Unix user name of user to login",
250 G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
251 pspecs[PROP_ENVIRONMENT] =
252 g_param_spec_pointer ("environment",
253 "environment variables",
254 "Environment variables for the session",
255 G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
257 g_object_class_install_properties (g_klass, N_PROPERTIES, pspecs);
259 signals[SIG_SESSION_CREATED] = g_signal_new ("session-created",
260 TLM_TYPE_SESSION, G_SIGNAL_RUN_LAST,
261 0, NULL, NULL, NULL, G_TYPE_NONE,
264 signals[SIG_SESSION_TERMINATED] = g_signal_new ("session-terminated",
265 TLM_TYPE_SESSION, G_SIGNAL_RUN_LAST,
266 0, NULL, NULL, NULL, G_TYPE_NONE,
269 signals[SIG_AUTHENTICATED] = g_signal_new ("authenticated",
270 TLM_TYPE_SESSION, G_SIGNAL_RUN_LAST,
271 0, NULL, NULL, NULL, G_TYPE_NONE,
274 signals[SIG_SESSION_ERROR] = g_signal_new ("session-error",
275 TLM_TYPE_SESSION, G_SIGNAL_RUN_LAST,
276 0, NULL, NULL, NULL, G_TYPE_NONE,
282 tlm_session_init (TlmSession *session)
284 TlmSessionPrivate *priv = TLM_SESSION_PRIV (session);
286 priv->service = NULL;
287 priv->env_hash = NULL;
288 priv->auth_session = NULL;
289 priv->sessionid = NULL;
290 priv->child_watch_id = 0;
291 priv->is_child_up = FALSE;
292 priv->can_emit_signal = TRUE;
293 priv->config = tlm_config_new ();
295 session->priv = priv;
297 struct stat tty_stat;
299 if (fstat (0, &tty_stat) == 0) {
300 priv->tty_uid = tty_stat.st_uid;
301 priv->tty_gid = tty_stat.st_gid;
307 if (tcgetattr (0, &priv->stdin_state) ||
308 tcgetattr (1, &priv->stdout_state) ||
309 tcgetattr (2, &priv->stderr_state))
310 WARN ("Failed to retrieve initial terminal state");
314 _setenv_to_session (const gchar *key, const gchar *val,
315 TlmSessionPrivate *user_data)
317 TlmSessionPrivate *priv = (TlmSessionPrivate *) user_data;
318 if (priv->session_pause)
319 tlm_auth_session_set_env (priv->auth_session,
321 (const gchar *) val);
323 setenv ((const char *) key, (const char *) val, 1);
327 _set_terminal (TlmSessionPrivate *priv)
333 struct stat tty_stat;
335 tty_dev = ttyname (0);
336 DBG ("trying to setup TTY '%s'", tty_dev);
341 if (access (tty_dev, R_OK|W_OK)) {
342 WARN ("TTY not accessible: %s", strerror(errno));
345 if (lstat (tty_dev, &tty_stat)) {
346 WARN ("lstat() failed: %s", strerror(errno));
349 if (tty_stat.st_nlink > 1 ||
350 !S_ISCHR (tty_stat.st_mode) ||
351 strncmp (tty_dev, "/dev/", 5)) {
352 WARN ("Invalid TTY");
356 tty_fd = open (tty_dev, O_RDWR | O_NONBLOCK);
358 WARN ("open() failed: %s", strerror(errno));
361 if (!isatty (tty_fd)) {
363 WARN ("isatty() failed");
366 if (ioctl (tty_fd, TIOCSCTTY, 1))
367 WARN ("ioctl(TIOCSCTTY) failed: %s", strerror(errno));
368 tty_pgid = getpgid (getpid ());
369 if (ioctl (tty_fd, TIOCSPGRP, &tty_pgid)) {
370 WARN ("ioctl(TIOCSPGRP) failed: %s", strerror(errno));
372 /*if (tcsetpgrp (tty_fd, getpgrp ()))
373 WARN ("tcsetpgrp() failed: %s", strerror(errno));*/
375 // close all old handles
376 for (i = 0; i < tty_fd; i++)
387 _set_environment (TlmSessionPrivate *priv)
389 gchar **envlist = tlm_auth_session_get_envlist(priv->auth_session);
390 const gchar *home_dir = NULL, *shell = NULL;
394 for (env = envlist; *env != NULL; ++env) {
395 DBG ("ENV : %s", *env);
402 const gchar *path = tlm_config_get_string (priv->config,
404 TLM_CONFIG_GENERAL_SESSION_PATH);
406 path = "/usr/local/bin:/usr/bin:/bin";
407 _setenv_to_session ("PATH", path, priv);
409 _setenv_to_session ("USER", priv->username, priv);
410 _setenv_to_session ("LOGNAME", priv->username, priv);
411 home_dir = tlm_user_get_home_dir (priv->username);
412 if (home_dir) _setenv_to_session ("HOME", home_dir, priv);
413 shell = tlm_user_get_shell (priv->username);
414 if (shell) _setenv_to_session ("SHELL", shell, priv);
415 _setenv_to_session ("XDG_SEAT", priv->seat_id, priv);
417 const gchar *xdg_data_dirs =
418 tlm_config_get_string (priv->config,
420 TLM_CONFIG_GENERAL_DATA_DIRS);
422 xdg_data_dirs = "/usr/share:/usr/local/share";
423 _setenv_to_session ("XDG_DATA_DIRS", xdg_data_dirs, priv);
426 g_hash_table_foreach (priv->env_hash,
427 (GHFunc) _setenv_to_session,
439 g_spawn_close_pid (pid);
441 TlmSession *session = TLM_SESSION (data);
443 DBG ("Sessiond(%p) with pid (%d) closed with status %d", session, pid,
446 session->priv->child_pid = 0;
447 session->priv->is_child_up = FALSE;
448 _clear_session (session);
449 if (session->priv->can_emit_signal)
450 g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
458 const gchar *pattern = "('.*?'|\".*?\"|\\S+)";
460 const char *shell = NULL;
461 const char *env_shell = NULL;
463 gchar **args_iter = NULL;
464 TlmSessionPrivate *priv = session->priv;
465 gchar **temp_strv = NULL;
467 priv = session->priv;
469 priv->username = g_strdup (tlm_auth_session_get_username (
470 priv->auth_session));
471 DBG ("session ID : %s", priv->sessionid);
473 priv->child_pid = fork ();
474 if (priv->child_pid) {
475 DBG ("establish handler for the child pid %u", priv->child_pid);
476 session->priv->child_watch_id = g_child_watch_add (priv->child_pid,
477 (GChildWatchFunc)_on_child_down_cb, session);
478 session->priv->is_child_up = TRUE;
482 /* ==================================
483 * this is child process here onwards
484 * ================================== */
488 //close all open descriptors other than stdin, stdout, stderr
489 open_max = sysconf (_SC_OPEN_MAX);
490 for (fd = 3; fd < open_max; fd++)
491 fcntl (fd, F_SETFD, FD_CLOEXEC);
493 uid_t target_uid = tlm_user_get_uid (priv->username);
494 gid_t target_gid = tlm_user_get_gid (priv->username);
496 if (fchown (0, target_uid, -1)) {
497 WARN ("Changing TTY access rights failed");
500 /*if (getppid() == 1) {
501 if (setsid () == (pid_t) -1)
502 WARN ("setsid() failed: %s", strerror (errno));
505 WARN ("setpgrp() failed: %s", strerror (errno));
508 DBG ("old pgid=%u", getpgrp ());
509 if (setsid () == (pid_t) -1)
510 WARN ("setsid() failed: %s", strerror (errno));
511 DBG ("new pgid=%u", getpgrp());
513 if (tlm_config_get_boolean (priv->config,
515 TLM_CONFIG_GENERAL_SETUP_TERMINAL,
517 /* usually terminal settings are handled by PAM */
518 _set_terminal (priv);
521 if (initgroups (priv->username, target_gid))
522 WARN ("initgroups() failed: %s", strerror(errno));
523 if (setregid (target_gid, target_gid))
524 WARN ("setregid() failed: %s", strerror(errno));
525 if (setreuid (target_uid, target_uid))
526 WARN ("setreuid() failed: %s", strerror(errno));
528 int grouplist_len = NGROUPS_MAX;
529 gid_t grouplist[NGROUPS_MAX];
530 grouplist_len = getgroups (grouplist_len, grouplist);
531 DBG ("group membership:");
532 for (i = 0; i < grouplist_len; i++)
533 DBG ("\t%s", getgrgid (grouplist[i])->gr_name);
535 DBG (" state:\n\truid=%d, euid=%d, rgid=%d, egid=%d (%s)",
536 getuid(), geteuid(), getgid(), getegid(), priv->username);
537 _set_environment (priv);
540 home = getenv("HOME");
542 DBG ("changing directory to : %s", home);
543 if (chdir (home) < 0)
544 WARN ("Failed to change directroy : %s", strerror (errno));
545 } else WARN ("Could not get home directory");
547 shell = tlm_config_get_string (priv->config,
549 TLM_CONFIG_GENERAL_SESSION_CMD);
551 DBG ("Session command : %s", shell);
552 temp_strv = g_regex_split_simple (pattern,
555 G_REGEX_MATCH_NOTEMPTY);
561 args = g_new0 (gchar *, g_strv_length (temp_strv));
562 for (temp_iter = temp_strv, args_iter = args;
566 gchar *item = g_strstrip (*temp_iter);
568 item_len = strlen (item);
572 if ((item[0] == '\"' && item[item_len - 1] == '\"') ||
573 (item[0] == '\'' && item[item_len - 1] == '\'')) {
574 item[item_len - 1] = '\0';
575 memmove (item, item + 1, item_len - 1);
577 *args_iter = g_strcompress (item);
580 g_strfreev (temp_strv);
581 } else if ((env_shell = getenv("SHELL"))){
582 /* use shell if no override configured */
583 args = g_new0 (gchar *, 2);
584 args[0] = g_strdup (env_shell);
586 /* in case shell is not defined, fall back to systemd --user */
587 args = g_new0 (gchar *, 3);
588 args[0] = g_strdup ("systemd");
589 args[1] = g_strdup ("--user");
594 while (args_iter && *args_iter) {
595 DBG ("\targv[%d]: %s", i, *args_iter);
598 execvp (args[0], args);
599 /* we reach here only in case of error */
601 DBG ("execl(): %s", strerror(errno));
609 return g_object_new (TLM_TYPE_SESSION, NULL);
613 tlm_session_start (TlmSession *session,
614 const gchar *seat_id, const gchar *service,
615 const gchar *username, const gchar *password,
616 GHashTable *environment)
618 GError *error = NULL;
619 g_return_val_if_fail (session && TLM_IS_SESSION(session), FALSE);
620 TlmSessionPrivate *priv = TLM_SESSION_PRIV(session);
622 if (!seat_id || !service || !username) {
623 error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SESSION_CREATION_FAILURE,
624 "Unable to create PAM sesssion as input data is invalid "
625 "seatid(%p) service(%p) username(%p)", seat_id, service,
627 g_signal_emit (session, signals[SIG_SESSION_ERROR], 0, error);
628 g_error_free (error);
632 g_object_set (G_OBJECT (session), "seat", seat_id, "service", service,
633 "username", username, "environment", environment, NULL);
635 priv->auth_session = tlm_auth_session_new (priv->service, priv->username,
638 if (!priv->auth_session) {
639 error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SESSION_CREATION_FAILURE,
640 "Unable to create PAM sesssion");
641 g_signal_emit (session, signals[SIG_SESSION_ERROR], 0, error);
642 g_error_free (error);
646 tlm_auth_session_putenv (priv->auth_session, "XDG_SEAT", priv->seat_id);
648 if (!tlm_auth_session_authenticate (priv->auth_session, &error)) {
650 //consistant error message flow
651 GError *err = TLM_GET_ERROR_FOR_ID (
652 TLM_ERROR_SESSION_CREATION_FAILURE,
653 "%d:%s", error->code, error->message);
654 g_error_free (error);
657 error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SESSION_CREATION_FAILURE,
658 "Unable to authenticate PAM sesssion");
660 g_signal_emit (session, signals[SIG_SESSION_ERROR], 0, error);
661 g_error_free (error);
664 g_signal_emit (session, signals[SIG_AUTHENTICATED], 0);
666 priv->session_pause = tlm_config_get_boolean (priv->config,
668 TLM_CONFIG_GENERAL_PAUSE_SESSION,
670 if (priv->session_pause) {
671 _set_environment (priv);
675 if (!tlm_auth_session_open (priv->auth_session, &error)) {
677 error = TLM_GET_ERROR_FOR_ID (TLM_ERROR_SESSION_CREATION_FAILURE,
678 "Unable to open PAM sesssion");
680 g_signal_emit (session, signals[SIG_SESSION_ERROR], 0, error);
681 g_error_free (error);
684 priv->sessionid = g_strdup (tlm_auth_session_get_sessionid (
685 priv->auth_session));
686 tlm_utils_log_utmp_entry (priv->username);
687 if (!priv->session_pause)
688 _exec_user_session (session);
689 g_signal_emit (session, signals[SIG_SESSION_CREATED], 0, priv->sessionid);
694 _terminate_timeout (gpointer user_data)
696 TlmSession *session = TLM_SESSION(user_data);
697 TlmSessionPrivate *priv = TLM_SESSION_PRIV(session);
699 switch (priv->last_sig)
702 DBG ("child %u didn't respond to SIGHUP, sending SIGTERM",
704 if (killpg (getpgid (priv->child_pid), SIGTERM))
705 WARN ("killpg(%u, SIGTERM): %s",
706 getpgid (priv->child_pid),
708 priv->last_sig = SIGTERM;
709 return G_SOURCE_CONTINUE;
711 DBG ("child %u didn't respond to SIGTERM, sending SIGKILL",
713 if (killpg (getpgid (priv->child_pid), SIGKILL))
714 WARN ("killpg(%u, SIGKILL): %s",
715 getpgid (priv->child_pid),
717 priv->last_sig = SIGKILL;
718 return G_SOURCE_CONTINUE;
720 DBG ("child %u didn't respond to SIGKILL, process is stuck in kernel",
723 _clear_session (session);
724 if (session->priv->can_emit_signal) {
725 GError *error = TLM_GET_ERROR_FOR_ID (
726 TLM_ERROR_SESSION_TERMINATION_FAILURE,
727 "Unable to terminate session - process is stuck"
729 g_signal_emit (session, signals[SIG_SESSION_ERROR], 0, error);
730 g_error_free (error);
732 return G_SOURCE_REMOVE;
734 WARN ("%d has unknown signaling state %d",
738 return G_SOURCE_REMOVE;
742 tlm_session_terminate (TlmSession *session)
744 g_return_if_fail (session && TLM_IS_SESSION(session));
745 TlmSessionPrivate *priv = TLM_SESSION_PRIV(session);
747 DBG ("Session Terminate");
749 if (!priv->is_child_up) {
750 DBG ("no child process is running - closing pam session");
751 _clear_session (session);
752 if (session->priv->can_emit_signal)
753 g_signal_emit (session, signals[SIG_SESSION_TERMINATED], 0);
757 if (killpg (getpgid (priv->child_pid), SIGHUP) < 0)
758 WARN ("kill(%u, SIGHUP): %s",
759 getpgid (priv->child_pid),
761 priv->last_sig = SIGHUP;
762 priv->timer_id = g_timeout_add_seconds (
763 tlm_config_get_uint (priv->config, TLM_CONFIG_GENERAL,
764 TLM_CONFIG_GENERAL_TERMINATE_TIMEOUT, 3),
770 tlm_session_reset_tty (TlmSession *session)
772 TlmSessionPrivate *priv = TLM_SESSION_PRIV(session);
774 if (fchown (0, priv->tty_uid, priv->tty_gid))
775 WARN ("Changing TTY access rights failed");
776 if (tcflush (0, TCIOFLUSH) ||
777 tcflush (1, TCIOFLUSH) ||
778 tcflush (2, TCIOFLUSH))
779 WARN ("Flushing stdio failed");
780 pid_t pgid = getpgid (getpid ());
781 if (tcsetpgrp (0, pgid) || tcsetpgrp (1, pgid) || tcsetpgrp (2, pgid))
782 WARN ("Change TTY controlling process failed");
783 if (tcsetattr (0, TCSANOW, &priv->stdin_state) ||
784 tcsetattr (1, TCSANOW, &priv->stdout_state) ||
785 tcsetattr (2, TCSANOW, &priv->stderr_state))
786 WARN ("Restoring TTY settings failed");