#include <grp.h>
#include <glib.h>
#include <security/pam_appl.h>
+#include <utmp.h>
#include <utmpx.h>
#include <sys/mman.h>
+#if HAVE_LIBAUDIT
+#include <libaudit.h>
+#endif
+
#include "configuration.h"
#include "session-child.h"
#include "session.h"
#include "console-kit.h"
#include "login1.h"
+#include "log-file.h"
#include "privileges.h"
#include "x-authority.h"
#include "configuration.h"
n_read = read (from_daemon_output, buf, count);
if (n_read < 0)
g_printerr ("Error reading from daemon: %s\n", strerror (errno));
-
+
return n_read;
}
g_printerr ("Invalid string length %d from daemon\n", length);
return NULL;
}
-
+
value = (*alloc_fn) (sizeof (char) * (length + 1));
read_data (value, length);
- value[length] = '\0';
+ value[length] = '\0';
return value;
}
return x_authority_new (x_authority_family, x_authority_address, x_authority_address_length, x_authority_number, x_authority_name, x_authority_data, x_authority_data_length);
}
+/* GNU provides this but we can't rely on that so let's make our own version */
+static void
+updwtmpx (const gchar *wtmp_file, struct utmpx *ut)
+{
+ struct utmp u;
+
+ memset (&u, 0, sizeof (u));
+ u.ut_type = ut->ut_type;
+ u.ut_pid = ut->ut_pid;
+ if (ut->ut_line)
+ strncpy (u.ut_line, ut->ut_line, sizeof (u.ut_line));
+ if (ut->ut_id)
+ strncpy (u.ut_id, ut->ut_id, sizeof (u.ut_id));
+ if (ut->ut_user)
+ strncpy (u.ut_user, ut->ut_user, sizeof (u.ut_user));
+ if (ut->ut_host)
+ strncpy (u.ut_host, ut->ut_host, sizeof (u.ut_host));
+ u.ut_tv.tv_sec = ut->ut_tv.tv_sec;
+ u.ut_tv.tv_usec = ut->ut_tv.tv_usec;
+
+ updwtmp (wtmp_file, &u);
+}
+
+#if HAVE_LIBAUDIT
+static void
+audit_event (int type, const gchar *username, uid_t uid, const gchar *remote_host_name, const gchar *tty, gboolean success)
+{
+ int auditfd, result;
+ const char *op = NULL;
+
+ auditfd = audit_open ();
+ if (auditfd < 0) {
+ g_printerr ("Error opening audit socket: %s\n", strerror (errno));
+ return;
+ }
+
+ if (type == AUDIT_USER_LOGIN)
+ op = "login";
+ else if (type == AUDIT_USER_LOGOUT)
+ op = "logout";
+ result = success == TRUE ? 1 : 0;
+
+ if (audit_log_acct_message (auditfd, type, NULL, op, username, uid, remote_host_name, NULL, tty, result) <= 0)
+ g_printerr ("Error writing audit message: %s\n", strerror (errno));
+
+ close (auditfd);
+}
+#endif
+
int
session_child_run (int argc, char **argv)
{
int i, version, fd, result;
gboolean auth_complete = TRUE;
User *user = NULL;
- gchar *log_filename, *log_backup_filename = NULL;
+ gchar *log_filename;
+ LogMode log_mode = LOG_MODE_BACKUP_AND_TRUNCATE;
gsize env_length;
gsize command_argc;
gchar **command_argv;
gchar *authentication_result_string;
gchar *service;
gchar *username;
- gchar *class;
gchar *tty;
gchar *remote_host_name;
gchar *xdisplay;
XAuthority *x_authority = NULL;
gchar *x_authority_filename;
GDBusConnection *bus;
+ const gchar *login1_session_id = NULL;
gchar *console_kit_cookie = NULL;
- gchar *login1_session = NULL;
-
- const gchar *path;
- GError *error = NULL;
const gchar *locale_value;
gchar *locale_var;
static const gchar * const locale_var_names[] = {
+ "LC_PAPER",
+ "LC_NAME",
+ "LC_ADDRESS",
+ "LC_TELEPHONE",
+ "LC_MEASUREMENT",
+ "LC_IDENTIFICATION",
"LC_COLLATE",
"LC_CTYPE",
"LC_MONETARY",
"LANG",
NULL
};
+ gid_t gid;
+ uid_t uid;
+ const gchar *home_directory;
+ GError *error = NULL;
#if !defined(GLIB_VERSION_2_36)
g_type_init ();
username = read_string ();
read_data (&do_authenticate, sizeof (do_authenticate));
read_data (&is_interactive, sizeof (is_interactive));
- class = read_string ();
+ read_string (); /* Used to be class, now we just use the environment variable */
tty = read_string ();
remote_host_name = read_string ();
xdisplay = read_string ();
pam_set_item (pam_handle, PAM_TTY, xdisplay);
}
else if (tty)
- pam_set_item (pam_handle, PAM_TTY, tty);
+ pam_set_item (pam_handle, PAM_TTY, tty);
#ifdef PAM_XAUTHDATA
if (x_authority)
/* See what user we ended up as */
if (pam_get_item (pam_handle, PAM_USER, (const void **) &new_username) != PAM_SUCCESS)
+ {
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
+ }
g_free (username);
username = g_strdup (new_username);
+ /* Write record to btmp database */
+ if (authentication_result == PAM_AUTH_ERR)
+ {
+ struct utmpx ut;
+ struct timeval tv;
+
+ memset (&ut, 0, sizeof (ut));
+ ut.ut_type = USER_PROCESS;
+ ut.ut_pid = getpid ();
+ if (xdisplay)
+ strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
+ if (tty && g_str_has_prefix (tty, "/dev/"))
+ strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
+ strncpy (ut.ut_user, username, sizeof (ut.ut_user));
+ if (xdisplay)
+ strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
+ else if (remote_host_name)
+ strncpy (ut.ut_host, remote_host_name, sizeof (ut.ut_host));
+ gettimeofday (&tv, NULL);
+ ut.ut_tv.tv_sec = tv.tv_sec;
+ ut.ut_tv.tv_usec = tv.tv_usec;
+
+ updwtmpx ("/var/log/btmp", &ut);
+
+#if HAVE_LIBAUDIT
+ audit_event (AUDIT_USER_LOGIN, username, -1, remote_host_name, tty, FALSE);
+#endif
+ }
+
/* Check account is valid */
if (authentication_result == PAM_SUCCESS)
authentication_result = pam_acct_mgmt (pam_handle, 0);
if (authentication_result == PAM_NEW_AUTHTOK_REQD)
- authentication_result = pam_chauthtok (pam_handle, 0);
+ authentication_result = pam_chauthtok (pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
}
else
authentication_result = PAM_SUCCESS;
if (!username)
{
g_printerr ("No user selected during authentication\n");
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
}
/* Stop if we didn't authenticated */
if (authentication_result != PAM_SUCCESS)
+ {
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
+ }
/* Get the command to run (blocks) */
log_filename = read_string ();
+ if (version >= 3)
+ read_data (&log_mode, sizeof (log_mode));
if (version >= 1)
{
g_free (tty);
- tty = read_string ();
+ tty = read_string ();
}
x_authority_filename = read_string ();
if (version >= 1)
command_argv[i] = read_string ();
command_argv[i] = NULL;
+ /* If nothing to run just refresh credentials because we successfully authenticated */
+ if (command_argc == 0)
+ {
+ pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
+ pam_end (pam_handle, 0);
+ return EXIT_SUCCESS;
+ }
+
/* Redirect stderr to a log file */
if (log_filename)
- log_backup_filename = g_strdup_printf ("%s.old", log_filename);
- if (!log_filename)
{
- fd = open ("/dev/null", O_WRONLY);
- dup2 (fd, STDERR_FILENO);
- close (fd);
+ if (g_path_is_absolute (log_filename))
+ {
+ fd = log_file_open (log_filename, log_mode);
+ dup2 (fd, STDERR_FILENO);
+ close (fd);
+ g_free (log_filename);
+ log_filename = NULL;
+ }
}
- else if (g_path_is_absolute (log_filename))
+ else
{
- rename (log_filename, log_backup_filename);
- fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
+ fd = open ("/dev/null", O_WRONLY);
dup2 (fd, STDERR_FILENO);
close (fd);
}
if (result != PAM_SUCCESS)
{
g_printerr ("Failed to establish PAM credentials: %s\n", pam_strerror (pam_handle, result));
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
}
-
+
/* Open the session */
result = pam_open_session (pam_handle, 0);
if (result != PAM_SUCCESS)
{
g_printerr ("Failed to open PAM session: %s\n", pam_strerror (pam_handle, result));
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
}
if (error)
g_printerr ("Unable to contact system bus: %s", error->message);
if (!bus)
+ {
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
+ }
- if (login1_is_running ())
+ /* Check what logind session we are, or fallback to ConsoleKit */
+ login1_session_id = pam_getenv (pam_handle, "XDG_SESSION_ID");
+ if (login1_session_id)
{
- login1_session = login1_get_session_id ();
- write_string (login1_session);
+ write_string (login1_session_id);
+ if (version >= 2)
+ write_string (NULL);
}
-
- if (!login1_session)
+ else
{
- /* Open a Console Kit session */
g_variant_builder_init (&ck_parameters, G_VARIANT_TYPE ("(a(sv))"));
g_variant_builder_open (&ck_parameters, G_VARIANT_TYPE ("a(sv)"));
g_variant_builder_add (&ck_parameters, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
- if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) == 0)
+ if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") == 0)
g_variant_builder_add (&ck_parameters, "(sv)", "session-type", g_variant_new_string ("LoginWindow"));
if (xdisplay)
{
else
g_variant_builder_add (&ck_parameters, "(sv)", "is-local", g_variant_new_boolean (TRUE));
console_kit_cookie = ck_open_session (&ck_parameters);
+ if (version >= 2)
+ write_string (NULL);
write_string (console_kit_cookie);
if (console_kit_cookie)
{
gchar *value;
+ gchar *runtime_dir;
value = g_strdup_printf ("XDG_SESSION_COOKIE=%s", console_kit_cookie);
pam_putenv (pam_handle, value);
g_free (value);
+
+ runtime_dir = ck_get_xdg_runtime_dir (console_kit_cookie);
+ if (runtime_dir)
+ {
+ gchar *value;
+ value = g_strdup_printf ("XDG_RUNTIME_DIR=%s", runtime_dir);
+ pam_putenv (pam_handle, value);
+ g_free (value);
+ g_free (runtime_dir);
+ }
}
}
drop_privileges = geteuid () == 0;
if (drop_privileges)
- privileges_drop (user);
+ privileges_drop (user_get_uid (user), user_get_gid (user));
result = x_authority_write (x_authority, XAUTH_WRITE_MODE_REPLACE, x_authority_filename, &error);
if (drop_privileges)
privileges_reclaim ();
g_printerr ("Error writing X authority: %s\n", error->message);
g_clear_error (&error);
if (!result)
+ {
+ pam_end (pam_handle, 0);
return EXIT_FAILURE;
+ }
value = g_strdup_printf ("XAUTHORITY=%s", x_authority_filename);
pam_putenv (pam_handle, value);
g_free (value);
}
- /* Put our tools directory in the path as a hack so we can use the legacy gdmflexiserver interface */
- path = pam_getenv (pam_handle, "PATH");
- if (path)
- pam_putenv (pam_handle, g_strdup_printf ("PATH=%s:%s", PKGLIBEXEC_DIR, path));
-
/* Catch terminate signal and pass it to the child */
signal (SIGTERM, signal_cb);
/* Run the command as the authenticated user */
- child_pid = fork ();
+ uid = user_get_uid (user);
+ gid = user_get_gid (user);
+ home_directory = user_get_home_directory (user);
+ child_pid = fork ();
if (child_pid == 0)
{
- // FIXME: This is not thread safe (particularly the printfs)
-
/* Make this process its own session */
if (setsid () < 0)
- g_printerr ("Failed to make process a new session: %s\n", strerror (errno));
+ _exit (errno);
/* Change to this user */
if (getuid () == 0)
{
- if (setgid (user_get_gid (user)) != 0)
- {
- g_printerr ("Failed to set group ID to %d: %s\n", user_get_gid (user), strerror (errno));
- _exit (EXIT_FAILURE);
- }
+ if (setgid (gid) != 0)
+ _exit (errno);
- if (setuid (user_get_uid (user)) != 0)
- {
- g_printerr ("Failed to set user ID to %d: %s\n", user_get_uid (user), strerror (errno));
- _exit (EXIT_FAILURE);
- }
+ if (setuid (uid) != 0)
+ _exit (errno);
}
/* Change working directory */
/* NOTE: This must be done after the permissions are changed because NFS filesystems can
* be setup so the local root user accesses the NFS files as 'nobody'. If the home directories
* are not system readable then the chdir can fail */
- if (chdir (user_get_home_directory (user)) != 0)
- {
- g_printerr ("Failed to change to home directory %s: %s\n", user_get_home_directory (user), strerror (errno));
- _exit (EXIT_FAILURE);
- }
+ if (chdir (home_directory) != 0)
+ _exit (errno);
- /* Redirect stderr to a log file */
- if (log_filename && !g_path_is_absolute (log_filename))
+ if (log_filename)
{
- rename (log_filename, log_backup_filename);
- fd = open (log_filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
- dup2 (fd, STDERR_FILENO);
- close (fd);
+ fd = log_file_open (log_filename, log_mode);
+ if (fd >= 0)
+ {
+ dup2 (fd, STDERR_FILENO);
+ close (fd);
+ }
}
+ /* Reset SIGPIPE handler so the child has default behaviour (we disabled it at LightDM start) */
+ signal (SIGPIPE, SIG_DFL);
+
/* Run the command */
execve (command_argv[0], command_argv, pam_getenvlist (pam_handle));
- g_printerr ("Failed to run command: %s\n", strerror (errno));
_exit (EXIT_FAILURE);
}
if (child_pid > 0)
{
/* Log to utmp */
- if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
+ if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") != 0)
{
struct utmpx ut;
struct timeval tv;
memset (&ut, 0, sizeof (ut));
ut.ut_type = USER_PROCESS;
ut.ut_pid = child_pid;
- strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
- strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
+ if (xdisplay)
+ strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
+ if (tty && g_str_has_prefix (tty, "/dev/"))
+ strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
strncpy (ut.ut_user, username, sizeof (ut.ut_user));
if (xdisplay)
strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;
+ /* Write records to utmp/wtmp databases */
setutxent ();
if (!pututxline (&ut))
g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
endutxent ();
+ updwtmpx ("/var/log/wtmp", &ut);
+
+#if HAVE_LIBAUDIT
+ audit_event (AUDIT_USER_LOGIN, username, uid, remote_host_name, tty, TRUE);
+#endif
}
waitpid (child_pid, &return_code, 0);
child_pid = 0;
/* Log to utmp */
- if (g_strcmp0 (class, XDG_SESSION_CLASS_GREETER) != 0)
+ if (g_strcmp0 (pam_getenv (pam_handle, "XDG_SESSION_CLASS"), "greeter") != 0)
{
struct utmpx ut;
struct timeval tv;
memset (&ut, 0, sizeof (ut));
ut.ut_type = DEAD_PROCESS;
ut.ut_pid = child_pid;
- strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
- strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
+ if (xdisplay)
+ strncpy (ut.ut_id, xdisplay, sizeof (ut.ut_id));
+ if (tty && g_str_has_prefix (tty, "/dev/"))
+ strncpy (ut.ut_line, tty + strlen ("/dev/"), sizeof (ut.ut_line));
strncpy (ut.ut_user, username, sizeof (ut.ut_user));
if (xdisplay)
strncpy (ut.ut_host, xdisplay, sizeof (ut.ut_host));
ut.ut_tv.tv_sec = tv.tv_sec;
ut.ut_tv.tv_usec = tv.tv_usec;
+ /* Write records to utmp/wtmp databases */
setutxent ();
if (!pututxline (&ut))
g_printerr ("Failed to write utmpx: %s\n", strerror (errno));
endutxent ();
+ updwtmpx ("/var/log/wtmp", &ut);
+
+#if HAVE_LIBAUDIT
+ audit_event (AUDIT_USER_LOGOUT, username, uid, remote_host_name, tty, TRUE);
+#endif
}
}
drop_privileges = geteuid () == 0;
if (drop_privileges)
- privileges_drop (user);
+ privileges_drop (user_get_uid (user), user_get_gid (user));
result = x_authority_write (x_authority, XAUTH_WRITE_MODE_REMOVE, x_authority_filename, &error);
if (drop_privileges)
privileges_reclaim ();
pam_close_session (pam_handle, 0);
/* Remove credentials */
- result = pam_setcred (pam_handle, PAM_DELETE_CRED);
+ pam_setcred (pam_handle, PAM_DELETE_CRED);
pam_end (pam_handle, 0);
pam_handle = NULL;