* Make --test-mode which runs as the current user
* Support displays acting as XDMCP terminals
+ * Support MIT-MAGIC-COOKIE-1 and XDM-AUTHORIZATION-1 authorization
Overview of changes in lightdm 0.0.4
)
AC_SUBST(XSERVER_BINARY)
+XAUTH_DIR=$localstatedir/run/lightdm/authority
+AC_ARG_WITH(xauth-dir,
+ AS_HELP_STRING([--with-xauth-dir=<dir>],
+ [X server authorization directory]),
+ if test x$withval != x; then
+ XAUTH_DIR="$withval"
+ fi
+)
+AC_SUBST(XAUTH_DIR)
+
XSESSION_DIR=/usr/share/xsessions
AC_ARG_WITH(xsession-dir,
AS_HELP_STRING([--with-xsession-dir=<dir>],
Config file: ${CONFIG_FILE}
D-Bus system directory: ${DBUS_SYS_DIR}
X server binary: ${XSERVER_BINARY}
+ X authorization dir: ${XAUTH_DIR}
XSessions dir: ${XSESSION_DIR}
Default session: ${DEFAULT_SESSION}
Greeter user: ${GREETER_USER}
theme.h \
user-manager.c \
user-manager.h \
+ xauth.c \
+ xauth.h \
xdmcp-protocol.c \
xdmcp-protocol.h \
xdmcp-server.c \
-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
-DLOCALE_DIR=\"$(localedir)\" \
-DXSERVER_BINARY=\"$(XSERVER_BINARY)\" \
+ -DXAUTH_DIR=\"$(XAUTH_DIR)\" \
-DXSESSIONS_DIR=\"$(XSESSION_DIR)\" \
-DDEFAULT_SESSION=\"$(DEFAULT_SESSION)\" \
-DGREETER_USER=\"$(GREETER_USER)\" \
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <dbus/dbus-glib.h>
#include "display-manager.h"
struct DisplayManagerPrivate
{
GKeyFile *config;
+
+ gchar *auth_dir;
gboolean test_mode;
GList *displays;
XDMCPServer *xdmcp_server;
+
+ guint auth_counter;
};
G_DEFINE_TYPE (DisplayManager, display_manager, G_TYPE_OBJECT);
return display_number;
}
+static gchar *
+get_authorization_path (DisplayManager *manager)
+{
+ gchar *path;
+
+ path = g_strdup_printf ("%s/%d", manager->priv->auth_dir, manager->priv->auth_counter);
+ manager->priv->auth_counter++;
+
+ return path;
+}
+
static void
start_session_cb (Display *display, Session *session, DisplayManager *manager)
{
gchar *string;
+ XAuthorization *authorization;
/* Connect using the session bus */
if (manager->priv->test_mode)
string = g_strdup_printf ("/org/gnome/LightDisplayManager/Display%d", display_get_index (display));
session_set_env (session, "LDM_DISPLAY", string);
+ authorization = xserver_get_authorization (display_get_xserver (display));
+ if (authorization)
+ {
+ gchar *path;
+
+ path = get_authorization_path (manager);
+ session_set_authorization (session, authorization, path);
+ g_free (path);
+ }
+
g_free (string);
}
xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session, DisplayManager *manager)
{
Display *display;
- gchar *address;
+ gchar *address, *path;
XServer *xserver;
+ XAuthorization *authorization;
display = add_display (manager);
address = g_inet_address_to_string (G_INET_ADDRESS (xdmcp_session_get_address (session)));
xserver = xserver_new (XSERVER_TYPE_REMOTE, address, xdmcp_session_get_display_number (session));
+ authorization = xauth_new (xdmcp_session_get_authorization_name (session),
+ xdmcp_session_get_authorization_data (session),
+ xdmcp_session_get_authorization_data_length (session));
+ path = get_authorization_path (manager);
+ xserver_set_authorization (xserver, authorization, path);
display_start (display, xserver, NULL, 0);
g_object_unref (xserver);
g_free (address);
+ g_free (path);
}
static guchar
}
}
+static void
+setup_auth_dir (DisplayManager *manager)
+{
+ GDir *dir;
+ GError *error = NULL;
+
+ g_mkdir_with_parents (manager->priv->auth_dir, S_IRWXU);
+ dir = g_dir_open (manager->priv->auth_dir, 0, &error);
+ if (!dir)
+ {
+ g_warning ("Authorization dir not created: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Clear out the directory */
+ while (TRUE)
+ {
+ const gchar *filename;
+ gchar *path;
+ GFile *file;
+
+ filename = g_dir_read_name (dir);
+ if (!filename)
+ break;
+
+ path = g_build_filename (manager->priv->auth_dir, filename, NULL);
+ file = g_file_new_for_path (filename);
+ g_file_delete (file, NULL, NULL);
+
+ g_free (path);
+ g_object_unref (file);
+ }
+
+ g_dir_close (dir);
+}
+
void
display_manager_start (DisplayManager *manager)
{
gchar *displays;
gchar **tokens, **i;
+
+ /* Make an empty authorization directory */
+ setup_auth_dir (manager);
/* Start the first display */
displays = g_key_file_get_string (manager->priv->config, "LightDM", "displays", NULL);
if (key)
{
guchar data[8];
+ gchar *path;
+ XAuthorization *authorization;
string_to_xdm_auth_key (key, data);
xserver_set_authentication (xserver, "XDM-AUTHENTICATION-1", data, 8);
- xserver_set_authorization (xserver, "XDM-AUTHORIZATION-1", data, 8);
+
+ authorization = xauth_new ("XDM-AUTHORIZATION-1", data, 8);
+ path = get_authorization_path (manager);
+ xserver_set_authorization (xserver, authorization, path);
+
+ g_free (path);
}
}
else
+ {
+ gchar *path;
+ XAuthorization *authorization;
+
xserver = xserver_new (XSERVER_TYPE_LOCAL, NULL, display_number);
+
+ authorization = xauth_new_cookie ();
+ path = get_authorization_path (manager);
+ xserver_set_authorization (xserver, authorization, path);
+ g_free (path);
+ }
g_free (xdmcp_manager);
if (manager->priv->test_mode)
case PROP_CONFIG:
self->priv->config = g_value_get_pointer (value);
self->priv->test_mode = g_key_file_get_boolean (self->priv->config, "LightDM", "test-mode", NULL);
+ if (self->priv->test_mode)
+ self->priv->auth_dir = g_build_filename (g_get_user_cache_dir (), "lightdm", "authority", NULL);
+ else
+ self->priv->auth_dir = g_strdup (XAUTH_DIR);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
-
static void
display_manager_get_property (GObject *object,
guint prop_id,
g_object_unref (self->priv->xdmcp_server);
for (link = self->priv->displays; link; link = link->next)
g_object_unref (link->data);
+ g_list_free (self->priv->displays);
}
static void
case SESSION_NONE:
break;
case SESSION_GREETER_PRE_CONNECT:
- g_error ("Failed to start greeter");
+ g_warning ("Failed to start greeter");
break;
case SESSION_GREETER:
if (display->priv->default_user && display->priv->timeout > 0)
/* Session process */
GPid pid;
+
+ /* X authorization */
+ XAuthorization *authorization;
+ gchar *authorization_path;
+ GFile *authorization_file;
};
G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
g_hash_table_insert (session->priv->env, g_strdup (name), g_strdup (value));
}
+void
+session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path)
+{
+ session->priv->authorization = authorization;
+ session->priv->authorization_path = g_strdup (path);
+}
+
+XAuthorization *session_get_authorization (Session *session)
+{
+ return session->priv->authorization;
+}
+
static void
session_watch_cb (GPid pid, gint status, gpointer data)
{
g_warning ("Unable to get information on user %s: %s", session->priv->username, strerror (errno));
return FALSE;
}
-
+
working_dir = user_info->pw_dir;
session_set_env (session, "USER", user_info->pw_name);
session_set_env (session, "HOME", user_info->pw_dir);
session_set_env (session, "SHELL", getenv ("SHELL"));
}
+ if (session->priv->authorization)
+ {
+ session->priv->authorization_file = xauth_write (session->priv->authorization, session->priv->authorization_path, &error);
+ if (session->priv->authorization_file)
+ session_set_env (session, "XAUTHORITY", session->priv->authorization_path);
+ else
+ g_warning ("Failed to write authorization: %s", error->message);
+ g_clear_error (&error);
+ }
+
env = session_get_env (session);
result = g_shell_parse_argv (session->priv->command, &argc, &argv, &error);
if (!result)
- g_error ("Failed to parse session command line: %s", error->message);
+ g_warning ("Failed to parse session command line: %s", error->message);
g_clear_error (&error);
if (!result)
return FALSE;
g_free (self->priv->username);
g_hash_table_unref (self->priv->env);
g_free (self->priv->command);
+ if (self->priv->authorization)
+ g_object_unref (self->priv->authorization);
+ g_free (self->priv->authorization_path);
+ if (self->priv->authorization_file)
+ {
+ g_file_delete (self->priv->authorization_file, NULL, NULL);
+ g_object_unref (self->priv->authorization_file);
+ }
}
static void
#define _SESSION_H_
#include <glib-object.h>
+#include "xauth.h"
G_BEGIN_DECLS
void session_set_env (Session *session, const gchar *name, const gchar *value);
+void session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path);
+
+XAuthorization *session_get_authorization (Session *session);
+
gboolean session_start (Session *session);
G_END_DECLS
--- /dev/null
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include <string.h>
+
+#include "xauth.h"
+
+struct XAuthorizationPrivate
+{
+ /* User who is using this authorization */
+ gchar *username;
+
+ /* Authorization scheme */
+ gchar *authorization_name;
+
+ /* Authorization data */
+ guchar *authorization_data;
+ gsize authorization_data_length;
+};
+
+G_DEFINE_TYPE (XAuthorization, xauth, G_TYPE_OBJECT);
+
+XAuthorization *
+xauth_new (const gchar *name, const guchar *data, gsize data_length)
+{
+ XAuthorization *auth = g_object_new (XAUTH_TYPE, NULL);
+
+ auth->priv->authorization_name = g_strdup (name);
+ auth->priv->authorization_data = g_malloc (data_length);
+ auth->priv->authorization_data_length = data_length;
+ memcpy (auth->priv->authorization_data, data, data_length);
+
+ return auth;
+}
+
+XAuthorization *xauth_new_cookie (void)
+{
+ guchar cookie[16];
+ gint i;
+
+ for (i = 0; i < 16; i++)
+ cookie[i] = g_random_int () & 0xFF;
+
+ return xauth_new ("MIT-MAGIC-COOKIE-1", cookie, 16);
+}
+
+const gchar *
+xauth_get_authorization_name (XAuthorization *auth)
+{
+ return auth->priv->authorization_name;
+}
+
+const guchar *
+xauth_get_authorization_data (XAuthorization *auth)
+{
+ return auth->priv->authorization_data;
+}
+
+gsize
+xauth_get_authorization_data_length (XAuthorization *auth)
+{
+ return auth->priv->authorization_data_length;
+}
+
+static void
+write_uint16 (GString *string, guint16 value)
+{
+ g_string_append_c (string, (gchar) value >> 8);
+ g_string_append_c (string, (gchar) value);
+}
+
+static void
+write_data (GString *string, guchar *value, gsize value_len)
+{
+ g_string_append_len (string, (gchar *) value, value_len);
+}
+
+static void
+write_string (GString *string, const gchar *value)
+{
+ write_uint16 (string, strlen (value));
+ g_string_append (string, value);
+}
+
+GFile *
+xauth_write (XAuthorization *auth, const gchar *path, GError **error)
+{
+ GFile *file;
+ GFileOutputStream *stream;
+ GString *data;
+ gboolean result;
+ gsize n_written;
+
+ file = g_file_new_for_path (path);
+
+ stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
+ if (!stream)
+ {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ data = g_string_sized_new (1024);
+ write_uint16 (data, 0xFFFF); /* FamilyWild - this entry is used for all connections */
+ write_string (data, ""); /* Not requires as using FamilyWild */
+ write_string (data, ""); /* Not requires as using FamilyWild */
+ write_string (data, auth->priv->authorization_name);
+ write_uint16 (data, auth->priv->authorization_data_length);
+ write_data (data, auth->priv->authorization_data, auth->priv->authorization_data_length);
+
+ result = g_output_stream_write_all (G_OUTPUT_STREAM (stream), data->str, data->len, &n_written, NULL, error);
+ g_string_free (data, TRUE);
+
+ g_object_unref (stream);
+ if (!result)
+ {
+ g_object_unref (file);
+ file = NULL;
+ }
+
+ return file;
+}
+
+static void
+xauth_init (XAuthorization *auth)
+{
+ auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, XAUTH_TYPE, XAuthorizationPrivate);
+ auth->priv->authorization_name = g_strdup ("");
+}
+
+static void
+xauth_finalize (GObject *object)
+{
+ XAuthorization *self;
+
+ self = XAUTH (object);
+
+ g_free (self->priv->username);
+ g_free (self->priv->authorization_name);
+ g_free (self->priv->authorization_data);
+}
+
+static void
+xauth_class_init (XAuthorizationClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = xauth_finalize;
+
+ g_type_class_add_private (klass, sizeof (XAuthorizationPrivate));
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef _XAUTH_H_
+#define _XAUTH_H_
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define XAUTH_TYPE (xauth_get_type())
+#define XAUTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XAUTH_TYPE, XAuthorization));
+
+typedef struct XAuthorizationPrivate XAuthorizationPrivate;
+
+typedef struct
+{
+ GObject parent_instance;
+ XAuthorizationPrivate *priv;
+} XAuthorization;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} XAuthorizationClass;
+
+GType xauth_get_type (void);
+
+XAuthorization *xauth_new (const gchar *name, const guchar *data, gsize data_length);
+
+XAuthorization *xauth_new_cookie (void);
+
+const gchar *xauth_get_authorization_name (XAuthorization *auth);
+
+const guchar *xauth_get_authorization_data (XAuthorization *auth);
+
+gsize xauth_get_authorization_data_length (XAuthorization *auth);
+
+GFile *xauth_write (XAuthorization *auth, const gchar *path, GError **error);
+
+G_END_DECLS
+
+#endif /* _XAUTH_H_ */
gboolean match_authorization = FALSE;
guchar *authorization_data = NULL;
gsize authorization_data_length = 0;
+ guchar *session_authorization_data = NULL;
+ gsize session_authorization_data_length = 0;
gchar **j;
- guchar session_key[8];
GInetAddress *address4 = NULL; /*, *address6 = NULL;*/
/* Must be using our authentication scheme */
}
/* Perform requested authorization */
- memset (session_key, 0, sizeof (session_key));
if (strcmp (server->priv->authorization_name, "MIT-MAGIC-COOKIE-1") == 0)
{
/* Data is the cookie */
else if (strcmp (server->priv->authorization_name, "XDM-AUTHORIZATION-1") == 0)
{
gint i;
- guchar key[8];
+ guchar key[8], session_key[8];
/* Setup key */
memset (key, 0, 8);
memcpy (key, server->priv->authentication_data, server->priv->authentication_data_length > 8 ? 8 : server->priv->authentication_data_length);
- /* Generate a private session key and encode it */
+ /* Generate a private session key */
+ // FIXME: Pick a good DES key?
for (i = 0; i < 8; i++)
session_key[i] = g_random_int () & 0xFF;
+
+ /* Encrypt the session key and send it to the server */
authorization_data = g_malloc (sizeof (guchar) * 8);
authorization_data_length = 8;
XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
+
+ /* Authorization data is the session key (64 bit) followed by the private key (56 bit) followed by an empty byte */
+ session_authorization_data = g_malloc (sizeof (guchar) * 16);
+ session_authorization_data_length = 8;
+ memcpy (session_authorization_data, session_key, 8);
+ memcpy (session_authorization_data + 8, key + 1, 7);
+ session_authorization_data[15] = 0;
}
for (i = 0; i < packet->Request.n_connections; i++)
session = add_session (server);
session->priv->address = address4; /*address6 ? address6 : address4;*/
- memcpy (session->priv->key, session_key, 8);
+ session->priv->authorization_name = g_strdup (server->priv->authorization_name);
+ session->priv->authorization_data = session_authorization_data;
+ session->priv->authorization_data_length = session_authorization_data_length;
response = xdmcp_packet_alloc (XDMCP_Accept);
response->Accept.session_id = xdmcp_session_get_id (session);
server->priv->port = XDM_UDP_PORT;
server->priv->hostname = g_strdup ("");
server->priv->status = g_strdup ("");
- server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+ server->priv->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
server->priv->authentication_name = g_strdup ("");
server->priv->authorization_name = g_strdup ("");
}
gchar *authorization_name;
- /* Session key for XDM-AUTHORIZATION-1 */
- guchar key[8];
+ guchar *authorization_data;
+ gsize authorization_data_length;
gboolean started;
return session->priv->authorization_name;
}
+const guchar *
+xdmcp_session_get_authorization_data (XDMCPSession *session)
+{
+ return session->priv->authorization_data;
+}
+
+const gsize
+xdmcp_session_get_authorization_data_length (XDMCPSession *session)
+{
+ return session->priv->authorization_data_length;
+}
+
guint16
xdmcp_session_get_display_number (XDMCPSession *session)
{
const gchar *xdmcp_session_get_authorization_name (XDMCPSession *session);
+const guchar *xdmcp_session_get_authorization_data (XDMCPSession *session);
+
+const gsize xdmcp_session_get_authorization_data_length (XDMCPSession *session);
+
guint16 xdmcp_session_get_display_number (XDMCPSession *session);
const gchar *xdmcp_session_get_display_class (XDMCPSession *session);
guchar *authentication_data;
gsize authentication_data_length;
- /* Authorization scheme to use */
- gchar *authorization_name;
-
- /* Authorization data */
- guchar *authorization_data;
- gsize authorization_data_length;
+ /* Authorization */
+ XAuthorization *authorization;
+ gchar *authorization_path;
+ GFile *authorization_file;
/* Display number */
gint display_number;
}
void
-xserver_set_authorization (XServer *server, const gchar *name, const guchar *data, gsize data_length)
-{
- g_free (server->priv->authorization_name);
- server->priv->authorization_name = g_strdup (name);
- g_free (server->priv->authorization_data);
- server->priv->authorization_data = g_malloc (data_length);
- server->priv->authorization_data_length = data_length;
- memcpy (server->priv->authorization_data, data, data_length);
-}
-
-const gchar *
-xserver_get_authorization_name (XServer *server)
+xserver_set_authorization (XServer *server, XAuthorization *authorization, const gchar *path)
{
- return server->priv->authorization_name;
+ server->priv->authorization = authorization;
+ server->priv->authorization_path = g_strdup (path);
}
-const guchar *
-xserver_get_authorization_data (XServer *server)
-{
- return server->priv->authorization_data;
-}
-
-gsize
-xserver_get_authorization_data_length (XServer *server)
+XAuthorization *
+xserver_get_authorization (XServer *server)
{
- return server->priv->authorization_data_length;
+ return server->priv->authorization;
}
static void
gint n_env = 0;
gchar *env_string;
//gint xserver_stdin, xserver_stdout, xserver_stderr;
+
+ g_return_val_if_fail (server->priv->pid == 0, FALSE);
/* Don't need to do anything if a remote server */
if (server->priv->type == XSERVER_TYPE_REMOTE)
env[n_env++] = g_strdup_printf ("XAUTHORITY=%s", getenv ("XAUTHORITY"));
env[n_env] = NULL;
+ /* Write the authorization file */
+ if (server->priv->authorization)
+ {
+ GError *error = NULL;
+
+ server->priv->authorization_file = xauth_write (server->priv->authorization, server->priv->authorization_path, &error);
+ if (!server->priv->authorization_file)
+ g_warning ("Failed to write authorization: %s", error->message);
+ g_clear_error (&error);
+ }
+
command = g_string_new (server->priv->command);
g_string_append_printf (command, " :%d", server->priv->display_number);
g_string_append (command, " -nr"); /* No root background */
//g_string_append_printf (command, " vt%d");
+
+ if (server->priv->authorization)
+ g_string_append_printf (command, " -auth %s", server->priv->authorization_path);
+
if (server->priv->type == XSERVER_TYPE_LOCAL_TERMINAL)
{
if (server->priv->port != 0)
result = g_shell_parse_argv (command->str, &argc, &argv, &error);
g_string_free (command, TRUE);
if (!result)
- g_error ("Failed to parse X server command line: %s", error->message);
+ g_warning ("Failed to parse X server command line: %s", error->message);
g_clear_error (&error);
if (!result)
return FALSE;
env,
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
xserver_fork_cb,
- NULL,
+ server,
&server->priv->pid,
//&xserver_stdin, &xserver_stdout, &xserver_stderr,
&error);
server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XSERVER_TYPE, XServerPrivate);
server->priv->command = g_strdup (XSERVER_BINARY);
server->priv->authentication_name = g_strdup ("");
- server->priv->authorization_name = g_strdup ("");
}
static void
g_free (self->priv->hostname);
g_free (self->priv->authentication_name);
g_free (self->priv->authentication_data);
- g_free (self->priv->authorization_name);
- g_free (self->priv->authorization_data);
g_free (self->priv->address);
+ if (self->priv->authorization)
+ g_object_unref (self->priv->authorization);
+ g_free (self->priv->authorization_path);
+ if (self->priv->authorization_file)
+ {
+ g_file_delete (self->priv->authorization_file, NULL, NULL);
+ g_object_unref (self->priv->authorization_file);
+ }
}
static void
#include <glib-object.h>
#include <dbus/dbus-glib.h>
+#include "xauth.h"
+
G_BEGIN_DECLS
#define XSERVER_TYPE (xserver_get_type())
gsize xserver_get_authentication_data_length (XServer *server);
-void xserver_set_authorization (XServer *server, const gchar *name, const guchar *data, gsize data_length);
-
-const gchar *xserver_get_authorization_name (XServer *server);
-
-const guchar *xserver_get_authorization_data (XServer *server);
+void xserver_set_authorization (XServer *server, XAuthorization *authorization, const gchar *path);
-gsize xserver_get_authorization_data_length (XServer *server);
+XAuthorization *xserver_get_authorization (XServer *server);
gboolean xserver_start (XServer *server);