Logo Search packages:      
Sourcecode: kdebase version File versions

xdmcp.c

/*

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 "dm.h"
#include "dm_error.h"

#ifdef XDMCP

#include "dm_auth.h"
#include "dm_socket.h"

#include <X11/X.h>
#include <X11/Xfuncs.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 BSD44SOCKETS
      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, (char **)&(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  *localDot, *remoteDot;
    
                  /* 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 BSD44SOCKETS
                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(BSD44SOCKETS) && !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;
          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);
          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)
      {
          d = FindDisplayBySessionID (sessionID);
          if (!d) {
            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 *myDot, *name, *lname;
          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
            LogInfo ("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 */


Generated by  Doxygen 1.6.0   Back to index