dtlogin: Coverity (memory corruption, moderate)
[oweals/cde.git] / cde / programs / dtlogin / xdmcp.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: xdmcp.c /main/5 1998/04/06 13:36:04 mgreess $ */
24 /* (c) Copyright 1997 The Open Group */
25 /*                                                                      *
26  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
27  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
28  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
29  * (c) Copyright 1993, 1994 Novell, Inc.                                *
30  */
31
32 /*
33  * @DEC_COPYRIGHT@
34  */
35 /*
36  * HISTORY
37  * $Log$
38  * Revision 1.1.2.3  1995/06/06  20:25:54  Chris_Beute
39  *      Code snapshot merge from March 15 and SIA changes
40  *      [1995/05/31  20:17:42  Chris_Beute]
41  *
42  * Revision 1.1.2.2  1995/04/21  13:05:47  Peter_Derr
43  *      dtlogin auth key fixes from deltacde
44  *      [1995/04/14  18:03:44  Peter_Derr]
45  * 
46  *      Merge in dtlogin changes to WaitForSomething() to support command
47  *      line console login.
48  *      [1995/04/14  17:40:05  Peter_Derr]
49  * 
50  *      Use R6 xdm code to handle XDMCP
51  *      [1995/04/10  19:24:11  Peter_Derr]
52  * 
53  * $EndLog$
54  */
55 /*
56
57 Copyright (c) 1988  X Consortium
58
59 Permission is hereby granted, free of charge, to any person obtaining
60 a copy of this software and associated documentation files (the
61 "Software"), to deal in the Software without restriction, including
62 without limitation the rights to use, copy, modify, merge, publish,
63 distribute, sublicense, and/or sell copies of the Software, and to
64 permit persons to whom the Software is furnished to do so, subject to
65 the following conditions:
66
67 The above copyright notice and this permission notice shall be included
68 in all copies or substantial portions of the Software.
69
70 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
71 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
72 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
73 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
74 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
75 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
76 OTHER DEALINGS IN THE SOFTWARE.
77
78 Except as contained in this notice, the name of the X Consortium shall
79 not be used in advertising or otherwise to promote the sale, use or
80 other dealings in this Software without prior written authorization
81 from the X Consortium.
82
83 */
84
85 /*
86  * xdm - display manager daemon
87  * Author:  Keith Packard, MIT X Consortium
88  *
89  * xdmcp.c - Support for XDMCP
90  */
91
92 # include "dm.h"
93
94 # include       <X11/X.h>
95 # include       <X11/Xfuncs.h>
96 # include       <sys/types.h>
97 # include       <ctype.h>
98
99 #include        <sys/socket.h>
100 #include        <netinet/in.h>
101 #include        <sys/un.h>
102 #include        <netdb.h>
103
104 #ifdef X_NOT_STDC_ENV
105 #define Time_t long
106 extern Time_t time ();
107 #else
108 #include <time.h>
109 #define Time_t time_t
110 #endif
111
112 #define getString(name,len)     ((name = malloc (len + 1)) ? 1 : 0)
113
114 /*
115  * interface to policy routines
116  */
117
118 extern ARRAY8Ptr        ChooseAuthentication ();
119 extern int              SelectConnectionTypeIndex ();
120
121 void query_respond (from, fromlen, length);
122 void broadcast_respond (struct sockaddr *from, int fromlen, int length);
123 void forward_respond (struct sockaddr *from, int fromlen, int length);
124 void request_respond (struct sockaddr *from, int fromlen, int length);
125 void send_willing (struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status);
126 void send_unwilling (struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status);
127 void send_accept (struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData);
128 void manage (struct sockaddr *from, int fromlen, int length);
129 void send_decline (struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status);
130 void send_failed (struct sockaddr *from, int fromlen, char *name, CARD32 sessionID, char *reason);
131 void send_refuse (struct sockaddr *from, int fromlen, CARD32 sessionID);
132 void send_alive (struct sockaddr *from, int fromlen, int length);
133
134
135
136 int     xdmcpFd = -1;
137 int     chooserFd = -1;
138
139 FD_TYPE WellKnownSocketsMask;
140 int     WellKnownSocketsMax;
141
142 #define pS(s)   ((s) ? ((char *) (s)) : "empty string")
143
144 void
145 DestroyWellKnownSockets (void)
146 {
147     if (xdmcpFd != -1)
148     {
149         close (xdmcpFd);
150         xdmcpFd = -1;
151     }
152     if (chooserFd != -1)
153     {
154         close (chooserFd);
155         chooserFd = -1;
156     }
157 }
158
159 int
160 AnyWellKnownSockets (void)
161 {
162     return xdmcpFd != -1 || chooserFd != -1;
163 }
164
165 static XdmcpBuffer      buffer;
166
167 /*ARGSUSED*/
168 static int
169 sendForward (CARD16 connectionType, ARRAY8Ptr address, char *closure)
170 {
171 #ifdef AF_INET
172     struct sockaddr_in      in_addr;
173 #endif
174 #ifdef AF_DECnet
175 #endif
176     struct sockaddr         *addr;
177     int                     addrlen;
178
179     switch (connectionType)
180     {
181 #ifdef AF_INET
182     case FamilyInternet:
183         addr = (struct sockaddr *) &in_addr;
184         bzero ((char *) &in_addr, sizeof (in_addr));
185 #ifdef BSD44SOCKETS
186         in_addr.sin_len = sizeof(in_addr);
187 #endif
188         in_addr.sin_family = AF_INET;
189         in_addr.sin_port = htons ((short) XDM_UDP_PORT);
190         if (address->length != 4)
191             return 0;
192         memmove( (char *) &in_addr.sin_addr, address->data, address->length);
193         addrlen = sizeof (struct sockaddr_in);
194         break;
195 #endif
196 #ifdef AF_DECnet
197     case FamilyDECnet:
198 #endif
199     default:
200         return 0;
201     }
202     XdmcpFlush (xdmcpFd, &buffer, addr, addrlen);
203     return 0;
204 }
205
206 extern char *NetaddrAddress();
207 extern char *NetaddrPort();
208
209 static void
210 ClientAddress (struct sockaddr *from, ARRAY8Ptr addr, ARRAY8Ptr port, CARD16 *type)
211 {
212     int length, family;
213     char *data;
214
215     data = NetaddrPort(from, &length);
216     XdmcpAllocARRAY8 (port, length);
217     memmove( port->data, data, length);
218     port->length = length;
219
220     family = ConvertAddr((char *)from, &length, &data);
221
222     XdmcpAllocARRAY8 (addr, length);
223     memmove( addr->data, data, length);
224     addr->length = length;
225
226     *type = family;
227 }
228
229 static void
230 all_query_respond (struct sockaddr *from, int fromlen, ARRAYofARRAY8Ptr authenticationNames, xdmOpCode type)
231 {
232     ARRAY8Ptr   authenticationName;
233     ARRAY8      status;
234     ARRAY8      addr;
235     CARD16      connectionType;
236     int         family;
237     int         length;
238
239     family = ConvertAddr((char *)from, &length, (char **) &(addr.data));
240
241     addr.length = length;       /* convert int to short */
242     Debug ("all_query_respond: conntype=%d, addr=%lx, len=%d\n",
243            family, *(addr.data), addr.length);
244     if (family < 0)
245         return;
246     connectionType = family;
247
248     if (type == INDIRECT_QUERY)
249         RememberIndirectClient (&addr, connectionType);
250     else
251         ForgetIndirectClient (&addr, connectionType);
252
253     authenticationName = ChooseAuthentication (authenticationNames);
254     if (Willing (&addr, connectionType, authenticationName, &status, type))
255         send_willing (from, fromlen, authenticationName, &status);
256     else
257         if (type == QUERY)
258             send_unwilling (from, fromlen, authenticationName, &status);
259     XdmcpDisposeARRAY8 (&status);
260 }
261
262 static void
263 indirect_respond (struct sockaddr *from, int fromlen, int length)
264 {
265     ARRAYofARRAY8   queryAuthenticationNames;
266     ARRAY8          clientAddress;
267     ARRAY8          clientPort;
268     CARD16          connectionType;
269     int             expectedLen;
270     int             i;
271     XdmcpHeader     header;
272     int             localHostAsWell;
273     
274     Debug ("Indirect respond %d\n", length);
275     if (!XdmcpReadARRAYofARRAY8 (&buffer, &queryAuthenticationNames))
276         return;
277     expectedLen = 1;
278     for (i = 0; i < (int)queryAuthenticationNames.length; i++)
279         expectedLen += 2 + queryAuthenticationNames.data[i].length;
280     if (length == expectedLen)
281     {
282         ClientAddress (from, &clientAddress, &clientPort, &connectionType);
283         /*
284          * set up the forward query packet
285          */
286         header.version = XDM_PROTOCOL_VERSION;
287         header.opcode = (CARD16) FORWARD_QUERY;
288         header.length = 0;
289         header.length += 2 + clientAddress.length;
290         header.length += 2 + clientPort.length;
291         header.length += 1;
292         for (i = 0; i < (int)queryAuthenticationNames.length; i++)
293             header.length += 2 + queryAuthenticationNames.data[i].length;
294         XdmcpWriteHeader (&buffer, &header);
295         XdmcpWriteARRAY8 (&buffer, &clientAddress);
296         XdmcpWriteARRAY8 (&buffer, &clientPort);
297         XdmcpWriteARRAYofARRAY8 (&buffer, &queryAuthenticationNames);
298
299         localHostAsWell = ForEachMatchingIndirectHost (&clientAddress, connectionType, sendForward, (char *) 0);
300         
301         XdmcpDisposeARRAY8 (&clientAddress);
302         XdmcpDisposeARRAY8 (&clientPort);
303         if (localHostAsWell)
304             all_query_respond (from, fromlen, &queryAuthenticationNames,
305                            INDIRECT_QUERY);
306     }
307     else
308     {
309         Debug ("Indirect length error got %d expect %d\n", length, expectedLen);
310     }
311     XdmcpDisposeARRAYofARRAY8 (&queryAuthenticationNames);
312 }
313
314 static void
315 ProcessRequestSocket (void)
316 {
317     XdmcpHeader         header;
318     struct sockaddr_in  addr;
319     int                 addrlen = sizeof addr;
320
321     Debug ("ProcessRequestSocket\n");
322     bzero ((char *) &addr, sizeof (addr));
323     if (!XdmcpFill (xdmcpFd, &buffer, &addr, &addrlen)) {
324         Debug ("XdmcpFill failed\n");
325         return;
326     }
327     if (!XdmcpReadHeader (&buffer, &header)) {
328         Debug ("XdmcpReadHeader failed\n");
329         return;
330     }
331     if (header.version != XDM_PROTOCOL_VERSION) {
332         Debug ("XDMCP header version read was %d, expected %d\n",
333                header.version, XDM_PROTOCOL_VERSION);
334         return;
335     }
336     Debug ("header: %d %d %d\n", header.version, header.opcode, header.length);
337     switch (header.opcode)
338     {
339     case BROADCAST_QUERY:
340         broadcast_respond (&addr, addrlen, header.length);
341         break;
342     case QUERY:
343         query_respond (&addr, addrlen, header.length);
344         break;
345     case INDIRECT_QUERY:
346         indirect_respond (&addr, addrlen, header.length);
347         break;
348     case FORWARD_QUERY:
349         forward_respond (&addr, addrlen, header.length);
350         break;
351     case REQUEST:
352         request_respond (&addr, addrlen, header.length);
353         break;
354     case MANAGE:
355         manage (&addr, addrlen, header.length);
356         break;
357     case KEEPALIVE:
358         send_alive (&addr, addrlen, header.length);
359         break;
360     }
361 }
362
363 /*
364  * dtlogin changes to WaitForSomething () merged in to support command line
365  * login.
366  */
367 void WaitForSomething (void)
368 {
369     FD_TYPE     reads;
370     struct timeval      timeout, *ptimeout;
371     int nready;
372     extern int Rescan, ChildReady, wakeupTime;
373
374     Debug ("WaitForSomething\n");
375     if (AnyWellKnownSockets () && !ChildReady || wakeupTime > 0 ) {
376         reads = WellKnownSocketsMask;
377
378         if (wakeupTime >= 0 ) {
379             timeout.tv_sec  = wakeupTime;
380             timeout.tv_usec = 10;
381             ptimeout = &timeout;
382             Debug("Setting timer on select() for %d seconds.\n", wakeupTime);
383         }
384         else
385             ptimeout = NULL;
386
387 #ifdef __hpux
388         nready = select (WellKnownSocketsMax + 1, (int *) &reads, 0, 0, ptimeout);
389 #else
390         nready = select (WellKnownSocketsMax + 1, &reads, 0, 0, ptimeout);
391 #endif
392         Debug ("select returns %d.  Rescan: %d  ChildReady: %d\n",
393                 nready, Rescan, ChildReady);
394         if (nready > 0)
395         {
396             if (xdmcpFd >= 0 && FD_ISSET (xdmcpFd, &reads))
397                 ProcessRequestSocket ();
398             if (chooserFd >= 0 && FD_ISSET (chooserFd, &reads))
399                 ProcessChooserSocket (chooserFd);
400         }
401         if (ChildReady)
402         {
403             WaitForChild ();
404         }
405         else
406             StartDisplays();
407     } else
408         WaitForChild ();
409 }
410
411 /*
412  * respond to a request on the UDP socket.
413  */
414
415 static ARRAY8   Hostname;
416
417 void
418 registerHostname (char *name, int namelen)
419 {
420     int i;
421
422     if (!XdmcpReallocARRAY8 (&Hostname, namelen))
423         return;
424     for (i = 0; i < namelen; i++)
425         Hostname.data[i] = name[i];
426 }
427
428 static void
429 direct_query_respond (struct sockaddr *from, int fromlen, int length, xdmOpCode type)
430 {
431     ARRAYofARRAY8   queryAuthenticationNames;
432     int             expectedLen;
433     int             i;
434     
435     if (!XdmcpReadARRAYofARRAY8 (&buffer, &queryAuthenticationNames))
436         return;
437     expectedLen = 1;
438     for (i = 0; i < (int)queryAuthenticationNames.length; i++)
439         expectedLen += 2 + queryAuthenticationNames.data[i].length;
440     if (length == expectedLen)
441         all_query_respond (from, fromlen, &queryAuthenticationNames, type);
442     XdmcpDisposeARRAYofARRAY8 (&queryAuthenticationNames);
443 }
444
445 void
446 query_respond (struct sockaddr *from, int fromlen, int length)
447 {
448     Debug ("Query respond %d\n", length);
449     direct_query_respond (from, fromlen, length, QUERY);
450 }
451
452 void
453 broadcast_respond (struct sockaddr *from, int fromlen, int length)
454 {
455     direct_query_respond (from, fromlen, length, BROADCAST_QUERY);
456 }
457
458 /* computes an X display name */
459 #if NeedWidePrototypes
460 char *
461 NetworkAddressToName(int connectionType, ARRAY8Ptr connectionAddress, int displayNumber)
462 #else
463 char *
464 NetworkAddressToName(CARD16 connectionType, ARRAY8Ptr connectionAddress, CARD16 displayNumber)
465 #endif
466 {
467     switch (connectionType)
468     {
469     case FamilyInternet:
470         {
471             CARD8               *data;
472             struct hostent      *hostent;
473             char                *name;
474             char                *localhost, *localHostname();
475
476             data = connectionAddress->data;
477             hostent = gethostbyaddr ((char *)data,
478                                      connectionAddress->length, AF_INET);
479
480             localhost = localHostname ();
481
482             /* 
483              * protect against bogus host names 
484              */
485             if (hostent && hostent->h_name && hostent->h_name[0]
486                         && (hostent->h_name[0] != '.'))
487             {
488                 if (!strcmp (localhost, hostent->h_name))
489                 {
490                     if (!getString (name, 10))
491                         return 0;
492                     sprintf (name, ":%d", displayNumber);
493                 }
494                 else
495                 {
496                     if (removeDomainname)
497                     {
498                         char    *localDot, *remoteDot;
499     
500                         /* check for a common domain name.  This
501                          * could reduce names by recognising common
502                          * super-domain names as well, but I don't think
503                          * this is as useful, and will confuse more
504                          * people
505                          */
506                         if ((localDot = strchr(localhost, '.')) &&
507                             (remoteDot = strchr(hostent->h_name, '.')))
508                         {
509                             /* smash the name in place; it won't
510                              * be needed later.
511                              */
512                             if (!strcmp (localDot+1, remoteDot+1))
513                                 *remoteDot = '\0';
514                         }
515                     }
516
517                     if (!getString (name, strlen (hostent->h_name) + 10))
518                         return 0;
519                     sprintf (name, "%s:%d", hostent->h_name, displayNumber);
520                 }
521             }
522             else
523             {
524                 if (!getString (name, 25))
525                     return 0;
526                 sprintf(name, "%d.%d.%d.%d:%d",
527                         data[0], data[1], data[2], data[3], displayNumber);
528             }
529             return name;
530         }
531 #ifdef DNET
532     case FamilyDECnet:
533         return NULL;
534 #endif /* DNET */
535     default:
536         return NULL;
537     }
538 }
539
540 /*ARGSUSED*/
541 void
542 forward_respond (struct sockaddr *from, int fromlen, int length)
543 {
544     ARRAY8          clientAddress;
545     ARRAY8          clientPort;
546     ARRAYofARRAY8   authenticationNames;
547     struct sockaddr *client;
548     int             clientlen;
549     int             expectedLen;
550     int             i;
551     
552     Debug ("Forward respond %d\n", length);
553     clientAddress.length = 0;
554     clientAddress.data = 0;
555     clientPort.length = 0;
556     clientPort.data = 0;
557     authenticationNames.length = 0;
558     authenticationNames.data = 0;
559     if (XdmcpReadARRAY8 (&buffer, &clientAddress) &&
560         XdmcpReadARRAY8 (&buffer, &clientPort) &&
561         XdmcpReadARRAYofARRAY8 (&buffer, &authenticationNames))
562     {
563         expectedLen = 0;
564         expectedLen += 2 + clientAddress.length;
565         expectedLen += 2 + clientPort.length;
566         expectedLen += 1;           /* authenticationNames */
567         for (i = 0; i < (int)authenticationNames.length; i++)
568             expectedLen += 2 + authenticationNames.data[i].length;
569         if (length == expectedLen)
570         {
571             int j;
572
573             j = 0;
574             for (i = 0; i < (int)clientPort.length; i++)
575                 j = j * 256 + clientPort.data[i];
576             Debug ("Forward client address (port %d)", j);
577             for (i = 0; i < (int)clientAddress.length; i++)
578                 Debug (" %d", clientAddress.data[i]);
579             Debug ("\n");
580             switch (from->sa_family)
581             {
582 #ifdef AF_INET
583             case AF_INET:
584                 {
585                     static struct sockaddr_in   in_addr;
586
587                     if (clientAddress.length != 4 ||
588                         clientPort.length != 2)
589                     {
590                         goto badAddress;
591                     }
592                     bzero ((char *) &in_addr, sizeof (in_addr));
593 #ifdef BSD44SOCKETS
594                     in_addr.sin_len = sizeof(in_addr);
595 #endif
596                     in_addr.sin_family = AF_INET;
597                     memmove( &in_addr.sin_addr, clientAddress.data, 4);
598                     memmove( (char *) &in_addr.sin_port, clientPort.data, 2);
599                     client = (struct sockaddr *) &in_addr;
600                     clientlen = sizeof (in_addr);
601                 }
602                 break;
603 #endif
604 #ifdef AF_UNIX
605             case AF_UNIX:
606                 {
607                     static struct sockaddr_un   un_addr;
608
609                     if (clientAddress.length >= sizeof (un_addr.sun_path))
610                         goto badAddress;
611                     bzero ((char *) &un_addr, sizeof (un_addr));
612                     un_addr.sun_family = AF_UNIX;
613                     memmove( un_addr.sun_path, clientAddress.data, clientAddress.length);
614                     un_addr.sun_path[clientAddress.length] = '\0';
615                     client = (struct sockaddr *) &un_addr;
616 #ifdef BSD44SOCKETS
617                     un_addr.sun_len = strlen(un_addr.sun_path);
618                     clientlen = SUN_LEN(&un_addr);
619 #else
620                     clientlen = sizeof (un_addr);
621 #endif
622                 }
623                 break;
624 #endif
625 #ifdef AF_CHAOS
626             case AF_CHAOS:
627                 goto badAddress;
628 #endif
629 #ifdef AF_DECnet
630             case AF_DECnet:
631                 goto badAddress;
632 #endif
633             }
634             all_query_respond (client, clientlen, &authenticationNames,
635                                FORWARD_QUERY);
636         }
637         else
638         {
639             Debug ("Forward length error got %d expect %d\n", length, expectedLen);
640         }
641     }
642 badAddress:
643     XdmcpDisposeARRAY8 (&clientAddress);
644     XdmcpDisposeARRAY8 (&clientPort);
645     XdmcpDisposeARRAYofARRAY8 (&authenticationNames);
646 }
647
648 void
649 send_willing (struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status)
650 {
651     XdmcpHeader header;
652
653     Debug ("Send willing %*.*s %*.*s\n", authenticationName->length,
654                                          authenticationName->length,
655                                          pS(authenticationName->data),
656                                          status->length,
657                                          status->length,
658                                          pS(status->data));
659     header.version = XDM_PROTOCOL_VERSION;
660     header.opcode = (CARD16) WILLING;
661     header.length = 6 + authenticationName->length +
662                     Hostname.length + status->length;
663     XdmcpWriteHeader (&buffer, &header);
664     XdmcpWriteARRAY8 (&buffer, authenticationName);
665     XdmcpWriteARRAY8 (&buffer, &Hostname);
666     XdmcpWriteARRAY8 (&buffer, status);
667     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
668 }
669
670 void
671 send_unwilling (struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status)
672 {
673     XdmcpHeader header;
674
675     Debug ("Send unwilling %*.*s %*.*s\n", authenticationName->length,
676                                          authenticationName->length,
677                                          pS(authenticationName->data),
678                                          status->length,
679                                          status->length,
680                                          pS(status->data));
681     header.version = XDM_PROTOCOL_VERSION;
682     header.opcode = (CARD16) UNWILLING;
683     header.length = 4 + Hostname.length + status->length;
684     XdmcpWriteHeader (&buffer, &header);
685     XdmcpWriteARRAY8 (&buffer, &Hostname);
686     XdmcpWriteARRAY8 (&buffer, status);
687     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
688 }
689
690 static unsigned long    globalSessionID;
691
692 #define NextSessionID()    (++globalSessionID)
693
694 void
695 init_session_id(void)
696 {
697     /* Set randomly so we are unlikely to reuse id's from a previous
698      * incarnation so we don't say "Alive" to those displays.
699      * Start with low digits 0 to make debugging easier.
700      */
701     globalSessionID = (time((Time_t)0)&0x7fff) * 16000;
702 }
703     
704 static ARRAY8 outOfMemory = { (CARD16) 13, (CARD8Ptr) "Out of memory" };
705 static ARRAY8 noValidAddr = { (CARD16) 16, (CARD8Ptr) "No valid address" };
706 static ARRAY8 noValidAuth = { (CARD16) 22, (CARD8Ptr) "No valid authorization" };
707 static ARRAY8 noAuthentic = { (CARD16) 29, (CARD8Ptr) "XDM has no authentication key" };
708
709 void
710 request_respond (struct sockaddr *from, int fromlen, int length)
711 {
712     CARD16          displayNumber;
713     ARRAY16         connectionTypes;
714     ARRAYofARRAY8   connectionAddresses;
715     ARRAY8          authenticationName;
716     ARRAY8          authenticationData;
717     ARRAYofARRAY8   authorizationNames;
718     ARRAY8          manufacturerDisplayID;
719     ARRAY8Ptr       reason;
720     int             expectlen;
721     int             i, j;
722     struct protoDisplay  *pdpy;
723     ARRAY8          authorizationName, authorizationData;
724     ARRAY8Ptr       connectionAddress;
725
726     Debug ("Request respond %d\n", length);
727     connectionTypes.data = 0;
728     connectionAddresses.data = 0;
729     authenticationName.data = 0;
730     authenticationData.data = 0;
731     authorizationNames.data = 0;
732     authorizationName.length = 0;
733     authorizationData.length = 0;
734     manufacturerDisplayID.data = 0;
735     if (XdmcpReadCARD16 (&buffer, &displayNumber) &&
736         XdmcpReadARRAY16 (&buffer, &connectionTypes) &&
737         XdmcpReadARRAYofARRAY8 (&buffer, &connectionAddresses) &&
738         XdmcpReadARRAY8 (&buffer, &authenticationName) &&
739         XdmcpReadARRAY8 (&buffer, &authenticationData) &&
740         XdmcpReadARRAYofARRAY8 (&buffer, &authorizationNames) &&
741         XdmcpReadARRAY8 (&buffer, &manufacturerDisplayID))
742     {
743         expectlen = 0;
744         expectlen += 2;                             /* displayNumber */
745         expectlen += 1 + 2*connectionTypes.length;  /* connectionTypes */
746         expectlen += 1;                             /* connectionAddresses */
747         for (i = 0; i < (int)connectionAddresses.length; i++)
748             expectlen += 2 + connectionAddresses.data[i].length;
749         expectlen += 2 + authenticationName.length; /* authenticationName */
750         expectlen += 2 + authenticationData.length; /* authenticationData */
751         expectlen += 1;                             /* authoriationNames */
752         for (i = 0; i < (int)authorizationNames.length; i++)
753             expectlen += 2 + authorizationNames.data[i].length;
754         expectlen += 2 + manufacturerDisplayID.length;  /* displayID */
755         if (expectlen != length)
756         {
757             Debug ("Request length error got %d expect %d\n", length, expectlen);
758             goto abort;
759         }
760         if (connectionTypes.length == 0 ||
761             connectionAddresses.length != connectionTypes.length)
762         {
763             reason = &noValidAddr;
764             pdpy = 0;
765             goto decline;
766         }
767         pdpy = FindProtoDisplay (from, fromlen, displayNumber);
768         if (!pdpy) {
769
770             /* Check this Display against the Manager's policy */
771             reason = Accept (from, fromlen, displayNumber);
772             if (reason)
773                 goto decline;
774
775             /* Check the Display's stream services against Manager's policy */
776             i = SelectConnectionTypeIndex (&connectionTypes,
777                                            &connectionAddresses);
778             if (i < 0) {
779                 reason = &noValidAddr;
780                 goto decline;
781             }
782         
783             /* The Manager considers this a new session */
784             connectionAddress = &connectionAddresses.data[i];
785             pdpy = NewProtoDisplay (from, fromlen, displayNumber,
786                                     connectionTypes.data[i], connectionAddress,
787                                     NextSessionID());
788             Debug ("NewProtoDisplay 0x%x\n", pdpy);
789             if (!pdpy) {
790                 reason = &outOfMemory;
791                 goto decline;
792             }
793         }
794         if (authorizationNames.length == 0)
795             j = 0;
796         else
797             j = SelectAuthorizationTypeIndex (&authenticationName,
798                                               &authorizationNames);
799         if (j < 0)
800         {
801             reason = &noValidAuth;
802             goto decline;
803         }
804         if (!CheckAuthentication (pdpy,
805                                   &manufacturerDisplayID,
806                                   &authenticationName,
807                                   &authenticationData))
808         {
809             reason = &noAuthentic;
810             goto decline;
811         }
812         if (j < (int)authorizationNames.length)
813         {
814             Xauth   *auth;
815             SetProtoDisplayAuthorization (pdpy,
816                 (unsigned short) authorizationNames.data[j].length,
817                 (char *) authorizationNames.data[j].data);
818             auth = pdpy->xdmcpAuthorization;
819             if (!auth)
820                 auth = pdpy->fileAuthorization;
821             if (auth)
822             {
823                 authorizationName.length = auth->name_length;
824                 authorizationName.data = (CARD8Ptr) auth->name;
825                 authorizationData.length = auth->data_length;
826                 authorizationData.data = (CARD8Ptr) auth->data;
827             }
828         }
829         if (pdpy)
830         {
831             send_accept (from, fromlen, pdpy->sessionID,
832                                         &authenticationName,
833                                         &authenticationData,
834                                         &authorizationName,
835                                         &authorizationData);
836         }
837         else
838         {
839 decline:    ;
840             send_decline (from, fromlen, &authenticationName,
841                                  &authenticationData,
842                                  reason);
843             if (pdpy)
844                 DisposeProtoDisplay (pdpy);
845         }
846     }
847 abort:
848     XdmcpDisposeARRAY16 (&connectionTypes);
849     XdmcpDisposeARRAYofARRAY8 (&connectionAddresses);
850     XdmcpDisposeARRAY8 (&authenticationName);
851     XdmcpDisposeARRAY8 (&authenticationData);
852     XdmcpDisposeARRAYofARRAY8 (&authorizationNames);
853     XdmcpDisposeARRAY8 (&manufacturerDisplayID);
854 }
855
856 void
857 send_accept (struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData)
858 {
859     XdmcpHeader header;
860
861     Debug ("Accept Session ID %d\n", sessionID);
862     header.version = XDM_PROTOCOL_VERSION;
863     header.opcode = (CARD16) ACCEPT;
864     header.length = 4;                      /* session ID */
865     header.length += 2 + authenticationName->length;
866     header.length += 2 + authenticationData->length;
867     header.length += 2 + authorizationName->length;
868     header.length += 2 + authorizationData->length;
869     XdmcpWriteHeader (&buffer, &header);
870     XdmcpWriteCARD32 (&buffer, sessionID);
871     XdmcpWriteARRAY8 (&buffer, authenticationName);
872     XdmcpWriteARRAY8 (&buffer, authenticationData);
873     XdmcpWriteARRAY8 (&buffer, authorizationName);
874     XdmcpWriteARRAY8 (&buffer, authorizationData);
875     XdmcpFlush (xdmcpFd, &buffer, to, tolen);
876 }
877
878 void
879 send_decline (struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status)
880 {
881     XdmcpHeader header;
882
883     Debug ("Decline %*.*s\n", status->length, status->length, pS(status->data));
884     header.version = XDM_PROTOCOL_VERSION;
885     header.opcode = (CARD16) DECLINE;
886     header.length = 0;
887     header.length += 2 + status->length;
888     header.length += 2 + authenticationName->length;
889     header.length += 2 + authenticationData->length;
890     XdmcpWriteHeader (&buffer, &header);
891     XdmcpWriteARRAY8 (&buffer, status);
892     XdmcpWriteARRAY8 (&buffer, authenticationName);
893     XdmcpWriteARRAY8 (&buffer, authenticationData);
894     XdmcpFlush (xdmcpFd, &buffer, to, tolen);
895 }
896
897 void
898 manage (struct sockaddr *from, int fromlen, int length)
899 {
900     CARD32              sessionID;
901     CARD16              displayNumber;
902     ARRAY8              displayClass;
903     int                 expectlen;
904     struct protoDisplay *pdpy;
905     struct display      *d;
906     char                *name = NULL;
907     char                *class = NULL;
908     XdmcpNetaddr        from_save;
909     ARRAY8              clientAddress, clientPort;
910     CARD16              connectionType;
911
912     Debug ("Manage %d\n", length);
913     displayClass.data = 0;
914     displayClass.length = 0;
915     if (XdmcpReadCARD32 (&buffer, &sessionID) &&
916         XdmcpReadCARD16 (&buffer, &displayNumber) &&
917         XdmcpReadARRAY8 (&buffer, &displayClass))
918     {
919         expectlen = 4 +                         /* session ID */
920                     2 +                         /* displayNumber */
921                     2 + displayClass.length;    /* displayClass */
922         if (expectlen != length)
923         {
924             Debug ("Manage length error got %d expect %d\n", length, expectlen);
925             goto abort;
926         }
927         pdpy = FindProtoDisplay (from, fromlen, displayNumber);
928         Debug ("Manage Session ID %d, pdpy 0x%x\n", sessionID, pdpy);
929         if (!pdpy || pdpy->sessionID != sessionID)
930         {
931             /*
932              * We may have already started a session for this display
933              * but it hasn't seen the response in the form of an
934              * XOpenDisplay() yet. So check if it is in the list of active
935              * displays, and if so check that the session id's match.
936              * If all this is true, then we have a duplicate request that
937              * can be ignored.
938              */
939             if (!pdpy 
940                 && (d = FindDisplayByAddress(from, fromlen, displayNumber))
941                 && d->sessionID == sessionID) {
942                      Debug("manage: got duplicate pkt, ignoring\n");
943                      goto abort;
944             }
945             Debug ("Session ID %d refused\n", sessionID);
946             if (pdpy)
947                 Debug ("Existing Session ID %d\n", pdpy->sessionID);
948             send_refuse (from, fromlen, sessionID);
949         }
950         else
951         {
952             name = NetworkAddressToName (pdpy->connectionType,
953                                          &pdpy->connectionAddress,
954                                          pdpy->displayNumber);
955             Debug ("Computed display name: %s\n", name);
956             if (!name)
957             {
958                 send_failed (from, fromlen, "(no name)", sessionID, "out of memory");
959                 goto abort;
960             }
961             d = FindDisplayByName (name);
962             if (d)
963             {
964                 extern void StopDisplay ();
965
966                 Debug ("Terminating active session for %s\n", d->name);
967                 StopDisplay (d);
968             }
969             class = malloc (displayClass.length + 1);
970             if (!class)
971             {
972                 send_failed (from, fromlen, name, sessionID, "out of memory");
973                 goto abort;
974             }
975             if (displayClass.length)
976             {
977                 memmove( class, displayClass.data, displayClass.length);
978                 class[displayClass.length] = '\0';
979             }
980             else
981             {
982                 free ((char *) class);
983                 class = (char *) NULL;
984             }
985             from_save = (XdmcpNetaddr) malloc (fromlen);
986             if (!from_save)
987             {
988                 send_failed (from, fromlen, name, sessionID, "out of memory");
989                 goto abort;
990             }
991             memmove( from_save, from, fromlen);
992             d = NewDisplay (name, class);
993             if (!d)
994             {
995                 free ((char *) from_save);
996                 send_failed (from, fromlen, name, sessionID, "out of memory");
997                 goto abort;
998             }
999             d->displayType.location = Foreign;
1000             d->displayType.lifetime = Transient;
1001             d->displayType.origin = FromXDMCP;
1002             d->sessionID = pdpy->sessionID;
1003             d->from = (struct sockaddr *)from_save;
1004             d->fromlen = fromlen;
1005             d->displayNumber = pdpy->displayNumber;
1006 #ifdef BYPASSLOGIN
1007             d->bypassLogin = 0;
1008 #endif /* BYPASSLOGIN */
1009             ClientAddress (from, &clientAddress, &clientPort, &connectionType);
1010             d->useChooser = 0;
1011             if (IsIndirectClient (&clientAddress, connectionType))
1012             {
1013                 Debug ("IsIndirectClient\n");
1014                 ForgetIndirectClient (&clientAddress, connectionType);
1015                 if (UseChooser (&clientAddress, connectionType))
1016                 {
1017                     d->useChooser = 1;
1018                     Debug ("Use chooser for %s\n", d->name);
1019                 }
1020             }
1021             d->clientAddr = clientAddress;
1022             d->connectionType = connectionType;
1023             XdmcpDisposeARRAY8 (&clientPort);
1024             if (pdpy->fileAuthorization)
1025             {
1026                 d->authorizations = (Xauth **) malloc (sizeof (Xauth *));
1027                 if (!d->authorizations)
1028                 {
1029                     free ((char *) from_save);
1030                     free ((char *) d);
1031                     send_failed (from, fromlen, name, sessionID, "out of memory");
1032                     goto abort;
1033                 }
1034                 d->authorizations[0] = pdpy->fileAuthorization;
1035                 d->authNum = 1;
1036                 pdpy->fileAuthorization = 0;
1037             }
1038             DisposeProtoDisplay (pdpy);
1039             Debug ("Starting display %s,%s\n", d->name, d->class);
1040             StartDisplay (d);
1041         }
1042     }
1043 abort:
1044     XdmcpDisposeARRAY8 (&displayClass);
1045     if (name) free ((char*) name);
1046     if (class) free ((char*) class);
1047 }
1048
1049 void
1050 SendFailed (struct display *d, char *reason)
1051 {
1052     Debug ("Display start failed, sending Failed\n");
1053     send_failed (d->from, d->fromlen, d->name, d->sessionID, reason);
1054 }
1055
1056 void
1057 send_failed (struct sockaddr *from, int fromlen, char *name, CARD32 sessionID, char *reason)
1058 {
1059     static char buf[256];
1060     XdmcpHeader header;
1061     ARRAY8      status;
1062
1063     sprintf (buf, "Session %d failed for display %s: %s",
1064              sessionID, name, reason);
1065     Debug ("Send failed %d %s\n", sessionID, buf);
1066     status.length = strlen (buf);
1067     status.data = (CARD8Ptr) buf;
1068     header.version = XDM_PROTOCOL_VERSION;
1069     header.opcode = (CARD16) FAILED;
1070     header.length = 6 + status.length;
1071     XdmcpWriteHeader (&buffer, &header);
1072     XdmcpWriteCARD32 (&buffer, sessionID);
1073     XdmcpWriteARRAY8 (&buffer, &status);
1074     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1075 }
1076
1077 void
1078 send_refuse (struct sockaddr *from, int fromlen, CARD32 sessionID)
1079 {
1080     XdmcpHeader header;
1081
1082     Debug ("Send refuse %d\n", sessionID);
1083     header.version = XDM_PROTOCOL_VERSION;
1084     header.opcode = (CARD16) REFUSE;
1085     header.length = 4;
1086     XdmcpWriteHeader (&buffer, &header);
1087     XdmcpWriteCARD32 (&buffer, sessionID);
1088     XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1089 }
1090
1091 void
1092 send_alive (struct sockaddr *from, int fromlen, int length)
1093 {
1094     CARD32              sessionID;
1095     CARD16              displayNumber;
1096     struct display      *d;
1097     XdmcpHeader         header;
1098     CARD8               sendRunning;
1099     CARD32              sendSessionID;
1100
1101     Debug ("Send alive\n");
1102     if (XdmcpReadCARD16 (&buffer, &displayNumber) &&
1103         XdmcpReadCARD32 (&buffer, &sessionID))
1104     {
1105         if (length == 6)
1106         {
1107             d = FindDisplayBySessionID (sessionID);
1108             if (!d) {
1109                 d = FindDisplayByAddress (from, fromlen, displayNumber);
1110             }
1111             sendRunning = 0;
1112             sendSessionID = 0;
1113             if (d && d->status == running)
1114             {
1115                 if (d->sessionID == sessionID)
1116                     sendRunning = 1;
1117                 sendSessionID = d->sessionID;
1118             }
1119             header.version = XDM_PROTOCOL_VERSION;
1120             header.opcode = (CARD16) ALIVE;
1121             header.length = 5;
1122             Debug ("alive: %d %d\n", sendRunning, sendSessionID);
1123             XdmcpWriteHeader (&buffer, &header);
1124             XdmcpWriteCARD8 (&buffer, sendRunning);
1125             XdmcpWriteCARD32 (&buffer, sendSessionID);
1126             XdmcpFlush (xdmcpFd, &buffer, from, fromlen);
1127         }
1128     }
1129 }
1130
1131 #if NeedWidePrototypes
1132 char *
1133 NetworkAddressToHostname (int connectionType, ARRAY8Ptr connectionAddress)
1134 #else
1135 char *
1136 NetworkAddressToHostname (CARD16 connectionType, ARRAY8Ptr connectionAddress)
1137 #endif
1138 {
1139     char    *name = 0;
1140
1141     switch (connectionType)
1142     {
1143     case FamilyInternet:
1144         {
1145             struct hostent      *hostent;
1146             char dotted[20];
1147             char *local_name;
1148
1149             hostent = gethostbyaddr ((char *)connectionAddress->data,
1150                                      connectionAddress->length, AF_INET);
1151
1152             if (hostent)
1153                 local_name = hostent->h_name;
1154             else {
1155                 /* can't get name, so use emergency fallback */
1156                 sprintf(dotted, "%d.%d.%d.%d",
1157                         connectionAddress->data[0],
1158                         connectionAddress->data[1],
1159                         connectionAddress->data[2],
1160                         connectionAddress->data[3]);
1161                 local_name = dotted;
1162
1163                 LogError ((unsigned char *)"Cannot convert Internet address %s to host name\n",
1164                           dotted);
1165             }
1166             if (!getString (name, strlen (local_name)))
1167                 break;
1168             strcpy (name, local_name);
1169             break;
1170         }
1171 #ifdef DNET
1172     case FamilyDECnet:
1173         break;
1174 #endif /* DNET */
1175     default:
1176         break;
1177     }
1178     return name;
1179 }
1180
1181 static int
1182 HostnameToNetworkAddress (char *name, CARD16 connectionType, ARRAY8Ptr connectionAddress)
1183 {
1184     switch (connectionType)
1185     {
1186     case FamilyInternet:
1187         {
1188             struct hostent      *hostent;
1189
1190             hostent = gethostbyname (name);
1191             if (!hostent)
1192                 return FALSE;
1193             if (!XdmcpAllocARRAY8 (connectionAddress, hostent->h_length))
1194                 return FALSE;
1195             memmove( connectionAddress->data, hostent->h_addr, hostent->h_length);
1196             return TRUE;
1197         }
1198 #ifdef DNET
1199     case FamilyDECnet:
1200         return FALSE;
1201 #endif
1202     }
1203     return FALSE;
1204 }
1205
1206 /*
1207  * converts a display name into a network address, using
1208  * the same rules as XOpenDisplay (algorithm cribbed from there)
1209  */
1210
1211 static int
1212 NameToNetworkAddress(char *name, CARD16Ptr connectionTypep, ARRAY8Ptr connectionAddress, CARD16Ptr displayNumber)
1213 {
1214     char    *colon, *display_number;
1215     char    hostname[1024];
1216     int     dnet = FALSE;
1217     CARD16  number;
1218     CARD16  connectionType;
1219
1220     colon = strchr(name, ':');
1221     if (!colon)
1222         return FALSE;
1223     if (colon != name)
1224     {
1225         if (colon - name > sizeof (hostname))
1226             return FALSE;
1227         strncpy (hostname, name, colon - name);
1228         hostname[colon - name] = '\0';
1229     }
1230     else
1231     {
1232         strcpy (hostname, localHostname ());
1233     }
1234     if (colon[1] == ':')
1235     {
1236         dnet = TRUE;
1237         colon++;
1238     }
1239 #ifndef DNETCONN
1240     if (dnet)
1241         return FALSE;
1242 #endif
1243     display_number = colon + 1;
1244     while (*display_number && *display_number != '.')
1245     {
1246         if (!isascii (*display_number) || !isdigit(*display_number))
1247             return FALSE;
1248     }
1249     if (display_number == colon + 1)
1250         return FALSE;
1251     number = atoi (colon + 1);
1252 #ifdef DNETCONN
1253     if (dnet)
1254         connectionType = FamilyDECnet;
1255     else
1256 #endif
1257         connectionType = FamilyInternet;
1258     if (!HostnameToNetworkAddress (hostname, connectionType, connectionAddress))
1259         return FALSE;
1260     *displayNumber = number;
1261     *connectionTypep = connectionType;
1262     return TRUE;
1263 }
1264