/* Maximum number of milliseconds client will resend manage requests before giving up */
#define MANAGE_TIMEOUT 126000
+/* Address sort support structure */
+typedef struct
+{
+ gsize index;
+ GInetAddress *address;
+} AddrSortItem;
+
XDMCPServer *
xdmcp_server_new (void)
{
return g_hash_table_lookup (server->priv->sessions, GINT_TO_POINTER ((gint) id));
}
+static gchar *
+socket_address_to_string (GSocketAddress *address)
+{
+ gchar *inet_text, *text;
+
+ inet_text = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
+ text = g_strdup_printf ("%s:%d", inet_text, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)));
+ g_free (inet_text);
+
+ return text;
+}
+
static void
send_packet (GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
{
+ gchar *address_string;
guint8 data[1024];
gssize n_written;
- g_debug ("Send %s", xdmcp_packet_tostring (packet));
+ address_string = socket_address_to_string (address);
+ g_debug ("Send %s to %s", xdmcp_packet_tostring (packet), address_string);
+ g_free (address_string);
n_written = xdmcp_packet_encode (packet, data, 1024);
if (n_written < 0)
}
static void
-handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
+handle_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, gchar **authentication_names)
{
XDMCPPacket *response;
gchar **i;
gchar *authentication_name = NULL;
/* If no authentication requested and we are configured for none then allow */
- if (packet->Query.authentication_names[0] == NULL && server->priv->key == NULL)
+ if (authentication_names[0] == NULL && server->priv->key == NULL)
authentication_name = "";
- for (i = packet->Query.authentication_names; *i; i++)
+ for (i = authentication_names; *i; i++)
{
if (strcmp (*i, get_authentication_name (server)) == 0 && server->priv->key != NULL)
{
if (server->priv->key)
response->Unwilling.status = g_strdup_printf ("No matching authentication, server requires %s", get_authentication_name (server));
else
- response->Unwilling.status = g_strdup ("Server does not support authentication");
+ response->Unwilling.status = g_strdup ("No matching authentication");
}
send_packet (socket, address, response);
xdmcp_packet_free (response);
}
+static void
+handle_forward_query (XDMCPServer *server, GSocket *socket, GSocketAddress *address, XDMCPPacket *packet)
+{
+ GSocketFamily family;
+ GInetAddress *client_inet_address;
+ GSocketAddress *client_address;
+ gint i;
+ guint16 port = 0;
+
+ family = g_socket_get_family (socket);
+ switch (family)
+ {
+ case G_SOCKET_FAMILY_IPV4:
+ if (packet->ForwardQuery.client_address.length != 4)
+ {
+ g_warning ("Ignoring IPv4 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
+ return;
+ }
+ break;
+ case G_SOCKET_FAMILY_IPV6:
+ if (packet->ForwardQuery.client_address.length != 16)
+ {
+ g_warning ("Ignoring IPv6 XDMCP ForwardQuery with client address of length %d", packet->ForwardQuery.client_address.length);
+ return;
+ }
+ break;
+ default:
+ g_warning ("Unknown socket family %d", family);
+ return;
+ }
+
+ for (i = 0; i < packet->ForwardQuery.client_port.length; i++)
+ port = port << 8 | packet->ForwardQuery.client_port.data[i];
+
+ client_inet_address = g_inet_address_new_from_bytes (packet->ForwardQuery.client_address.data, family);
+ client_address = g_inet_socket_address_new (client_inet_address, port);
+ g_object_unref (client_inet_address);
+
+ handle_query (server, socket, client_address, packet->ForwardQuery.authentication_names);
+
+ g_object_unref (client_address);
+}
+
static guint8
atox (char c)
{
}
}
-static gssize
-find_address (GInetAddress **addresses, gsize length, GSocketFamily family)
+/* Sort function to order XDMCP addresses by which is best to connect to */
+static gint
+compare_addresses (gconstpointer a, gconstpointer b, gpointer user_data)
{
- int i;
-
- for (i = 0; i < length; i++)
+ const AddrSortItem *item_a = a;
+ const AddrSortItem *item_b = b;
+ GInetAddress *source_address = user_data;
+ GSocketFamily family_a;
+ GSocketFamily family_b;
+ gboolean is_link_local;
+
+ /* Prefer non link-local addresses */
+ is_link_local = g_inet_address_get_is_link_local (item_a->address);
+ if (is_link_local != g_inet_address_get_is_link_local (item_b->address))
+ return is_link_local ? 1 : -1;
+
+ /* Prefer the source address family */
+ family_a = g_inet_address_get_family (item_a->address);
+ family_b = g_inet_address_get_family (item_b->address);
+ if (family_a != family_b)
{
- GInetAddress *address = addresses[i];
- if (address && g_inet_address_get_family (address) == family)
- return i;
+ GSocketFamily family;
+
+ family = g_inet_address_get_family (source_address);
+ if (family_a == family)
+ return -1;
+ if (family_b == family)
+ return 1;
+ return family_a < family_b ? -1 : 1;
}
- return -1;
+ /* Check equality */
+ if (g_inet_address_equal (item_a->address, item_b->address))
+ return 0;
+
+ /* Prefer the source address */
+ if (g_inet_address_equal (source_address, item_a->address))
+ return -1;
+ if (g_inet_address_equal (source_address, item_b->address))
+ return 1;
+
+ /* Addresses are not equal, but preferences are: order is undefined */
+ return 0;
}
static XDMCPConnection *
choose_connection (XDMCPPacket *packet, GInetAddress *source_address)
{
- GInetAddress **addresses;
gsize addresses_length, i;
+ GArray *addresses;
gssize index = -1;
+ AddrSortItem addr;
addresses_length = packet->Request.n_connections;
- addresses = malloc (sizeof (GInetAddress *) * addresses_length);
+ if (addresses_length == 0)
+ return NULL;
+
+ addresses = g_array_sized_new (FALSE, FALSE, sizeof addr, addresses_length);
+ if (!addresses)
+ return NULL;
+
for (i = 0; i < addresses_length; i++)
- addresses[i] = connection_to_address (&packet->Request.connections[i]);
+ {
+ addr.address = connection_to_address (&packet->Request.connections[i]);
+ if (addr.address)
+ {
+ addr.index = i;
+ g_array_append_val (addresses, addr);
+ }
+ }
- /* Use the address the request came in on as this is the least likely to have firewall / routing issues */
- for (i = 0; i < addresses_length && index < 0; i++)
- if (g_inet_address_equal (source_address, addresses[i]))
- index = i;
+ /* Sort the addresses according to our preferences */
+ g_array_sort_with_data (addresses, compare_addresses, source_address);
- /* Otherwise try and find an address that matches the incoming type */
- if (index < 0)
- index = find_address (addresses, addresses_length, g_inet_address_get_family (source_address));
+ /* Use the best address */
+ if (addresses->len)
+ index = g_array_index (addresses, AddrSortItem, 0).index;
- /* Otherwise use the first available */
- if (index < 0 && addresses_length > 0)
- index = 0;
+ /* Free the local sort array and items */
+ for (i = 0; i < addresses->len; i++)
+ g_object_unref (g_array_index (addresses, AddrSortItem, i).address);
+ g_object_unref (addresses);
- for (i = 0; i < addresses_length; i++)
- g_object_unref (addresses[i]);
- g_free (addresses);
+ return index >= 0 ? &packet->Request.connections[index] : NULL;
+}
+
+static gboolean
+has_string (gchar **list, const gchar *text)
+{
+ gchar **i;
- return &packet->Request.connections[index];
+ for (i = list; *i; i++)
+ if (strcmp (*i, text) == 0)
+ return TRUE;
+
+ return FALSE;
}
static void
{
XDMCPPacket *response;
XDMCPSession *session;
- guint8 *authentication_data = NULL;
- gsize authentication_data_length = 0;
- gboolean match_authorization = FALSE;
- gchar *authorization_name;
- guint8 *authorization_data = NULL;
- gsize authorization_data_length = 0;
- guint8 *session_authorization_data = NULL;
- gsize session_authorization_data_length = 0;
- gchar **j;
+ gchar *authentication_name = NULL, *decline_status = NULL, *authorization_name, *display_number;
+ guint8 *authentication_data = NULL, *authorization_data = NULL, *session_authorization_data = NULL;
+ gsize authentication_data_length = 0, authorization_data_length = 0, session_authorization_data_length = 0;
XDMCPConnection *connection;
- gchar *display_number;
XdmAuthKeyRec rho;
- /* Choose an address to connect back on */
- connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
-
- /* Decline if haven't got an address we can connect on */
- if (!connection)
- {
- response = xdmcp_packet_alloc (XDMCP_Decline);
- response->Decline.status = g_strdup ("No valid address found");
- response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
- response->Decline.authentication_data.data = authentication_data;
- response->Decline.authentication_data.length = authentication_data_length;
- send_packet (socket, address, response);
- xdmcp_packet_free (response);
- return;
- }
-
- /* Must be using our authentication scheme */
- if (strcmp (packet->Request.authentication_name, get_authentication_name (server)) != 0)
+ /* Check authentication */
+ if (strcmp (packet->Request.authentication_name, "") == 0)
{
- response = xdmcp_packet_alloc (XDMCP_Decline);
- if (server->priv->key)
- response->Decline.status = g_strdup_printf ("Server only supports %s authentication", get_authentication_name (server));
+ if (!server->priv->key)
+ {
+ if (!has_string (packet->Request.authorization_names, "MIT-MAGIC-COOKIE-1"))
+ decline_status = g_strdup ("No matching authorization, server requires MIT-MAGIC-COOKIE-1");
+ }
else
- response->Decline.status = g_strdup ("Server does not support authentication");
- response->Decline.authentication_name = g_strdup ("");
- send_packet (socket, address, response);
- xdmcp_packet_free (response);
- return;
+ decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
}
-
- /* Perform requested authentication */
- if (server->priv->key)
+ else if (strcmp (packet->Request.authentication_name, "XDM-AUTHENTICATION-1") == 0 && server->priv->key)
{
- guint8 input[8], key[8];
-
- memset (input, 0, 8);
- memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length > 8 ? 8 : packet->Request.authentication_data.length);
+ if (packet->Request.authentication_data.length == 8)
+ {
+ guint8 input[8], key[8];
- /* Setup key */
- decode_key (server->priv->key, key);
+ memcpy (input, packet->Request.authentication_data.data, packet->Request.authentication_data.length);
- /* Decode message from server */
- authentication_data = g_malloc (sizeof (guint8) * 8);
- authentication_data_length = 8;
+ /* Setup key */
+ decode_key (server->priv->key, key);
- XdmcpUnwrap (input, key, rho.data, authentication_data_length);
- XdmcpIncrementKey (&rho);
- XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
+ /* Decode message from server */
+ authentication_name = g_strdup ("XDM-AUTHENTICATION-1");
+ authentication_data = g_malloc (sizeof (guint8) * 8);
+ authentication_data_length = 8;
+
+ XdmcpUnwrap (input, key, rho.data, authentication_data_length);
+ XdmcpIncrementKey (&rho);
+ XdmcpWrap (rho.data, key, authentication_data, authentication_data_length);
- authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
+ if (!has_string (packet->Request.authorization_names, "XDM-AUTHORIZATION-1"))
+ decline_status = g_strdup ("No matching authorization, server requires XDM-AUTHORIZATION-1");
+ }
+ else
+ decline_status = g_strdup ("Invalid XDM-AUTHENTICATION-1 data provided");
}
else
- authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
-
- /* Check if they support our authorization */
- for (j = packet->Request.authorization_names; *j; j++)
{
- if (strcmp (*j, authorization_name) == 0)
- {
- match_authorization = TRUE;
- break;
- }
+ if (strcmp (packet->Request.authentication_name, "") == 0)
+ decline_status = g_strdup_printf ("No matching authentication, server does not support unauthenticated connections");
+ else if (server->priv->key)
+ decline_status = g_strdup ("No matching authentication, server requires XDM-AUTHENTICATION-1");
+ else
+ decline_status = g_strdup ("No matching authentication, server only supports unauthenticated connections");
}
- /* Decline if don't support out authorization */
- if (!match_authorization)
+ /* Choose an address to connect back on */
+ connection = choose_connection (packet, g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
+ if (!connection && !decline_status)
+ decline_status = g_strdup ("No valid address found");
+
+ if (!authentication_name)
+ authentication_name = g_strdup ("");
+
+ /* Decline if request was not valid */
+ if (decline_status)
{
response = xdmcp_packet_alloc (XDMCP_Decline);
- response->Decline.status = g_strdup_printf ("Server requires %s authorization", authorization_name);
- g_free (authorization_name);
- response->Decline.authentication_name = g_strdup (packet->Request.authentication_name);
+ response->Decline.status = decline_status;
+ response->Decline.authentication_name = authentication_name;
response->Decline.authentication_data.data = authentication_data;
response->Decline.authentication_data.length = authentication_data_length;
send_packet (socket, address, response);
return;
}
- /* Perform requested authorization */
+ /* Generate authorization data */
if (server->priv->key)
{
gint i;
XdmcpWrap (session_key, key, authorization_data, authorization_data_length);
/* Authorization data is the number received from the client followed by the private session key */
+ authorization_name = g_strdup ("XDM-AUTHORIZATION-1");
session_authorization_data = g_malloc (16);
session_authorization_data_length = 16;
XdmcpDecrementKey (&rho);
auth = x_authority_new_cookie (XAUTH_FAMILY_WILD, NULL, 0, "");
authorization_data = x_authority_copy_authorization_data (auth);
authorization_data_length = x_authority_get_authorization_data_length (auth);
+ authorization_name = g_strdup ("MIT-MAGIC-COOKIE-1");
session_authorization_data = x_authority_copy_authorization_data (auth);
session_authorization_data_length = x_authority_get_authorization_data_length (auth);
response = xdmcp_packet_alloc (XDMCP_Accept);
response->Accept.session_id = xdmcp_session_get_id (session);
- response->Accept.authentication_name = g_strdup (packet->Request.authentication_name);
+ response->Accept.authentication_name = authentication_name;
response->Accept.authentication_data.data = authentication_data;
response->Accept.authentication_data.length = authentication_data_length;
response->Accept.authorization_name = authorization_name;
packet = xdmcp_packet_decode ((guint8 *)data, n_read);
if (packet)
{
- g_debug ("Got %s", xdmcp_packet_tostring (packet));
+ gchar *packet_string, *address_string;
+
+ packet_string = xdmcp_packet_tostring (packet);
+ address_string = socket_address_to_string (address);
+ g_debug ("Got %s from %s", packet_string, address_string);
+ g_free (packet_string);
+ g_free (address_string);
switch (packet->opcode)
{
case XDMCP_BroadcastQuery:
case XDMCP_Query:
case XDMCP_IndirectQuery:
- handle_query (server, socket, address, packet);
+ handle_query (server, socket, address, packet->Query.authentication_names);
+ break;
+ case XDMCP_ForwardQuery:
+ handle_forward_query (server, socket, address, packet);
break;
case XDMCP_Request:
handle_request (server, socket, address, packet);
static void
xdmcp_server_finalize (GObject *object)
{
- XDMCPServer *self;
-
- self = XDMCP_SERVER (object);
+ XDMCPServer *self = XDMCP_SERVER (object);
- if (self->priv->socket)
- g_object_unref (self->priv->socket);
- if (self->priv->socket6)
- g_object_unref (self->priv->socket6);
+ g_clear_object (&self->priv->socket);
+ g_clear_object (&self->priv->socket6);
g_free (self->priv->listen_address);
g_free (self->priv->hostname);
g_free (self->priv->status);