From eaef567cabc5c0dda9706faa0e17e20909131ade Mon Sep 17 00:00:00 2001 From: simpoir Date: Fri, 30 Jan 2009 04:14:48 +0000 Subject: ajout initial du tree jhbuild [jhbuild base] --- (limited to 'src/sugar/gsm-xsmp.c') diff --git a/src/sugar/gsm-xsmp.c b/src/sugar/gsm-xsmp.c new file mode 100644 index 0000000..aa9dff1 --- /dev/null +++ b/src/sugar/gsm-xsmp.c @@ -0,0 +1,535 @@ +/* xsmp.c + * Copyright (C) 2007 Novell, Inc. + * + * 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 + * Lesser 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gsm-client-xsmp.h" +#include "gsm-xsmp.h" + +#include +#include +#include +#include + +#ifdef HAVE_X11_XTRANS_XTRANS_H +/* Get the proto for _IceTransNoListen */ +#define ICE_t +#define TRANS_SERVER +#include +#undef ICE_t +#undef TRANS_SERVER +#endif /* HAVE_X11_XTRANS_XTRANS_H */ + +static IceListenObj *xsmp_sockets; +static int num_xsmp_sockets, num_local_xsmp_sockets; + +static gboolean update_iceauthority (gboolean adding); + +static gboolean accept_ice_connection (GIOChannel *source, + GIOCondition condition, + gpointer data); +static Status accept_xsmp_connection (SmsConn conn, + SmPointer manager_data, + unsigned long *mask_ret, + SmsCallbacks *callbacks_ret, + char **failure_reason_ret); + +static void ice_error_handler (IceConn conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence_num, + int error_class, + int severity, + IcePointer values); +static void ice_io_error_handler (IceConn conn); +static void sms_error_handler (SmsConn sms_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence_num, + int error_class, + int severity, + IcePointer values); +/** + * gsm_xsmp_init: + * + * Initializes XSMP. Notably, it creates the XSMP listening socket and + * sets the SESSION_MANAGER environment variable to point to it. + **/ +char * +gsm_xsmp_init (void) +{ + char error[256]; + mode_t saved_umask; + char *network_id_list; + int i; + + /* Set up sane error handlers */ + IceSetErrorHandler (ice_error_handler); + IceSetIOErrorHandler (ice_io_error_handler); + SmsSetErrorHandler (sms_error_handler); + + /* Initialize libSM; we pass NULL for hostBasedAuthProc to disable + * host-based authentication. + */ + if (!SmsInitialize (PACKAGE, VERSION, accept_xsmp_connection, + NULL, NULL, sizeof (error), error)) + g_error("Could not initialize libSM: %s", error); + +#ifdef HAVE_X11_XTRANS_XTRANS_H + /* By default, IceListenForConnections will open one socket for each + * transport type known to X. We don't want connections from remote + * hosts, so for security reasons it would be best if ICE didn't + * even open any non-local sockets. So we use an internal ICElib + * method to disable them here. Unfortunately, there is no way to + * ask X what transport types it knows about, so we're forced to + * guess. + */ + _IceTransNoListen ("tcp"); +#endif + + /* Create the XSMP socket. Older versions of IceListenForConnections + * have a bug which causes the umask to be set to 0 on certain types + * of failures. Probably not an issue on any modern systems, but + * we'll play it safe. + */ + saved_umask = umask (0); + umask (saved_umask); + if (!IceListenForConnections (&num_xsmp_sockets, &xsmp_sockets, + sizeof (error), error)) + g_error ("Could not create ICE listening socket: %s", error); + umask (saved_umask); + + /* Find the local sockets in the returned socket list and move them + * to the start of the list. + */ + for (i = num_local_xsmp_sockets = 0; i < num_xsmp_sockets; i++) + { + char *id = IceGetListenConnectionString (xsmp_sockets[i]); + + if (!strncmp (id, "local/", sizeof ("local/") - 1) || + !strncmp (id, "unix/", sizeof ("unix/") - 1)) + { + if (i > num_local_xsmp_sockets) + { + IceListenObj tmp = xsmp_sockets[i]; + xsmp_sockets[i] = xsmp_sockets[num_local_xsmp_sockets]; + xsmp_sockets[num_local_xsmp_sockets] = tmp; + } + num_local_xsmp_sockets++; + } + free (id); + } + + if (num_local_xsmp_sockets == 0) + g_error ("IceListenForConnections did not return a local listener!"); + +#ifdef HAVE_X11_XTRANS_XTRANS_H + if (num_local_xsmp_sockets != num_xsmp_sockets) + { + /* Xtrans was apparently compiled with support for some + * non-local transport besides TCP (which we disabled above); we + * won't create IO watches on those extra sockets, so + * connections to them will never be noticed, but they're still + * there, which is inelegant. + * + * If the g_warning below is triggering for you and you want to + * stop it, the fix is to add additional _IceTransNoListen() + * calls above. + */ + network_id_list = + IceComposeNetworkIdList (num_xsmp_sockets - num_local_xsmp_sockets, + xsmp_sockets + num_local_xsmp_sockets); + g_warning ("IceListenForConnections returned %d non-local listeners: %s", + num_xsmp_sockets - num_local_xsmp_sockets, network_id_list); + free (network_id_list); + } +#endif + + /* Update .ICEauthority with new auth entries for our socket */ + if (!update_iceauthority (TRUE)) + { + /* FIXME: is this really fatal? Hm... */ + g_error ("Could not update ICEauthority file %s", + IceAuthFileName ()); + } + + network_id_list = IceComposeNetworkIdList (num_local_xsmp_sockets, + xsmp_sockets); + + return network_id_list; +} + +/** + * gsm_xsmp_run: + * + * Sets the XSMP server to start accepting connections. + **/ +void +gsm_xsmp_run (void) +{ + GIOChannel *channel; + int i; + + for (i = 0; i < num_local_xsmp_sockets; i++) + { + channel = g_io_channel_unix_new (IceGetListenConnectionNumber (xsmp_sockets[i])); + g_io_add_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR, + accept_ice_connection, xsmp_sockets[i]); + g_io_channel_unref (channel); + } +} + +/** + * gsm_xsmp_shutdown: + * + * Shuts down the XSMP server and closes the ICE listening socket + **/ +void +gsm_xsmp_shutdown (void) +{ + update_iceauthority (FALSE); + + IceFreeListenObjs (num_xsmp_sockets, xsmp_sockets); + xsmp_sockets = NULL; +} + +/** + * gsm_xsmp_generate_client_id: + * + * Generates a new XSMP client ID. + * + * Return value: an XSMP client ID. + **/ +char * +gsm_xsmp_generate_client_id (void) +{ + static int sequence = -1; + static guint rand1 = 0, rand2 = 0; + static pid_t pid = 0; + struct timeval tv; + + /* The XSMP spec defines the ID as: + * + * Version: "1" + * Address type and address: + * "1" + an IPv4 address as 8 hex digits + * "2" + a DECNET address as 12 hex digits + * "6" + an IPv6 address as 32 hex digits + * Time stamp: milliseconds since UNIX epoch as 13 decimal digits + * Process-ID type and process-ID: + * "1" + POSIX PID as 10 decimal digits + * Sequence number as 4 decimal digits + * + * XSMP client IDs are supposed to be globally unique: if + * SmsGenerateClientID() is unable to determine a network + * address for the machine, it gives up and returns %NULL. + * GNOME and KDE have traditionally used a fourth address + * format in this case: + * "0" + 16 random hex digits + * + * We don't even bother trying SmsGenerateClientID(), since the + * user's IP address is probably "192.168.1.*" anyway, so a random + * number is actually more likely to be globally unique. + */ + + if (!rand1) + { + rand1 = g_random_int (); + rand2 = g_random_int (); + pid = getpid (); + } + + sequence = (sequence + 1) % 10000; + gettimeofday (&tv, NULL); + return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d", + rand1, rand2, + (unsigned long) tv.tv_sec, + (unsigned) tv.tv_usec, + (unsigned long) pid, + sequence); +} + +/* This is called (by glib via xsmp->ice_connection_watch) when a + * connection is first received on the ICE listening socket. (We + * expect that the client will then initiate XSMP on the connection; + * if it does not, GsmClientXSMP will eventually time out and close + * the connection.) + * + * FIXME: it would probably make more sense to not create a + * GsmClientXSMP object until accept_xsmp_connection, below (and to do + * the timing-out here in xsmp.c). + */ +static gboolean +accept_ice_connection (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + IceListenObj listener = data; + IceConn ice_conn; + IceAcceptStatus status; + GsmClientXSMP *client; + + g_debug ("accept_ice_connection()"); + + ice_conn = IceAcceptConnection (listener, &status); + if (status != IceAcceptSuccess) + { + g_debug ("IceAcceptConnection returned %d", status); + return TRUE; + } + + client = gsm_client_xsmp_new (ice_conn); + ice_conn->context = client; + return TRUE; +} + +/* This is called (by libSM) when XSMP is initiated on an ICE + * connection that was already accepted by accept_ice_connection. + */ +static Status +accept_xsmp_connection (SmsConn sms_conn, SmPointer manager_data, + unsigned long *mask_ret, SmsCallbacks *callbacks_ret, + char **failure_reason_ret) +{ + IceConn ice_conn; + GsmClientXSMP *client; + + /* FIXME: what about during shutdown but before gsm_xsmp_shutdown? */ + if (!xsmp_sockets) + { + g_debug ("In shutdown, rejecting new client"); + + *failure_reason_ret = + strdup (_("Refusing new client connection because the session is currently being shut down\n")); + return FALSE; + } + + ice_conn = SmsGetIceConnection (sms_conn); + client = ice_conn->context; + + g_return_val_if_fail (client != NULL, TRUE); + + gsm_client_xsmp_connect (client, sms_conn, mask_ret, callbacks_ret); + return TRUE; +} + +/* ICEauthority stuff */ + +/* Various magic numbers stolen from iceauth.c */ +#define GSM_ICE_AUTH_RETRIES 10 +#define GSM_ICE_AUTH_INTERVAL 2 /* 2 seconds */ +#define GSM_ICE_AUTH_LOCK_TIMEOUT 600 /* 10 minutes */ + +#define GSM_ICE_MAGIC_COOKIE_AUTH_NAME "MIT-MAGIC-COOKIE-1" +#define GSM_ICE_MAGIC_COOKIE_LEN 16 + +static IceAuthFileEntry * +auth_entry_new (const char *protocol, const char *network_id) +{ + IceAuthFileEntry *file_entry; + IceAuthDataEntry data_entry; + + file_entry = malloc (sizeof (IceAuthFileEntry)); + + file_entry->protocol_name = strdup (protocol); + file_entry->protocol_data = NULL; + file_entry->protocol_data_length = 0; + file_entry->network_id = strdup (network_id); + file_entry->auth_name = strdup (GSM_ICE_MAGIC_COOKIE_AUTH_NAME); + file_entry->auth_data = IceGenerateMagicCookie (GSM_ICE_MAGIC_COOKIE_LEN); + file_entry->auth_data_length = GSM_ICE_MAGIC_COOKIE_LEN; + + /* Also create an in-memory copy, which is what the server will + * actually use for checking client auth. + */ + data_entry.protocol_name = file_entry->protocol_name; + data_entry.network_id = file_entry->network_id; + data_entry.auth_name = file_entry->auth_name; + data_entry.auth_data = file_entry->auth_data; + data_entry.auth_data_length = file_entry->auth_data_length; + IceSetPaAuthData (1, &data_entry); + + return file_entry; +} + +static gboolean +update_iceauthority (gboolean adding) +{ + char *filename = IceAuthFileName (); + char **our_network_ids; + FILE *fp; + IceAuthFileEntry *auth_entry; + GSList *entries, *e; + int i; + gboolean ok = FALSE; + + if (IceLockAuthFile (filename, GSM_ICE_AUTH_RETRIES, GSM_ICE_AUTH_INTERVAL, + GSM_ICE_AUTH_LOCK_TIMEOUT) != IceAuthLockSuccess) + return FALSE; + + our_network_ids = g_malloc (num_local_xsmp_sockets * sizeof (char *)); + for (i = 0; i < num_local_xsmp_sockets; i++) + our_network_ids[i] = IceGetListenConnectionString (xsmp_sockets[i]); + + entries = NULL; + + fp = fopen (filename, "r+"); + if (fp) + { + while ((auth_entry = IceReadAuthFileEntry (fp)) != NULL) + { + /* Skip/delete entries with no network ID (invalid), or with + * our network ID; if we're starting up, an entry with our + * ID must be a stale entry left behind by an old process, + * and if we're shutting down, it won't be valid in the + * future, so either way we want to remove it from the list. + */ + if (!auth_entry->network_id) + { + IceFreeAuthFileEntry (auth_entry); + continue; + } + + for (i = 0; i < num_local_xsmp_sockets; i++) + { + if (!strcmp (auth_entry->network_id, our_network_ids[i])) + { + IceFreeAuthFileEntry (auth_entry); + break; + } + } + if (i != num_local_xsmp_sockets) + continue; + + entries = g_slist_prepend (entries, auth_entry); + } + + rewind (fp); + } + else + { + int fd; + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + { + g_warning ("Unable to read ICE authority file: %s", filename); + goto cleanup; + } + + fd = open (filename, O_CREAT | O_WRONLY, 0600); + fp = fdopen (fd, "w"); + if (!fp) + { + g_warning ("Unable to write to ICE authority file: %s", filename); + if (fd != -1) + close (fd); + goto cleanup; + } + } + + if (adding) + { + for (i = 0; i < num_local_xsmp_sockets; i++) + { + entries = g_slist_append (entries, + auth_entry_new ("ICE", our_network_ids[i])); + entries = g_slist_prepend (entries, + auth_entry_new ("XSMP", our_network_ids[i])); + } + } + + for (e = entries; e; e = e->next) + { + IceAuthFileEntry *auth_entry = e->data; + IceWriteAuthFileEntry (fp, auth_entry); + IceFreeAuthFileEntry (auth_entry); + } + g_slist_free (entries); + + fclose (fp); + ok = TRUE; + + cleanup: + IceUnlockAuthFile (filename); + for (i = 0; i < num_local_xsmp_sockets; i++) + free (our_network_ids[i]); + g_free (our_network_ids); + + return ok; +} + +/* Error handlers */ + +static void +ice_error_handler (IceConn conn, Bool swap, int offending_minor_opcode, + unsigned long offending_sequence, int error_class, + int severity, IcePointer values) +{ + g_debug ("ice_error_handler (%p, %s, %d, %lx, %d, %d)", + conn, swap ? "TRUE" : "FALSE", offending_minor_opcode, + offending_sequence, error_class, severity); + + if (severity == IceCanContinue) + return; + + /* FIXME: the ICElib docs are completely vague about what we're + * supposed to do in this case. Need to verify that calling + * IceCloseConnection() here is guaranteed to cause neither + * free-memory-reads nor leaks. + */ + IceCloseConnection (conn); +} + +static void +ice_io_error_handler (IceConn conn) +{ + g_debug ("ice_io_error_handler (%p)", conn); + + /* We don't need to do anything here; the next call to + * IceProcessMessages() for this connection will receive + * IceProcessMessagesIOError and we can handle the error there. + */ +} + +static void +sms_error_handler (SmsConn conn, Bool swap, int offending_minor_opcode, + unsigned long offending_sequence_num, int error_class, + int severity, IcePointer values) +{ + g_debug ("sms_error_handler (%p, %s, %d, %lx, %d, %d)", + conn, swap ? "TRUE" : "FALSE", offending_minor_opcode, + offending_sequence_num, error_class, severity); + + /* We don't need to do anything here; if the connection needs to be + * closed, libSM will do that itself. + */ +} -- cgit v0.9.1