3483 lines
122 KiB
C
3483 lines
122 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 1998, 1999, 2000 Martin K. Petersen <mkp@mkp.net>
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#ifdef HAVE_SYS_SOCKIO_H
|
|
#include <sys/sockio.h>
|
|
#endif
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
#include <glib-object.h>
|
|
|
|
#ifdef ENABLE_X11_SUPPORT
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xmd.h>
|
|
#include <X11/Xdmcp.h>
|
|
#endif
|
|
|
|
#include "gdm-common.h"
|
|
#include "gdm-xdmcp-chooser-display.h"
|
|
#include "gdm-display-factory.h"
|
|
#include "gdm-launch-environment.h"
|
|
#include "gdm-xdmcp-display-factory.h"
|
|
#include "gdm-display-store.h"
|
|
#include "gdm-settings-direct.h"
|
|
#include "gdm-settings-keys.h"
|
|
|
|
/*
|
|
* On Sun, we need to define allow_severity and deny_severity to link
|
|
* against libwrap.
|
|
*/
|
|
#ifdef __sun
|
|
#include <syslog.h>
|
|
int allow_severity = LOG_INFO;
|
|
int deny_severity = LOG_WARNING;
|
|
#endif
|
|
|
|
#define DEFAULT_PORT 177
|
|
#define DEFAULT_USE_MULTICAST FALSE
|
|
#define DEFAULT_MULTICAST_ADDRESS "ff02::1"
|
|
#define DEFAULT_HONOR_INDIRECT TRUE
|
|
#define DEFAULT_MAX_DISPLAYS_PER_HOST 1
|
|
#define DEFAULT_MAX_DISPLAYS 16
|
|
#define DEFAULT_MAX_PENDING_DISPLAYS 4
|
|
#define DEFAULT_MAX_WAIT 30
|
|
#define DEFAULT_MAX_WAIT_INDIRECT 30
|
|
#define DEFAULT_WILLING_SCRIPT GDMCONFDIR "/Xwilling"
|
|
|
|
#define GDM_MAX_FORWARD_QUERIES 10
|
|
#define GDM_FORWARD_QUERY_TIMEOUT 30
|
|
#define MANAGED_FORWARD_INTERVAL 1500 /* 1.5 seconds */
|
|
|
|
/* some extra XDMCP opcodes that xdm will happily ignore since they'll be
|
|
* the wrong XDMCP version anyway */
|
|
#define GDM_XDMCP_PROTOCOL_VERSION 1001
|
|
enum {
|
|
GDM_XDMCP_FIRST_OPCODE = 1000, /*just a marker, not an opcode */
|
|
|
|
GDM_XDMCP_MANAGED_FORWARD = 1000,
|
|
/* manager (master) -> manager
|
|
* A packet with MANAGED_FORWARD is sent to the
|
|
* manager that sent the forward query from the manager to
|
|
* which forward query was sent. It indicates that the forward
|
|
* was fully processed and that the client now has either
|
|
* a managed session, or has been sent denial, refuse or failed.
|
|
* (if the denial gets lost then client gets dumped into the
|
|
* chooser again). This should be resent a few times
|
|
* until some (short) timeout or until GOT_MANAGED_FORWARD
|
|
* is sent. GDM sends at most 3 packates with 1.5 seconds
|
|
* between each.
|
|
*
|
|
* Argument is ARRAY8 with the address of the originating host */
|
|
GDM_XDMCP_GOT_MANAGED_FORWARD,
|
|
/* manager -> manager (master)
|
|
* A single packet with GOT_MANAGED_FORWARD is sent to indicate
|
|
* that we did receive the MANAGED_FORWARD packet. The argument
|
|
* must match the MANAGED_FORWARD one or it will just be ignored.
|
|
*
|
|
* Argument is ARRAY8 with the address of the originating host */
|
|
GDM_XDMCP_LAST_OPCODE /*just a marker, not an opcode */
|
|
};
|
|
|
|
/*
|
|
* We don't support XDM-AUTHENTICATION-1 and XDM-AUTHORIZATION-1.
|
|
*
|
|
* The latter would be quite useful to avoid sending unencrypted
|
|
* cookies over the wire. Unfortunately it isn't supported without
|
|
* XDM-AUTHENTICATION-1 which requires a key database with private
|
|
* keys from all X terminals on your LAN. Fun, fun, fun.
|
|
*
|
|
* Furthermore user passwords go over the wire in cleartext anyway,
|
|
* so protecting cookies is not that important.
|
|
*/
|
|
|
|
typedef struct _XdmAuth {
|
|
ARRAY8 authentication;
|
|
ARRAY8 authorization;
|
|
} XdmAuthRec, *XdmAuthPtr;
|
|
|
|
static XdmAuthRec serv_authlist = {
|
|
{ (CARD16) 0, (CARD8 *) 0 },
|
|
{ (CARD16) 0, (CARD8 *) 0 }
|
|
};
|
|
|
|
/* NOTE: Timeout and max are hardcoded */
|
|
typedef struct _ForwardQuery {
|
|
time_t acctime;
|
|
GdmAddress *dsp_address;
|
|
GdmAddress *from_address;
|
|
} ForwardQuery;
|
|
|
|
typedef struct _IndirectClient {
|
|
int id;
|
|
GdmAddress *dsp_address;
|
|
GdmAddress *chosen_address;
|
|
time_t acctime;
|
|
} IndirectClient;
|
|
|
|
typedef struct {
|
|
int times;
|
|
guint handler;
|
|
GdmAddress *manager;
|
|
GdmAddress *origin;
|
|
GdmXdmcpDisplayFactory *xdmcp_display_factory;
|
|
} ManagedForward;
|
|
|
|
struct _GdmXdmcpDisplayFactory
|
|
{
|
|
GdmDisplayFactory parent;
|
|
|
|
GSList *forward_queries;
|
|
GSList *managed_forwards;
|
|
GSList *indirect_clients;
|
|
|
|
int socket_fd;
|
|
gint32 session_serial;
|
|
guint socket_watch_id;
|
|
XdmcpBuffer buf;
|
|
|
|
guint num_sessions;
|
|
guint num_pending_sessions;
|
|
|
|
char *sysid;
|
|
char *hostname;
|
|
ARRAY8 servhost;
|
|
|
|
/* configuration */
|
|
guint port;
|
|
gboolean use_multicast;
|
|
char *multicast_address;
|
|
gboolean honor_indirect;
|
|
char *willing_script;
|
|
guint max_displays_per_host;
|
|
guint max_displays;
|
|
guint max_pending_displays;
|
|
guint max_wait;
|
|
guint max_wait_indirect;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_PORT,
|
|
PROP_USE_MULTICAST,
|
|
PROP_MULTICAST_ADDRESS,
|
|
PROP_HONOR_INDIRECT,
|
|
PROP_WILLING_SCRIPT,
|
|
PROP_MAX_DISPLAYS_PER_HOST,
|
|
PROP_MAX_DISPLAYS,
|
|
PROP_MAX_PENDING_DISPLAYS,
|
|
PROP_MAX_WAIT,
|
|
PROP_MAX_WAIT_INDIRECT,
|
|
};
|
|
|
|
static void gdm_xdmcp_display_factory_class_init (GdmXdmcpDisplayFactoryClass *klass);
|
|
static void gdm_xdmcp_display_factory_init (GdmXdmcpDisplayFactory *manager);
|
|
static void gdm_xdmcp_display_factory_finalize (GObject *object);
|
|
static void gdm_xdmcp_send_alive (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
CARD16 dspnum,
|
|
CARD32 sessid);
|
|
static gpointer xdmcp_display_factory_object = NULL;
|
|
|
|
G_DEFINE_TYPE (GdmXdmcpDisplayFactory, gdm_xdmcp_display_factory, GDM_TYPE_DISPLAY_FACTORY)
|
|
|
|
/* Theory of operation:
|
|
*
|
|
* Process idles waiting for UDP packets on port 177.
|
|
* Incoming packets are decoded and checked against tcp_wrapper.
|
|
*
|
|
* A typical session looks like this:
|
|
*
|
|
* Display sends Query/BroadcastQuery to Manager.
|
|
*
|
|
* Manager selects an appropriate authentication scheme from the
|
|
* display's list of supported ones and sends Willing/Unwilling.
|
|
*
|
|
* Assuming the display accepts the auth. scheme it sends back a
|
|
* Request.
|
|
*
|
|
* If the manager accepts to service the display (i.e. loadavg is low)
|
|
* it sends back an Accept containing a unique SessionID. The
|
|
* SessionID is stored in an accept queue by the Manager. Should the
|
|
* manager refuse to start a session a Decline is sent to the display.
|
|
*
|
|
* The display returns a Manage request containing the supplied
|
|
* SessionID. The manager will then start a session on the display. In
|
|
* case the SessionID is not on the accept queue the manager returns
|
|
* Refuse. If the manager fails to open the display for connections
|
|
* Failed is returned.
|
|
*
|
|
* During the session the display periodically sends KeepAlive packets
|
|
* to the manager. The manager responds with Alive.
|
|
*
|
|
* Similarly the manager xpings the display once in a while and shuts
|
|
* down the connection on failure.
|
|
*
|
|
*/
|
|
|
|
GQuark
|
|
gdm_xdmcp_display_factory_error_quark (void)
|
|
{
|
|
static GQuark ret = 0;
|
|
if (ret == 0) {
|
|
ret = g_quark_from_static_string ("gdm_xdmcp_display_factory_error");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gint32
|
|
get_next_session_serial (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
gint32 serial;
|
|
|
|
again:
|
|
if (factory->session_serial != G_MAXINT32) {
|
|
serial = factory->session_serial++;
|
|
} else {
|
|
serial = g_random_int ();
|
|
}
|
|
|
|
if (serial == 0) {
|
|
goto again;
|
|
}
|
|
|
|
return serial;
|
|
}
|
|
|
|
/* for debugging */
|
|
static const char *
|
|
ai_family_str (struct addrinfo *ai)
|
|
{
|
|
const char *str;
|
|
switch (ai->ai_family) {
|
|
case AF_INET:
|
|
str = "inet";
|
|
break;
|
|
case AF_INET6:
|
|
str = "inet6";
|
|
break;
|
|
case AF_UNIX:
|
|
str = "unix";
|
|
break;
|
|
case AF_UNSPEC:
|
|
str = "unspecified";
|
|
break;
|
|
default:
|
|
str = "unknown";
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/* for debugging */
|
|
static const char *
|
|
ai_type_str (struct addrinfo *ai)
|
|
{
|
|
const char *str;
|
|
switch (ai->ai_socktype) {
|
|
case SOCK_STREAM:
|
|
str = "stream";
|
|
break;
|
|
case SOCK_DGRAM:
|
|
str = "datagram";
|
|
break;
|
|
case SOCK_SEQPACKET:
|
|
str = "seqpacket";
|
|
break;
|
|
case SOCK_RAW:
|
|
str = "raw";
|
|
break;
|
|
default:
|
|
str = "unknown";
|
|
break;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/* for debugging */
|
|
static const char *
|
|
ai_protocol_str (struct addrinfo *ai)
|
|
{
|
|
const char *str;
|
|
switch (ai->ai_protocol) {
|
|
case 0:
|
|
str = "default";
|
|
break;
|
|
case IPPROTO_TCP:
|
|
str = "TCP";
|
|
break;
|
|
case IPPROTO_UDP:
|
|
str = "UDP";
|
|
break;
|
|
case IPPROTO_RAW:
|
|
str = "raw";
|
|
break;
|
|
default:
|
|
str = "unknown";
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/* for debugging */
|
|
static char *
|
|
ai_flags_str (struct addrinfo *ai)
|
|
{
|
|
GString *str;
|
|
|
|
str = g_string_new ("");
|
|
if (ai->ai_flags == 0) {
|
|
g_string_append (str, "none");
|
|
} else {
|
|
if (ai->ai_flags & AI_PASSIVE) {
|
|
g_string_append (str, "passive ");
|
|
}
|
|
if (ai->ai_flags & AI_CANONNAME) {
|
|
g_string_append (str, "canon ");
|
|
}
|
|
if (ai->ai_flags & AI_NUMERICHOST) {
|
|
g_string_append (str, "numhost ");
|
|
}
|
|
if (ai->ai_flags & AI_NUMERICSERV) {
|
|
g_string_append (str, "numserv ");
|
|
}
|
|
#ifdef AI_V4MAPPEP
|
|
if (ai->ai_flags & AI_V4MAPPED) {
|
|
g_string_append (str, "v4mapped ");
|
|
}
|
|
#endif
|
|
#ifdef AI_ALL
|
|
if (ai->ai_flags & AI_ALL) {
|
|
g_string_append (str, "all ");
|
|
}
|
|
#endif
|
|
}
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
/* for debugging */
|
|
static void
|
|
debug_addrinfo (struct addrinfo *ai)
|
|
{
|
|
char *str;
|
|
str = ai_flags_str (ai);
|
|
g_debug ("GdmXdmcpDisplayFactory: addrinfo family=%s type=%s proto=%s flags=%s",
|
|
ai_family_str (ai),
|
|
ai_type_str (ai),
|
|
ai_protocol_str (ai),
|
|
str);
|
|
g_free (str);
|
|
}
|
|
|
|
static int
|
|
create_socket (struct addrinfo *ai)
|
|
{
|
|
int sock;
|
|
|
|
sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
|
if (sock < 0) {
|
|
g_warning ("socket: %s", g_strerror (errno));
|
|
return sock;
|
|
}
|
|
|
|
#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY)
|
|
if (ai->ai_family == AF_INET6) {
|
|
int zero = 0;
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0)
|
|
g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
|
|
}
|
|
#endif
|
|
|
|
if (bind (sock, ai->ai_addr, ai->ai_addrlen) < 0) {
|
|
g_warning ("bind: %s", g_strerror (errno));
|
|
close (sock);
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
static int
|
|
do_bind (guint port,
|
|
int family,
|
|
struct sockaddr_storage * hostaddr)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *ai_list;
|
|
struct addrinfo *ai;
|
|
char strport[NI_MAXSERV];
|
|
int gaierr;
|
|
int sock;
|
|
|
|
sock = -1;
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_family = family;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
|
|
snprintf (strport, sizeof (strport), "%u", port);
|
|
|
|
ai_list = NULL;
|
|
if ((gaierr = getaddrinfo (NULL, strport, &hints, &ai_list)) != 0) {
|
|
g_error ("Unable to connect to socket: %s", gai_strerror (gaierr));
|
|
}
|
|
|
|
/* should only be one but.. */
|
|
for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
|
|
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
|
|
continue;
|
|
}
|
|
|
|
debug_addrinfo (ai);
|
|
|
|
if (sock < 0) {
|
|
char *host;
|
|
char *serv;
|
|
GdmAddress *addr;
|
|
|
|
addr = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
|
|
|
|
host = NULL;
|
|
serv = NULL;
|
|
gdm_address_get_numeric_info (addr, &host, &serv);
|
|
g_debug ("GdmXdmcpDisplayFactory: Attempting to bind to host %s port %s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
g_free (host);
|
|
g_free (serv);
|
|
gdm_address_free (addr);
|
|
|
|
sock = create_socket (ai);
|
|
if (sock >= 0) {
|
|
if (hostaddr != NULL) {
|
|
memcpy (hostaddr, ai->ai_addr, ai->ai_addrlen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
freeaddrinfo (ai_list);
|
|
|
|
return sock;
|
|
}
|
|
|
|
static void
|
|
setup_multicast (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
#ifdef ENABLE_IPV6
|
|
/* Checking and Setting Multicast options */
|
|
{
|
|
/*
|
|
* socktemp is a temporary socket for getting info about
|
|
* available interfaces
|
|
*/
|
|
int socktemp;
|
|
int i;
|
|
int num;
|
|
char *buf;
|
|
struct ipv6_mreq mreq;
|
|
|
|
/* For interfaces' list */
|
|
struct ifconf ifc;
|
|
struct ifreq *ifr;
|
|
|
|
socktemp = socket (AF_INET, SOCK_DGRAM, 0);
|
|
#ifdef SIOCGIFNUM
|
|
if (ioctl (socktemp, SIOCGIFNUM, &num) < 0) {
|
|
num = 64;
|
|
}
|
|
#else
|
|
num = 64;
|
|
#endif /* SIOCGIFNUM */
|
|
ifc.ifc_len = sizeof (struct ifreq) * num;
|
|
ifc.ifc_buf = buf = malloc (ifc.ifc_len);
|
|
|
|
if (ioctl (socktemp, SIOCGIFCONF, &ifc) >= 0) {
|
|
ifr = ifc.ifc_req;
|
|
num = ifc.ifc_len / sizeof (struct ifreq); /* No of interfaces */
|
|
|
|
/* Joining multicast group with all interfaces */
|
|
for (i = 0 ; i < num ; i++) {
|
|
struct ifreq ifreq;
|
|
int ifindex;
|
|
|
|
memset (&ifreq, 0, sizeof (ifreq));
|
|
strncpy (ifreq.ifr_name, ifr[i].ifr_name, sizeof (ifreq.ifr_name));
|
|
/* paranoia */
|
|
ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0';
|
|
|
|
if (ioctl (socktemp, SIOCGIFFLAGS, &ifreq) < 0) {
|
|
g_debug ("GdmXdmcpDisplayFactory: Could not get SIOCGIFFLAGS for %s",
|
|
ifr[i].ifr_name);
|
|
}
|
|
|
|
ifindex = if_nametoindex (ifr[i].ifr_name);
|
|
|
|
if ((!(ifreq.ifr_flags & IFF_UP) ||
|
|
(ifreq.ifr_flags & IFF_LOOPBACK)) ||
|
|
((ifindex == 0 ) && (errno == ENXIO))) {
|
|
/* Not a valid interface or loopback interface*/
|
|
continue;
|
|
}
|
|
|
|
mreq.ipv6mr_interface = ifindex;
|
|
inet_pton (AF_INET6,
|
|
factory->multicast_address,
|
|
&mreq.ipv6mr_multiaddr);
|
|
|
|
setsockopt (factory->socket_fd,
|
|
IPPROTO_IPV6,
|
|
IPV6_JOIN_GROUP,
|
|
&mreq,
|
|
sizeof (mreq));
|
|
}
|
|
}
|
|
g_free (buf);
|
|
close (socktemp);
|
|
}
|
|
#endif /* ENABLE_IPV6 */
|
|
}
|
|
|
|
static void
|
|
fd_set_close_on_exec (int fd)
|
|
{
|
|
int flags;
|
|
|
|
flags = fcntl (fd, F_GETFD, 0);
|
|
if (flags < 0) {
|
|
return;
|
|
}
|
|
|
|
flags |= FD_CLOEXEC;
|
|
|
|
fcntl (fd, F_SETFD, flags);
|
|
}
|
|
|
|
static gboolean
|
|
open_port (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
struct sockaddr_storage serv_sa = { 0 };
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Start up on host %s, port %d",
|
|
factory->hostname ? factory->hostname : "(null)",
|
|
factory->port);
|
|
|
|
/* Open socket for communications */
|
|
#ifdef ENABLE_IPV6
|
|
factory->socket_fd = do_bind (factory->port, AF_INET6, &serv_sa);
|
|
if (factory->socket_fd < 0)
|
|
#endif
|
|
factory->socket_fd = do_bind (factory->port, AF_INET, &serv_sa);
|
|
|
|
if G_UNLIKELY (factory->socket_fd < 0) {
|
|
g_warning (_("Could not create socket!"));
|
|
return FALSE;
|
|
}
|
|
|
|
fd_set_close_on_exec (factory->socket_fd);
|
|
|
|
if (factory->use_multicast) {
|
|
setup_multicast (factory);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef HAVE_TCPWRAPPERS
|
|
|
|
/*
|
|
* Avoids a warning, my tcpd.h file doesn't include this prototype, even
|
|
* though the library does include the function and the manpage mentions it
|
|
*/
|
|
extern int hosts_ctl (char *daemon,
|
|
char *client_name,
|
|
char *client_addr,
|
|
char *client_user);
|
|
#endif
|
|
|
|
static gboolean
|
|
gdm_xdmcp_host_allow (GdmAddress *address)
|
|
{
|
|
#ifdef HAVE_TCPWRAPPERS
|
|
char *client;
|
|
char *host;
|
|
gboolean ret;
|
|
|
|
host = NULL;
|
|
client = NULL;
|
|
|
|
/* Find client hostname */
|
|
gdm_address_get_hostname (address, &client);
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
ret = hosts_ctl ("gdm", client, host, "");
|
|
|
|
g_free (host);
|
|
g_free (client);
|
|
|
|
return ret;
|
|
#else /* HAVE_TCPWRAPPERS */
|
|
return (TRUE);
|
|
#endif /* HAVE_TCPWRAPPERS */
|
|
}
|
|
|
|
typedef struct {
|
|
GdmAddress *address;
|
|
int count;
|
|
} CountDisplayData;
|
|
|
|
static void
|
|
count_displays_from_host (const char *id,
|
|
GdmDisplay *display,
|
|
CountDisplayData *data)
|
|
{
|
|
GdmAddress *address;
|
|
|
|
if (GDM_IS_XDMCP_DISPLAY (display)) {
|
|
address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
|
|
|
|
if (gdm_address_equal (address, data->address)) {
|
|
data->count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
gdm_xdmcp_num_displays_from_host (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address)
|
|
{
|
|
CountDisplayData data;
|
|
GdmDisplayStore *store;
|
|
|
|
data.count = 0;
|
|
data.address = address;
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
gdm_display_store_foreach (store,
|
|
(GdmDisplayStoreFunc)count_displays_from_host,
|
|
&data);
|
|
|
|
return data.count;
|
|
}
|
|
|
|
typedef struct {
|
|
GdmAddress *address;
|
|
int display_num;
|
|
} LookupHostData;
|
|
|
|
static gboolean
|
|
lookup_by_host (const char *id,
|
|
GdmDisplay *display,
|
|
LookupHostData *data)
|
|
{
|
|
GdmAddress *this_address;
|
|
int disp_num;
|
|
|
|
if (! GDM_IS_XDMCP_DISPLAY (display)) {
|
|
return FALSE;
|
|
}
|
|
|
|
this_address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
|
|
gdm_display_get_x11_display_number (display, &disp_num, NULL);
|
|
|
|
if (gdm_address_equal (this_address, data->address)
|
|
&& disp_num == data->display_num) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GdmDisplay *
|
|
gdm_xdmcp_display_lookup_by_host (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int display_num)
|
|
{
|
|
GdmDisplay *display;
|
|
LookupHostData *data;
|
|
GdmDisplayStore *store;
|
|
|
|
data = g_new0 (LookupHostData, 1);
|
|
data->address = address;
|
|
data->display_num = display_num;
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
display = gdm_display_store_find (store,
|
|
(GdmDisplayStoreFunc)lookup_by_host,
|
|
data);
|
|
g_free (data);
|
|
|
|
return display;
|
|
}
|
|
|
|
static char *
|
|
get_willing_output (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
char *output;
|
|
char **argv;
|
|
FILE *fd;
|
|
char buf[256];
|
|
|
|
output = NULL;
|
|
buf[0] = '\0';
|
|
|
|
if (factory->willing_script == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
argv = NULL;
|
|
if (! g_shell_parse_argv (factory->willing_script, NULL, &argv, NULL)) {
|
|
goto out;
|
|
}
|
|
|
|
if (argv == NULL ||
|
|
argv[0] == NULL ||
|
|
g_access (argv[0], X_OK) != 0) {
|
|
goto out;
|
|
}
|
|
|
|
fd = popen (factory->willing_script, "r");
|
|
if (fd == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (fgets (buf, sizeof (buf), fd) == NULL) {
|
|
pclose (fd);
|
|
goto out;
|
|
}
|
|
|
|
pclose (fd);
|
|
|
|
output = g_strdup (buf);
|
|
|
|
out:
|
|
return output;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_willing (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address)
|
|
{
|
|
ARRAY8 status;
|
|
XdmcpHeader header;
|
|
static char *last_status = NULL;
|
|
static time_t last_willing = 0;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending WILLING to %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
if (last_willing == 0 || time (NULL) - 3 > last_willing) {
|
|
char *s;
|
|
|
|
g_free (last_status);
|
|
|
|
s = get_willing_output (factory);
|
|
if (s != NULL) {
|
|
last_status = s;
|
|
} else {
|
|
last_status = g_strdup (factory->sysid);
|
|
}
|
|
}
|
|
|
|
if (! gdm_address_is_local (address) &&
|
|
gdm_xdmcp_num_displays_from_host (factory, address) >= factory->max_displays_per_host) {
|
|
/*
|
|
* Don't translate, this goes over the wire to servers where we
|
|
* don't know the charset or language, so it must be ascii
|
|
*/
|
|
status.data = (CARD8 *) g_strdup_printf ("%s (Server is busy)",
|
|
last_status);
|
|
} else {
|
|
status.data = (CARD8 *) g_strdup (last_status);
|
|
}
|
|
|
|
status.length = strlen ((char *) status.data);
|
|
|
|
header.opcode = (CARD16) WILLING;
|
|
header.length = 6 + serv_authlist.authentication.length;
|
|
header.length += factory->servhost.length + status.length;
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
|
|
/* Hardcoded authentication */
|
|
XdmcpWriteARRAY8 (&factory->buf, &serv_authlist.authentication);
|
|
XdmcpWriteARRAY8 (&factory->buf, &factory->servhost);
|
|
XdmcpWriteARRAY8 (&factory->buf, &status);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
g_free (status.data);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_unwilling (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int type)
|
|
{
|
|
ARRAY8 status;
|
|
XdmcpHeader header;
|
|
static time_t last_time = 0;
|
|
char *host;
|
|
|
|
/* only send at most one packet per second,
|
|
no harm done if we don't send it at all */
|
|
if (last_time + 1 >= time (NULL)) {
|
|
return;
|
|
}
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending UNWILLING to %s",
|
|
host ? host : "(null)");
|
|
g_warning ("Denied XDMCP query from host %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
/*
|
|
* Don't translate, this goes over the wire to servers where we
|
|
* don't know the charset or language, so it must be ascii
|
|
*/
|
|
status.data = (CARD8 *) "Display not authorized to connect";
|
|
status.length = strlen ((char *) status.data);
|
|
|
|
header.opcode = (CARD16) UNWILLING;
|
|
header.length = 4 + factory->servhost.length + status.length;
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
|
|
XdmcpWriteARRAY8 (&factory->buf, &factory->servhost);
|
|
XdmcpWriteARRAY8 (&factory->buf, &status);
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
last_time = time (NULL);
|
|
}
|
|
|
|
#define SIN(__s) ((struct sockaddr_in *) __s)
|
|
#define SIN6(__s) ((struct sockaddr_in6 *) __s)
|
|
|
|
static void
|
|
set_port_for_request (GdmAddress *address,
|
|
ARRAY8 *port)
|
|
{
|
|
struct sockaddr_storage *ss;
|
|
|
|
ss = gdm_address_peek_sockaddr_storage (address);
|
|
|
|
/* we depend on this being 2 elsewhere as well */
|
|
port->length = 2;
|
|
|
|
switch (ss->ss_family) {
|
|
case AF_INET:
|
|
port->data = (CARD8 *)g_memdup2 (&(SIN (ss)->sin_port), port->length);
|
|
break;
|
|
case AF_INET6:
|
|
port->data = (CARD8 *)g_memdup2 (&(SIN6 (ss)->sin6_port), port->length);
|
|
break;
|
|
default:
|
|
port->data = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_address_for_request (GdmAddress *address,
|
|
ARRAY8 *addr)
|
|
{
|
|
struct sockaddr_storage *ss;
|
|
|
|
ss = gdm_address_peek_sockaddr_storage (address);
|
|
|
|
switch (ss->ss_family) {
|
|
case AF_INET:
|
|
addr->length = sizeof (struct in_addr);
|
|
addr->data = g_memdup2 (&SIN (ss)->sin_addr, addr->length);
|
|
break;
|
|
case AF_INET6:
|
|
addr->length = sizeof (struct in6_addr);
|
|
addr->data = g_memdup2 (&SIN6 (ss)->sin6_addr, addr->length);
|
|
break;
|
|
default:
|
|
addr->length = 0;
|
|
addr->data = NULL;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_forward_query (GdmXdmcpDisplayFactory *factory,
|
|
IndirectClient *ic,
|
|
GdmAddress *address,
|
|
GdmAddress *display_address,
|
|
ARRAYofARRAY8Ptr authlist)
|
|
{
|
|
XdmcpHeader header;
|
|
int i;
|
|
ARRAY8 addr;
|
|
ARRAY8 port;
|
|
char *host;
|
|
char *serv;
|
|
|
|
g_assert (ic != NULL);
|
|
g_assert (ic->chosen_address != NULL);
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (ic->chosen_address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending forward query to %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
host = NULL;
|
|
serv = NULL;
|
|
gdm_address_get_numeric_info (display_address, &host, &serv);
|
|
g_debug ("GdmXdmcpDisplayFactory: Query contains %s:%s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
g_free (host);
|
|
g_free (serv);
|
|
|
|
set_port_for_request (address, &port);
|
|
set_address_for_request (display_address, &addr);
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) FORWARD_QUERY;
|
|
header.length = 0;
|
|
header.length += 2 + addr.length;
|
|
header.length += 2 + port.length;
|
|
header.length += 1;
|
|
for (i = 0; i < authlist->length; i++) {
|
|
header.length += 2 + authlist->data[i].length;
|
|
}
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteARRAY8 (&factory->buf, &addr);
|
|
XdmcpWriteARRAY8 (&factory->buf, &port);
|
|
XdmcpWriteARRAYofARRAY8 (&factory->buf, authlist);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (ic->chosen_address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (ic->chosen_address)));
|
|
|
|
g_free (port.data);
|
|
g_free (addr.data);
|
|
}
|
|
|
|
static void
|
|
handle_any_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
ARRAYofARRAY8Ptr authentication_names,
|
|
int type)
|
|
{
|
|
gdm_xdmcp_send_willing (factory, address);
|
|
}
|
|
|
|
static void
|
|
handle_direct_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len,
|
|
int type)
|
|
{
|
|
ARRAYofARRAY8 clnt_authlist;
|
|
int expected_len;
|
|
int i;
|
|
int res;
|
|
|
|
res = XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist);
|
|
if G_UNLIKELY (! res) {
|
|
g_warning ("Could not extract authlist from packet");
|
|
return;
|
|
}
|
|
|
|
expected_len = 1;
|
|
|
|
for (i = 0 ; i < clnt_authlist.length ; i++) {
|
|
expected_len += 2 + clnt_authlist.data[i].length;
|
|
}
|
|
|
|
if (len == expected_len) {
|
|
handle_any_query (factory, address, &clnt_authlist, type);
|
|
} else {
|
|
g_warning ("Error in checksum");
|
|
}
|
|
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_broadcast_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
if (gdm_xdmcp_host_allow (address)) {
|
|
handle_direct_query (factory, address, len, BROADCAST_QUERY);
|
|
} else {
|
|
/* just ignore it */
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
if (gdm_xdmcp_host_allow (address)) {
|
|
handle_direct_query (factory, address, len, QUERY);
|
|
} else {
|
|
gdm_xdmcp_send_unwilling (factory, address, QUERY);
|
|
}
|
|
}
|
|
|
|
static IndirectClient *
|
|
indirect_client_create (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *dsp_address)
|
|
{
|
|
IndirectClient *ic;
|
|
|
|
ic = g_new0 (IndirectClient, 1);
|
|
ic->dsp_address = gdm_address_copy (dsp_address);
|
|
|
|
factory->indirect_clients = g_slist_prepend (factory->indirect_clients, ic);
|
|
|
|
return ic;
|
|
}
|
|
|
|
static void
|
|
indirect_client_destroy (GdmXdmcpDisplayFactory *factory,
|
|
IndirectClient *ic)
|
|
{
|
|
if (ic == NULL) {
|
|
return;
|
|
}
|
|
|
|
factory->indirect_clients = g_slist_remove (factory->indirect_clients, ic);
|
|
|
|
ic->acctime = 0;
|
|
|
|
{
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (ic->dsp_address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Disposing IndirectClient for %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
}
|
|
|
|
g_free (ic->dsp_address);
|
|
ic->dsp_address = NULL;
|
|
g_free (ic->chosen_address);
|
|
ic->chosen_address = NULL;
|
|
|
|
g_free (ic);
|
|
}
|
|
|
|
static IndirectClient *
|
|
indirect_client_lookup_by_chosen (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *chosen_address,
|
|
GdmAddress *origin_address)
|
|
{
|
|
GSList *li;
|
|
char *host;
|
|
IndirectClient *ret;
|
|
|
|
g_assert (chosen_address != NULL);
|
|
g_assert (origin_address != NULL);
|
|
|
|
ret = NULL;
|
|
|
|
for (li = factory->indirect_clients; li != NULL; li = li->next) {
|
|
IndirectClient *ic = li->data;
|
|
|
|
if (ic != NULL
|
|
&& ic->chosen_address != NULL
|
|
&& gdm_address_equal (ic->chosen_address, chosen_address)) {
|
|
if (gdm_address_equal (ic->dsp_address, origin_address)) {
|
|
ret = ic;
|
|
goto out;
|
|
} else if (gdm_address_is_loopback (ic->dsp_address)
|
|
&& gdm_address_is_local (origin_address)) {
|
|
ret = ic;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
gdm_address_get_numeric_info (chosen_address, &host, NULL);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Chosen %s host not found",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/* lookup by origin */
|
|
static IndirectClient *
|
|
indirect_client_lookup (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address)
|
|
{
|
|
GSList *li;
|
|
GSList *qlist;
|
|
IndirectClient *ret;
|
|
time_t curtime;
|
|
|
|
g_assert (address != NULL);
|
|
|
|
curtime = time (NULL);
|
|
ret = NULL;
|
|
|
|
qlist = g_slist_copy (factory->indirect_clients);
|
|
|
|
for (li = qlist; li != NULL; li = li->next) {
|
|
IndirectClient *ic;
|
|
char *host;
|
|
char *serv;
|
|
|
|
ic = (IndirectClient *) li->data;
|
|
|
|
if (ic == NULL) {
|
|
continue;
|
|
}
|
|
|
|
host = NULL;
|
|
serv = NULL;
|
|
gdm_address_get_numeric_info (ic->dsp_address, &host, &serv);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: comparing %s:%s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
if (gdm_address_equal (ic->dsp_address, address)) {
|
|
ret = ic;
|
|
g_free (host);
|
|
g_free (serv);
|
|
break;
|
|
}
|
|
|
|
if (ic->acctime > 0 && curtime > ic->acctime + factory->max_wait_indirect) {
|
|
g_debug ("GdmXdmcpDisplayFactory: Disposing stale forward query from %s:%s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
|
|
indirect_client_destroy (factory, ic);
|
|
}
|
|
|
|
g_free (host);
|
|
g_free (serv);
|
|
}
|
|
|
|
g_slist_free (qlist);
|
|
|
|
if (ret == NULL) {
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Host %s not found",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_indirect_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
ARRAYofARRAY8 clnt_authlist;
|
|
int expected_len;
|
|
int i;
|
|
int res;
|
|
IndirectClient *ic;
|
|
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
/* ignore the request */
|
|
return;
|
|
}
|
|
|
|
if (! factory->honor_indirect) {
|
|
/* ignore it */
|
|
return;
|
|
}
|
|
|
|
if (factory->num_sessions > factory->max_displays ||
|
|
(!gdm_address_is_local (address) &&
|
|
gdm_xdmcp_num_displays_from_host (factory, address) > factory->max_displays_per_host)) {
|
|
g_debug ("GdmXdmcpDisplayFactory: reached maximum number of clients - ignoring indirect query");
|
|
return;
|
|
}
|
|
|
|
res = XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist);
|
|
if G_UNLIKELY (! res) {
|
|
g_warning ("Could not extract authlist from packet");
|
|
return;
|
|
}
|
|
|
|
expected_len = 1;
|
|
|
|
for (i = 0 ; i < clnt_authlist.length ; i++) {
|
|
expected_len += 2 + clnt_authlist.data[i].length;
|
|
}
|
|
|
|
/* Try to look up the display in
|
|
* the pending list. If found send a FORWARD_QUERY to the
|
|
* chosen manager. Otherwise alloc a new indirect display. */
|
|
|
|
if (len != expected_len) {
|
|
g_warning ("Error in checksum");
|
|
goto out;
|
|
}
|
|
|
|
|
|
ic = indirect_client_lookup (factory, address);
|
|
|
|
if (ic != NULL && ic->chosen_address != NULL) {
|
|
/* if user chose us, then just send willing */
|
|
if (gdm_address_is_local (ic->chosen_address)) {
|
|
g_debug ("GdmXdmcpDisplayFactory: the chosen address is local - dropping indirect");
|
|
|
|
/* get rid of indirect, so that we don't get
|
|
* the chooser */
|
|
indirect_client_destroy (factory, ic);
|
|
gdm_xdmcp_send_willing (factory, address);
|
|
} else if (gdm_address_is_loopback (address)) {
|
|
/* woohoo! fun, I have no clue how to get
|
|
* the correct ip, SO I just send forward
|
|
* queries with all the different IPs */
|
|
const GList *list = gdm_address_peek_local_list ();
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: the chosen address is a loopback");
|
|
|
|
while (list != NULL) {
|
|
GdmAddress *saddr = list->data;
|
|
|
|
if (! gdm_address_is_loopback (saddr)) {
|
|
/* forward query to * chosen host */
|
|
gdm_xdmcp_send_forward_query (factory,
|
|
ic,
|
|
address,
|
|
saddr,
|
|
&clnt_authlist);
|
|
}
|
|
|
|
list = list->next;
|
|
}
|
|
} else {
|
|
/* or send forward query to chosen host */
|
|
gdm_xdmcp_send_forward_query (factory,
|
|
ic,
|
|
address,
|
|
address,
|
|
&clnt_authlist);
|
|
}
|
|
} else if (ic == NULL) {
|
|
ic = indirect_client_create (factory, address);
|
|
if (ic != NULL) {
|
|
gdm_xdmcp_send_willing (factory, address);
|
|
}
|
|
} else {
|
|
gdm_xdmcp_send_willing (factory, address);
|
|
}
|
|
|
|
out:
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
|
|
}
|
|
|
|
static void
|
|
forward_query_destroy (GdmXdmcpDisplayFactory *factory,
|
|
ForwardQuery *q)
|
|
{
|
|
if (q == NULL) {
|
|
return;
|
|
}
|
|
|
|
factory->forward_queries = g_slist_remove (factory->forward_queries, q);
|
|
|
|
q->acctime = 0;
|
|
|
|
{
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (q->dsp_address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Disposing %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
}
|
|
|
|
g_free (q->dsp_address);
|
|
q->dsp_address = NULL;
|
|
g_free (q->from_address);
|
|
q->from_address = NULL;
|
|
|
|
g_free (q);
|
|
}
|
|
|
|
static gboolean
|
|
remove_oldest_forward (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
GSList *li;
|
|
ForwardQuery *oldest = NULL;
|
|
|
|
for (li = factory->forward_queries; li != NULL; li = li->next) {
|
|
ForwardQuery *query = li->data;
|
|
|
|
if (oldest == NULL || query->acctime < oldest->acctime) {
|
|
oldest = query;
|
|
}
|
|
}
|
|
|
|
if (oldest != NULL) {
|
|
forward_query_destroy (factory, oldest);
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static ForwardQuery *
|
|
forward_query_create (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *mgr_address,
|
|
GdmAddress *dsp_address)
|
|
{
|
|
ForwardQuery *q;
|
|
int count;
|
|
|
|
count = g_slist_length (factory->forward_queries);
|
|
|
|
while (count > GDM_MAX_FORWARD_QUERIES && remove_oldest_forward (factory)) {
|
|
count--;
|
|
}
|
|
|
|
q = g_new0 (ForwardQuery, 1);
|
|
q->dsp_address = gdm_address_copy (dsp_address);
|
|
q->from_address = gdm_address_copy (mgr_address);
|
|
|
|
factory->forward_queries = g_slist_prepend (factory->forward_queries, q);
|
|
|
|
return q;
|
|
}
|
|
|
|
static ForwardQuery *
|
|
forward_query_lookup (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address)
|
|
{
|
|
GSList *li;
|
|
GSList *qlist;
|
|
ForwardQuery *ret;
|
|
time_t curtime;
|
|
|
|
curtime = time (NULL);
|
|
ret = NULL;
|
|
|
|
qlist = g_slist_copy (factory->forward_queries);
|
|
|
|
for (li = qlist; li != NULL; li = li->next) {
|
|
ForwardQuery *q;
|
|
char *host;
|
|
char *serv;
|
|
|
|
q = (ForwardQuery *) li->data;
|
|
|
|
if (q == NULL) {
|
|
continue;
|
|
}
|
|
|
|
host = NULL;
|
|
serv = NULL;
|
|
gdm_address_get_numeric_info (q->dsp_address, &host, &serv);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: comparing %s:%s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
if (gdm_address_equal (q->dsp_address, address)) {
|
|
ret = q;
|
|
g_free (host);
|
|
g_free (serv);
|
|
break;
|
|
}
|
|
|
|
if (q->acctime > 0 && curtime > q->acctime + GDM_FORWARD_QUERY_TIMEOUT) {
|
|
g_debug ("GdmXdmcpDisplayFactory: Disposing stale forward query from %s:%s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
|
|
forward_query_destroy (factory, q);
|
|
}
|
|
|
|
g_free (host);
|
|
g_free (serv);
|
|
}
|
|
|
|
g_slist_free (qlist);
|
|
|
|
if (ret == NULL) {
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Host %s not found",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
create_address_from_request (ARRAY8 *req_addr,
|
|
ARRAY8 *req_port,
|
|
int family,
|
|
GdmAddress **address)
|
|
{
|
|
uint16_t port;
|
|
char host_buf [NI_MAXHOST];
|
|
char serv_buf [NI_MAXSERV];
|
|
char *serv;
|
|
const char *host;
|
|
struct addrinfo hints;
|
|
struct addrinfo *ai_list;
|
|
struct addrinfo *ai;
|
|
int gaierr;
|
|
gboolean found;
|
|
|
|
if (address != NULL) {
|
|
*address = NULL;
|
|
}
|
|
|
|
if (req_addr == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
serv = NULL;
|
|
if (req_port != NULL) {
|
|
/* port must always be length 2 */
|
|
if (req_port->length != 2) {
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy (&port, req_port->data, 2);
|
|
snprintf (serv_buf, sizeof (serv_buf), "%d", ntohs (port));
|
|
serv = serv_buf;
|
|
} else {
|
|
/* assume XDM_UDP_PORT */
|
|
snprintf (serv_buf, sizeof (serv_buf), "%d", XDM_UDP_PORT);
|
|
serv = serv_buf;
|
|
}
|
|
|
|
host = NULL;
|
|
if (req_addr->length == 4) {
|
|
host = inet_ntop (AF_INET,
|
|
(const void *)req_addr->data,
|
|
host_buf,
|
|
sizeof (host_buf));
|
|
} else if (req_addr->length == 16) {
|
|
host = inet_ntop (AF_INET6,
|
|
(const void *)req_addr->data,
|
|
host_buf,
|
|
sizeof (host_buf));
|
|
}
|
|
|
|
if (host == NULL) {
|
|
g_warning ("Bad address");
|
|
return FALSE;
|
|
}
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_family = family;
|
|
/* this should convert IPv4 address to IPv6 if needed */
|
|
#ifdef AI_V4MAPPED
|
|
hints.ai_flags = AI_V4MAPPED;
|
|
#endif
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
if ((gaierr = getaddrinfo (host, serv, &hints, &ai_list)) != 0) {
|
|
g_warning ("Unable to get address: %s", gai_strerror (gaierr));
|
|
return FALSE;
|
|
}
|
|
|
|
/* just take the first one */
|
|
ai = ai_list;
|
|
|
|
found = FALSE;
|
|
if (ai != NULL) {
|
|
found = TRUE;
|
|
if (address != NULL) {
|
|
*address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
|
|
}
|
|
}
|
|
|
|
freeaddrinfo (ai_list);
|
|
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_whack_queued_managed_forwards (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
GdmAddress *origin)
|
|
{
|
|
GSList *li;
|
|
|
|
for (li = factory->managed_forwards; li != NULL; li = li->next) {
|
|
ManagedForward *mf = li->data;
|
|
|
|
if (gdm_address_equal (mf->manager, address) &&
|
|
gdm_address_equal (mf->origin, origin)) {
|
|
factory->managed_forwards = g_slist_remove_link (factory->managed_forwards, li);
|
|
g_slist_free_1 (li);
|
|
g_source_remove (mf->handler);
|
|
/* mf freed by glib */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_forward_query (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
ARRAY8 clnt_addr;
|
|
ARRAY8 clnt_port;
|
|
ARRAYofARRAY8 clnt_authlist;
|
|
int i;
|
|
int explen;
|
|
GdmAddress *disp_address;
|
|
char *host;
|
|
char *serv;
|
|
|
|
disp_address = NULL;
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
char *host2;
|
|
|
|
host2 = NULL;
|
|
gdm_address_get_numeric_info (address, &host2, NULL);
|
|
|
|
g_warning ("%s: Got FORWARD_QUERY from banned host %s",
|
|
"gdm_xdmcp_handle_forward query",
|
|
host2 ? host2 : "(null)");
|
|
g_free (host2);
|
|
return;
|
|
}
|
|
|
|
/* Read display address */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_addr)) {
|
|
g_warning ("%s: Could not read display address",
|
|
"gdm_xdmcp_handle_forward_query");
|
|
return;
|
|
}
|
|
|
|
/* Read display port */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_port)) {
|
|
XdmcpDisposeARRAY8 (&clnt_addr);
|
|
g_warning ("%s: Could not read display port number",
|
|
"gdm_xdmcp_handle_forward_query");
|
|
return;
|
|
}
|
|
|
|
/* Extract array of authentication names from Xdmcp packet */
|
|
if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist)) {
|
|
XdmcpDisposeARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAY8 (&clnt_port);
|
|
g_warning ("%s: Could not extract authlist from packet",
|
|
"gdm_xdmcp_handle_forward_query");
|
|
return;
|
|
}
|
|
|
|
/* Crude checksumming */
|
|
explen = 1;
|
|
explen += 2 + clnt_addr.length;
|
|
explen += 2 + clnt_port.length;
|
|
|
|
for (i = 0 ; i < clnt_authlist.length ; i++) {
|
|
char *s = g_strndup ((char *) clnt_authlist.data[i].data,
|
|
clnt_authlist.length);
|
|
g_debug ("GdmXdmcpDisplayFactory: authlist: %s", s);
|
|
g_free (s);
|
|
|
|
explen += 2 + clnt_authlist.data[i].length;
|
|
}
|
|
|
|
if G_UNLIKELY (len != explen) {
|
|
g_warning ("%s: Error in checksum",
|
|
"gdm_xdmcp_handle_forward_query");
|
|
goto out;
|
|
}
|
|
|
|
if (! create_address_from_request (&clnt_addr, &clnt_port, gdm_address_get_family_type (address), &disp_address)) {
|
|
g_warning ("Unable to parse address for request");
|
|
goto out;
|
|
}
|
|
|
|
gdm_xdmcp_whack_queued_managed_forwards (factory,
|
|
address,
|
|
disp_address);
|
|
|
|
host = NULL;
|
|
serv = NULL;
|
|
gdm_address_get_numeric_info (disp_address, &host, &serv);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got FORWARD_QUERY for display: %s, port %s",
|
|
host ? host : "(null)", serv ? serv : "(null)");
|
|
g_free (host);
|
|
g_free (serv);
|
|
|
|
/* Check with tcp_wrappers if display is allowed to access */
|
|
if (gdm_xdmcp_host_allow (disp_address)) {
|
|
ForwardQuery *q;
|
|
|
|
q = forward_query_lookup (factory, disp_address);
|
|
if (q != NULL) {
|
|
forward_query_destroy (factory, q);
|
|
}
|
|
|
|
forward_query_create (factory, address, disp_address);
|
|
|
|
gdm_xdmcp_send_willing (factory, disp_address);
|
|
}
|
|
|
|
out:
|
|
|
|
gdm_address_free (disp_address);
|
|
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
|
|
XdmcpDisposeARRAY8 (&clnt_port);
|
|
XdmcpDisposeARRAY8 (&clnt_addr);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_really_send_managed_forward (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
GdmAddress *origin)
|
|
{
|
|
ARRAY8 addr;
|
|
XdmcpHeader header;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending MANAGED_FORWARD to %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
set_address_for_request (origin, &addr);
|
|
|
|
header.opcode = (CARD16) GDM_XDMCP_MANAGED_FORWARD;
|
|
header.length = 4 + addr.length;
|
|
header.version = GDM_XDMCP_PROTOCOL_VERSION;
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
|
|
XdmcpWriteARRAY8 (&factory->buf, &addr);
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
g_free (addr.data);
|
|
}
|
|
|
|
static gboolean
|
|
managed_forward_handler (ManagedForward *mf)
|
|
{
|
|
if (mf->xdmcp_display_factory->socket_fd > 0) {
|
|
gdm_xdmcp_really_send_managed_forward (mf->xdmcp_display_factory,
|
|
mf->manager,
|
|
mf->origin);
|
|
}
|
|
|
|
mf->times++;
|
|
if (mf->xdmcp_display_factory->socket_fd <= 0 || mf->times >= 2) {
|
|
mf->xdmcp_display_factory->managed_forwards = g_slist_remove (mf->xdmcp_display_factory->managed_forwards, mf);
|
|
mf->handler = 0;
|
|
/* mf freed by glib */
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
managed_forward_free (ManagedForward *mf)
|
|
{
|
|
gdm_address_free (mf->origin);
|
|
gdm_address_free (mf->manager);
|
|
g_free (mf);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_managed_forward (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
GdmAddress *origin)
|
|
{
|
|
ManagedForward *mf;
|
|
|
|
gdm_xdmcp_really_send_managed_forward (factory, address, origin);
|
|
|
|
mf = g_new0 (ManagedForward, 1);
|
|
mf->times = 0;
|
|
mf->xdmcp_display_factory = factory;
|
|
|
|
mf->manager = gdm_address_copy (address);
|
|
mf->origin = gdm_address_copy (origin);
|
|
|
|
mf->handler = g_timeout_add_full (G_PRIORITY_DEFAULT,
|
|
MANAGED_FORWARD_INTERVAL,
|
|
(GSourceFunc)managed_forward_handler,
|
|
mf,
|
|
(GDestroyNotify)managed_forward_free);
|
|
factory->managed_forwards = g_slist_prepend (factory->managed_forwards, mf);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_got_managed_forward (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
GdmAddress *origin)
|
|
{
|
|
ARRAY8 addr;
|
|
XdmcpHeader header;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending GOT_MANAGED_FORWARD to %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
set_address_for_request (origin, &addr);
|
|
|
|
header.opcode = (CARD16) GDM_XDMCP_GOT_MANAGED_FORWARD;
|
|
header.length = 4 + addr.length;
|
|
header.version = GDM_XDMCP_PROTOCOL_VERSION;
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
|
|
XdmcpWriteARRAY8 (&factory->buf, &addr);
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
}
|
|
|
|
static void
|
|
count_sessions (const char *id,
|
|
GdmDisplay *display,
|
|
GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
if (GDM_IS_XDMCP_DISPLAY (display)) {
|
|
int status;
|
|
|
|
status = gdm_display_get_status (display);
|
|
|
|
if (status == GDM_DISPLAY_MANAGED) {
|
|
factory->num_sessions++;
|
|
} else if (status == GDM_DISPLAY_UNMANAGED) {
|
|
factory->num_pending_sessions++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_recount_sessions (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
GdmDisplayStore *store;
|
|
|
|
factory->num_sessions = 0;
|
|
factory->num_pending_sessions = 0;
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
gdm_display_store_foreach (store,
|
|
(GdmDisplayStoreFunc)count_sessions,
|
|
factory);
|
|
}
|
|
|
|
static gboolean
|
|
purge_displays (const char *id,
|
|
GdmDisplay *display,
|
|
GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
if (GDM_IS_XDMCP_DISPLAY (display)) {
|
|
int status;
|
|
time_t currtime;
|
|
time_t acctime;
|
|
|
|
currtime = time (NULL);
|
|
status = gdm_display_get_status (display);
|
|
acctime = gdm_display_get_creation_time (display);
|
|
|
|
if (status == GDM_DISPLAY_UNMANAGED &&
|
|
currtime > acctime + factory->max_wait) {
|
|
/* return TRUE to remove display */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_displays_purge (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
GdmDisplayStore *store;
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
|
|
gdm_display_store_foreach_remove (store,
|
|
(GdmDisplayStoreFunc)purge_displays,
|
|
factory);
|
|
|
|
gdm_xdmcp_recount_sessions (factory);
|
|
}
|
|
|
|
typedef struct {
|
|
const char *hostname;
|
|
int display_num;
|
|
} RemoveHostData;
|
|
|
|
static gboolean
|
|
remove_host (const char *id,
|
|
GdmDisplay *display,
|
|
RemoveHostData *data)
|
|
{
|
|
char *hostname;
|
|
int disp_num;
|
|
|
|
if (! GDM_IS_XDMCP_DISPLAY (display)) {
|
|
return FALSE;
|
|
}
|
|
|
|
gdm_display_get_remote_hostname (display, &hostname, NULL);
|
|
gdm_display_get_x11_display_number (display, &disp_num, NULL);
|
|
|
|
if (disp_num == data->display_num &&
|
|
hostname != NULL &&
|
|
data->hostname != NULL &&
|
|
strcmp (hostname, data->hostname) == 0) {
|
|
/* return TRUE to remove */
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
display_dispose_check (GdmXdmcpDisplayFactory *factory,
|
|
const char *hostname,
|
|
int display_num)
|
|
{
|
|
RemoveHostData *data;
|
|
GdmDisplayStore *store;
|
|
|
|
if (hostname == NULL) {
|
|
return;
|
|
}
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: display_dispose_check (%s:%d)",
|
|
hostname ? hostname : "(null)", display_num);
|
|
|
|
data = g_new0 (RemoveHostData, 1);
|
|
data->hostname = hostname;
|
|
data->display_num = display_num;
|
|
gdm_display_store_foreach_remove (store,
|
|
(GdmDisplayStoreFunc)remove_host,
|
|
data);
|
|
g_free (data);
|
|
|
|
gdm_xdmcp_recount_sessions (factory);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_decline (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
const char *reason)
|
|
{
|
|
XdmcpHeader header;
|
|
ARRAY8 authentype;
|
|
ARRAY8 authendata;
|
|
ARRAY8 status;
|
|
ForwardQuery *fq;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending DECLINE to %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
|
|
authentype.data = (CARD8 *) 0;
|
|
authentype.length = (CARD16) 0;
|
|
|
|
authendata.data = (CARD8 *) 0;
|
|
authendata.length = (CARD16) 0;
|
|
|
|
status.data = (CARD8 *) reason;
|
|
status.length = strlen ((char *) status.data);
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) DECLINE;
|
|
header.length = 2 + status.length;
|
|
header.length += 2 + authentype.length;
|
|
header.length += 2 + authendata.length;
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteARRAY8 (&factory->buf, &status);
|
|
XdmcpWriteARRAY8 (&factory->buf, &authentype);
|
|
XdmcpWriteARRAY8 (&factory->buf, &authendata);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
/* Send MANAGED_FORWARD to indicate that the connection
|
|
* reached some sort of resolution */
|
|
fq = forward_query_lookup (factory, address);
|
|
if (fq != NULL) {
|
|
gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
|
|
forward_query_destroy (factory, fq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_hostname_selected (GdmXdmcpChooserDisplay *display,
|
|
const char *hostname,
|
|
GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
struct addrinfo hints;
|
|
struct addrinfo *ai_list;
|
|
struct addrinfo *ai;
|
|
int gaierr;
|
|
GdmAddress *address;
|
|
IndirectClient *ic;
|
|
gchar *xdmcp_port;
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: hostname selected: %s",
|
|
hostname ? hostname : "(null)");
|
|
|
|
address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
|
|
|
|
g_assert (address != NULL);
|
|
|
|
ic = indirect_client_lookup (factory, address);
|
|
|
|
if (ic->chosen_address != NULL) {
|
|
gdm_address_free (ic->chosen_address);
|
|
ic->chosen_address = NULL;
|
|
}
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_family = gdm_address_get_family_type (address);
|
|
/* this should convert IPv4 address to IPv6 if needed */
|
|
#ifdef AI_V4MAPPED
|
|
hints.ai_flags = AI_V4MAPPED;
|
|
#endif
|
|
|
|
xdmcp_port = g_strdup_printf ("%d", XDM_UDP_PORT);
|
|
if ((gaierr = getaddrinfo (hostname, xdmcp_port, &hints, &ai_list)) != 0) {
|
|
g_warning ("Unable to get address: %s", gai_strerror (gaierr));
|
|
g_free (xdmcp_port);
|
|
return;
|
|
}
|
|
g_free (xdmcp_port);
|
|
|
|
/* just take the first one */
|
|
ai = ai_list;
|
|
|
|
if (ai != NULL) {
|
|
char *ip;
|
|
ic->chosen_address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
|
|
|
|
ip = NULL;
|
|
gdm_address_get_numeric_info (ic->chosen_address, &ip, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: hostname resolves to %s",
|
|
ip ? ip : "(null)");
|
|
g_free (ip);
|
|
}
|
|
|
|
freeaddrinfo (ai_list);
|
|
}
|
|
|
|
static void
|
|
on_client_disconnected (GdmDisplay *display)
|
|
{
|
|
if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED)
|
|
return;
|
|
|
|
gdm_display_stop_greeter_session (display);
|
|
gdm_display_unmanage (display);
|
|
gdm_display_finish (display);
|
|
}
|
|
|
|
static void
|
|
on_display_status_changed (GdmDisplay *display,
|
|
GParamSpec *arg1,
|
|
GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
int status;
|
|
GdmLaunchEnvironment *launch_environment;
|
|
GdmSession *session;
|
|
GdmAddress *address;
|
|
gint32 session_number;
|
|
int display_number;
|
|
|
|
launch_environment = NULL;
|
|
g_object_get (display, "launch-environment", &launch_environment, NULL);
|
|
|
|
session = NULL;
|
|
if (launch_environment != NULL) {
|
|
session = gdm_launch_environment_get_session (launch_environment);
|
|
}
|
|
|
|
status = gdm_display_get_status (display);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: xdmcp display status changed: %d", status);
|
|
switch (status) {
|
|
case GDM_DISPLAY_FINISHED:
|
|
g_object_get (display,
|
|
"remote-address", &address,
|
|
"x11-display-number", &display_number,
|
|
"session-number", &session_number,
|
|
NULL);
|
|
gdm_xdmcp_send_alive (factory, address, display_number, session_number);
|
|
|
|
gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
|
|
break;
|
|
case GDM_DISPLAY_FAILED:
|
|
gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
|
|
break;
|
|
case GDM_DISPLAY_UNMANAGED:
|
|
if (session != NULL) {
|
|
g_signal_handlers_disconnect_by_func (G_OBJECT (session),
|
|
G_CALLBACK (on_client_disconnected),
|
|
display);
|
|
}
|
|
break;
|
|
case GDM_DISPLAY_PREPARED:
|
|
break;
|
|
case GDM_DISPLAY_MANAGED:
|
|
if (session != NULL) {
|
|
g_signal_connect_object (G_OBJECT (session),
|
|
"client-disconnected",
|
|
G_CALLBACK (on_client_disconnected),
|
|
display, G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (G_OBJECT (session),
|
|
"disconnected",
|
|
G_CALLBACK (on_client_disconnected),
|
|
display, G_CONNECT_SWAPPED);
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
g_clear_object (&launch_environment);
|
|
}
|
|
|
|
static GdmDisplay *
|
|
gdm_xdmcp_display_create (GdmXdmcpDisplayFactory *factory,
|
|
const char *hostname,
|
|
GdmAddress *address,
|
|
int displaynum)
|
|
{
|
|
GdmDisplay *display;
|
|
GdmDisplayStore *store;
|
|
gboolean use_chooser;
|
|
const char *session_types[] = { "x11", NULL };
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Creating xdmcp display for %s:%d",
|
|
hostname ? hostname : "(null)", displaynum);
|
|
|
|
use_chooser = FALSE;
|
|
if (factory->honor_indirect) {
|
|
IndirectClient *ic;
|
|
|
|
ic = indirect_client_lookup (factory, address);
|
|
|
|
/* This was an indirect thingie and nothing was yet chosen,
|
|
* use a chooser */
|
|
if (ic != NULL && ic->chosen_address == NULL) {
|
|
use_chooser = TRUE;
|
|
}
|
|
}
|
|
|
|
if (use_chooser) {
|
|
display = gdm_xdmcp_chooser_display_new (hostname,
|
|
displaynum,
|
|
address,
|
|
get_next_session_serial (factory));
|
|
g_signal_connect (display, "hostname-selected", G_CALLBACK (on_hostname_selected), factory);
|
|
} else {
|
|
display = gdm_xdmcp_display_new (hostname,
|
|
displaynum,
|
|
address,
|
|
get_next_session_serial (factory));
|
|
}
|
|
|
|
if (display == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
g_object_set (G_OBJECT (display),
|
|
"session-type", session_types[0],
|
|
"supported-session-types", session_types,
|
|
NULL);
|
|
|
|
if (! gdm_display_prepare (display)) {
|
|
gdm_display_unmanage (display);
|
|
g_object_unref (display);
|
|
display = NULL;
|
|
goto out;
|
|
}
|
|
|
|
g_signal_connect_after (display,
|
|
"notify::status",
|
|
G_CALLBACK (on_display_status_changed),
|
|
factory);
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
gdm_display_store_add (store, display);
|
|
|
|
factory->num_pending_sessions++;
|
|
out:
|
|
|
|
return display;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_accept (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
CARD32 session_id,
|
|
ARRAY8Ptr authentication_name,
|
|
ARRAY8Ptr authentication_data,
|
|
ARRAY8Ptr authorization_name,
|
|
ARRAY8Ptr authorization_data)
|
|
{
|
|
XdmcpHeader header;
|
|
char *host;
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) ACCEPT;
|
|
header.length = 4;
|
|
header.length += 2 + authentication_name->length;
|
|
header.length += 2 + authentication_data->length;
|
|
header.length += 2 + authorization_name->length;
|
|
header.length += 2 + authorization_data->length;
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteCARD32 (&factory->buf, session_id);
|
|
XdmcpWriteARRAY8 (&factory->buf, authentication_name);
|
|
XdmcpWriteARRAY8 (&factory->buf, authentication_data);
|
|
XdmcpWriteARRAY8 (&factory->buf, authorization_name);
|
|
XdmcpWriteARRAY8 (&factory->buf, authorization_data);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending ACCEPT to %s with SessionID=%ld",
|
|
host ? host : "(null)",
|
|
(long)session_id);
|
|
g_free (host);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_request (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
CARD16 clnt_dspnum;
|
|
ARRAY16 clnt_conntyp;
|
|
ARRAYofARRAY8 clnt_addr;
|
|
ARRAY8 clnt_authname;
|
|
ARRAY8 clnt_authdata;
|
|
ARRAYofARRAY8 clnt_authorization_names;
|
|
ARRAY8 clnt_manufacturer;
|
|
int explen;
|
|
int i;
|
|
gboolean mitauth;
|
|
gboolean entered;
|
|
char *hostname;
|
|
|
|
mitauth = FALSE;
|
|
entered = FALSE;
|
|
|
|
hostname = NULL;
|
|
gdm_address_get_numeric_info (address, &hostname, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got REQUEST from %s",
|
|
hostname ? hostname : "(null)");
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
g_warning ("%s: Got REQUEST from banned host %s",
|
|
"gdm_xdmcp_handle_request",
|
|
hostname ? hostname : "(null)");
|
|
goto out;
|
|
}
|
|
|
|
gdm_xdmcp_displays_purge (factory); /* Purge pending displays */
|
|
|
|
/* Remote display number */
|
|
if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
|
|
g_warning ("%s: Could not read Display Number",
|
|
"gdm_xdmcp_handle_request");
|
|
goto out;
|
|
}
|
|
|
|
/* We don't care about connection type. Address says it all */
|
|
if G_UNLIKELY (! XdmcpReadARRAY16 (&factory->buf, &clnt_conntyp)) {
|
|
g_warning ("%s: Could not read Connection Type",
|
|
"gdm_xdmcp_handle_request");
|
|
goto out;
|
|
}
|
|
|
|
/* This is TCP/IP - we don't care */
|
|
if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_addr)) {
|
|
g_warning ("%s: Could not read Client Address",
|
|
"gdm_xdmcp_handle_request");
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
goto out;
|
|
}
|
|
|
|
/* Read authentication type */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_authname)) {
|
|
g_warning ("%s: Could not read Authentication Names",
|
|
"gdm_xdmcp_handle_request");
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
goto out;
|
|
}
|
|
|
|
/* Read authentication data */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_authdata)) {
|
|
g_warning ("%s: Could not read Authentication Data",
|
|
"gdm_xdmcp_handle_request");
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
XdmcpDisposeARRAY8 (&clnt_authname);
|
|
goto out;
|
|
}
|
|
|
|
/* Read and select from supported authorization list */
|
|
if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authorization_names)) {
|
|
g_warning ("%s: Could not read Authorization List",
|
|
"gdm_xdmcp_handle_request");
|
|
XdmcpDisposeARRAY8 (&clnt_authdata);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
XdmcpDisposeARRAY8 (&clnt_authname);
|
|
goto out;
|
|
}
|
|
|
|
/* libXdmcp doesn't terminate strings properly so we cheat and use strncmp () */
|
|
for (i = 0 ; i < clnt_authorization_names.length ; i++) {
|
|
if (clnt_authorization_names.data[i].length == 18 &&
|
|
strncmp ((char *) clnt_authorization_names.data[i].data, "MIT-MAGIC-COOKIE-1", 18) == 0) {
|
|
mitauth = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Manufacturer ID */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_manufacturer)) {
|
|
g_warning ("%s: Could not read Manufacturer ID",
|
|
"gdm_xdmcp_handle_request");
|
|
XdmcpDisposeARRAY8 (&clnt_authname);
|
|
XdmcpDisposeARRAY8 (&clnt_authdata);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
goto out;
|
|
}
|
|
|
|
/* Crude checksumming */
|
|
explen = 2; /* Display Number */
|
|
explen += 1 + 2 * clnt_conntyp.length; /* Connection Type */
|
|
explen += 1; /* Connection Address */
|
|
for (i = 0 ; i < clnt_addr.length ; i++) {
|
|
explen += 2 + clnt_addr.data[i].length;
|
|
}
|
|
explen += 2 + clnt_authname.length; /* Authentication Name */
|
|
explen += 2 + clnt_authdata.length; /* Authentication Data */
|
|
explen += 1; /* Authorization Names */
|
|
for (i = 0 ; i < clnt_authorization_names.length ; i++) {
|
|
explen += 2 + clnt_authorization_names.data[i].length;
|
|
}
|
|
|
|
explen += 2 + clnt_manufacturer.length;
|
|
|
|
if G_UNLIKELY (explen != len) {
|
|
g_warning ("%s: Failed checksum from %s",
|
|
"gdm_xdmcp_handle_request",
|
|
hostname ? hostname : "(null)");
|
|
|
|
XdmcpDisposeARRAY8 (&clnt_authname);
|
|
XdmcpDisposeARRAY8 (&clnt_authdata);
|
|
XdmcpDisposeARRAY8 (&clnt_manufacturer);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
goto out;
|
|
}
|
|
|
|
{
|
|
char *s = g_strndup ((char *) clnt_manufacturer.data, clnt_manufacturer.length);
|
|
g_debug ("GdmXdmcpDisplayFactory: xdmcp_pending=%d, MaxPending=%d, xdmcp_sessions=%d, MaxSessions=%d, ManufacturerID=%s",
|
|
factory->num_pending_sessions,
|
|
factory->max_pending_displays,
|
|
factory->num_sessions,
|
|
factory->max_displays,
|
|
s != NULL ? s : "");
|
|
g_free (s);
|
|
}
|
|
|
|
/* Check if ok to manage display */
|
|
if (mitauth &&
|
|
factory->num_sessions < factory->max_displays &&
|
|
(gdm_address_is_local (address) ||
|
|
gdm_xdmcp_num_displays_from_host (factory, address) < factory->max_displays_per_host)) {
|
|
entered = TRUE;
|
|
}
|
|
|
|
if (entered) {
|
|
|
|
/* Check if we are already talking to this host */
|
|
display_dispose_check (factory, hostname, clnt_dspnum);
|
|
|
|
if (factory->num_pending_sessions >= factory->max_pending_displays) {
|
|
g_debug ("GdmXdmcpDisplayFactory: maximum pending");
|
|
/* Don't translate, this goes over the wire to servers where we
|
|
* don't know the charset or language, so it must be ascii */
|
|
gdm_xdmcp_send_decline (factory, address, "Maximum pending servers");
|
|
} else {
|
|
GdmDisplay *display;
|
|
|
|
display = gdm_xdmcp_display_create (factory,
|
|
hostname,
|
|
address,
|
|
clnt_dspnum);
|
|
|
|
if (display != NULL) {
|
|
ARRAY8 authentication_name;
|
|
ARRAY8 authentication_data;
|
|
ARRAY8 authorization_name;
|
|
ARRAY8 authorization_data;
|
|
gint32 session_number;
|
|
const char *x11_cookie;
|
|
gsize x11_cookie_size;
|
|
char *name;
|
|
|
|
x11_cookie = NULL;
|
|
x11_cookie_size = 0;
|
|
gdm_display_get_x11_cookie (display, &x11_cookie, &x11_cookie_size, NULL);
|
|
|
|
name = NULL;
|
|
gdm_display_get_x11_display_name (display, &name, NULL);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending authorization key for display %s", name ? name : "(null)");
|
|
g_free (name);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: cookie len %d", (int) x11_cookie_size);
|
|
|
|
session_number = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
|
|
|
|
/* the send accept will fail if cookie is null */
|
|
g_assert (x11_cookie != NULL);
|
|
|
|
authentication_name.data = NULL;
|
|
authentication_name.length = 0;
|
|
authentication_data.data = NULL;
|
|
authentication_data.length = 0;
|
|
|
|
authorization_name.data = (CARD8 *) "MIT-MAGIC-COOKIE-1";
|
|
authorization_name.length = strlen ((char *) authorization_name.data);
|
|
|
|
authorization_data.data = (CARD8 *) x11_cookie;
|
|
authorization_data.length = x11_cookie_size;
|
|
|
|
/* the addrs are NOT copied */
|
|
gdm_xdmcp_send_accept (factory,
|
|
address,
|
|
session_number,
|
|
&authentication_name,
|
|
&authentication_data,
|
|
&authorization_name,
|
|
&authorization_data);
|
|
}
|
|
}
|
|
} else {
|
|
/* Don't translate, this goes over the wire to servers where we
|
|
* don't know the charset or language, so it must be ascii */
|
|
if ( ! mitauth) {
|
|
gdm_xdmcp_send_decline (factory,
|
|
address,
|
|
"Only MIT-MAGIC-COOKIE-1 supported");
|
|
} else if (factory->num_sessions >= factory->max_displays) {
|
|
g_warning ("Maximum number of open XDMCP sessions reached");
|
|
gdm_xdmcp_send_decline (factory,
|
|
address,
|
|
"Maximum number of open sessions reached");
|
|
} else {
|
|
g_debug ("GdmXdmcpDisplayFactory: Maximum number of open XDMCP sessions from host %s reached",
|
|
hostname ? hostname : "(null)");
|
|
gdm_xdmcp_send_decline (factory,
|
|
address,
|
|
"Maximum number of open sessions from your host reached");
|
|
}
|
|
}
|
|
|
|
XdmcpDisposeARRAY8 (&clnt_authname);
|
|
XdmcpDisposeARRAY8 (&clnt_authdata);
|
|
XdmcpDisposeARRAY8 (&clnt_manufacturer);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
|
|
XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
|
|
XdmcpDisposeARRAY16 (&clnt_conntyp);
|
|
out:
|
|
g_free (hostname);
|
|
}
|
|
|
|
static gboolean
|
|
lookup_by_session_id (const char *id,
|
|
GdmDisplay *display,
|
|
gpointer data)
|
|
{
|
|
CARD32 sessid;
|
|
CARD32 session_id;
|
|
|
|
sessid = GPOINTER_TO_INT (data);
|
|
|
|
if (! GDM_IS_XDMCP_DISPLAY (display)) {
|
|
return FALSE;
|
|
}
|
|
|
|
session_id = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
|
|
|
|
if (session_id == sessid) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GdmDisplay *
|
|
gdm_xdmcp_display_lookup (GdmXdmcpDisplayFactory *factory,
|
|
CARD32 sessid)
|
|
{
|
|
GdmDisplay *display;
|
|
GdmDisplayStore *store;
|
|
|
|
if (sessid == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
|
|
display = gdm_display_store_find (store,
|
|
(GdmDisplayStoreFunc)lookup_by_session_id,
|
|
GINT_TO_POINTER (sessid));
|
|
|
|
return display;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_failed (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
CARD32 sessid)
|
|
{
|
|
XdmcpHeader header;
|
|
ARRAY8 status;
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending FAILED to %ld", (long)sessid);
|
|
|
|
/*
|
|
* Don't translate, this goes over the wire to servers where we
|
|
* don't know the charset or language, so it must be ascii
|
|
*/
|
|
status.data = (CARD8 *) "Failed to start session";
|
|
status.length = strlen ((char *) status.data);
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) FAILED;
|
|
header.length = 6 + status.length;
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteCARD32 (&factory->buf, sessid);
|
|
XdmcpWriteARRAY8 (&factory->buf, &status);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_refuse (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
CARD32 sessid)
|
|
{
|
|
XdmcpHeader header;
|
|
ForwardQuery *fq;
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending REFUSE to %ld",
|
|
(long)sessid);
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) REFUSE;
|
|
header.length = 4;
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteCARD32 (&factory->buf, sessid);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
|
|
/*
|
|
* This was from a forwarded query quite apparently so
|
|
* send MANAGED_FORWARD
|
|
*/
|
|
fq = forward_query_lookup (factory, address);
|
|
if (fq != NULL) {
|
|
gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
|
|
forward_query_destroy (factory, fq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_manage (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
CARD32 clnt_sessid;
|
|
CARD16 clnt_dspnum;
|
|
ARRAY8 clnt_dspclass;
|
|
GdmDisplay *display;
|
|
ForwardQuery *fq;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got MANAGE from %s",
|
|
host ? host : "(null)");
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
g_warning ("%s: Got Manage from banned host %s",
|
|
"gdm_xdmcp_handle_manage",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
return;
|
|
}
|
|
|
|
/* SessionID */
|
|
if G_UNLIKELY (! XdmcpReadCARD32 (&factory->buf, &clnt_sessid)) {
|
|
g_warning ("%s: Could not read Session ID",
|
|
"gdm_xdmcp_handle_manage");
|
|
goto out;
|
|
}
|
|
|
|
/* Remote display number */
|
|
if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
|
|
g_warning ("%s: Could not read Display Number",
|
|
"gdm_xdmcp_handle_manage");
|
|
goto out;
|
|
}
|
|
|
|
/* Display Class */
|
|
if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_dspclass)) {
|
|
g_warning ("%s: Could not read Display Class",
|
|
"gdm_xdmcp_handle_manage");
|
|
goto out;
|
|
}
|
|
|
|
{
|
|
char *s = g_strndup ((char *) clnt_dspclass.data, clnt_dspclass.length);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got display=%d, SessionID=%ld Class=%s from %s",
|
|
(int)clnt_dspnum,
|
|
(long)clnt_sessid,
|
|
s != NULL ? s : "",
|
|
host);
|
|
|
|
g_free (s);
|
|
}
|
|
|
|
display = gdm_xdmcp_display_lookup (factory, clnt_sessid);
|
|
if (display != NULL &&
|
|
gdm_display_get_status (display) == GDM_DISPLAY_PREPARED) {
|
|
char *name;
|
|
|
|
name = NULL;
|
|
gdm_display_get_x11_display_name (display, &name, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Looked up %s",
|
|
name ? name : "(null)");
|
|
g_free (name);
|
|
|
|
if (factory->honor_indirect) {
|
|
IndirectClient *ic;
|
|
|
|
ic = indirect_client_lookup (factory, address);
|
|
|
|
/* This was an indirect thingie and nothing was yet chosen,
|
|
* use a chooser */
|
|
if (ic != NULL && ic->chosen_address == NULL) {
|
|
g_debug ("GdmXdmcpDisplayFactory: use chooser");
|
|
/*d->use_chooser = TRUE;
|
|
d->indirect_id = ic->id;*/
|
|
} else {
|
|
/*d->indirect_id = 0;
|
|
d->use_chooser = FALSE;*/
|
|
if (ic != NULL) {
|
|
indirect_client_destroy (factory, ic);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
}
|
|
|
|
/* this was from a forwarded query quite apparently so
|
|
* send MANAGED_FORWARD */
|
|
fq = forward_query_lookup (factory, address);
|
|
if (fq != NULL) {
|
|
gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
|
|
forward_query_destroy (factory, fq);
|
|
}
|
|
|
|
factory->num_sessions++;
|
|
factory->num_pending_sessions--;
|
|
|
|
/* Start greeter/session */
|
|
if (! gdm_display_manage (display)) {
|
|
gdm_xdmcp_send_failed (factory, address, clnt_sessid);
|
|
g_debug ("GdmXdmcpDisplayFactory: Failed to manage display");
|
|
}
|
|
} else if (display != NULL &&
|
|
gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) {
|
|
g_debug ("GdmXdmcpDisplayFactory: Session ID %ld already managed",
|
|
(long)clnt_sessid);
|
|
} else {
|
|
g_warning ("GdmXdmcpDisplayFactory: Failed to look up session ID %ld",
|
|
(long)clnt_sessid);
|
|
gdm_xdmcp_send_refuse (factory, address, clnt_sessid);
|
|
}
|
|
|
|
out:
|
|
XdmcpDisposeARRAY8 (&clnt_dspclass);
|
|
g_free (host);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_managed_forward (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
ARRAY8 clnt_address;
|
|
char *host;
|
|
GdmAddress *disp_address;
|
|
IndirectClient *ic;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from %s",
|
|
host ? host : "(null)");
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
g_warning ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from banned host %s",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
return;
|
|
}
|
|
g_free (host);
|
|
|
|
/* Hostname */
|
|
if G_UNLIKELY ( ! XdmcpReadARRAY8 (&factory->buf, &clnt_address)) {
|
|
g_warning ("%s: Could not read address",
|
|
"gdm_xdmcp_handle_managed_forward");
|
|
return;
|
|
}
|
|
|
|
disp_address = NULL;
|
|
if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) {
|
|
g_warning ("Unable to parse address for request");
|
|
XdmcpDisposeARRAY8 (&clnt_address);
|
|
return;
|
|
}
|
|
|
|
ic = indirect_client_lookup_by_chosen (factory, address, disp_address);
|
|
if (ic != NULL) {
|
|
indirect_client_destroy (factory, ic);
|
|
}
|
|
|
|
/* Note: we send GOT even on not found, just in case our previous
|
|
* didn't get through and this was a second managed forward */
|
|
gdm_xdmcp_send_got_managed_forward (factory, address, disp_address);
|
|
|
|
gdm_address_free (disp_address);
|
|
|
|
XdmcpDisposeARRAY8 (&clnt_address);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_got_managed_forward (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
GdmAddress *disp_address;
|
|
ARRAY8 clnt_address;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from %s",
|
|
host ? host : "(null)");
|
|
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
g_warning ("%s: Got GOT_MANAGED_FORWARD from banned host %s",
|
|
"gdm_xdmcp_handle_request", host ? host : "(null)");
|
|
g_free (host);
|
|
return;
|
|
}
|
|
g_free (host);
|
|
|
|
/* Hostname */
|
|
if G_UNLIKELY ( ! XdmcpReadARRAY8 (&factory->buf, &clnt_address)) {
|
|
g_warning ("%s: Could not read address",
|
|
"gdm_xdmcp_handle_got_managed_forward");
|
|
return;
|
|
}
|
|
|
|
if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) {
|
|
g_warning ("%s: Could not read address",
|
|
"gdm_xdmcp_handle_got_managed_forward");
|
|
XdmcpDisposeARRAY8 (&clnt_address);
|
|
return;
|
|
}
|
|
|
|
gdm_xdmcp_whack_queued_managed_forwards (factory, address, disp_address);
|
|
|
|
gdm_address_free (disp_address);
|
|
|
|
XdmcpDisposeARRAY8 (&clnt_address);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_send_alive (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
CARD16 dspnum,
|
|
CARD32 sessid)
|
|
{
|
|
XdmcpHeader header;
|
|
GdmDisplay *display;
|
|
int send_running = 0;
|
|
CARD32 send_sessid = 0;
|
|
|
|
display = gdm_xdmcp_display_lookup (factory, sessid);
|
|
if (display == NULL) {
|
|
display = gdm_xdmcp_display_lookup_by_host (factory, address, dspnum);
|
|
}
|
|
|
|
if (display != NULL) {
|
|
int status;
|
|
|
|
send_sessid = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
|
|
status = gdm_display_get_status (display);
|
|
|
|
if (status == GDM_DISPLAY_MANAGED) {
|
|
send_running = 1;
|
|
}
|
|
}
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Sending ALIVE to %ld (running %d, sessid %ld)",
|
|
(long)sessid,
|
|
send_running,
|
|
(long)send_sessid);
|
|
|
|
header.version = XDM_PROTOCOL_VERSION;
|
|
header.opcode = (CARD16) ALIVE;
|
|
header.length = 5;
|
|
|
|
XdmcpWriteHeader (&factory->buf, &header);
|
|
XdmcpWriteCARD8 (&factory->buf, send_running);
|
|
XdmcpWriteCARD32 (&factory->buf, send_sessid);
|
|
|
|
XdmcpFlush (factory->socket_fd,
|
|
&factory->buf,
|
|
(XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
|
|
(int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_handle_keepalive (GdmXdmcpDisplayFactory *factory,
|
|
GdmAddress *address,
|
|
int len)
|
|
{
|
|
CARD16 clnt_dspnum;
|
|
CARD32 clnt_sessid;
|
|
char *host;
|
|
|
|
host = NULL;
|
|
gdm_address_get_numeric_info (address, &host, NULL);
|
|
g_debug ("GdmXdmcpDisplayFactory: Got KEEPALIVE from %s",
|
|
host ? host : "(null)");
|
|
|
|
/* Check with tcp_wrappers if client is allowed to access */
|
|
if (! gdm_xdmcp_host_allow (address)) {
|
|
g_warning ("%s: Got KEEPALIVE from banned host %s",
|
|
"gdm_xdmcp_handle_keepalive",
|
|
host ? host : "(null)");
|
|
g_free (host);
|
|
return;
|
|
}
|
|
g_free (host);
|
|
|
|
/* Remote display number */
|
|
if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
|
|
g_warning ("%s: Could not read Display Number",
|
|
"gdm_xdmcp_handle_keepalive");
|
|
return;
|
|
}
|
|
|
|
/* SessionID */
|
|
if G_UNLIKELY (! XdmcpReadCARD32 (&factory->buf, &clnt_sessid)) {
|
|
g_warning ("%s: Could not read Session ID",
|
|
"gdm_xdmcp_handle_keepalive");
|
|
return;
|
|
}
|
|
|
|
gdm_xdmcp_send_alive (factory, address, clnt_dspnum, clnt_sessid);
|
|
}
|
|
|
|
static const char *
|
|
opcode_string (int opcode)
|
|
{
|
|
static const char * const opcode_names[] = {
|
|
NULL,
|
|
"BROADCAST_QUERY",
|
|
"QUERY",
|
|
"INDIRECT_QUERY",
|
|
"FORWARD_QUERY",
|
|
"WILLING",
|
|
"UNWILLING",
|
|
"REQUEST",
|
|
"ACCEPT",
|
|
"DECLINE",
|
|
"MANAGE",
|
|
"REFUSE",
|
|
"FAILED",
|
|
"KEEPALIVE",
|
|
"ALIVE"
|
|
};
|
|
static const char * const gdm_opcode_names[] = {
|
|
"MANAGED_FORWARD",
|
|
"GOT_MANAGED_FORWARD"
|
|
};
|
|
|
|
|
|
if (opcode < G_N_ELEMENTS (opcode_names)) {
|
|
return opcode_names [opcode];
|
|
} else if (opcode >= GDM_XDMCP_FIRST_OPCODE &&
|
|
opcode < GDM_XDMCP_LAST_OPCODE) {
|
|
return gdm_opcode_names [opcode - GDM_XDMCP_FIRST_OPCODE];
|
|
} else {
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
decode_packet (GIOChannel *source,
|
|
GIOCondition cond,
|
|
GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
struct sockaddr_storage clnt_ss;
|
|
GdmAddress *address;
|
|
gint ss_len;
|
|
XdmcpHeader header;
|
|
char *host;
|
|
char *port;
|
|
int res;
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: decode_packet: GIOCondition %d", (int)cond);
|
|
|
|
if ( ! (cond & G_IO_IN)) {
|
|
return TRUE;
|
|
}
|
|
|
|
ss_len = (int) sizeof (clnt_ss);
|
|
|
|
res = XdmcpFill (factory->socket_fd, &factory->buf, (XdmcpNetaddr)&clnt_ss, &ss_len);
|
|
if G_UNLIKELY (! res) {
|
|
g_debug ("GdmXdmcpDisplayFactory: Could not create XDMCP buffer!");
|
|
return TRUE;
|
|
}
|
|
|
|
res = XdmcpReadHeader (&factory->buf, &header);
|
|
if G_UNLIKELY (! res) {
|
|
g_warning ("GdmXdmcpDisplayFactory: Could not read XDMCP header!");
|
|
return TRUE;
|
|
}
|
|
|
|
if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION &&
|
|
header.version != GDM_XDMCP_PROTOCOL_VERSION) {
|
|
g_warning ("XDMCP: Incorrect XDMCP version!");
|
|
return TRUE;
|
|
}
|
|
|
|
address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len);
|
|
if (address == NULL) {
|
|
g_warning ("XDMCP: Unable to parse address");
|
|
return TRUE;
|
|
}
|
|
|
|
gdm_address_debug (address);
|
|
|
|
host = NULL;
|
|
port = NULL;
|
|
gdm_address_get_numeric_info (address, &host, &port);
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Received opcode %s from client %s : %s",
|
|
opcode_string (header.opcode),
|
|
host ? host : "(null)",
|
|
port ? port : "(null)");
|
|
|
|
switch (header.opcode) {
|
|
case BROADCAST_QUERY:
|
|
gdm_xdmcp_handle_broadcast_query (factory, address, header.length);
|
|
break;
|
|
|
|
case QUERY:
|
|
gdm_xdmcp_handle_query (factory, address, header.length);
|
|
break;
|
|
|
|
case INDIRECT_QUERY:
|
|
gdm_xdmcp_handle_indirect_query (factory, address, header.length);
|
|
break;
|
|
|
|
case FORWARD_QUERY:
|
|
gdm_xdmcp_handle_forward_query (factory, address, header.length);
|
|
break;
|
|
|
|
case REQUEST:
|
|
gdm_xdmcp_handle_request (factory, address, header.length);
|
|
break;
|
|
|
|
case MANAGE:
|
|
gdm_xdmcp_handle_manage (factory, address, header.length);
|
|
break;
|
|
|
|
case KEEPALIVE:
|
|
gdm_xdmcp_handle_keepalive (factory, address, header.length);
|
|
break;
|
|
|
|
case GDM_XDMCP_MANAGED_FORWARD:
|
|
gdm_xdmcp_handle_managed_forward (factory, address, header.length);
|
|
break;
|
|
|
|
case GDM_XDMCP_GOT_MANAGED_FORWARD:
|
|
gdm_xdmcp_handle_got_managed_forward (factory, address, header.length);
|
|
break;
|
|
|
|
default:
|
|
g_debug ("GdmXdmcpDisplayFactory: Unknown opcode from client %s : %s",
|
|
host ? host : "(null)",
|
|
port ? port : "(null)");
|
|
|
|
break;
|
|
}
|
|
|
|
g_free (host);
|
|
g_free (port);
|
|
|
|
gdm_address_free (address);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gdm_xdmcp_display_factory_start (GdmDisplayFactory *base_factory)
|
|
{
|
|
gboolean ret;
|
|
GIOChannel *ioc;
|
|
GdmXdmcpDisplayFactory *factory = GDM_XDMCP_DISPLAY_FACTORY (base_factory);
|
|
gboolean res;
|
|
|
|
g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory), FALSE);
|
|
g_return_val_if_fail (factory->socket_fd == -1, FALSE);
|
|
|
|
/* read configuration */
|
|
res = gdm_settings_direct_get_uint (GDM_KEY_UDP_PORT,
|
|
&(factory->port));
|
|
res = res && gdm_settings_direct_get_boolean (GDM_KEY_MULTICAST,
|
|
&(factory->use_multicast));
|
|
res = res && gdm_settings_direct_get_string (GDM_KEY_MULTICAST_ADDR,
|
|
&(factory->multicast_address));
|
|
res = res && gdm_settings_direct_get_boolean (GDM_KEY_INDIRECT,
|
|
&(factory->honor_indirect));
|
|
res = res && gdm_settings_direct_get_uint (GDM_KEY_DISPLAYS_PER_HOST,
|
|
&(factory->max_displays_per_host));
|
|
res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_SESSIONS,
|
|
&(factory->max_displays));
|
|
res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_PENDING,
|
|
&(factory->max_pending_displays));
|
|
res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_WAIT,
|
|
&(factory->max_wait));
|
|
res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_WAIT_INDIRECT,
|
|
&(factory->max_wait_indirect));
|
|
res = res && gdm_settings_direct_get_string (GDM_KEY_WILLING,
|
|
&(factory->willing_script));
|
|
|
|
if (! res) {
|
|
return res;
|
|
}
|
|
|
|
ret = open_port (factory);
|
|
if (! ret) {
|
|
return ret;
|
|
}
|
|
|
|
g_debug ("GdmXdmcpDisplayFactory: Starting to listen on XDMCP port");
|
|
|
|
ioc = g_io_channel_unix_new (factory->socket_fd);
|
|
|
|
g_io_channel_set_encoding (ioc, NULL, NULL);
|
|
g_io_channel_set_buffered (ioc, FALSE);
|
|
|
|
factory->socket_watch_id = g_io_add_watch_full (ioc,
|
|
G_PRIORITY_DEFAULT,
|
|
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
|
|
(GIOFunc)decode_packet,
|
|
factory,
|
|
NULL);
|
|
g_io_channel_unref (ioc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gdm_xdmcp_display_factory_stop (GdmDisplayFactory *base_factory)
|
|
{
|
|
GdmXdmcpDisplayFactory *factory = GDM_XDMCP_DISPLAY_FACTORY (base_factory);
|
|
|
|
g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory), FALSE);
|
|
g_return_val_if_fail (factory->socket_fd != -1, FALSE);
|
|
|
|
g_clear_handle_id (&factory->socket_watch_id, g_source_remove);
|
|
|
|
if (factory->socket_fd > 0) {
|
|
VE_IGNORE_EINTR (close (factory->socket_fd));
|
|
factory->socket_fd = -1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gdm_xdmcp_display_factory_set_port (GdmXdmcpDisplayFactory *factory,
|
|
guint port)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->port = port;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_use_multicast (GdmXdmcpDisplayFactory *factory,
|
|
gboolean use_multicast)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->use_multicast = use_multicast;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_multicast_address (GdmXdmcpDisplayFactory *factory,
|
|
const char *address)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
g_free (factory->multicast_address);
|
|
factory->multicast_address = g_strdup (address);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_honor_indirect (GdmXdmcpDisplayFactory *factory,
|
|
gboolean honor_indirect)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->honor_indirect = honor_indirect;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_max_displays_per_host (GdmXdmcpDisplayFactory *factory,
|
|
guint num)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->max_displays_per_host = num;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_max_displays (GdmXdmcpDisplayFactory *factory,
|
|
guint num)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->max_displays = num;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_max_pending_displays (GdmXdmcpDisplayFactory *factory,
|
|
guint num)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->max_pending_displays = num;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_max_wait (GdmXdmcpDisplayFactory *factory,
|
|
guint num)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->max_wait = num;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_max_wait_indirect (GdmXdmcpDisplayFactory *factory,
|
|
guint num)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
factory->max_wait_indirect = num;
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_willing_script (GdmXdmcpDisplayFactory *factory,
|
|
const char *script)
|
|
{
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
|
|
|
|
g_free (factory->willing_script);
|
|
factory->willing_script = g_strdup (script);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdmXdmcpDisplayFactory *self;
|
|
|
|
self = GDM_XDMCP_DISPLAY_FACTORY (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PORT:
|
|
gdm_xdmcp_display_factory_set_port (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_USE_MULTICAST:
|
|
gdm_xdmcp_display_factory_set_use_multicast (self, g_value_get_boolean (value));
|
|
break;
|
|
case PROP_MULTICAST_ADDRESS:
|
|
gdm_xdmcp_display_factory_set_multicast_address (self, g_value_get_string (value));
|
|
break;
|
|
case PROP_HONOR_INDIRECT:
|
|
gdm_xdmcp_display_factory_set_honor_indirect (self, g_value_get_boolean (value));
|
|
break;
|
|
case PROP_MAX_DISPLAYS_PER_HOST:
|
|
gdm_xdmcp_display_factory_set_max_displays_per_host (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_MAX_DISPLAYS:
|
|
gdm_xdmcp_display_factory_set_max_displays (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_MAX_PENDING_DISPLAYS:
|
|
gdm_xdmcp_display_factory_set_max_pending_displays (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_MAX_WAIT:
|
|
gdm_xdmcp_display_factory_set_max_wait (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_MAX_WAIT_INDIRECT:
|
|
gdm_xdmcp_display_factory_set_max_wait_indirect (self, g_value_get_uint (value));
|
|
break;
|
|
case PROP_WILLING_SCRIPT:
|
|
gdm_xdmcp_display_factory_set_willing_script (self, g_value_get_string (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GdmXdmcpDisplayFactory *self;
|
|
|
|
self = GDM_XDMCP_DISPLAY_FACTORY (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PORT:
|
|
g_value_set_uint (value, self->port);
|
|
break;
|
|
case PROP_USE_MULTICAST:
|
|
g_value_set_boolean (value, self->use_multicast);
|
|
break;
|
|
case PROP_MULTICAST_ADDRESS:
|
|
g_value_set_string (value, self->multicast_address);
|
|
break;
|
|
case PROP_HONOR_INDIRECT:
|
|
g_value_set_boolean (value, self->honor_indirect);
|
|
break;
|
|
case PROP_MAX_DISPLAYS_PER_HOST:
|
|
g_value_set_uint (value, self->max_displays_per_host);
|
|
break;
|
|
case PROP_MAX_DISPLAYS:
|
|
g_value_set_uint (value, self->max_displays);
|
|
break;
|
|
case PROP_MAX_PENDING_DISPLAYS:
|
|
g_value_set_uint (value, self->max_pending_displays);
|
|
break;
|
|
case PROP_MAX_WAIT:
|
|
g_value_set_uint (value, self->max_wait);
|
|
break;
|
|
case PROP_MAX_WAIT_INDIRECT:
|
|
g_value_set_uint (value, self->max_wait_indirect);
|
|
break;
|
|
case PROP_WILLING_SCRIPT:
|
|
g_value_set_string (value, self->willing_script);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_class_init (GdmXdmcpDisplayFactoryClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GdmDisplayFactoryClass *factory_class = GDM_DISPLAY_FACTORY_CLASS (klass);
|
|
|
|
object_class->get_property = gdm_xdmcp_display_factory_get_property;
|
|
object_class->set_property = gdm_xdmcp_display_factory_set_property;
|
|
object_class->finalize = gdm_xdmcp_display_factory_finalize;
|
|
|
|
factory_class->start = gdm_xdmcp_display_factory_start;
|
|
factory_class->stop = gdm_xdmcp_display_factory_stop;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_PORT,
|
|
g_param_spec_uint ("port",
|
|
"UDP port",
|
|
"UDP port",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_PORT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_USE_MULTICAST,
|
|
g_param_spec_boolean ("use-multicast",
|
|
NULL,
|
|
NULL,
|
|
DEFAULT_USE_MULTICAST,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MULTICAST_ADDRESS,
|
|
g_param_spec_string ("multicast-address",
|
|
"multicast-address",
|
|
"multicast-address",
|
|
DEFAULT_MULTICAST_ADDRESS,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_HONOR_INDIRECT,
|
|
g_param_spec_boolean ("honor-indirect",
|
|
NULL,
|
|
NULL,
|
|
DEFAULT_HONOR_INDIRECT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_WILLING_SCRIPT,
|
|
g_param_spec_string ("willing-script",
|
|
"willing-script",
|
|
"willing-script",
|
|
DEFAULT_WILLING_SCRIPT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAX_DISPLAYS_PER_HOST,
|
|
g_param_spec_uint ("max-displays-per-host",
|
|
"max-displays-per-host",
|
|
"max-displays-per-host",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_MAX_DISPLAYS_PER_HOST,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAX_DISPLAYS,
|
|
g_param_spec_uint ("max-displays",
|
|
"max-displays",
|
|
"max-displays",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_MAX_DISPLAYS,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAX_PENDING_DISPLAYS,
|
|
g_param_spec_uint ("max-pending-displays",
|
|
"max-pending-displays",
|
|
"max-pending-displays",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_MAX_PENDING_DISPLAYS,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAX_WAIT,
|
|
g_param_spec_uint ("max-wait",
|
|
"max-wait",
|
|
"max-wait",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_MAX_WAIT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAX_WAIT_INDIRECT,
|
|
g_param_spec_uint ("max-wait-indirect",
|
|
"max-wait-indirect",
|
|
"max-wait-indirect",
|
|
0,
|
|
G_MAXINT,
|
|
DEFAULT_MAX_WAIT_INDIRECT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_init (GdmXdmcpDisplayFactory *factory)
|
|
{
|
|
char hostbuf[1024];
|
|
struct utsname name;
|
|
|
|
factory->socket_fd = -1;
|
|
|
|
factory->session_serial = g_random_int ();
|
|
|
|
/* Fetch and store local hostname in XDMCP friendly format */
|
|
hostbuf[1023] = '\0';
|
|
if G_UNLIKELY (gethostname (hostbuf, 1023) != 0) {
|
|
g_warning ("Could not get server hostname: %s!", g_strerror (errno));
|
|
strcpy (hostbuf, "localhost.localdomain");
|
|
}
|
|
|
|
uname (&name);
|
|
factory->sysid = g_strconcat (name.sysname,
|
|
" ",
|
|
name.release,
|
|
NULL);
|
|
|
|
factory->hostname = g_strdup (hostbuf);
|
|
|
|
factory->servhost.data = (CARD8 *) g_strdup (hostbuf);
|
|
factory->servhost.length = strlen ((char *) factory->servhost.data);
|
|
}
|
|
|
|
static void
|
|
gdm_xdmcp_display_factory_finalize (GObject *object)
|
|
{
|
|
GdmXdmcpDisplayFactory *factory;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (object));
|
|
|
|
factory = GDM_XDMCP_DISPLAY_FACTORY (object);
|
|
|
|
g_return_if_fail (factory != NULL);
|
|
|
|
g_clear_handle_id (&factory->socket_watch_id, g_source_remove);
|
|
|
|
if (factory->socket_fd > 0) {
|
|
close (factory->socket_fd);
|
|
factory->socket_fd = -1;
|
|
}
|
|
|
|
g_slist_free (factory->forward_queries);
|
|
g_slist_free (factory->managed_forwards);
|
|
|
|
g_free (factory->sysid);
|
|
g_free (factory->hostname);
|
|
g_free (factory->multicast_address);
|
|
g_free (factory->willing_script);
|
|
|
|
/* FIXME: Free servhost */
|
|
|
|
G_OBJECT_CLASS (gdm_xdmcp_display_factory_parent_class)->finalize (object);
|
|
}
|
|
|
|
GdmXdmcpDisplayFactory *
|
|
gdm_xdmcp_display_factory_new (GdmDisplayStore *store)
|
|
{
|
|
if (xdmcp_display_factory_object != NULL) {
|
|
g_object_ref (xdmcp_display_factory_object);
|
|
} else {
|
|
xdmcp_display_factory_object = g_object_new (GDM_TYPE_XDMCP_DISPLAY_FACTORY,
|
|
"display-store", store,
|
|
NULL);
|
|
g_object_add_weak_pointer (xdmcp_display_factory_object,
|
|
(gpointer *) &xdmcp_display_factory_object);
|
|
}
|
|
|
|
return GDM_XDMCP_DISPLAY_FACTORY (xdmcp_display_factory_object);
|
|
}
|