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