/* Copyright 1988, 1998 The Open Group 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 OPEN GROUP 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 The Open Group 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 Open Group. */ /* * xdm - display manager daemon * Author: Keith Packard, MIT X Consortium * * xdmcp.c - Support for XDMCP */ #include "dm.h" #include "dm_auth.h" #include "dm_error.h" #ifdef XDMCP # include # include # include # include # include "dm_socket.h" # ifndef X_NO_SYS_UN # include # endif # include # include # include # define Time_t time_t # define getString(name,len) ((name = malloc (len + 1)) ? 1 : 0) /* * misc externs */ extern volatile int Rescan; extern int sourceAddress, ChildReady; /* * 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, char *name, CARD32 sessionID, 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); # ifdef STREAMSCONN int xdmcpFd = -1; # endif int chooserFd = -1; # if defined(IPv6) && defined(AF_INET6) int chooserFd6 = -1; # endif FD_TYPE WellKnownSocketsMask; int WellKnownSocketsMax; # define pS(s) ((s) ? ((char *) (s)) : "empty string") void DestroyWellKnownSockets (void) { # ifdef STREAMSCONN if (xdmcpFd != -1) { close (xdmcpFd); FD_CLR(xdmcpFd, &WellKnownSocketsMask); xdmcpFd = -1; } # endif if (chooserFd != -1) { close (chooserFd); FD_CLR(chooserFd, &WellKnownSocketsMask); chooserFd = -1; } # if defined(IPv6) && defined(AF_INET6) if (chooserFd6 != -1) { close (chooserFd6); FD_CLR(chooserFd6, &WellKnownSocketsMask); chooserFd6 = -1; } # endif CloseListenSockets(); } static int FD_ANYSET(fd_set *fds) { int i; char *mask = (char *) fds; for (i = 0 ; i < sizeof(fds); i++) { if (mask[i]) return TRUE; } return FALSE; } int AnyWellKnownSockets (void) { return # ifdef STREAMS_CONN xdmcpFd != -1 || # endif # if defined(IPv6) && defined(AF_INET6) chooserFd6 != -1 || # endif chooserFd != -1 || FD_ANYSET(&WellKnownSocketsMask); } static XdmcpBuffer buffer; /*ARGSUSED*/ 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 = {0, NULL}; ARRAY8 addr = {0, NULL}; CARD16 connectionType; int family; int length; const char *addrstring; # if defined(IPv6) && defined(AF_INET6) char addrbuf[INET6_ADDRSTRLEN] = ""; # endif family = ConvertAddr((XdmcpNetaddr) from, &length, (char **)&(addr.data)); addr.length = length; /* convert int to short */ if (debugLevel > 0) { # if defined(IPv6) && defined(AF_INET6) void *ipaddr; int af_type; if (family == FamilyInternet6) { ipaddr = & ((struct sockaddr_in6 *) from)->sin6_addr; af_type = AF_INET6; } else { ipaddr = & ((struct sockaddr_in *) from)->sin_addr; af_type = AF_INET; } addrstring = inet_ntop(af_type, ipaddr, addrbuf, sizeof(addrbuf)); # else addrstring = inet_ntoa(((struct sockaddr_in *)from)->sin_addr); # endif Debug("all_query_respond: conntype=%d, addr=%s, len=%d\n", family, addrstring, addr.length); } 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 = {0, NULL}; ARRAY8 clientAddress = {0, NULL}; ARRAY8 clientPort = {0, NULL}; 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; } } void WaitForSomething (void) { FD_TYPE reads; int nready; Debug ("WaitForSomething\n"); if (AnyWellKnownSockets () && !ChildReady) { reads = WellKnownSocketsMask; nready = select (WellKnownSocketsMax + 1, &reads, NULL, NULL, NULL); Debug ("select returns %d. Rescan: %d ChildReady: %d\n", nready, Rescan, ChildReady); if (nready > 0) { # ifdef STREAMSCONN if (xdmcpFd >= 0 && FD_ISSET (xdmcpFd, &reads)) ProcessRequestSocket (xdmcpFd); # endif if (chooserFd >= 0 && FD_ISSET (chooserFd, &reads)) { # ifdef ISC if (!ChildReady) { WaitForSomething (); } else # endif ProcessChooserSocket (chooserFd); FD_CLR(chooserFd, &reads); } # if defined(IPv6) && defined(AF_INET6) if (chooserFd6 >= 0 && FD_ISSET (chooserFd6, &reads)) { # ifdef ISC if (!ChildReady) { WaitForSomething (); } else # endif ProcessChooserSocket (chooserFd6); FD_CLR(chooserFd6, &reads); } # endif ProcessListenSockets(&reads); } if (ChildReady) { WaitForChild (); } } else WaitForChild (); } /* * respond to a request on the UDP socket. */ static ARRAY8 Hostname; void registerHostname ( char *name, int namelen) { int i; if (!XdmcpReallocARRAY8 (&Hostname, namelen)) return; for (i = 0; i < namelen; i++) Hostname.data[i] = name[i]; } static void direct_query_respond ( struct sockaddr *from, int fromlen, int length, xdmOpCode type, int fd) { ARRAYofARRAY8 queryAuthenticationNames = {0, NULL}; 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: { CARD8 *data; struct hostent *hostent; char *hostname = NULL; char *name; char *localhost; int multiHomed = 0; struct addrinfo hints, *ai = NULL, *nai; int type; if (connectionType == FamilyInternet6) type = AF_INET6; else type = AF_INET; data = connectionAddress->data; hostent = gethostbyaddr ((char *)data, connectionAddress->length, type); if (hostent) { if (sourceAddress) { bzero(&hints, sizeof(hints)); hints.ai_flags = AI_CANONNAME; if (getaddrinfo(hostent->h_name, NULL, &hints, &ai) == 0) { hostname = ai->ai_canonname; for (nai = ai->ai_next; nai!=NULL; nai=nai->ai_next) { if ((ai->ai_protocol == nai->ai_protocol) && (ai->ai_addrlen == nai->ai_addrlen) && (memcmp(ai->ai_addr,nai->ai_addr, ai->ai_addrlen) != 0) ) { multiHomed = 1; } } } } else { hostname = hostent->h_name; } } localhost = localHostname (); /* * protect against bogus host names */ if (hostname && hostname[0] && (hostname[0] != '.') && !multiHomed) { if (!strcmp (localhost, hostname)) { if (asprintf(&name, ":%d", displayNumber) < 0) { if (ai) freeaddrinfo(ai); return NULL; } } 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'; } } if (asprintf (&name, "%s:%d", hostname, displayNumber) < 0) { if (ai) freeaddrinfo(ai); return NULL; } } } else { if (!getString (name, INET6_ADDRSTRLEN + 10)) { if (ai) freeaddrinfo(ai); return NULL; } if (multiHomed) { if (connectionType == FamilyInternet) { data = (CARD8 *) &((struct sockaddr_in *)originalAddress)-> sin_addr; } else { data = (CARD8 *) &((struct sockaddr_in6 *)originalAddress)->sin6_addr; } } if (inet_ntop(type, data, name, INET6_ADDRSTRLEN) == NULL) { free(name); if (ai) freeaddrinfo(ai); return NULL; } snprintf(name + strlen(name), 10, ":%d", displayNumber); } if (ai) freeaddrinfo(ai); return name; } # else /* IPv6 */ { CARD8 *data; struct hostent *hostent; char *name; char *localhost; int multiHomed = 0; data = connectionAddress->data; hostent = gethostbyaddr ((char *)data, connectionAddress->length, AF_INET); if (sourceAddress && hostent) { hostent = gethostbyname(hostent->h_name); if (hostent) multiHomed = hostent->h_addr_list[1] != NULL; } localhost = localHostname (); /* * protect against bogus host names */ if (hostent && hostent->h_name && hostent->h_name[0] && (hostent->h_name[0] != '.') && !multiHomed) { if (!strcmp (localhost, hostent->h_name)) { if (asprintf(&name, ":%d", displayNumber) < 0) { return NULL; } } 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(hostent->h_name, '.'))) { /* smash the name in place; it won't * be needed later. */ if (!strcmp (localDot+1, remoteDot+1)) *remoteDot = '\0'; } } if (asprintf(&name, "%s:%d", hostent->h_name, displayNumber) < 0) { return NULL; } } } else { if (multiHomed) data = (CARD8 *) &((struct sockaddr_in *)originalAddress)-> sin_addr.s_addr; if (asprintf(&name, "%d.%d.%d.%d:%d", data[0], data[1], data[2], data[3], displayNumber) < 0) { return NULL; } } return name; } # endif /* IPv6 */ # ifdef DNET case FamilyDECnet: return NULL; # endif /* DNET */ default: return NULL; } } /*ARGSUSED*/ static void forward_respond ( struct sockaddr *from, int fromlen, int length, int fd) { ARRAY8 clientAddress = {0, NULL}; ARRAY8 clientPort = {0, NULL}; ARRAYofARRAY8 authenticationNames = {0, NULL}; struct sockaddr *client; int clientlen; int expectedLen; int i; Debug ("Forward respond %d\n", length); 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)", j); for (i = 0; i < (int)clientAddress.length; i++) Debug (" %d", clientAddress.data[i]); Debug ("\n"); 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.sin6_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(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 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->length, pS(authenticationName->data), status->length, status->length, pS(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->length, pS(authenticationName->data), status->length, status->length, pS(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; } 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 = {0, NULL}; ARRAYofARRAY8 connectionAddresses = {0, NULL}; ARRAY8 authenticationName = {0, NULL}; ARRAY8 authenticationData = {0, NULL}; ARRAYofARRAY8 authorizationNames = {0, NULL}; ARRAY8 manufacturerDisplayID = {0, NULL}; ARRAY8Ptr reason = NULL; int expectlen; int i, j; struct protoDisplay *pdpy = NULL; ARRAY8 authorizationName = {0, NULL}, authorizationData = {0, NULL}; ARRAY8Ptr connectionAddress; Debug ("Request respond %d\n", length); 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; 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->length, pS(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 = {0, NULL}; int expectlen; struct protoDisplay *pdpy; struct display *d; char *name = NULL; char *class = NULL; XdmcpNetaddr from_save; ARRAY8 clientAddress = {0, NULL}, clientPort = {0, NULL}; CARD16 connectionType; Debug ("Manage %d\n", length); 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); Debug ("Computed display name: %s for: %s\n", name, (char *)pdpy->connectionAddress.data); if (!name) { send_failed (from, fromlen, "(no name)", sessionID, "out of memory", fd); goto abort; } d = FindDisplayByName (name); if (d) { Debug ("Terminating active session for %s\n", d->name); StopDisplay (d); } class = malloc (displayClass.length + 1); if (!class) { send_failed (from, fromlen, name, sessionID, "out of memory", fd); goto abort; } if (displayClass.length) { memmove( class, displayClass.data, displayClass.length); class[displayClass.length] = '\0'; } else { free ((char *) class); class = (char *) NULL; } from_save = (XdmcpNetaddr) malloc (fromlen); if (!from_save) { send_failed (from, fromlen, name, sessionID, "out of memory", fd); goto abort; } memmove( from_save, from, fromlen); d = NewDisplay (name, class); if (!d) { free ((char *) from_save); send_failed (from, fromlen, name, sessionID, "out of memory", fd); goto abort; } d->displayType.location = Foreign; d->displayType.lifetime = Transient; d->displayType.origin = FromXDMCP; d->sessionID = pdpy->sessionID; d->from = from_save; d->fromlen = 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 = NULL; } DisposeProtoDisplay (pdpy); Debug ("Starting display %s,%s\n", d->name, d->class); StartDisplay (d); } } abort: XdmcpDisposeARRAY8 (&displayClass); if (name) free ((char*) name); if (class) free ((char*) class); } void SendFailed ( struct display *d, char *reason) { Debug ("Display start failed, sending Failed\n"); send_failed ((struct sockaddr *)(d->from), d->fromlen, d->name, d->sessionID, reason, d->xdmcpFd); } static void send_failed ( struct sockaddr *from, int fromlen, char *name, CARD32 sessionID, char *reason, int fd) { static char buf[256]; XdmcpHeader header; ARRAY8 status; snprintf (buf, sizeof(buf), "Session %ld failed for display %.100s: %s", (long) sessionID, name, reason); Debug ("Send failed %ld %s\n", (long) sessionID, 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) { char *name = NULL; switch (connectionType) { case FamilyInternet: # if defined(IPv6) && defined(AF_INET6) case FamilyInternet6: # endif { struct hostent *hostent = NULL; # if defined(IPv6) && defined(AF_INET6) char dotted[INET6_ADDRSTRLEN]; # else char dotted[20]; # endif char *local_name = ""; int af_type; # if defined(IPv6) && defined(AF_INET6) if (connectionType == FamilyInternet6) af_type = AF_INET6; else # endif af_type = AF_INET; hostent = gethostbyaddr ((char *)connectionAddress->data, connectionAddress->length, af_type); if (hostent) { /* check for DNS spoofing */ # if defined(IPv6) && defined(AF_INET6) struct addrinfo *ai = NULL, *nai; if (getaddrinfo(hostent->h_name, NULL, NULL, &ai) == 0) { for (nai = ai; nai != NULL; nai = nai->ai_next) { if ((af_type == nai->ai_family) && ( ((nai->ai_family == AF_INET) && (connectionAddress->length == sizeof(struct in_addr)) && (memcmp(connectionAddress->data, &((struct sockaddr_in *)nai->ai_addr)->sin_addr, connectionAddress->length) == 0)) || ((nai->ai_family == AF_INET6) && (connectionAddress->length == sizeof(struct in6_addr)) && (memcmp(connectionAddress->data, &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr, connectionAddress->length) == 0)))) break; } if (nai == NULL) { inet_ntop(af_type, connectionAddress->data, dotted, sizeof(dotted)); LogError("Possible DNS spoof attempt %s->%s.\n", dotted, hostent->h_name); hostent = NULL; } else { local_name = hostent->h_name; } freeaddrinfo(ai); } else { hostent = NULL; } # else char *s = strdup(hostent->h_name); /* fscking non-reentrancy of getXXX() */ if ((hostent = gethostbyname(s))) { if (memcmp((char*)connectionAddress->data, hostent->h_addr, hostent->h_length) != 0) { LogError("Possible DNS spoof attempt.\n"); hostent = NULL; /* so it enters next if() */ } else { local_name = hostent->h_name; } } free(s); # endif } if (!hostent) { /* can't get name, so use emergency fallback */ # if defined(IPv6) && defined(AF_INET6) inet_ntop(af_type, connectionAddress->data, dotted, sizeof(dotted)); # else snprintf(dotted, sizeof(dotted), "%d.%d.%d.%d", connectionAddress->data[0], connectionAddress->data[1], connectionAddress->data[2], connectionAddress->data[3]); # endif local_name = dotted; LogError ("Cannot convert Internet address %s to host name\n", dotted); } name = strdup (local_name); break; } # ifdef DNET case FamilyDECnet: break; # endif /* DNET */ default: break; } return name; } # if 0 static int HostnameToNetworkAddress ( char *name, CARD16 connectionType, ARRAY8Ptr connectionAddress) { switch (connectionType) { case FamilyInternet: { struct hostent *hostent; hostent = gethostbyname (name); if (!hostent) return FALSE; if (!XdmcpAllocARRAY8 (connectionAddress, hostent->h_length)) return FALSE; memmove( connectionAddress->data, hostent->h_addr, hostent->h_length); return TRUE; } # ifdef DNET case FamilyDECnet: return FALSE; # endif } return FALSE; } /* * converts a display name into a network address, using * the same rules as XOpenDisplay (algorithm cribbed from there) */ static int NameToNetworkAddress( char *name, CARD16Ptr connectionTypep, ARRAY8Ptr connectionAddress, CARD16Ptr displayNumber) { char *colon, *display_number; char hostname[1024]; int dnet = FALSE; CARD16 number; CARD16 connectionType; colon = strchr(name, ':'); if (!colon) return FALSE; if (colon != name) { if (colon - name > sizeof (hostname)) return FALSE; strncpy (hostname, name, colon - name); hostname[colon - name] = '\0'; } else { strcpy (hostname, localHostname ()); } if (colon[1] == ':') { dnet = TRUE; colon++; } # ifndef DNETCONN if (dnet) return FALSE; # endif display_number = colon + 1; while (*display_number && *display_number != '.') { if (!isascii (*display_number) || !isdigit(*display_number)) return FALSE; } if (display_number == colon + 1) return FALSE; number = atoi (colon + 1); # ifdef DNETCONN if (dnet) connectionType = FamilyDECnet; else # endif connectionType = FamilyInternet; if (!HostnameToNetworkAddress (hostname, connectionType, connectionAddress)) return FALSE; *displayNumber = number; *connectionTypep = connectionType; return TRUE; } # endif #endif /* XDMCP */