summaryrefslogtreecommitdiffstats
path: root/tdm/backend/xdmcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'tdm/backend/xdmcp.c')
-rw-r--r--tdm/backend/xdmcp.c1165
1 files changed, 1165 insertions, 0 deletions
diff --git a/tdm/backend/xdmcp.c b/tdm/backend/xdmcp.c
new file mode 100644
index 000000000..6abaf5fc8
--- /dev/null
+++ b/tdm/backend/xdmcp.c
@@ -0,0 +1,1165 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+Copyright 2001-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * xdmcp.c - Support for XDMCP
+ */
+
+#include <config.h>
+
+#ifdef XDMCP
+
+#include "dm.h"
+#include "dm_error.h"
+#include "dm_auth.h"
+#include "dm_socket.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#include <netdb.h>
+#if defined(IPv6) && defined(AF_INET6)
+# include <arpa/inet.h>
+#endif
+
+/*
+ * Forward reference
+ */
+static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd);
+static void manage( struct sockaddr *from, int fromlen, int length, int fd );
+static void query_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void request_respond( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd );
+static void send_alive( struct sockaddr *from, int fromlen, int length, int fd );
+static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd );
+static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd );
+static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd );
+static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd );
+
+
+static XdmcpBuffer buffer;
+
+static void
+sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure )
+{
+#ifdef AF_INET
+ struct sockaddr_in in_addr;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_in6 in6_addr;
+#endif
+#ifdef AF_DECnet
+#endif
+ struct sockaddr *addr;
+ int addrlen;
+
+ switch (connectionType) {
+#ifdef AF_INET
+ case FamilyInternet:
+ addr = (struct sockaddr *)&in_addr;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+# endif
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 4)
+ return;
+ memmove( (char *)&in_addr.sin_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+ addr = (struct sockaddr *)&in6_addr;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+# ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+# endif
+ in6_addr.sin6_family = AF_INET6;
+ in6_addr.sin6_port = htons( (short)XDM_UDP_PORT );
+ if (address->length != 16)
+ return;
+ memmove( (char *)&in6_addr.sin6_addr, address->data, address->length );
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+#ifdef AF_DECnet
+ case FamilyDECnet:
+#endif
+ default:
+ return;
+ }
+ XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen );
+ return;
+}
+
+static void
+ClientAddress( struct sockaddr *from,
+ ARRAY8Ptr addr, /* return */
+ ARRAY8Ptr port, /* return */
+ CARD16 *type ) /* return */
+{
+ int length, family;
+ char *data;
+
+ data = NetaddrPort( (XdmcpNetaddr)from, &length );
+ XdmcpAllocARRAY8( port, length );
+ memmove( port->data, data, length );
+ port->length = length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &data );
+ XdmcpAllocARRAY8( addr, length );
+ memmove( addr->data, data, length );
+ addr->length = length;
+
+ *type = family;
+}
+
+static void
+all_query_respond( struct sockaddr *from, int fromlen,
+ ARRAYofARRAY8Ptr authenticationNames,
+ xdmOpCode type, int fd )
+{
+ ARRAY8Ptr authenticationName;
+ ARRAY8 status;
+ ARRAY8 addr;
+ CARD16 connectionType;
+ int family;
+ int length;
+
+ family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) );
+ addr.length = length; /* convert int to short */
+ Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n",
+ family, addr.length, addr.data );
+ if (family < 0)
+ return;
+ connectionType = family;
+
+ if (type == INDIRECT_QUERY)
+ RememberIndirectClient( &addr, connectionType );
+ else
+ ForgetIndirectClient( &addr, connectionType );
+
+ authenticationName = ChooseAuthentication( authenticationNames );
+ if (Willing( &addr, connectionType, authenticationName, &status, type ))
+ send_willing( from, fromlen, authenticationName, &status, fd );
+ else
+ if (type == QUERY)
+ send_unwilling( from, fromlen, authenticationName, &status, fd );
+ XdmcpDisposeARRAY8( &status );
+}
+
+static void
+indirect_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ CARD16 connectionType;
+ int expectedLen;
+ int i;
+ XdmcpHeader header;
+ int localHostAsWell;
+
+ Debug( "<indirect> respond %d\n", length );
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen) {
+ ClientAddress( from, &clientAddress, &clientPort, &connectionType );
+ /*
+ * set up the forward query packet
+ */
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FORWARD_QUERY;
+ header.length = 0;
+ header.length += 2 + clientAddress.length;
+ header.length += 2 + clientPort.length;
+ header.length += 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ header.length += 2 + queryAuthenticationNames.data[i].length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &clientAddress );
+ XdmcpWriteARRAY8( &buffer, &clientPort );
+ XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames );
+
+ localHostAsWell =
+ ForEachMatchingIndirectHost( &clientAddress, connectionType,
+ sendForward, (char *)fd );
+
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ if (localHostAsWell)
+ all_query_respond( from, fromlen, &queryAuthenticationNames,
+ INDIRECT_QUERY, fd );
+ } else
+ Debug( "<indirect> length error got %d expect %d\n",
+ length, expectedLen );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+void
+ProcessRequestSocket( int fd )
+{
+ XdmcpHeader header;
+#if defined(IPv6) && defined(AF_INET6)
+ struct sockaddr_storage addr;
+#else
+ struct sockaddr addr;
+#endif
+ int addrlen = sizeof(addr);
+
+ Debug( "ProcessRequestSocket\n" );
+ bzero( (char *)&addr, sizeof(addr) );
+ if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) {
+ Debug( "XdmcpFill failed\n" );
+ return;
+ }
+ if (!XdmcpReadHeader( &buffer, &header )) {
+ Debug( "XdmcpReadHeader failed\n" );
+ return;
+ }
+ if (header.version != XDM_PROTOCOL_VERSION) {
+ Debug( "XDMCP header version read was %d, expected %d\n",
+ header.version, XDM_PROTOCOL_VERSION );
+ return;
+ }
+ Debug( "header: %d %d %d\n", header.version, header.opcode, header.length );
+ switch (header.opcode) {
+ case BROADCAST_QUERY:
+ broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case QUERY:
+ query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case INDIRECT_QUERY:
+ indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case FORWARD_QUERY:
+ forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd);
+ break;
+ case REQUEST:
+ request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case MANAGE:
+ manage( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ case KEEPALIVE:
+ send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd );
+ break;
+ }
+}
+
+/*
+ * respond to a request on the UDP socket.
+ */
+
+static void
+direct_query_respond( struct sockaddr *from, int fromlen,
+ int length, xdmOpCode type, int fd )
+{
+ ARRAYofARRAY8 queryAuthenticationNames;
+ int expectedLen;
+ int i;
+
+ if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames ))
+ return;
+ expectedLen = 1;
+ for (i = 0; i < (int)queryAuthenticationNames.length; i++)
+ expectedLen += 2 + queryAuthenticationNames.data[i].length;
+ if (length == expectedLen)
+ all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd );
+ XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames );
+}
+
+static void
+query_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ Debug( "<query> respond %d\n", length );
+ direct_query_respond( from, fromlen, length, QUERY, fd );
+}
+
+static void
+broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd );
+}
+
+/* computes an X display name */
+
+static char *
+NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress,
+ struct sockaddr *originalAddress, CARD16 displayNumber )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ CARD8 *data;
+ struct hostent *hostent;
+ char *hostname = NULL;
+ char *name;
+ const char *localhost;
+ int multiHomed = 0;
+ int type;
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai = NULL, *nai, hints;
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ type = AF_INET6;
+ else
+#endif
+ type = AF_INET;
+
+ data = connectionAddress->data;
+ hostent = gethostbyaddr( (char *)data,
+ connectionAddress->length, type );
+ if (hostent) {
+ if (sourceAddress) {
+#if defined(IPv6) && defined(AF_INET6)
+ bzero( &hints, sizeof(hints) );
+ hints.ai_flags = AI_CANONNAME;
+ if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) {
+ hostname = ai->ai_canonname;
+ for (nai = ai->ai_next; nai; nai = nai->ai_next)
+ if (ai->ai_protocol == nai->ai_protocol &&
+ memcmp( ai->ai_addr, nai->ai_addr,
+ ai->ai_addrlen ))
+ multiHomed = 1;
+ }
+#else
+ hostent = gethostbyname( hostent->h_name );
+ if (hostent && hostent->h_addrtype == AF_INET) {
+ multiHomed = hostent->h_addr_list[1] != NULL;
+ hostname = hostent->h_name;
+ }
+#endif
+ } else
+ hostname = hostent->h_name;
+ }
+
+ localhost = localHostname();
+
+ /*
+ * protect against bogus host names
+ */
+ if (hostname && *hostname && *hostname != '.' && !multiHomed) {
+ if (!strcmp( localhost, hostname ))
+ ASPrintf( &name, "localhost:%d", displayNumber );
+ else {
+ if (removeDomainname) {
+ char *remoteDot;
+ char *localDot;
+
+ /* check for a common domain name. This
+ * could reduce names by recognising common
+ * super-domain names as well, but I don't think
+ * this is as useful, and will confuse more
+ * people
+ */
+ if ((localDot = strchr( localhost, '.' )) &&
+ (remoteDot = strchr( hostname, '.' )))
+ {
+ /* smash the name in place; it won't
+ * be needed later.
+ */
+ if (!strcmp( localDot+1, remoteDot+1 ))
+ *remoteDot = '\0';
+ }
+ }
+
+ ASPrintf( &name, "%s:%d", hostname, displayNumber );
+ }
+ } else {
+#if defined(IPv6) && defined(AF_INET6)
+ if (multiHomed) {
+ if (connectionType == FamilyInternet) {
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ } else {
+ data = (CARD8 *)
+ &((struct sockaddr_in6 *)originalAddress)->sin6_addr;
+ }
+ }
+ inet_ntop( type, data, dotted, sizeof(dotted) );
+ ASPrintf( &name, "%s:%d", dotted, displayNumber );
+#else
+ if (multiHomed)
+ data = (CARD8 *)
+ &((struct sockaddr_in *)originalAddress)->sin_addr;
+ ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber );
+#endif
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ if (ai)
+ freeaddrinfo( ai );
+#endif
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ return NULL;
+#endif /* DNET */
+ default:
+ return NULL;
+ }
+}
+
+/*ARGSUSED*/
+static void
+forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED,
+ int length, int fd)
+{
+ ARRAY8 clientAddress;
+ ARRAY8 clientPort;
+ ARRAYofARRAY8 authenticationNames;
+ struct sockaddr *client;
+ int clientlen;
+ int expectedLen;
+ int i;
+
+ Debug( "<forward> respond %d\n", length );
+ clientAddress.length = 0;
+ clientAddress.data = 0;
+ clientPort.length = 0;
+ clientPort.data = 0;
+ authenticationNames.length = 0;
+ authenticationNames.data = 0;
+ if (XdmcpReadARRAY8( &buffer, &clientAddress ) &&
+ XdmcpReadARRAY8( &buffer, &clientPort ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames ))
+ {
+ expectedLen = 0;
+ expectedLen += 2 + clientAddress.length;
+ expectedLen += 2 + clientPort.length;
+ expectedLen += 1; /* authenticationNames */
+ for (i = 0; i < (int)authenticationNames.length; i++)
+ expectedLen += 2 + authenticationNames.data[i].length;
+ if (length == expectedLen) {
+ int j;
+
+ j = 0;
+ for (i = 0; i < (int)clientPort.length; i++)
+ j = j * 256 + clientPort.data[i];
+ Debug( "<forward> client address (port %d) %[*hhu\n", j,
+ clientAddress.length, clientAddress.data );
+ switch (from->sa_family) {
+#ifdef AF_INET
+ case AF_INET:
+ {
+ struct sockaddr_in in_addr;
+
+ if (clientAddress.length != 4 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in_addr, sizeof(in_addr) );
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ in_addr.sin_len = sizeof(in_addr);
+#endif
+ in_addr.sin_family = AF_INET;
+ memmove( &in_addr.sin_addr, clientAddress.data, 4 );
+ memmove( (char *)&in_addr.sin_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in_addr;
+ clientlen = sizeof(in_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#if defined(IPv6) && defined(AF_INET6)
+ case AF_INET6:
+ {
+ struct sockaddr_in6 in6_addr;
+
+ if (clientAddress.length != 16 || clientPort.length != 2)
+ goto badAddress;
+ bzero( (char *)&in6_addr, sizeof(in6_addr) );
+#ifdef SIN6_LEN
+ in6_addr.sin6_len = sizeof(in6_addr);
+#endif
+ in6_addr.sin6_family = AF_INET6;
+ memmove( &in6_addr,clientAddress.data,clientAddress.length );
+ memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 );
+ client = (struct sockaddr *)&in6_addr;
+ clientlen = sizeof(in6_addr);
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_UNIX
+ case AF_UNIX:
+ {
+ struct sockaddr_un un_addr;
+
+ if (clientAddress.length >= sizeof(un_addr.sun_path))
+ goto badAddress;
+ bzero( (char *)&un_addr, sizeof(un_addr) );
+ un_addr.sun_family = AF_UNIX;
+ memmove( un_addr.sun_path, clientAddress.data, clientAddress.length );
+ un_addr.sun_path[clientAddress.length] = '\0';
+ client = (struct sockaddr *)&un_addr;
+#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN)
+ un_addr.sun_len = strlen( un_addr.sun_path );
+ clientlen = SUN_LEN( &un_addr );
+#else
+ clientlen = sizeof(un_addr);
+#endif
+ all_query_respond( client, clientlen, &authenticationNames,
+ FORWARD_QUERY, fd );
+ }
+ break;
+#endif
+#ifdef AF_CHAOS
+ case AF_CHAOS:
+ goto badAddress;
+#endif
+#ifdef AF_DECnet
+ case AF_DECnet:
+ goto badAddress;
+#endif
+ }
+ } else
+ Debug( "<forward> length error got %d expect %d\n", length, expectedLen );
+ }
+ badAddress:
+ XdmcpDisposeARRAY8( &clientAddress );
+ XdmcpDisposeARRAY8( &clientPort );
+ XdmcpDisposeARRAYofARRAY8( &authenticationNames );
+}
+
+static ARRAY8 Hostname;
+
+static void
+send_willing( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <willing> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)WILLING;
+ header.length =
+ 6 + authenticationName->length + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_unwilling( struct sockaddr *from, int fromlen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length,
+ authenticationName->data,
+ status->length,
+ status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)UNWILLING;
+ header.length = 4 + Hostname.length + status->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, &Hostname );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static unsigned long globalSessionID;
+
+#define NextSessionID() (++globalSessionID)
+
+void init_session_id( void )
+{
+ /* Set randomly so we are unlikely to reuse id's from a previous
+ * incarnation so we don't say "Alive" to those displays.
+ * Start with low digits 0 to make debugging easier.
+ */
+ globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000;
+
+ Hostname.data = (char *)localHostname();
+ Hostname.length = strlen( Hostname.data );
+}
+
+static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" };
+static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" };
+static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" };
+static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" };
+
+static void
+request_respond( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD16 displayNumber;
+ ARRAY16 connectionTypes;
+ ARRAYofARRAY8 connectionAddresses;
+ ARRAY8 authenticationName;
+ ARRAY8 authenticationData;
+ ARRAYofARRAY8 authorizationNames;
+ ARRAY8 manufacturerDisplayID;
+ ARRAY8Ptr reason = 0;
+ int expectlen;
+ int i, j;
+ struct protoDisplay *pdpy;
+ ARRAY8 authorizationName, authorizationData;
+ ARRAY8Ptr connectionAddress;
+
+ Debug( "<request> respond %d\n", length );
+ connectionTypes.data = 0;
+ connectionAddresses.data = 0;
+ authenticationName.data = 0;
+ authenticationData.data = 0;
+ authorizationNames.data = 0;
+ authorizationName.length = 0;
+ authorizationData.length = 0;
+ manufacturerDisplayID.data = 0;
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY16( &buffer, &connectionTypes ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationName ) &&
+ XdmcpReadARRAY8( &buffer, &authenticationData ) &&
+ XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) &&
+ XdmcpReadARRAY8( &buffer, &manufacturerDisplayID ))
+ {
+ expectlen = 0;
+ expectlen += 2; /* displayNumber */
+ expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */
+ expectlen += 1; /* connectionAddresses */
+ for (i = 0; i < (int)connectionAddresses.length; i++)
+ expectlen += 2 + connectionAddresses.data[i].length;
+ expectlen += 2 + authenticationName.length; /* authenticationName */
+ expectlen += 2 + authenticationData.length; /* authenticationData */
+ expectlen += 1; /* authoriationNames */
+ for (i = 0; i < (int)authorizationNames.length; i++)
+ expectlen += 2 + authorizationNames.data[i].length;
+ expectlen += 2 + manufacturerDisplayID.length; /* displayID */
+ if (expectlen != length) {
+ Debug( "<request> length error got %d expect %d\n",
+ length, expectlen );
+ goto abort;
+ }
+ if (connectionTypes.length == 0 ||
+ connectionAddresses.length != connectionTypes.length)
+ {
+ reason = &noValidAddr;
+ pdpy = 0;
+ goto decline;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ if (!pdpy) {
+
+ /* Check this Display against the Manager's policy */
+ reason = Accept( from, fromlen, displayNumber );
+ if (reason)
+ goto decline;
+
+ /* Check the Display's stream services against Manager's policy */
+ i = SelectConnectionTypeIndex( &connectionTypes,
+ &connectionAddresses );
+ if (i < 0) {
+ reason = &noValidAddr;
+ goto decline;
+ }
+
+ /* The Manager considers this a new session */
+ connectionAddress = &connectionAddresses.data[i];
+ pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber,
+ connectionTypes.data[i], connectionAddress,
+ NextSessionID() );
+ Debug( "NewProtoDisplay %p\n", pdpy );
+ if (!pdpy) {
+ reason = &outOfMemory;
+ goto decline;
+ }
+ }
+ if (authorizationNames.length == 0)
+ j = 0;
+ else
+ j = SelectAuthorizationTypeIndex( &authenticationName,
+ &authorizationNames );
+ if (j < 0) {
+ reason = &noValidAuth;
+ goto decline;
+ }
+ if (!CheckAuthentication( pdpy,
+ &manufacturerDisplayID,
+ &authenticationName,
+ &authenticationData ))
+ {
+ reason = &noAuthentic;
+ goto decline;
+ }
+ if (j < (int)authorizationNames.length) {
+ Xauth *auth;
+ SetProtoDisplayAuthorization( pdpy,
+ (unsigned short)authorizationNames.data[j].length,
+ (char *)authorizationNames.data[j].data );
+ auth = pdpy->xdmcpAuthorization;
+ if (!auth)
+ auth = pdpy->fileAuthorization;
+ if (auth) {
+ authorizationName.length = auth->name_length;
+ authorizationName.data = (CARD8Ptr) auth->name;
+ authorizationData.length = auth->data_length;
+ authorizationData.data = (CARD8Ptr) auth->data;
+ }
+ }
+ if (pdpy) {
+ send_accept( from, fromlen, pdpy->sessionID,
+ &authenticationName,
+ &authenticationData,
+ &authorizationName,
+ &authorizationData, fd );
+ } else {
+ decline:
+ send_decline( from, fromlen, &authenticationName,
+ &authenticationData,
+ reason, fd );
+ if (pdpy)
+ DisposeProtoDisplay( pdpy );
+ }
+ }
+ abort:
+ XdmcpDisposeARRAY16( &connectionTypes );
+ XdmcpDisposeARRAYofARRAY8( &connectionAddresses );
+ XdmcpDisposeARRAY8( &authenticationName );
+ XdmcpDisposeARRAY8( &authenticationData );
+ XdmcpDisposeARRAYofARRAY8( &authorizationNames );
+ XdmcpDisposeARRAY8( &manufacturerDisplayID );
+}
+
+static void
+send_accept( struct sockaddr *to, int tolen, CARD32 sessionID,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData,
+ int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<accept> session ID %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ACCEPT;
+ header.length = 4; /* session ID */
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ header.length += 2 + authorizationName->length;
+ header.length += 2 + authorizationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpWriteARRAY8( &buffer, authorizationName );
+ XdmcpWriteARRAY8( &buffer, authorizationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+send_decline( struct sockaddr *to, int tolen,
+ ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData,
+ ARRAY8Ptr status, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "<decline> %.*s\n", status->length, status->data );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)DECLINE;
+ header.length = 0;
+ header.length += 2 + status->length;
+ header.length += 2 + authenticationName->length;
+ header.length += 2 + authenticationData->length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteARRAY8( &buffer, status );
+ XdmcpWriteARRAY8( &buffer, authenticationName );
+ XdmcpWriteARRAY8( &buffer, authenticationData );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen );
+}
+
+static void
+manage( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ ARRAY8 displayClass;
+ int expectlen;
+ struct protoDisplay *pdpy;
+ struct display *d;
+ char *name = NULL;
+ char *class2 = NULL;
+ XdmcpNetaddr from_save;
+ ARRAY8 clientAddress, clientPort;
+ CARD16 connectionType;
+
+ Debug( "<manage> %d\n", length );
+ displayClass.data = 0;
+ displayClass.length = 0;
+ if (XdmcpReadCARD32( &buffer, &sessionID ) &&
+ XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadARRAY8( &buffer, &displayClass ))
+ {
+ expectlen = 4 + /* session ID */
+ 2 + /* displayNumber */
+ 2 + displayClass.length; /* displayClass */
+ if (expectlen != length) {
+ Debug( "<manage> length error got %d expect %d\n", length, expectlen );
+ goto abort;
+ }
+ pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber );
+ Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy );
+ if (!pdpy || pdpy->sessionID != sessionID) {
+ /*
+ * We may have already started a session for this display
+ * but it hasn't seen the response in the form of an
+ * XOpenDisplay() yet. So check if it is in the list of active
+ * displays, and if so check that the session id's match.
+ * If all this is true, then we have a duplicate request that
+ * can be ignored.
+ */
+ if (!pdpy &&
+ (d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber )) &&
+ d->sessionID == sessionID)
+ {
+ Debug( "manage: got duplicate pkt, ignoring\n" );
+ goto abort;
+ }
+ Debug( "session ID %ld refused\n", (long)sessionID );
+ if (pdpy)
+ Debug( "existing session ID %ld\n", (long)pdpy->sessionID );
+ send_refuse( from, fromlen, sessionID, fd );
+ } else {
+ name = NetworkAddressToName( pdpy->connectionType,
+ &pdpy->connectionAddress,
+ from,
+ pdpy->displayNumber );
+ if (!name) {
+ Debug( "could not compute display name\n" );
+ send_failed( from, fromlen, "(no name)", sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ Debug( "computed display name: %s\n", name );
+ if ((d = FindDisplayByName( name ))) {
+ Debug( "terminating active session for %s\n", d->name );
+ StopDisplay( d );
+ }
+ if (displayClass.length) {
+ if (!StrNDup( &class2, (char *)displayClass.data,
+ displayClass.length ))
+ {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ }
+ if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) {
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ memmove( from_save, from, fromlen );
+ if (!(d = NewDisplay( name ))) {
+ free( (char *)from_save );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->class2 = class2;
+ class2 = 0;
+ d->displayType = dForeign | dTransient | dFromXDMCP;
+ d->sessionID = pdpy->sessionID;
+ d->from.data = (unsigned char *)from_save;
+ d->from.length = fromlen;
+ d->displayNumber = pdpy->displayNumber;
+ ClientAddress( from, &clientAddress, &clientPort,
+ &connectionType );
+ d->useChooser = 0;
+ d->xdmcpFd = fd;
+ if (IsIndirectClient( &clientAddress, connectionType )) {
+ Debug( "IsIndirectClient\n" );
+ ForgetIndirectClient( &clientAddress, connectionType );
+ if (UseChooser( &clientAddress, connectionType )) {
+ d->useChooser = 1;
+ Debug( "use chooser for %s\n", d->name );
+ }
+ }
+ d->clientAddr = clientAddress;
+ d->connectionType = connectionType;
+ d->remoteHost = NetworkAddressToHostname (pdpy->connectionType,
+ &pdpy->connectionAddress);
+
+ XdmcpDisposeARRAY8( &clientPort );
+ if (pdpy->fileAuthorization) {
+ d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) );
+ if (!d->authorizations) {
+ free( (char *)from_save );
+ free( (char *)d );
+ send_failed( from, fromlen, name, sessionID,
+ "out of memory", fd );
+ goto abort;
+ }
+ d->authorizations[0] = pdpy->fileAuthorization;
+ d->authNum = 1;
+ pdpy->fileAuthorization = 0;
+ }
+ DisposeProtoDisplay( pdpy );
+ Debug( "starting display %s,%s\n", d->name, d->class2 );
+ if (LoadDisplayResources( d ) < 0) {
+ LogError( "Unable to read configuration for display %s; "
+ "stopping it.\n", d->name );
+ StopDisplay( d );
+ } else
+ StartDisplay( d );
+ CloseGetter();
+ }
+ }
+abort:
+ XdmcpDisposeARRAY8( &displayClass );
+ if (name)
+ free( (char *)name );
+ if (class2)
+ free( (char *)class2 );
+}
+
+void
+SendFailed( struct display *d, const char *reason )
+{
+ Debug( "display start failed, sending <failed>\n" );
+ send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name,
+ d->sessionID, reason, d->xdmcpFd );
+}
+
+static void
+send_failed( struct sockaddr *from, int fromlen,
+ const char *name, CARD32 sessionID, const char *reason, int fd )
+{
+ char buf[360];
+ XdmcpHeader header;
+ ARRAY8 status;
+
+ sprintf( buf, "Session %ld failed for display %.260s: %s",
+ (long)sessionID, name, reason );
+ Debug( "send_failed(%\"s)\n", buf );
+ status.length = strlen( buf );
+ status.data = (CARD8Ptr) buf;
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)FAILED;
+ header.length = 6 + status.length;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpWriteARRAY8( &buffer, &status );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd )
+{
+ XdmcpHeader header;
+
+ Debug( "send <refuse> %ld\n", (long)sessionID );
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)REFUSE;
+ header.length = 4;
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD32( &buffer, sessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+}
+
+static void
+send_alive( struct sockaddr *from, int fromlen, int length, int fd )
+{
+ CARD32 sessionID;
+ CARD16 displayNumber;
+ struct display *d;
+ XdmcpHeader header;
+ CARD8 sendRunning;
+ CARD32 sendSessionID;
+
+ Debug( "send <alive>\n" );
+ if (XdmcpReadCARD16( &buffer, &displayNumber ) &&
+ XdmcpReadCARD32( &buffer, &sessionID ))
+ {
+ if (length == 6) {
+ if (!(d = FindDisplayBySessionID( sessionID )))
+ d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen,
+ displayNumber );
+ sendRunning = 0;
+ sendSessionID = 0;
+ if (d && d->status == running) {
+ if (d->sessionID == sessionID)
+ sendRunning = 1;
+ sendSessionID = d->sessionID;
+ }
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16)ALIVE;
+ header.length = 5;
+ Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID );
+ XdmcpWriteHeader( &buffer, &header );
+ XdmcpWriteCARD8( &buffer, sendRunning );
+ XdmcpWriteCARD32( &buffer, sendSessionID );
+ XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen );
+ }
+ }
+}
+
+char *
+NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress )
+{
+ switch (connectionType) {
+ case FamilyInternet:
+#if defined(IPv6) && defined(AF_INET6)
+ case FamilyInternet6:
+#endif
+ {
+ struct hostent *he;
+ char *name, *lname;
+ char *myDot;
+ int af_type;
+#if defined(IPv6) && defined(AF_INET6)
+ char dotted[INET6_ADDRSTRLEN];
+
+ if (connectionType == FamilyInternet6)
+ af_type = AF_INET6;
+ else
+#endif
+ af_type = AF_INET;
+
+ he = gethostbyaddr( (char *)connectionAddress->data,
+ connectionAddress->length, af_type );
+ if (he) {
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai, *nai;
+ if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ goto oki;
+ }
+ }
+ freeaddrinfo( ai );
+#else
+ if ((he = gethostbyname( he->h_name )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ goto oki;
+#endif
+ LogError( "DNS spoof attempt or misconfigured resolver.\n" );
+ }
+ goto gotnone;
+ oki:
+ if (StrDup( &name, he->h_name ) &&
+ !strchr( name, '.' ) &&
+ (myDot = strchr( localHostname(), '.' )))
+ {
+ if (ASPrintf( &lname, "%s%s", name, myDot )) {
+#if defined(IPv6) && defined(AF_INET6)
+ if (!getaddrinfo( lname, NULL, NULL, &ai )) {
+ for (nai = ai; nai; nai = nai->ai_next) {
+ if (af_type == nai->ai_family &&
+ !memcmp( nai->ai_family == AF_INET ?
+ (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr :
+ (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr,
+ connectionAddress->data,
+ connectionAddress->length ))
+ {
+ freeaddrinfo( ai );
+ free( name );
+ return lname;
+ }
+ }
+ freeaddrinfo( ai );
+ }
+#else
+ if ((he = gethostbyname( lname )) &&
+ he->h_addrtype == AF_INET)
+ {
+ int i;
+ for (i = 0; he->h_addr_list[i]; i++)
+ if (!memcmp( he->h_addr_list[i],
+ connectionAddress->data, 4 ))
+ {
+ free( name );
+ return lname;
+ }
+ }
+#endif
+ free( lname );
+ }
+ }
+ } else {
+ gotnone:
+ /* can't get name, so use emergency fallback */
+#if defined(IPv6) && defined(AF_INET6)
+ inet_ntop( af_type, connectionAddress->data,
+ dotted, sizeof(dotted) );
+ StrDup( &name, dotted );
+#else
+ ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data );
+#endif
+ LogWarn( "Cannot convert Internet address %s to host name\n",
+ name );
+ }
+ return name;
+ }
+#ifdef DNET
+ case FamilyDECnet:
+ break;
+#endif /* DNET */
+ default:
+ break;
+ }
+ return 0;
+}
+
+#endif /* XDMCP */
+