diff options
Diffstat (limited to 'mate-session/mdm.c')
-rw-r--r-- | mate-session/mdm.c | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/mate-session/mdm.c b/mate-session/mdm.c new file mode 100644 index 0000000..e6e2eac --- /dev/null +++ b/mate-session/mdm.c @@ -0,0 +1,496 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 Raffaele Sandrini + * Copyright (C) 2005 Red Hat, Inc. + * Copyright (C) 2002, 2003 George Lebl + * Copyright (C) 2001 Queen of England, + * + * 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: + * Raffaele Sandrini <[email protected]> + * George Lebl <[email protected]> + * Mark McLoughlin <[email protected]> + */ + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <X11/Xauth.h> +#include <gdk/gdk.h> + +#include "mdm.h" + +#define MDM_PROTOCOL_UPDATE_INTERVAL 1 /* seconds */ + +#define MDM_PROTOCOL_SOCKET_PATH "/var/run/mdm_socket" + +#define MDM_PROTOCOL_MSG_CLOSE "CLOSE" +#define MDM_PROTOCOL_MSG_VERSION "VERSION" +#define MDM_PROTOCOL_MSG_AUTHENTICATE "AUTH_LOCAL" +#define MDM_PROTOCOL_MSG_QUERY_ACTION "QUERY_LOGOUT_ACTION" +#define MDM_PROTOCOL_MSG_SET_ACTION "SET_SAFE_LOGOUT_ACTION" +#define MDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER" + +#define MDM_ACTION_STR_NONE "NONE" +#define MDM_ACTION_STR_SHUTDOWN "HALT" +#define MDM_ACTION_STR_REBOOT "REBOOT" +#define MDM_ACTION_STR_SUSPEND "SUSPEND" + +typedef struct { + int fd; + char* auth_cookie; + + MdmLogoutAction available_actions; + MdmLogoutAction current_actions; + + time_t last_update; +} MdmProtocolData; + +static MdmProtocolData mdm_protocol_data = { + 0, + NULL, + MDM_LOGOUT_ACTION_NONE, + MDM_LOGOUT_ACTION_NONE, + 0 +}; + +static char* mdm_send_protocol_msg(MdmProtocolData* data, const char* msg) +{ + GString* retval; + char buf[256]; + char* p; + int len; + + p = g_strconcat(msg, "\n", NULL); + + if (write(data->fd, p, strlen(p)) < 0) + { + g_free(p); + + g_warning("Failed to send message to MDM: %s", g_strerror(errno)); + + return NULL; + } + + g_free(p); + + p = NULL; + retval = NULL; + + while ((len = read(data->fd, buf, sizeof(buf) - 1)) > 0) + { + buf[len] = '\0'; + + if (!retval) + { + retval = g_string_new(buf); + } + else + { + retval = g_string_append(retval, buf); + } + + if ((p = strchr(retval->str, '\n'))) + { + break; + } + } + + if (p) + { + *p = '\0'; + } + + return retval ? g_string_free(retval, FALSE) : NULL; +} + +static char* get_display_number(void) +{ + const char* display_name; + char* retval; + char* p; + + display_name = gdk_display_get_name(gdk_display_get_default()); + + p = strchr(display_name, ':'); + + if (!p) + { + return g_strdup("0"); + } + + while (*p == ':') + { + p++; + } + + retval = g_strdup(p); + + p = strchr(retval, '.'); + + if (p != NULL) + { + *p = '\0'; + } + + return retval; +} + +static gboolean mdm_authenticate_connection(MdmProtocolData* data) +{ + #define MDM_MIT_MAGIC_COOKIE_LEN 16 + + const char* xau_path; + FILE* f; + Xauth* xau; + char* display_number; + gboolean retval; + + if (data->auth_cookie) + { + char* msg; + char* response; + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", data->auth_cookie); + response = mdm_send_protocol_msg(data, msg); + g_free(msg); + + if (response && !strcmp(response, "OK")) + { + g_free(response); + return TRUE; + } + else + { + g_free(response); + g_free(data->auth_cookie); + data->auth_cookie = NULL; + } + } + + if (!(xau_path = XauFileName())) + { + return FALSE; + } + + if (!(f = fopen(xau_path, "r"))) + { + return FALSE; + } + + retval = FALSE; + display_number = get_display_number(); + + while ((xau = XauReadAuth(f))) + { + char buffer[40]; /* 2*16 == 32, so 40 is enough */ + char* msg; + char* response; + int i; + + if (xau->family != FamilyLocal || strncmp(xau->number, display_number, xau->number_length) || strncmp(xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) || xau->data_length != MDM_MIT_MAGIC_COOKIE_LEN) + { + XauDisposeAuth(xau); + continue; + } + + for (i = 0; i < MDM_MIT_MAGIC_COOKIE_LEN; i++) + { + g_snprintf(buffer + 2 * i, 3, "%02x", (guint)(guchar) xau->data[i]); + } + + XauDisposeAuth(xau); + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer); + response = mdm_send_protocol_msg(data, msg); + g_free(msg); + + if (response && !strcmp(response, "OK")) + { + data->auth_cookie = g_strdup(buffer); + g_free(response); + retval = TRUE; + break; + } + + g_free(response); + } + + g_free(display_number); + + fclose(f); + + return retval; + + #undef MDM_MIT_MAGIC_COOKIE_LEN +} + +static void mdm_shutdown_protocol_connection(MdmProtocolData *data) +{ + if (data->fd) + { + close(data->fd); + } + + data->fd = 0; +} + +static gboolean mdm_init_protocol_connection(MdmProtocolData* data) +{ + struct sockaddr_un addr; + char* response; + + g_assert(data->fd <= 0); + + if (g_file_test(MDM_PROTOCOL_SOCKET_PATH, G_FILE_TEST_EXISTS)) + { + strcpy(addr.sun_path, MDM_PROTOCOL_SOCKET_PATH); + } + else if (g_file_test("/tmp/.mdm_socket", G_FILE_TEST_EXISTS)) + { + strcpy(addr.sun_path, "/tmp/.mdm_socket"); + } + else + { + return FALSE; + } + + data->fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (data->fd < 0) + { + g_warning("Failed to create MDM socket: %s", g_strerror(errno)); + + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + addr.sun_family = AF_UNIX; + + if (connect(data->fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) + { + g_warning("Failed to establish a connection with MDM: %s", g_strerror(errno)); + + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_VERSION); + + if (!response || strncmp(response, "MDM ", strlen("MDM ") != 0)) + { + g_free(response); + + g_warning("Failed to get protocol version from MDM"); + mdm_shutdown_protocol_connection(data); + + return FALSE; + } + + g_free(response); + + if (!mdm_authenticate_connection(data)) + { + g_warning("Failed to authenticate with MDM"); + mdm_shutdown_protocol_connection(data); + return FALSE; + } + + return TRUE; +} + +static void mdm_parse_query_response(MdmProtocolData* data, const char* response) +{ + char** actions; + int i; + + data->available_actions = MDM_LOGOUT_ACTION_NONE; + data->current_actions = MDM_LOGOUT_ACTION_NONE; + + if (strncmp(response, "OK ", 3) != 0) + { + return; + } + + response += 3; + + actions = g_strsplit(response, ";", -1); + + for (i = 0; actions[i]; i++) + { + MdmLogoutAction action = MDM_LOGOUT_ACTION_NONE; + gboolean selected = FALSE; + char* str = actions [i]; + int len; + + len = strlen(str); + + if (!len) + { + continue; + } + + if (str[len - 1] == '!') + { + selected = TRUE; + str[len - 1] = '\0'; + } + + if (!strcmp(str, MDM_ACTION_STR_SHUTDOWN)) + { + action = MDM_LOGOUT_ACTION_SHUTDOWN; + } + else if (!strcmp(str, MDM_ACTION_STR_REBOOT)) + { + action = MDM_LOGOUT_ACTION_REBOOT; + } + else if (!strcmp(str, MDM_ACTION_STR_SUSPEND)) + { + action = MDM_LOGOUT_ACTION_SUSPEND; + } + + data->available_actions |= action; + + if (selected) + { + data->current_actions |= action; + } + } + + g_strfreev(actions); +} + +static void mdm_update_logout_actions(MdmProtocolData* data) +{ + time_t current_time; + char* response; + + current_time = time(NULL); + + if (current_time <= (data->last_update + MDM_PROTOCOL_UPDATE_INTERVAL)) + { + return; + } + + data->last_update = current_time; + + if (!mdm_init_protocol_connection(data)) + { + return; + } + + if ((response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_QUERY_ACTION))) + { + mdm_parse_query_response(data, response); + g_free(response); + } + + mdm_shutdown_protocol_connection(data); +} + +gboolean mdm_is_available(void) +{ + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return FALSE; + } + + mdm_shutdown_protocol_connection(&mdm_protocol_data); + + return TRUE; +} + +gboolean mdm_supports_logout_action(MdmLogoutAction action) +{ + mdm_update_logout_actions(&mdm_protocol_data); + + return (mdm_protocol_data.available_actions & action) != 0; +} + +MdmLogoutAction mdm_get_logout_action(void) +{ + mdm_update_logout_actions(&mdm_protocol_data); + + return mdm_protocol_data.current_actions; +} + +void mdm_set_logout_action(MdmLogoutAction action) +{ + char* action_str = NULL; + char* msg; + char* response; + + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return; + } + + switch (action) + { + case MDM_LOGOUT_ACTION_NONE: + action_str = MDM_ACTION_STR_NONE; + break; + case MDM_LOGOUT_ACTION_SHUTDOWN: + action_str = MDM_ACTION_STR_SHUTDOWN; + break; + case MDM_LOGOUT_ACTION_REBOOT: + action_str = MDM_ACTION_STR_REBOOT; + break; + case MDM_LOGOUT_ACTION_SUSPEND: + action_str = MDM_ACTION_STR_SUSPEND; + break; + } + + msg = g_strdup_printf(MDM_PROTOCOL_MSG_SET_ACTION " %s", action_str); + + response = mdm_send_protocol_msg(&mdm_protocol_data, msg); + + g_free(msg); + g_free(response); + + mdm_protocol_data.last_update = 0; + + mdm_shutdown_protocol_connection(&mdm_protocol_data); +} + +void mdm_new_login(void) +{ + char* response; + + if (!mdm_init_protocol_connection(&mdm_protocol_data)) + { + return; + } + + response = mdm_send_protocol_msg(&mdm_protocol_data, MDM_PROTOCOL_MSG_FLEXI_XSERVER); + + g_free(response); + + mdm_protocol_data.last_update = 0; + + mdm_shutdown_protocol_connection(&mdm_protocol_data); +} |