]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Stop file descriptors leaking into the session processes
authorRobert Ancell <robert.ancell@canonical.com>
Mon, 5 Mar 2012 00:23:32 +0000 (11:23 +1100)
committerRobert Ancell <robert.ancell@canonical.com>
Mon, 5 Mar 2012 00:23:32 +0000 (11:23 +1100)
17 files changed:
NEWS
src/display.c
src/greeter.c
src/greeter.h
src/lightdm.c
src/process.c
src/session-child.c
src/session.c
tests/Makefile.am
tests/scripts/open-file-descriptors.conf [new file with mode: 0644]
tests/scripts/vnc-open-file-descriptors.conf [new file with mode: 0644]
tests/scripts/xdmcp-open-file-descriptors.conf [new file with mode: 0644]
tests/src/libsystem.c
tests/src/test-session.c
tests/test-open-file-descriptors [new file with mode: 0755]
tests/test-vnc-open-file-descriptors [new file with mode: 0755]
tests/test-xdmcp-open-file-descriptors [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 4b853c3a6926c32af9daa67b7d6f30193156cbbd..af1cd352739e618d2651754433205cb328ab4f8a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
 Overview of changes in lightdm 1.1.4
 
+    * Stop file descriptors leaking into the session processes
     * Change session directory once user permissions are set so it works
       on NFS filesystems that don't allow root to access files.
     * Restructure session code so the PAM authentication is run in its
index da6949ddb59c9d53952ff3b15e3b57b965d8082c..40a0efcf6f1e2e31c32cb6ed235068de77000de4 100644 (file)
@@ -430,7 +430,7 @@ start_greeter (Display *display)
     }
 
     g_signal_connect_after (display->priv->session, "stopped", G_CALLBACK (greeter_session_stopped_cb), display);
-    result = session_start (display->priv->session, display->priv->pam_service, greeter_user, FALSE, FALSE);
+    result = greeter_start (display->priv->greeter, display->priv->pam_service, greeter_user);
     g_free (greeter_user);
 
     if (!result)
index 434a6bd3c1846f8b4413adc2d79f4348aa422fe7..bcdc2ae32d6e094ede742e2be036f85060ffbea6 100644 (file)
@@ -61,8 +61,6 @@ struct GreeterPrivate
     gboolean guest_account_authenticated;
 
     /* Communication channels to communicate with */
-    int to_greeter_pipe[2];
-    int from_greeter_pipe[2];
     GIOChannel *to_greeter_channel;
     GIOChannel *from_greeter_channel;
 };
@@ -95,38 +93,12 @@ static gboolean read_cb (GIOChannel *source, GIOCondition condition, gpointer da
 Greeter *
 greeter_new (Session *session, const gchar *pam_service)
 {
-    gchar *value;
     Greeter *greeter;
 
     greeter = g_object_new (GREETER_TYPE, NULL);
     greeter->priv->session = g_object_ref (session);
     greeter->priv->pam_service = g_strdup (pam_service);
 
-    /* Create a pipe to talk with the greeter */
-    if (pipe (greeter->priv->to_greeter_pipe) != 0 || pipe (greeter->priv->from_greeter_pipe) != 0)
-    {
-        g_warning ("Failed to create pipes: %s", strerror (errno));
-        //return;
-    }
-    greeter->priv->to_greeter_channel = g_io_channel_unix_new (greeter->priv->to_greeter_pipe[1]);
-    g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, NULL);
-    greeter->priv->from_greeter_channel = g_io_channel_unix_new (greeter->priv->from_greeter_pipe[0]);
-    g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, NULL);
-    g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
-    g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
-
-    /* Let the greeter session know how to communicate with the daemon */
-    value = g_strdup_printf ("%d", greeter->priv->from_greeter_pipe[1]);
-    session_set_env (greeter->priv->session, "LIGHTDM_TO_SERVER_FD", value);
-    g_free (value);
-    value = g_strdup_printf ("%d", greeter->priv->to_greeter_pipe[0]);
-    session_set_env (greeter->priv->session, "LIGHTDM_FROM_SERVER_FD", value);
-    g_free (value);
-
-    /* Don't allow the daemon end of the pipes to be accessed in child processes */
-    fcntl (greeter->priv->to_greeter_pipe[1], F_SETFD, FD_CLOEXEC);
-    fcntl (greeter->priv->from_greeter_pipe[0], F_SETFD, FD_CLOEXEC);
-
     return greeter;
 }
 
@@ -650,6 +622,47 @@ greeter_get_start_session (Greeter *greeter)
     return greeter->priv->start_session;
 }
 
+gboolean
+greeter_start (Greeter *greeter, const gchar *service, const gchar *username)
+{
+    int to_greeter_pipe[2], from_greeter_pipe[2];
+    gboolean result = FALSE;
+    gchar *value;
+
+    /* Create a pipe to talk with the greeter */
+    if (pipe (to_greeter_pipe) != 0 || pipe (from_greeter_pipe) != 0)
+    {
+        g_warning ("Failed to create pipes: %s", strerror (errno));
+        return FALSE;
+    }
+    greeter->priv->to_greeter_channel = g_io_channel_unix_new (to_greeter_pipe[1]);
+    g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, NULL);
+    greeter->priv->from_greeter_channel = g_io_channel_unix_new (from_greeter_pipe[0]);
+    g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, NULL);
+    g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
+    g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
+
+    /* Let the greeter session know how to communicate with the daemon */
+    value = g_strdup_printf ("%d", from_greeter_pipe[1]);
+    session_set_env (greeter->priv->session, "LIGHTDM_TO_SERVER_FD", value);
+    g_free (value);
+    value = g_strdup_printf ("%d", to_greeter_pipe[0]);
+    session_set_env (greeter->priv->session, "LIGHTDM_FROM_SERVER_FD", value);
+    g_free (value);
+
+    /* Don't allow the daemon end of the pipes to be accessed in child processes */
+    fcntl (to_greeter_pipe[1], F_SETFD, FD_CLOEXEC);
+    fcntl (from_greeter_pipe[0], F_SETFD, FD_CLOEXEC);
+
+    result = session_start (greeter->priv->session, service, username, FALSE, FALSE);
+
+    /* Close the session ends of the pipe */
+    close (to_greeter_pipe[0]);
+    close (from_greeter_pipe[1]);
+
+    return result;
+}
+
 static Session *
 greeter_real_start_authentication (Greeter *greeter, const gchar *username)
 {
index 56eea002b930014cdc33086e14984a5da1b0609a..88fb2bc8dcd5546a4b54c4ce7ce4ad84a72b1541 100644 (file)
@@ -49,6 +49,8 @@ Session *greeter_get_authentication_session (Greeter *greeter);
 
 gboolean greeter_get_start_session (Greeter *greeter);
 
+gboolean greeter_start (Greeter *greeter, const gchar *service, const gchar *username);
+
 G_END_DECLS
 
 #endif /* _GREETER_H_ */
index 4bc5c0c1b5316da5239a16c4c8e2f9147b876303..e2b9f8d2c84a26caa32d94b60e684a7bad1eb213 100644 (file)
@@ -124,6 +124,7 @@ log_init (void)
     g_free (log_dir);
 
     log_fd = open (path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    fcntl (log_fd, F_SETFD, FD_CLOEXEC);
     g_log_set_default_handler (log_cb, NULL);
 
     g_debug ("Logging to %s", path);
index 5236ab8d83feab56cda97d142e6ac035342173f3..2910cd99b938be1eca992ac0f2e5a53961559dbb 100644 (file)
@@ -377,6 +377,8 @@ process_class_init (ProcessClass *klass)
     processes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
     if (pipe (signal_pipe) != 0)
         g_critical ("Failed to create signal pipe");
+    fcntl (signal_pipe[0], F_SETFD, FD_CLOEXEC);
+    fcntl (signal_pipe[1], F_SETFD, FD_CLOEXEC);
     g_io_add_watch (g_io_channel_unix_new (signal_pipe[0]), G_IO_IN, handle_signal, NULL);
     action.sa_sigaction = signal_cb;
     sigemptyset (&action.sa_mask);
index 9ea86deb2322bc83cac24b7becfb110794852d1a..7eaa645cb80c13ece40f8603e29c61e4d24a08ed 100644 (file)
@@ -170,7 +170,7 @@ session_child_run (int argc, char **argv)
     GDBusConnection *bus;
     gchar *console_kit_cookie;
     GError *error = NULL;
-  
+
     g_type_init ();
 
     /* Make input non-blocking */
@@ -193,10 +193,13 @@ session_child_run (int argc, char **argv)
     to_daemon_input = atoi (argv[3]);
     if (from_daemon_output == 0 || to_daemon_input == 0)
     {
-        g_printerr ("Invalid LIGHTDM_DAEMON_PIPE\n");
+        g_printerr ("Invalid file descriptors %s %s\n", argv[2], argv[3]);
         return EXIT_FAILURE;
     }
-    g_unsetenv ("LIGHTDM_DAEMON_PIPE");
+
+    /* Don't let these pipes leak to the command we will run */
+    fcntl (from_daemon_output, F_SETFD, FD_CLOEXEC);
+    fcntl (to_daemon_input, F_SETFD, FD_CLOEXEC);
 
     /* Read a version number so we can handle upgrades (i.e. a newer version of session child is run for an old daemon */
     read_data (&version, sizeof (version));
index cb300d651a1037e852c5bfcd0ab8977690cd8929..c2b4e7ad16c6ff8226165f59064696e703bd9885 100644 (file)
@@ -326,7 +326,7 @@ session_start (Session *session, const gchar *service, const gchar *username, gb
     /* Create pipes to talk to the child */
     if (pipe (to_child_pipe) < 0 || pipe (from_child_pipe) < 0)
     {
-        g_warning ("Failed to create pipe to communicated with session process: %s", strerror (errno));
+        g_warning ("Failed to create pipe to communicate with session process: %s", strerror (errno));
         return FALSE;
     }
     to_child_output = to_child_pipe[0];
index e9a893e8938749f7c68b119ccfaa3c2530190381..99d91175cfda8a48275e347f6cf7fd560417a63e 100644 (file)
@@ -91,7 +91,9 @@ TESTS = \
        test-xdmcp-login \
        test-no-accounts-service \
        test-console-kit \
-       test-no-console-kit
+       test-no-console-kit \
+       test-open-file-descriptors \
+       test-xdmcp-open-file-descriptors
 
 #      test-session-exit-error
 #      test-greeter-no-exit
diff --git a/tests/scripts/open-file-descriptors.conf b/tests/scripts/open-file-descriptors.conf
new file mode 100644 (file)
index 0000000..13dad3c
--- /dev/null
@@ -0,0 +1,47 @@
+#
+# Check session doesn't have any file descriptors to the daemon open.
+# Use a greeter so its file descriptors are around at the time the session starts.
+#
+
+[LightDM]
+minimum-display-number=50
+
+#?RUNNER DAEMON-START
+
+# X server starts
+#?XSERVER :50 START
+#?XSERVER :50 INDICATE-READY
+
+# LightDM connects to X server
+#?XSERVER :50 ACCEPT-CONNECT
+
+# Greeter starts
+#?GREETER :50 START
+#?XSERVER :50 ACCEPT-CONNECT
+#?GREETER :50 CONNECT-XSERVER
+#?GREETER :50 CONNECT-TO-DAEMON
+#?GREETER :50 CONNECTED-TO-DAEMON
+
+# Log in
+#?*GREETER :50 AUTHENTICATE USERNAME=have-password1
+#?GREETER :50 SHOW-PROMPT TEXT="Password:"
+#?*GREETER :50 RESPOND TEXT="password"
+#?GREETER :50 AUTHENTICATION-COMPLETE USERNAME=have-password1 AUTHENTICATED=TRUE
+#?*GREETER :50 START-SESSION
+#?GREETER :50 TERMINATE SIGNAL=15
+
+# Session starts
+#?SESSION :50 START USER=have-password1
+#?XSERVER :50 ACCEPT-CONNECT
+#?SESSION :50 CONNECT-XSERVER
+
+# Check file descriptors
+#?*SESSION :50 LIST-UNKNOWN-FILE-DESCRIPTORS
+#?SESSION :50 LIST-UNKNOWN-FILE-DESCRIPTORS FDS=
+
+# Cleanup
+#?*STOP-DAEMON
+# Don't know what order they will terminate
+#?(SESSION :50 TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?(SESSION :50 TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/vnc-open-file-descriptors.conf b/tests/scripts/vnc-open-file-descriptors.conf
new file mode 100644 (file)
index 0000000..d4daa68
--- /dev/null
@@ -0,0 +1,62 @@
+#
+# Check that a VNC session doesn't have any unknown file descriptors
+#
+
+[LightDM]
+minimum-display-number=50
+start-default-seat=false
+
+[VNCServer]
+enabled=true
+port=9999
+
+#?RUNNER DAEMON-START
+#?*WAIT 1
+
+# Start a VNC client
+#?*START-VNC-CLIENT ARGS="::9999"
+#?VNC-CLIENT START
+#?VNC-CLIENT CONNECT SERVER=::9999
+
+# Xvnc server starts
+#?XSERVER :50 START GEOMETRY=1024x768 DEPTH=8
+
+# Negotiate with Xvnc
+#?VNC-CLIENT CONNECTED VERSION="RFB 003.007"
+
+#?XSERVER :50 INDICATE-READY
+
+#?XSERVER :50 VNC-CLIENT-CONNECT VERSION="RFB 003.003"
+
+# LightDM connects to X server
+#?XSERVER :50 ACCEPT-CONNECT
+
+# Greeter starts and connects to remote X server
+#?GREETER :50 START
+#?XSERVER :50 ACCEPT-CONNECT
+#?GREETER :50 CONNECT-XSERVER
+#?GREETER :50 CONNECT-TO-DAEMON
+#?GREETER :50 CONNECTED-TO-DAEMON
+
+# Log in
+#?*GREETER :50 AUTHENTICATE USERNAME=have-password1
+#?GREETER :50 SHOW-PROMPT TEXT="Password:"
+#?*GREETER :50 RESPOND TEXT="password"
+#?GREETER :50 AUTHENTICATION-COMPLETE USERNAME=have-password1 AUTHENTICATED=TRUE
+#?*GREETER :50 START-SESSION
+#?GREETER :50 TERMINATE SIGNAL=15
+
+# Session starts
+#?SESSION :50 START USER=have-password1
+#?XSERVER :50 ACCEPT-CONNECT
+#?SESSION :50 CONNECT-XSERVER
+
+# Check file descriptors
+#?*SESSION :50 LIST-UNKNOWN-FILE-DESCRIPTORS
+#?SESSION :50 LIST-UNKNOWN-FILE-DESCRIPTORS FDS=
+
+# Clean up
+#?*STOP-DAEMON
+#?(SESSION :50 TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?(SESSION :50 TERMINATE SIGNAL=15|XSERVER :50 TERMINATE SIGNAL=15)
+#?RUNNER DAEMON-EXIT STATUS=0
diff --git a/tests/scripts/xdmcp-open-file-descriptors.conf b/tests/scripts/xdmcp-open-file-descriptors.conf
new file mode 100644 (file)
index 0000000..779b6be
--- /dev/null
@@ -0,0 +1,57 @@
+#
+# Check that an XDMCP session doesn't have any unknown file descriptors
+#
+
+[LightDM]
+start-default-seat=false
+minimum-display-number=50
+
+[XDMCPServer]
+enabled=true
+port=9999
+
+#?RUNNER DAEMON-START
+#?*WAIT 1
+
+# Start a remote X server to log in with XDMCP
+#?*START-XSERVER ARGS=":98 -query localhost -port 9999 -nolisten unix"
+#?XSERVER :98 START
+#?XSERVER :98 SEND-QUERY
+
+# Negotiate with daemon
+#?XSERVER :98 GOT-WILLING AUTHENTICATION-NAME="" HOSTNAME="" STATUS=""
+#?XSERVER :98 SEND-REQUEST DISPLAY-NUMBER=98 AUTHORIZATION-NAME="MIT-MAGIC-COOKIE-1" MFID="TEST XSERVER"
+#?XSERVER :98 GOT-ACCEPT SESSION-ID=[0-9]* AUTHENTICATION-NAME="" AUTHORIZATION-NAME="MIT-MAGIC-COOKIE-1"
+#?XSERVER :98 SEND-MANAGE SESSION-ID=[0-9]* DISPLAY-NUMBER=98 DISPLAY-CLASS="DISPLAY CLASS"
+
+# LightDM connects to X server
+#?XSERVER :98 TCP-ACCEPT-CONNECT
+
+# Greeter starts and connects to remote X server
+#?GREETER 127.0.0.1:98 START
+#?XSERVER :98 TCP-ACCEPT-CONNECT
+#?GREETER 127.0.0.1:98 CONNECT-XSERVER
+#?GREETER 127.0.0.1:98 CONNECT-TO-DAEMON
+#?GREETER 127.0.0.1:98 CONNECTED-TO-DAEMON
+
+# Log in
+#?*GREETER 127.0.0.1:98 AUTHENTICATE USERNAME=have-password1
+#?GREETER 127.0.0.1:98 SHOW-PROMPT TEXT="Password:"
+#?*GREETER 127.0.0.1:98 RESPOND TEXT="password"
+#?GREETER 127.0.0.1:98 AUTHENTICATION-COMPLETE USERNAME=have-password1 AUTHENTICATED=TRUE
+#?*GREETER 127.0.0.1:98 START-SESSION
+#?GREETER 127.0.0.1:98 TERMINATE SIGNAL=15
+
+# Session starts
+#?SESSION 127.0.0.1:98 START USER=have-password1
+#?XSERVER :98 TCP-ACCEPT-CONNECT
+#?SESSION 127.0.0.1:98 CONNECT-XSERVER
+
+# Check file descriptors
+#?*SESSION 127.0.0.1:98 LIST-UNKNOWN-FILE-DESCRIPTORS
+#?SESSION 127.0.0.1:98 LIST-UNKNOWN-FILE-DESCRIPTORS FDS=
+
+# Clean up
+#?*STOP-DAEMON
+#?SESSION 127.0.0.1:98 TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
index d964e82f3d5aefcc78a46b766689bee4c32076f2..1ebfb082e47f2affebf8b59eaf949c1a0e783ecd 100644 (file)
@@ -4,6 +4,7 @@
 #include <pwd.h>
 #include <security/pam_appl.h>
 #include <unistd.h>
+#include <fcntl.h>
 #define __USE_GNU
 #include <dlfcn.h>
 #ifdef __linux__
@@ -59,19 +60,31 @@ setuid (uid_t uid)
 
 #ifdef __linux__
 int
-open (const char *pathname, int flags, mode_t mode)
+open (const char *pathname, int flags, ...)
 {
     int (*_open) (const char * pathname, int flags, mode_t mode);
+    int mode = 0;
+  
+    if (flags & O_CREAT)
+    {
+        va_list ap;
+        va_start (ap, flags);
+        mode = va_arg (ap, int);
+        va_end (ap);
+    }
 
     _open = (int (*)(const char * pathname, int flags, mode_t mode)) dlsym (RTLD_NEXT, "open");      
     if (strcmp (pathname, "/dev/console") == 0)
     {
         if (console_fd < 0)
-            console_fd = _open ("/dev/null", 0, 0);
+        {
+            console_fd = _open ("/dev/null", flags, mode);
+            fcntl (console_fd, F_SETFD, FD_CLOEXEC);
+        }
         return console_fd;
     }
     else
-        return _open(pathname, flags, mode);
+        return _open (pathname, flags, mode);
 }
 
 int
index fcb68954aa740cfdf66f99714c352d766b218271..8df53d450e87ff4b0ef69ede51e30b7925e2ed28 100644 (file)
@@ -4,6 +4,7 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <xcb/xcb.h>
 #include <glib.h>
 #include <glib-object.h>
 
 #include "status.h"
 
+static GString *open_fds;
+
 static GKeyFile *config;
 
+static xcb_connection_t *connection;
+
 static void
 quit_cb (int signum)
 {
@@ -103,13 +108,28 @@ request_cb (const gchar *request)
         g_clear_error (&error);
     }
     g_free (r);
+
+    r = g_strdup_printf ("SESSION %s LIST-UNKNOWN-FILE-DESCRIPTORS", getenv ("DISPLAY"));
+    if (strcmp (request, r) == 0)
+        status_notify ("SESSION %s LIST-UNKNOWN-FILE-DESCRIPTORS FDS=%s", getenv ("DISPLAY"), open_fds->str);
+    g_free (r);
 }
 
 int
 main (int argc, char **argv)
 {
     GMainLoop *loop;
-    xcb_connection_t *connection;
+    int fd, open_max;
+
+    open_fds = g_string_new ("");
+    open_max = sysconf (_SC_OPEN_MAX);
+    for (fd = STDERR_FILENO + 1; fd < open_max; fd++)
+    {
+        if (fcntl (fd, F_GETFD) >= 0)
+            g_string_append_printf (open_fds, "%d,", fd);
+    }
+    if (g_str_has_suffix (open_fds->str, ","))
+        open_fds->str[strlen (open_fds->str) - 1] = '\0';
 
     signal (SIGINT, quit_cb);
     signal (SIGTERM, quit_cb);
diff --git a/tests/test-open-file-descriptors b/tests/test-open-file-descriptors
new file mode 100755 (executable)
index 0000000..c067f1d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner open-file-descriptors test-gobject-greeter
diff --git a/tests/test-vnc-open-file-descriptors b/tests/test-vnc-open-file-descriptors
new file mode 100755 (executable)
index 0000000..ada6a3b
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner vnc-open-file-descriptors test-gobject-greeter
diff --git a/tests/test-xdmcp-open-file-descriptors b/tests/test-xdmcp-open-file-descriptors
new file mode 100755 (executable)
index 0000000..d8ddec3
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner xdmcp-open-file-descriptors test-gobject-greeter