home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xdm / access.c next >
Encoding:
C/C++ Source or Header  |  1993-07-21  |  15.9 KB  |  741 lines

  1. /*
  2.  * $XConsortium: access.c,v 1.13 92/04/21 11:33:37 gildea Exp $
  3.  *
  4.  * Copyright 1990 Massachusetts Institute of Technology
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software and its
  7.  * documentation for any purpose is hereby granted without fee, provided that
  8.  * the above copyright notice appear in all copies and that both that
  9.  * copyright notice and this permission notice appear in supporting
  10.  * documentation, and that the name of M.I.T. not be used in advertising or
  11.  * publicity pertaining to distribution of the software without specific,
  12.  * written prior permission.  M.I.T. makes no representations about the
  13.  * suitability of this software for any purpose.  It is provided "as is"
  14.  * without express or implied warranty.
  15.  *
  16.  * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  17.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
  18.  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  20.  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
  21.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  *
  23.  * Author:  Keith Packard, MIT X Consortium
  24.  */
  25.  
  26. /*
  27.  * Access control for XDMCP - keep a database of allowable display addresses
  28.  * and (potentially) a list of hosts to send ForwardQuery packets to
  29.  */
  30.  
  31. # include   "dm.h"
  32.  
  33. #ifdef XDMCP
  34.  
  35. # include   <X11/Xos.h>
  36. # include   <X11/Xdmcp.h>
  37. # include   <X11/X.h>
  38. # include   <stdio.h>
  39. # include   <ctype.h>
  40. # include   <netinet/in.h>
  41. # include   <netdb.h>
  42. # include   <sys/socket.h>
  43.  
  44. #define ALIAS_CHARACTER        '%'
  45. #define NEGATE_CHARACTER    '!'
  46. #define CHOOSER_STRING        "CHOOSER"
  47. #define BROADCAST_STRING    "BROADCAST"
  48.  
  49. #define HOST_ALIAS    0
  50. #define HOST_ADDRESS    1
  51. #define HOST_BROADCAST    2
  52. #define HOST_CHOOSER    3
  53.  
  54. typedef struct _hostEntry {
  55.     struct _hostEntry    *next;
  56.     int        type;
  57.     union _hostOrAlias {
  58.     char    *aliasName;
  59.     ARRAY8    hostAddress;
  60.     } entry;
  61. } HostEntry;
  62.  
  63. #define DISPLAY_ALIAS        0
  64. #define DISPLAY_PATTERN        1
  65. #define DISPLAY_ADDRESS        2
  66.  
  67. typedef struct _displayEntry {
  68.     struct _displayEntry    *next;
  69.     int                type;
  70.     int                notAllowed;
  71.     int                chooser;
  72.     union _displayType {
  73.     char            *aliasName;
  74.     char            *displayPattern;
  75.     struct _display {
  76.         ARRAY8        clientAddress;
  77.         CARD16        connectionType;
  78.     } displayAddress;
  79.     } entry;
  80.     HostEntry            *hosts;
  81. } DisplayEntry;
  82.  
  83. static DisplayEntry    *database;
  84.  
  85. static ARRAY8        localAddress;
  86.  
  87. ARRAY8Ptr
  88. getLocalAddress ()
  89. {
  90.     static int    haveLocalAddress;
  91.     
  92.     if (!haveLocalAddress)
  93.     {
  94.     struct hostent    *hostent;
  95.  
  96.     hostent = gethostbyname (localHostname());
  97.     XdmcpAllocARRAY8 (&localAddress, hostent->h_length);
  98.     bcopy (hostent->h_addr, localAddress.data, hostent->h_length);
  99.     }
  100.     return &localAddress;
  101. }
  102.  
  103. static void
  104. FreeHostEntry (h)
  105.     HostEntry        *h;
  106. {
  107.     switch (h->type) {
  108.     case HOST_ALIAS:
  109.     free (h->entry.aliasName);
  110.     break;
  111.     case HOST_ADDRESS:
  112.     XdmcpDisposeARRAY8 (&h->entry.hostAddress);
  113.     break;
  114.     case HOST_CHOOSER:
  115.     break;
  116.     }
  117.     free ((char *) h);
  118. }
  119.  
  120. static void
  121. FreeDisplayEntry (d)
  122.     DisplayEntry    *d;
  123. {
  124.     HostEntry    *h, *next;
  125.     switch (d->type) {
  126.     case DISPLAY_ALIAS:
  127.     free (d->entry.aliasName);
  128.     break;
  129.     case DISPLAY_PATTERN:
  130.     free (d->entry.displayPattern);
  131.     break;
  132.     case DISPLAY_ADDRESS:
  133.     XdmcpDisposeARRAY8 (&d->entry.displayAddress);
  134.     break;
  135.     }
  136.     for (h = d->hosts; h; h = next) {
  137.     next = h->next;
  138.     FreeHostEntry (h);
  139.     }
  140.     free ((char *) d);
  141. }
  142.  
  143. static void
  144. FreeAccessDatabase ()
  145. {
  146.     DisplayEntry    *d, *next;
  147.  
  148.     for (d = database; d; d = next)
  149.     {
  150.     next = d->next;
  151.     FreeDisplayEntry (d);
  152.     }
  153.     database = 0;
  154. }
  155.  
  156. #define WORD_LEN    256
  157. static char    wordBuffer[WORD_LEN];
  158. static int    nextIsEOF;
  159.  
  160. static char *
  161. ReadWord (file, EOFatEOL)
  162.     FILE    *file;
  163.     int        EOFatEOL;
  164. {
  165.     int        c;
  166.     char    *wordp;
  167.     int        quoted;
  168.  
  169.     wordp = wordBuffer;
  170.     if (nextIsEOF)
  171.     {
  172.     nextIsEOF = FALSE;
  173.     return NULL;
  174.     }
  175.     quoted = FALSE;
  176.     for (;;) {
  177.     c = getc (file);
  178.     switch (c) {
  179.     case '#':
  180.         if (quoted)
  181.         {
  182.         *wordp++ = c;
  183.         break;
  184.         }
  185.         while ((c = getc (file)) != EOF && c != '\n')
  186.         ;
  187.     case '\n':
  188.     case EOF:
  189.         if (c == EOF || (EOFatEOL && !quoted))
  190.         {
  191.         ungetc (c, file);
  192.         if (wordp == wordBuffer)
  193.             return NULL;
  194.         *wordp = '\0';
  195.         nextIsEOF = TRUE;
  196.         return wordBuffer;
  197.         }
  198.     case ' ':
  199.     case '\t':
  200.         if (wordp != wordBuffer)
  201.         {
  202.         ungetc (c, file);
  203.         *wordp = '\0';
  204.         return wordBuffer;
  205.         }
  206.         break;
  207.     case '\\':
  208.         if (!quoted)
  209.         {
  210.         quoted = TRUE;
  211.         continue;
  212.         }
  213.     default:
  214.         *wordp++ = c;
  215.         break;
  216.     }
  217.     quoted = FALSE;
  218.     }
  219. }
  220.  
  221. static HostEntry *
  222. ReadHostEntry (file)
  223.     FILE    *file;
  224. {
  225.     char        *hostOrAlias;
  226.     HostEntry        *h;
  227.     struct hostent  *hostent;
  228.  
  229. tryagain:
  230.     hostOrAlias = ReadWord (file, TRUE);
  231.     if (!hostOrAlias)
  232.     return NULL;
  233.     h = (HostEntry *) malloc (sizeof (DisplayEntry));
  234.     if (*hostOrAlias == ALIAS_CHARACTER)
  235.     {
  236.     h->type = HOST_ALIAS;
  237.     h->entry.aliasName = malloc (strlen (hostOrAlias) + 1);
  238.     if (!h->entry.aliasName) {
  239.         free ((char *) h);
  240.         return NULL;
  241.     }
  242.     strcpy (h->entry.aliasName, hostOrAlias);
  243.     }
  244.     else if (!strcmp (hostOrAlias, CHOOSER_STRING))
  245.     {
  246.     h->type = HOST_CHOOSER;
  247.     }
  248.     else if (!strcmp (hostOrAlias, BROADCAST_STRING))
  249.     {
  250.     h->type = HOST_BROADCAST;
  251.     }
  252.     else
  253.     {
  254.     h->type = HOST_ADDRESS;
  255.     hostent = gethostbyname (hostOrAlias);
  256.     if (!hostent)
  257.     {
  258.         Debug ("No such host %s\n", hostOrAlias);
  259.         LogError ("Access file \"%s\", host \"%s\" not found\n", accessFile, hostOrAlias);
  260.         free ((char *) h);
  261.         goto tryagain;
  262.     }
  263.     if (!XdmcpAllocARRAY8 (&h->entry.hostAddress, hostent->h_length))
  264.     {
  265.         LogOutOfMem ("ReadHostEntry\n");
  266.         free ((char *) h);
  267.         return NULL;
  268.     }
  269.     bcopy (hostent->h_addr, h->entry.hostAddress.data, hostent->h_length);
  270.     }
  271.     return h;
  272. }
  273.  
  274. static int
  275. HasGlobCharacters (s)
  276.     char    *s;
  277. {
  278.     for (;;)
  279.     switch (*s++) {
  280.     case '?':
  281.     case '*':
  282.         return 1;
  283.     case '\0':
  284.         return 0;
  285.     }
  286. }
  287.  
  288. static DisplayEntry *
  289. ReadDisplayEntry (file)
  290.     FILE    *file;
  291. {
  292.     char        *displayOrAlias;
  293.     DisplayEntry    *d;
  294.     struct _display *display;
  295.     HostEntry        *h, **prev;
  296.     struct hostent  *hostent;
  297.     
  298.     displayOrAlias = ReadWord (file, FALSE);
  299.     if (!displayOrAlias)
  300.         return NULL;
  301.     d = (DisplayEntry *) malloc (sizeof (DisplayEntry));
  302.     d->notAllowed = 0;
  303.     d->chooser = 0;
  304.     if (*displayOrAlias == ALIAS_CHARACTER)
  305.     {
  306.     d->type = DISPLAY_ALIAS;
  307.     d->entry.aliasName = malloc (strlen (displayOrAlias) + 1);
  308.     if (!d->entry.aliasName)
  309.     {
  310.         free ((char *) d);
  311.         return NULL;
  312.     }
  313.     strcpy (d->entry.aliasName, displayOrAlias);
  314.     }
  315.     else
  316.     {
  317.     if (*displayOrAlias == NEGATE_CHARACTER)
  318.     {
  319.         d->notAllowed = 1;
  320.         ++displayOrAlias;
  321.     }
  322.         if (HasGlobCharacters (displayOrAlias))
  323.         {
  324.         d->type = DISPLAY_PATTERN;
  325.         d->entry.displayPattern = malloc (strlen (displayOrAlias) + 1);
  326.         if (!d->entry.displayPattern)
  327.         {
  328.             free ((char *) d);
  329.             return NULL;
  330.         }
  331.         strcpy (d->entry.displayPattern, displayOrAlias);
  332.         }
  333.         else
  334.         {
  335.         if ((hostent = gethostbyname (displayOrAlias)) == NULL)
  336.         {
  337.         LogError ("Access file %s, display %s unknown\n", accessFile, displayOrAlias);
  338.         free ((char *) d);
  339.         return NULL;
  340.         }
  341.         d->type = DISPLAY_ADDRESS;
  342.         display = &d->entry.displayAddress;
  343.         if (!XdmcpAllocARRAY8 (&display->clientAddress, hostent->h_length))
  344.         {
  345.             free ((char *) d);
  346.             return NULL;
  347.         }
  348.         bcopy (hostent->h_addr, display->clientAddress.data, hostent->h_length);
  349.         switch (hostent->h_addrtype)
  350.         {
  351. #ifdef AF_UNIX
  352.         case AF_UNIX:
  353.             display->connectionType = FamilyLocal;
  354.             break;
  355. #endif
  356. #ifdef AF_INET
  357.         case AF_INET:
  358.             display->connectionType = FamilyInternet;
  359.             break;
  360. #endif
  361. #ifdef AF_DECnet
  362.         case AF_DECnet:
  363.             display->connectionType = FamilyDECnet;
  364.             break;
  365. #endif
  366.         default:
  367.             display->connectionType = FamilyLocal;
  368.             break;
  369.         }
  370.         }
  371.     }
  372.     prev = &d->hosts;
  373.     while (h = ReadHostEntry (file))
  374.     {
  375.     if (h->type == HOST_CHOOSER)
  376.     {
  377.         FreeHostEntry (h);
  378.         d->chooser = 1;
  379.     } else {
  380.         *prev = h;
  381.         prev = &h->next;
  382.     }
  383.     }
  384.     *prev = NULL;
  385.     return d;
  386. }
  387.  
  388. static
  389. ReadAccessDatabase (file)
  390.     FILE    *file;
  391. {
  392.     DisplayEntry    *d, **prev;
  393.  
  394.     prev = &database;
  395.     while (d = ReadDisplayEntry (file))
  396.     {
  397.     *prev = d;
  398.     prev = &d->next;
  399.     }
  400.     *prev = NULL;
  401. }
  402.  
  403. ScanAccessDatabase ()
  404. {
  405.     FILE    *datafile;
  406.  
  407.     FreeAccessDatabase ();
  408.     if (*accessFile)
  409.     {
  410.         datafile = fopen (accessFile, "r");
  411.         if (!datafile)
  412.     {
  413.         LogError ("Cannot open access control file %s, no XDMCP reqeusts will be granted\n", accessFile);
  414.         return 0;
  415.     }
  416.     ReadAccessDatabase (datafile);
  417.     fclose (datafile);
  418.     }
  419.     return 1;
  420. }
  421.  
  422. /*
  423.  * calls the given function for each valid indirect entry.  Returns TRUE if
  424.  * the local host exists on any of the lists, else FALSE
  425.  */
  426.  
  427. #define MAX_DEPTH   32
  428.  
  429. static int indirectAlias ();
  430.  
  431. static int
  432. scanHostlist (h, clientAddress, connectionType, function, closure, depth, broadcast)
  433.     HostEntry    *h;
  434.     ARRAY8Ptr    clientAddress;
  435.     CARD16    connectionType;
  436.     int        (*function)();
  437.     char    *closure;
  438.     int        depth;
  439.     int        broadcast;
  440. {
  441.     int    haveLocalhost = 0;
  442.  
  443.     for (; h; h = h->next)
  444.     {
  445.     switch (h->type) {
  446.     case HOST_ALIAS:
  447.         if (indirectAlias (h->entry.aliasName, clientAddress,
  448.                    connectionType, function, closure, depth,
  449.                    broadcast))
  450.         haveLocalhost = 1;
  451.         break;
  452.     case HOST_ADDRESS:
  453.         if (XdmcpARRAY8Equal (getLocalAddress(), &h->entry.hostAddress))
  454.         haveLocalhost = 1;
  455.         else if (function)
  456.         (*function) (connectionType, &h->entry.hostAddress, closure);
  457.         break;
  458.     case HOST_BROADCAST:
  459.         if (broadcast)
  460.         {
  461.         ARRAY8    temp;
  462.  
  463.         if (function)
  464.         {
  465.             temp.data = (BYTE *) BROADCAST_STRING;
  466.             temp.length = strlen ((char *)temp.data);
  467.             (*function) (connectionType, &temp, closure);
  468.         }
  469.         }
  470.         break;
  471.     }
  472.     }
  473.     return haveLocalhost;
  474. }
  475.  
  476. /* Returns non-0 iff string is matched by pattern.  Does case folding.
  477.  */
  478. static int
  479. patternMatch (string, pattern)
  480.     char    *string, *pattern;
  481. {
  482.     int        p, s;
  483.  
  484.     if (!string)
  485.     string = "";
  486.  
  487.     for (;;)
  488.     {
  489.     s = *string++;
  490.     switch (p = *pattern++) {
  491.     case '*':
  492.         if (!*pattern)
  493.         return 1;
  494.         for (string--; *string; string++)
  495.         if (patternMatch (string, pattern))
  496.             return 1;
  497.         return 0;
  498.     case '?':
  499.         if (s == '\0')
  500.         return 0;
  501.         break;
  502.     case '\0':
  503.         return s == '\0';
  504.     case '\\':
  505.         p = *pattern++;
  506.         /* fall through */
  507.     default:
  508.         if (isupper(p)) p = tolower(p);
  509.         if (isupper(s)) s = tolower(s);
  510.         if (p != s)
  511.         return 0;
  512.     }
  513.     }
  514. }
  515.  
  516. static int
  517. indirectAlias (alias, clientAddress, connectionType, function, closure, depth,
  518.            broadcast)
  519.     char    *alias;
  520.     ARRAY8Ptr    clientAddress;
  521.     CARD16    connectionType;
  522.     int        (*function)();
  523.     char    *closure;
  524.     int        depth;
  525.     int        broadcast;
  526. {
  527.     DisplayEntry    *d;
  528.     int            haveLocalhost = 0;
  529.  
  530.     if (depth == MAX_DEPTH)
  531.     return 0;
  532.     for (d = database; d; d = d->next)
  533.     {
  534.     if (d->type != DISPLAY_ALIAS || !patternMatch (alias, d->entry.aliasName))
  535.         continue;
  536.     if (scanHostlist (d->hosts, clientAddress, connectionType,
  537.               function, closure, depth + 1, broadcast))
  538.     {
  539.         haveLocalhost = 1;
  540.     }
  541.     }
  542.     return haveLocalhost;
  543. }
  544.  
  545. ARRAY8Ptr IndirectChoice ();
  546.  
  547. ForEachMatchingIndirectHost (clientAddress, connectionType, function, closure)
  548.     ARRAY8Ptr    clientAddress;
  549.     CARD16    connectionType;
  550.     int        (*function)();
  551.     char    *closure;
  552. {
  553.     int            haveLocalhost = 0;
  554.     DisplayEntry    *d;
  555.     char        *clientName = 0, *NetworkAddressToHostname ();
  556.  
  557.     for (d = database; d; d = d->next)
  558.     {
  559.         switch (d->type) {
  560.         case DISPLAY_ALIAS:
  561.         continue;
  562.         case DISPLAY_PATTERN:
  563.         if (!clientName)
  564.         clientName = NetworkAddressToHostname (connectionType,
  565.                                clientAddress);
  566.         if (!patternMatch (clientName, d->entry.displayPattern))
  567.         continue;
  568.         break;
  569.         case DISPLAY_ADDRESS:
  570.         if (d->entry.displayAddress.connectionType != connectionType ||
  571.             !XdmcpARRAY8Equal (&d->entry.displayAddress.clientAddress,
  572.                   clientAddress))
  573.         {
  574.         continue;
  575.         }
  576.         break;
  577.         }
  578.     if (!d->hosts)
  579.         continue;
  580.     if (d->notAllowed)
  581.         break;
  582.     if (d->chooser)
  583.     {
  584.         ARRAY8Ptr    choice;
  585.  
  586.         choice = IndirectChoice (clientAddress, connectionType);
  587.         if (!choice || XdmcpARRAY8Equal (getLocalAddress(), choice))
  588.         haveLocalhost = 1;
  589.         else
  590.         (*function) (connectionType, choice, closure);
  591.     }
  592.     else if (scanHostlist (d->hosts, clientAddress, connectionType,
  593.               function, closure, 0, FALSE))
  594.     {
  595.         haveLocalhost = 1;
  596.     }
  597.     break;
  598.     }
  599.     if (clientName)
  600.     free (clientName);
  601.     return haveLocalhost;
  602. }
  603.  
  604. UseChooser (clientAddress, connectionType)
  605.     ARRAY8Ptr    clientAddress;
  606.     CARD16    connectionType;
  607. {
  608.     DisplayEntry    *d;
  609.     char        *clientName = 0, *NetworkAddressToHostname ();
  610.  
  611.     for (d = database; d; d = d->next)
  612.     {
  613.         switch (d->type) {
  614.         case DISPLAY_ALIAS:
  615.         continue;
  616.         case DISPLAY_PATTERN:
  617.         if (!clientName)
  618.         clientName = NetworkAddressToHostname (connectionType,
  619.                                clientAddress);
  620.         if (!patternMatch (clientName, d->entry.displayPattern))
  621.         continue;
  622.         break;
  623.         case DISPLAY_ADDRESS:
  624.         if (d->entry.displayAddress.connectionType != connectionType ||
  625.             !XdmcpARRAY8Equal (&d->entry.displayAddress.clientAddress,
  626.                   clientAddress))
  627.         {
  628.         continue;
  629.         }
  630.         break;
  631.         }
  632.     if (!d->hosts)
  633.         continue;
  634.     if (d->notAllowed)
  635.         break;
  636.     if (d->chooser && !IndirectChoice (clientAddress, connectionType))
  637.         return 1;
  638.     break;
  639.     }
  640.     return 0;
  641. }
  642.  
  643. ForEachChooserHost (clientAddress, connectionType, function, closure)
  644.     ARRAY8Ptr    clientAddress;
  645.     CARD16    connectionType;
  646.     int        (*function)();
  647.     char    *closure;
  648. {
  649.     int            haveLocalhost = 0;
  650.     DisplayEntry    *d;
  651.     char        *clientName = 0, *NetworkAddressToHostname ();
  652.  
  653.     for (d = database; d; d = d->next)
  654.     {
  655.         switch (d->type) {
  656.         case DISPLAY_ALIAS:
  657.         continue;
  658.         case DISPLAY_PATTERN:
  659.         if (!clientName)
  660.         clientName = NetworkAddressToHostname (connectionType,
  661.                                clientAddress);
  662.         if (!patternMatch (clientName, d->entry.displayPattern))
  663.         continue;
  664.         break;
  665.         case DISPLAY_ADDRESS:
  666.         if (d->entry.displayAddress.connectionType != connectionType ||
  667.             !XdmcpARRAY8Equal (&d->entry.displayAddress.clientAddress,
  668.                   clientAddress))
  669.         {
  670.         continue;
  671.         }
  672.         break;
  673.         }
  674.     if (!d->hosts)
  675.         continue;
  676.     if (d->notAllowed)
  677.         break;
  678.     if (!d->chooser)
  679.         break;
  680.     if (scanHostlist (d->hosts, clientAddress, connectionType,
  681.               function, closure, 0, TRUE))
  682.     {
  683.         haveLocalhost = 1;
  684.     }
  685.     break;
  686.     }
  687.     if (clientName)
  688.     free (clientName);
  689.     if (haveLocalhost)
  690.     (*function) (connectionType, getLocalAddress(), closure);
  691. }
  692.  
  693. /*
  694.  * returns TRUE if the given client is acceptable to the local host.  The
  695.  * given display client is acceptable if it occurs without a host list.
  696.  */
  697.  
  698. AcceptableDisplayAddress (clientAddress, connectionType, type)
  699.     ARRAY8Ptr    clientAddress;
  700.     CARD16    connectionType;
  701.     xdmOpCode    type;
  702. {
  703.     DisplayEntry    *d;
  704.     char        *clientName = 0, *NetworkAddressToHostname ();
  705.  
  706.     if (!*accessFile)
  707.     return 1;
  708.     if (type == INDIRECT_QUERY)
  709.     return 1;
  710.     for (d = database; d; d = d->next)
  711.     {
  712.     if (d->hosts)
  713.         continue;
  714.         switch (d->type) {
  715.         case DISPLAY_ALIAS:
  716.         continue;
  717.         case DISPLAY_PATTERN:
  718.         if (!clientName)
  719.         clientName = NetworkAddressToHostname (connectionType,
  720.                                clientAddress);
  721.         if (!patternMatch (clientName, d->entry.displayPattern))
  722.         continue;
  723.         break;
  724.         case DISPLAY_ADDRESS:
  725.         if (d->entry.displayAddress.connectionType != connectionType ||
  726.             !XdmcpARRAY8Equal (&d->entry.displayAddress.clientAddress,
  727.                   clientAddress))
  728.         {
  729.         continue;
  730.         }
  731.         break;
  732.         }
  733.     break;
  734.     }
  735.     if (clientName)
  736.     free (clientName);
  737.     return (d != 0) && (d->notAllowed == 0);
  738. }
  739.  
  740. #endif /* XDMCP */
  741.