diff options
| author | William Jon McCann <mccann@jhu.edu> | 2007-03-02 17:51:37 -0500 | 
|---|---|---|
| committer | William Jon McCann <mccann@jhu.edu> | 2007-03-02 17:51:37 -0500 | 
| commit | 4f0911bf685f51b51d05a69a40d3950debb995a0 (patch) | |
| tree | 1b5d38a8e52126e200c8954b3c185ca7ff4f3f94 | |
| parent | 75ad4b862611bd6a39bda06db3db4c9737657dad (diff) | |
add linux backends for collecting session information
These tools will be used to generate and verify the parameters
used to open a session.
| -rw-r--r-- | configure.ac | 5 | ||||
| -rw-r--r-- | src/proc-linux.c | 57 | ||||
| -rw-r--r-- | src/proc.h | 20 | ||||
| -rw-r--r-- | tools/linux/Makefile.am | 23 | ||||
| -rw-r--r-- | tools/linux/ck-collect-session-info.c | 390 | ||||
| -rw-r--r-- | tools/linux/ck-get-x11-server-pid.c | 75 | 
6 files changed, 560 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac index 4b8f8ba..4cd3b58 100644 --- a/configure.ac +++ b/configure.ac @@ -34,6 +34,7 @@ AM_GLIB_GNU_GETTEXT  DBUS_REQUIRED_VERSION=0.30  GLIB_REQUIRED_VERSION=2.7.0 +GDK_REQUIRED_VERSION=2.8.0  AC_CHECK_HEADERS(unistd.h)  AC_TYPE_UID_T @@ -47,6 +48,10 @@ PKG_CHECK_MODULES(CONSOLE_KIT,  PKG_CHECK_MODULES(LIBDBUS,  	dbus-1 >= $DBUS_REQUIRED_VERSION  ) +PKG_CHECK_MODULES(TOOLS, +        gdk-2.0 >= $GDK_REQUIRED_VERSION +) +  AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)  EXTRA_COMPILE_WARNINGS(yes) diff --git a/src/proc-linux.c b/src/proc-linux.c index 080293e..b766183 100644 --- a/src/proc-linux.c +++ b/src/proc-linux.c @@ -421,6 +421,62 @@ proc_stat_free (proc_stat_t *stat)          g_free (stat);  } +GHashTable * +proc_pid_get_env_hash (pid_t pid) +{ +        char       *path; +        gboolean    res; +        char       *contents; +        gsize       length; +        GError     *error; +        GHashTable *hash; +        int         i; +        gboolean    last_was_null; + +        contents = NULL; +        hash = NULL; + +        path = g_strdup_printf ("/proc/%u/environ", (guint)pid); + +        error = NULL; +        res = g_file_get_contents (path, +                                   &contents, +                                   &length, +                                   &error); +        if (! res) { +                g_warning ("Couldn't read %s: %s", path, error->message); +                g_error_free (error); +                goto out; +        } + +        hash = g_hash_table_new_full (g_str_hash, +                                      g_str_equal, +                                      g_free, +                                      g_free); + +        last_was_null = TRUE; +        for (i = 0; i < length; i++) { +                if (contents[i] == '\0') { +                        last_was_null = TRUE; +                        continue; +                } +                if (last_was_null) { +                        char **vals; +                        vals = g_strsplit (contents + i, "=", 2); +                        if (vals != NULL) { +                                g_hash_table_insert (hash, vals[0], vals[1]); +                        } +                } +                last_was_null = FALSE; +        } + + out: +        g_free (contents); +        g_free (path); + +        return hash; +} +  char *  proc_pid_get_env (pid_t       pid,                    const char *var) @@ -468,6 +524,7 @@ proc_pid_get_env (pid_t       pid,                          val = g_strdup (contents + i + prefix_len);                          break;                  } +                last_was_null = FALSE;          }   out: @@ -27,15 +27,17 @@ G_BEGIN_DECLS  typedef struct _proc_stat_t proc_stat_t; -gboolean proc_stat_new_for_pid (pid_t         pid, -                                proc_stat_t **stat, -                                GError      **error); -char    *proc_stat_get_tty     (proc_stat_t  *stat); -char    *proc_stat_get_cmd     (proc_stat_t  *stat); -void     proc_stat_free        (proc_stat_t  *stat); - -char    *proc_pid_get_env      (pid_t         pid, -                                const char   *var); +gboolean     proc_stat_new_for_pid (pid_t         pid, +                                    proc_stat_t **stat, +                                    GError      **error); +char        *proc_stat_get_tty     (proc_stat_t  *stat); +char        *proc_stat_get_cmd     (proc_stat_t  *stat); +void         proc_stat_free        (proc_stat_t  *stat); + +char        *proc_pid_get_env      (pid_t         pid, +                                    const char   *var); + +GHashTable  *proc_pid_get_env_hash (pid_t         pid);  G_END_DECLS diff --git a/tools/linux/Makefile.am b/tools/linux/Makefile.am index 5091efe..3283b5e 100644 --- a/tools/linux/Makefile.am +++ b/tools/linux/Makefile.am @@ -9,6 +9,7 @@ SUBDIRS = \  INCLUDES =							\  	-I.							\  	-I$(srcdir)						\ +	-I$(top_srcdir)/src					\  	$(CONSOLE_KIT_CFLAGS)					\  	$(DISABLE_DEPRECATED_CFLAGS)				\  	-DPREFIX=\""$(prefix)"\" 				\ @@ -22,7 +23,27 @@ INCLUDES =							\  	$(TOOLS_CFLAGS)						\  	$(NULL) -libexec_PROGRAMS = \ +libexec_PROGRAMS = 			\ +	ck-collect-session-info		\ +	ck-get-x11-server-pid		\ +	$(NULL) + +ck_collect_session_info_SOURCES =	\ +	$(top_srcdir)/src/proc.h	\ +	$(top_srcdir)/src/proc-linux.c	\ +	ck-collect-session-info.c	\ +	$(NULL) + +ck_collect_session_info_LDADD =		\ +	$(TOOLS_LIBS)			\ +	$(NULL) + +ck_get_x11_server_pid_SOURCES =		\ +	ck-get-x11-server-pid.c		\ +	$(NULL) + +ck_get_x11_server_pid_LDADD =		\ +	$(TOOLS_LIBS)			\  	$(NULL)  EXTRA_DIST =				\ diff --git a/tools/linux/ck-collect-session-info.c b/tools/linux/ck-collect-session-info.c new file mode 100644 index 0000000..2979da0 --- /dev/null +++ b/tools/linux/ck-collect-session-info.c @@ -0,0 +1,390 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <pwd.h> +#include <errno.h> + +#include <glib.h> + +#include "proc.h" + +typedef struct { +        uid_t    uid; +        pid_t    pid; +        char    *display_device; +        char    *x11_display; +        gboolean x11_can_connect; +        char    *hostname; +        char    *session_type; +        gboolean is_local; +        gboolean is_local_is_set; +} SessionInfo; + +static void +session_info_free (SessionInfo *si) +{ +        g_free (si->display_device); +        g_free (si->x11_display); +        g_free (si->hostname); +        g_free (si->session_type); +        g_free (si); +} + +static void +setuid_child_setup_func (SessionInfo *si) +{ +        int            res; +        struct passwd *pwent; + +        errno = 0; +        pwent = getpwuid (si->uid); +        if (pwent == NULL) { +                g_warning ("Unable to lookup UID: %s", g_strerror (errno)); +                exit (1); +        } + +        /* set the group */ +        errno = 0; +        res = setgid (pwent->pw_gid); +        if (res == -1) { +                g_warning ("Error performing setgid: %s", g_strerror (errno)); +                exit (1); +        } + +        /* become the user */ +        errno = 0; +        res = setuid (si->uid); +        if (res == -1) { +                g_warning ("Error performing setuid: %s", g_strerror (errno)); +                exit (1); +        } +} + +static GPtrArray * +get_filtered_environment (pid_t pid) +{ +        GPtrArray  *env; +        GHashTable *hash; +        int         i; +        static const char *allowed_env_vars [] = { +                "DISPLAY", +                "XAUTHORITY", +                "XAUTHLOCALHOSTNAME", +                "SSH_CLIENT", +                "SSH_CONNECTION", +                "SSH_TTY", +                "HOME", +        }; + +        env = g_ptr_array_new (); + +        g_ptr_array_add (env, g_strdup ("PATH=/bin:/usr/bin")); + +        hash = proc_pid_get_env_hash (pid); + +        for (i = 0; i < G_N_ELEMENTS (allowed_env_vars); i++) { +                const char *var; +                const char *val; +                var = allowed_env_vars [i]; +                val = g_hash_table_lookup (hash, var); +                if (val != NULL) { +                        char *str; +                        str = g_strdup_printf ("%s=%s", var, val); +                        g_ptr_array_add (env, str); +                } +        } + +        g_ptr_array_add (env, NULL); + +        g_hash_table_destroy (hash); + +        return env; +} + +static void +get_x11_server_pid (SessionInfo *si, +                    gboolean    *can_connect, +                    guint       *pid) +{ +        gboolean   res; +        char      *err; +        char      *out; +        int        status; +        int        i; +        GError    *error; +        guint      num; +        char      *argv[4]; +        GPtrArray *env; + +        if (can_connect != NULL) { +                *can_connect = FALSE; +        } +        if (pid != NULL) { +                *pid = 0; +        } + +        /* get the applicable environment */ +        env = get_filtered_environment (si->pid); + +        num = 0; + +        argv[0] = LIBEXECDIR "/ck-get-x11-server-pid"; +        argv[1] = NULL; + +        error = NULL; +        out = NULL; +        err = NULL; +        status = -1; +        res = g_spawn_sync (NULL, +                            argv, +                            (char **)env->pdata, +                            0, +                            (GSpawnChildSetupFunc)setuid_child_setup_func, +                            si, +                            &out, +                            &err, +                            &status, +                            &error); +        for (i = 0; i < env->len; i++) { +                g_free (g_ptr_array_index (env, i)); +        } +        g_ptr_array_free (env, TRUE); + +        if (error != NULL) { +                g_warning ("Unable to PID for x11 server: %s", error->message); +                g_error_free (error); +        } + +        if (status == 0) { +                if (res && out != NULL) { +                        guint v; +                        char  c; + +                        if (1 == sscanf (out, "%u %c", &v, &c)) { +                                num = v; +                        } +                } + +                if (can_connect != NULL) { +                        *can_connect = TRUE; +                } +        } + + +        if (err != NULL && err[0] != '\0') { +                g_warning ("%s", err); +        } + +        if (pid != NULL) { +                *pid = num; +        } + +        g_free (out); +        g_free (err); +} + +/* Looking at the XFree86_VT property on the root window + * doesn't work very well because it is difficult to + * distinguish local from remote systems and the value + * can't necessarily be trusted.  So instead we connect + * to the server and use peer credentials to find the + * local PID and then find its tty. + */ +static void +fill_x11_info (SessionInfo *si) +{ +        guint        xorg_pid; +        gboolean     can_connect; +        gboolean     res; +        proc_stat_t *xorg_stat; +        GError      *error; + +        /* assume this is true then check it */ +        si->x11_display = proc_pid_get_env (si->pid, "DISPLAY"); + +        if (si->x11_display == NULL) { +                /* no point continuing */ +                si->x11_can_connect = FALSE; +                return; +        } + +        xorg_pid = 0; +        can_connect = FALSE; +        get_x11_server_pid (si, &can_connect, &xorg_pid); + +        si->x11_can_connect = can_connect; +        if (! can_connect) { +                g_free (si->x11_display); +                si->x11_display = NULL; +                return; +        } + +        if (xorg_pid < 2) { +                /* keep the tty value */ +                /* if we can connect but don't have a pid +                 * then we're not local */ + +                si->is_local = FALSE; +                si->is_local_is_set = TRUE; +                return; +        } + +        error = NULL; +        res = proc_stat_new_for_pid (xorg_pid, &xorg_stat, &error); +        if (! res) { +                if (error != NULL) { +                        g_warning ("stat on pid %d failed: %s", xorg_pid, error->message); +                        g_error_free (error); +                } +                /* keep the tty value */ +                return; +        } + +        /* overwrite the tty value */ +        g_free (si->display_device); +        si->display_device = proc_stat_get_tty (xorg_stat); +        proc_stat_free (xorg_stat); + +        si->is_local = TRUE; +        si->is_local_is_set = TRUE; + +        g_free (si->hostname); +        si->hostname = g_strdup ("localhost"); +} + +static gboolean +fill_session_info (SessionInfo *si) +{ +        proc_stat_t *stat; +        GError      *error; +        gboolean     res; + +        error = NULL; +        res = proc_stat_new_for_pid (si->pid, &stat, &error); +        if (! res) { +                if (error != NULL) { +                        g_warning ("stat on pid %d failed: %s", si->pid, error->message); +                        g_error_free (error); +                } + +                return FALSE; +        } + +        si->display_device = proc_stat_get_tty (stat); +        si->session_type = proc_stat_get_cmd (stat); +        proc_stat_free (stat); + +        fill_x11_info (si); + +        return TRUE; +} + +static void +print_session_info (SessionInfo *si) +{ +        if (si->display_device != NULL) { +                printf ("display-device = %s\n", si->display_device); +        } +        if (si->x11_display != NULL) { +                printf ("x11-display = %s\n", si->x11_display); +        } +        if (si->session_type != NULL) { +                printf ("session-type = %s\n", si->session_type); +        } +        if (si->hostname != NULL) { +                printf ("host-name = %s\n", si->hostname); +        } +        if (si->is_local_is_set == TRUE) { +                printf ("is-local = %s\n", si->is_local ? "true" : "false"); +        } +} + +static gboolean +collect_session_info (uid_t uid, +                      pid_t pid) +{ +        SessionInfo *si; +        gboolean     ret; + +        si = g_new0 (SessionInfo, 1); + +        si->uid = uid; +        si->pid = pid; + +        ret = fill_session_info (si); +        if (ret) { +                print_session_info (si); +        } + +        session_info_free (si); + +        return ret; +} + +int +main (int    argc, +      char **argv) +{ +        GOptionContext     *context; +        gboolean            ret; +        GError             *error; +        static int          user_id; +        static int          process_id; +        static GOptionEntry entries [] = { +                { "uid", 0, 0, G_OPTION_ARG_INT, &user_id, "User ID", NULL }, +                { "pid", 0, 0, G_OPTION_ARG_INT, &process_id, "Process ID", NULL }, +                { NULL } +        }; + +        context = g_option_context_new (NULL); +        g_option_context_add_main_entries (context, entries, NULL); +        error = NULL; +        ret = g_option_context_parse (context, &argc, &argv, &error); +        g_option_context_free (context); + +        if (! ret) { +                g_warning ("%s", error->message); +                g_error_free (error); +                exit (1); +        } + +        if (user_id < 500) { +                g_warning ("Invalid UID"); +                exit (1); +        } + +        if (process_id < 2) { +                g_warning ("Invalid PID"); +                exit (1); +        } + +        ret = collect_session_info (user_id, process_id); + +	return ret; +} diff --git a/tools/linux/ck-get-x11-server-pid.c b/tools/linux/ck-get-x11-server-pid.c new file mode 100644 index 0000000..4e5d6c5 --- /dev/null +++ b/tools/linux/ck-get-x11-server-pid.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * 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 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +static void +print_peer_pid (int fd) +{ +#ifdef SO_PEERCRED +        struct ucred cr; +        int cr_len = sizeof (cr); + +        if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && cr_len == sizeof (cr)) { +                /* paranoia check for peer running as root */ +                if (cr.uid == 0) { +                        printf ("%u\n", cr.pid); +                } +        } else { +                g_warning ("Failed to getsockopt() credentials, returned len %d/%d: %s\n", +                           cr_len, +                           (int) sizeof (cr), +                           g_strerror (errno)); +        } +#endif +} + +int +main (int    argc, +      char **argv) +{ +        int fd; +        int ret; + +        ret = 1; + +        gdk_init (&argc, &argv); + +        fd = ConnectionNumber (GDK_DISPLAY()); + +        if (fd > 0) { +                ret = 0; +                print_peer_pid (fd); +        } + +	return ret; +}  | 
