auth.c revision 1d778f8e
1/*
2
3Copyright 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/*
30 * xdm - display manager daemon
31 * Author:  Keith Packard, MIT X Consortium
32 *
33 * auth.c
34 *
35 * maintain the authorization generation daemon
36 */
37
38#include <X11/X.h>
39#include <X11/Xlibint.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42
43#include "dm.h"
44#include "dm_auth.h"
45#include "dm_error.h"
46
47#include <errno.h>
48
49#include <sys/ioctl.h>
50
51#ifdef TCPCONN
52# include "dm_socket.h"
53#endif
54
55
56
57#ifdef SVR4
58# include <netdb.h>
59# include <sys/sockio.h>
60# include <sys/stropts.h>
61#endif
62#ifdef __GNU__
63# include <netdb.h>
64# undef SIOCGIFCONF
65#else /* __GNU__ */
66# include <net/if.h>
67#endif /* __GNU__ */
68
69#if defined(TCPCONN) && !defined(WIN32)
70# include <netinet/in.h>
71#endif
72
73/* Solaris provides an extended interface SIOCGLIFCONF for IPv6 support.
74 */
75#ifdef SIOCGLIFCONF
76# define USE_SIOCGLIFCONF
77#endif
78
79#ifdef HAVE_SYS_PARAM_H
80# include <sys/param.h>
81# ifdef BSD
82#  if (BSD >= 199103)
83#   define VARIABLE_IFREQ
84#  endif
85# endif
86#endif
87
88
89struct AuthProtocol {
90    unsigned short  name_length;
91    const char	    *name;
92    void	    (*InitAuth)(unsigned short len, char *name);
93    Xauth	    *(*GetAuth)(unsigned short len, char *name);
94    void	    (*GetXdmcpAuth)(
95			struct protoDisplay	*pdpy,
96			unsigned short	authorizationNameLen,
97			char		*authorizationName);
98    int		    inited;
99};
100
101static struct AuthProtocol AuthProtocols[] = {
102{ (unsigned short) 18,	"MIT-MAGIC-COOKIE-1",
103    MitInitAuth, MitGetAuth, NULL
104},
105#ifdef HASXDMAUTH
106{ (unsigned short) 19,	"XDM-AUTHORIZATION-1",
107    XdmInitAuth, XdmGetAuth, XdmGetXdmcpAuth,
108},
109#endif
110#ifdef SECURE_RPC
111{ (unsigned short) 9, "SUN-DES-1",
112    SecureRPCInitAuth, SecureRPCGetAuth, NULL,
113},
114#endif
115#ifdef K5AUTH
116{ (unsigned short) 14, "MIT-KERBEROS-5",
117    Krb5InitAuth, Krb5GetAuth, NULL,
118},
119#endif
120};
121
122#define NUM_AUTHORIZATION (sizeof (AuthProtocols) / sizeof (AuthProtocols[0]))
123
124static struct AuthProtocol *
125findProtocol (unsigned short name_length, char *name)
126{
127    int	i;
128
129    for (i = 0; i < NUM_AUTHORIZATION; i++)
130	if (AuthProtocols[i].name_length == name_length &&
131	    memcmp(AuthProtocols[i].name, name, name_length) == 0)
132	{
133	    return &AuthProtocols[i];
134	}
135    return (struct AuthProtocol *) 0;
136}
137
138int
139ValidAuthorization (unsigned short name_length, char *name)
140{
141    if (findProtocol (name_length, name))
142	return TRUE;
143    return FALSE;
144}
145
146static Xauth *
147GenerateAuthorization (unsigned short name_length, char *name)
148{
149    struct AuthProtocol	*a;
150    Xauth   *auth = NULL;
151    int	    i;
152
153    Debug ("GenerateAuthorization %*.*s\n",
154	    name_length, name_length, name);
155    a = findProtocol (name_length, name);
156    if (a)
157    {
158	if (!a->inited)
159	{
160	    (*a->InitAuth) (name_length, name);
161	    a->inited = TRUE;
162	}
163	auth = (*a->GetAuth) (name_length, name);
164	if (auth)
165	{
166	    Debug ("Got %p (%d %*.*s) ", auth,
167		auth->name_length, auth->name_length,
168		auth->name_length, auth->name);
169	    for (i = 0; i < (int)auth->data_length; i++)
170		Debug (" %02x", auth->data[i] & 0xff);
171	    Debug ("\n");
172	}
173	else
174	    Debug ("Got (null)\n");
175    }
176    else
177    {
178	Debug ("Unknown authorization %*.*s\n", name_length, name_length, name);
179    }
180    return auth;
181}
182
183#ifdef XDMCP
184
185void
186SetProtoDisplayAuthorization (
187    struct protoDisplay	*pdpy,
188    unsigned short	authorizationNameLen,
189    char		*authorizationName)
190{
191    struct AuthProtocol	*a;
192    Xauth   *auth;
193
194    a = findProtocol (authorizationNameLen, authorizationName);
195    pdpy->xdmcpAuthorization = pdpy->fileAuthorization = NULL;
196    if (a)
197    {
198	if (!a->inited)
199	{
200	    (*a->InitAuth) (authorizationNameLen, authorizationName);
201	    a->inited = TRUE;
202	}
203	if (a->GetXdmcpAuth)
204	{
205	    (*a->GetXdmcpAuth) (pdpy, authorizationNameLen, authorizationName);
206	    auth = pdpy->xdmcpAuthorization;
207	}
208	else
209	{
210	    auth = (*a->GetAuth) (authorizationNameLen, authorizationName);
211	    pdpy->fileAuthorization = auth;
212	    pdpy->xdmcpAuthorization = NULL;
213	}
214	if (auth)
215	    Debug ("Got %p (%d %*.*s)\n", auth,
216		auth->name_length, auth->name_length,
217		auth->name_length, auth->name);
218	else
219	    Debug ("Got (null)\n");
220    }
221}
222
223#endif /* XDMCP */
224
225void
226CleanUpFileName (char *src, char *dst, int len)
227{
228    while (*src) {
229	if (--len <= 0)
230		break;
231	switch (*src & 0x7f)
232	{
233	case '/':
234	    *dst++ = '_';
235	    break;
236	case '-':
237	    *dst++ = '.';
238	    break;
239	default:
240	    *dst++ = (*src & 0x7f);
241	}
242	++src;
243    }
244    *dst = '\0';
245}
246
247/* Checks to see if specified directory exists, makes it if not
248 * Returns: 0 if already exists, 1 if created, < 0 if error occurred
249 */
250static int
251CheckServerAuthDir (const char *path, struct stat *statb, int mode)
252{
253    int r = stat(path, statb);
254
255    if (r != 0) {
256	if (errno == ENOENT) {
257	    r = mkdir(path, mode);
258	    if (r < 0) {
259		LogError ("cannot make authentication directory %s: %s\n",
260			  path, _SysErrorMsg (errno));
261	    } else {
262		r = 1;
263	    }
264	} else {
265	    LogError ("cannot access authentication directory %s: %s\n",
266		      path, _SysErrorMsg (errno));
267	}
268    } else { /* Directory already exists */
269	if (!S_ISDIR(statb->st_mode)) {
270	    LogError ("cannot make authentication directory %s: %s\n",
271		      path, "file with that name already exists");
272	    return -1;
273	}
274    }
275
276    return r;
277}
278
279static char authdir1[] = "authdir";
280static char authdir2[] = "authfiles";
281
282static int
283MakeServerAuthFile (struct display *d, FILE ** file)
284{
285    int len;
286#ifdef MAXNAMELEN
287# define NAMELEN	MAXNAMELEN
288#else
289# define NAMELEN	255
290#endif
291    char    cleanname[NAMELEN];
292    int r;
293#ifdef HAVE_MKSTEMP
294    int fd;
295#endif
296    struct stat	statb;
297
298    *file = NULL;
299
300    if (!d->authFile) {
301	if (d->clientAuthFile && *d->clientAuthFile) {
302	    d->authFile = strdup(d->clientAuthFile);
303	    if (!d->authFile)
304		return FALSE;
305	} else {
306	    CleanUpFileName (d->name, cleanname, NAMELEN - 8);
307
308	    /* Make authDir if it doesn't already exist */
309	    r = CheckServerAuthDir(authDir, &statb, 0755);
310	    if (r < 0) {
311		return FALSE;
312	    }
313
314	    len = strlen (authDir) + strlen (authdir1) + strlen (authdir2)
315		+ strlen (cleanname) + 14;
316	    d->authFile = malloc (len);
317	    if (!d->authFile)
318		return FALSE;
319
320	    snprintf (d->authFile, len, "%s/%s", authDir, authdir1);
321	    r = CheckServerAuthDir(d->authFile, &statb, 0700);
322	    if (r == 0) {
323		if (statb.st_uid != 0)
324		    (void) chown(d->authFile, 0, statb.st_gid);
325		if ((statb.st_mode & 0077) != 0)
326		    (void) chmod(d->authFile, statb.st_mode & 0700);
327	    } else if (r < 0) {
328		free (d->authFile);
329		d->authFile = NULL;
330		return FALSE;
331	    }
332
333	    snprintf (d->authFile, len, "%s/%s/%s",
334		      authDir, authdir1, authdir2);
335	    r = CheckServerAuthDir(d->authFile, &statb, 0700);
336	    if (r < 0) {
337		free (d->authFile);
338		d->authFile = NULL;
339		return FALSE;
340	    }
341	    snprintf (d->authFile, len, "%s/%s/%s/A%s-XXXXXX",
342		      authDir, authdir1, authdir2, cleanname);
343#ifdef HAVE_MKSTEMP
344	    fd = mkstemp (d->authFile);
345	    if (fd < 0) {
346		LogError ("cannot make authentication file %s: %s\n",
347			  d->authFile, _SysErrorMsg (errno));
348		free (d->authFile);
349		d->authFile = NULL;
350		return FALSE;
351	    }
352
353	    *file = fdopen(fd, "w");
354	    if (!*file)
355		(void) close (fd);
356	    return TRUE;
357#else
358	    (void) mktemp (d->authFile);
359#endif
360	}
361    }
362
363    (void) unlink (d->authFile);
364    *file = fopen (d->authFile, "w");
365    return TRUE;
366}
367
368int
369SaveServerAuthorizations (
370    struct display  *d,
371    Xauth	    **auths,
372    int		    count)
373{
374    FILE	*auth_file;
375    mode_t	mask;
376    int		ret;
377    int		i;
378    const char	dummy_auth[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
379		               "XXXXXXXXXXXXXXXXX"; /* 64 "X"s */
380    int		err = 0;
381
382    mask = umask (0077);
383    ret = MakeServerAuthFile(d, &auth_file);
384    umask (mask);
385    if (!ret)
386	return FALSE;
387    if (!auth_file) {
388	LogError ("cannot open server authorization file %s: %s\n",
389		  d->authFile, _SysErrorMsg (errno));
390	ret = FALSE;
391    }
392    else
393    {
394	Debug ("File: %s auth: %p\n", d->authFile, auths);
395	ret = TRUE;
396	if (count == 0)
397	{
398		/*
399		 * This is a crude hack to determine whether we really can
400		 * write to the auth file even if we don't have real data
401		 * to write right now.
402		 */
403
404		/*
405		 * Write garbage data to file to provoke ENOSPC and other
406		 * errors.
407		 */
408		(void) fprintf (auth_file, "%s", dummy_auth);
409		(void) fflush (auth_file);
410		if (ferror (auth_file))
411		{
412		    err = errno;
413		    ret = FALSE;
414		}
415		/*
416		 * Rewind so that the garbage data is overwritten later.
417		 */
418		rewind(auth_file);
419	}
420	for (i = 0; i < count; i++)
421	{
422	    /*
423	     * User-based auths may not have data until
424	     * a user logs in.  In which case don't write
425	     * to the auth file so xrdb and setup programs don't fail.
426	     */
427	    if (auths[i]->data_length > 0) {
428		if (!XauWriteAuth (auth_file, auths[i]))
429		{
430		    Debug ("XauWriteAuth() failed\n");
431		}
432		(void) fflush (auth_file);
433		if (ferror (auth_file))
434		{
435		    err = errno;
436		    ret = FALSE;
437		}
438            }
439	}
440	/*
441	 * XXX: This is not elegant, but stdio has no truncation function.
442	 */
443	if (ftruncate(fileno(auth_file), ftell(auth_file)))
444	{
445		Debug ("ftruncate() failed\n");
446	}
447	fclose (auth_file);
448
449    }
450    if (ret == FALSE)
451    {
452	LogError ("Cannot write to server authorization file %s%s%s\n",
453		  d->authFile,
454		  err ? ": " : "",
455		  err ? _SysErrorMsg (errno) : "");
456	free (d->authFile);
457	d->authFile = NULL;
458    }
459    return ret;
460}
461
462void
463SetLocalAuthorization (struct display *d)
464{
465    Xauth	*auth, **auths;
466    int		i, j;
467
468    if (d->authorizations)
469    {
470	for (i = 0; i < d->authNum; i++)
471	    XauDisposeAuth (d->authorizations[i]);
472	free (d->authorizations);
473	d->authorizations = (Xauth **) NULL;
474	d->authNum = 0;
475    }
476    if (!d->authNames)
477	return;
478    for (i = 0; d->authNames[i]; i++)
479	;
480    d->authNameNum = i;
481    free (d->authNameLens);
482    d->authNameLens = malloc (d->authNameNum * sizeof (unsigned short));
483    if (!d->authNameLens)
484	return;
485    for (i = 0; i < d->authNameNum; i++)
486	d->authNameLens[i] = strlen (d->authNames[i]);
487    auths = malloc (d->authNameNum * sizeof (Xauth *));
488    if (!auths)
489	return;
490    j = 0;
491    for (i = 0; i < d->authNameNum; i++)
492    {
493	auth = GenerateAuthorization (d->authNameLens[i], d->authNames[i]);
494	if (auth)
495	    auths[j++] = auth;
496    }
497    if (SaveServerAuthorizations (d, auths, j))
498    {
499	d->authorizations = auths;
500	d->authNum = j;
501    }
502    else
503    {
504	for (i = 0; i < j; i++)
505	    XauDisposeAuth (auths[i]);
506	free (auths);
507    }
508}
509
510/*
511 * Set the authorization to use for xdm's initial connection
512 * to the X server.  Cannot use user-based authorizations
513 * because no one has logged in yet, so we don't have any
514 * user credentials.
515 * Well, actually we could use SUN-DES-1 because we tell the server
516 * to allow root in.  This is bogus and should be fixed.
517 */
518void
519SetAuthorization (struct display *d)
520{
521    register Xauth **auth = d->authorizations;
522    int i;
523
524    for (i = 0; i < d->authNum; i++)
525    {
526	if (auth[i]->name_length == 9 &&
527	    memcmp(auth[i]->name, "SUN-DES-1", 9) == 0)
528	    continue;
529	if (auth[i]->name_length == 14 &&
530	    memcmp(auth[i]->name, "MIT-KERBEROS-5", 14) == 0)
531	    continue;
532	XSetAuthorization (auth[i]->name, (int) auth[i]->name_length,
533			   auth[i]->data, (int) auth[i]->data_length);
534    }
535}
536
537static int
538openFiles (char *name, char *new_name, FILE **oldp, FILE **newp)
539{
540	mode_t	mask;
541	int newfd;
542
543	strcpy (new_name, name);
544	strcat (new_name, "-n");
545	/*
546	 * Set safe umask for file creation operations.
547	 */
548	mask = umask (0077);
549	/*
550	 * Unlink the authorization file we intend to create, and then open
551	 * it with O_CREAT | O_EXCL to avoid race-based symlink attacks.
552	 */
553	(void) unlink (new_name);
554	newfd = open (new_name, O_WRONLY | O_CREAT | O_EXCL, 0600);
555	if (newfd >= 0) {
556	    *newp = fdopen (newfd, "w");
557	    if (*newp == NULL)
558		close(newfd);
559	}
560	else
561	{
562	    LogError ("Cannot create file %s: %s\n", new_name,
563		      _SysErrorMsg (errno));
564	    *newp = NULL;
565	}
566	/*
567	 * There are no more attempts to create files after this point;
568	 * restore the original umask.
569	 */
570	(void) umask (mask);
571	if (!*newp) {
572		Debug ("can't open new file %s\n", new_name);
573		return 0;
574	}
575	if (!*oldp)
576	    *oldp = fopen (name, "r");
577	Debug ("opens succeeded %s %s\n", name, new_name);
578	return 1;
579}
580
581static int
582binaryEqual (char *a, char *b, unsigned short len)
583{
584	while (len-- > 0)
585		if (*a++ != *b++)
586			return FALSE;
587	return TRUE;
588}
589
590static void
591dumpBytes (unsigned short len, char *data)
592{
593	unsigned short	i;
594
595	Debug ("%d: ", len);
596	for (i = 0; i < len; i++)
597		Debug ("%02x ", data[i] & 0377);
598	Debug ("\n");
599}
600
601static void
602dumpAuth (Xauth *auth)
603{
604	Debug ("family: %d\n", auth->family);
605	Debug ("addr:   ");
606	dumpBytes (auth->address_length, auth->address);
607	Debug ("number: ");
608	dumpBytes (auth->number_length, auth->number);
609	Debug ("name:   ");
610	dumpBytes (auth->name_length, auth->name);
611	Debug ("data:   ");
612	dumpBytes (auth->data_length, auth->data);
613}
614
615struct addrList {
616	unsigned short	family;
617	unsigned short	address_length;
618	char	*address;
619	unsigned short	number_length;
620	char	*number;
621	unsigned short	name_length;
622	char	*name;
623	struct addrList	*next;
624};
625
626static struct addrList	*addrs;
627
628static void
629initAddrs (void)
630{
631	addrs = NULL;
632}
633
634static void
635doneAddrs (void)
636{
637	struct addrList	*a, *n;
638	for (a = addrs; a; a = n) {
639		n = a->next;
640		free (a->address);
641		free (a->number);
642		free (a);
643	}
644	addrs = NULL;
645}
646
647static int checkEntry (Xauth *auth);
648
649static void
650saveEntry (Xauth *auth)
651{
652	struct addrList	*new;
653
654	new = malloc (sizeof (struct addrList));
655	if (!new) {
656		LogOutOfMem ("saveEntry");
657		return;
658	}
659	if ((new->address_length = auth->address_length) > 0) {
660		new->address = malloc (auth->address_length);
661		if (!new->address) {
662			LogOutOfMem ("saveEntry");
663			free (new);
664			return;
665		}
666		memcpy(new->address, auth->address, (int) auth->address_length);
667	} else
668		new->address = NULL;
669	if ((new->number_length = auth->number_length) > 0) {
670		new->number = malloc (auth->number_length);
671		if (!new->number) {
672			LogOutOfMem ("saveEntry");
673			free (new->address);
674			free (new);
675			return;
676		}
677		memcpy(new->number, auth->number, (int) auth->number_length);
678	} else
679		new->number = NULL;
680	if ((new->name_length = auth->name_length) > 0) {
681		new->name = malloc (auth->name_length);
682		if (!new->name) {
683			LogOutOfMem ("saveEntry");
684			free (new->number);
685			free (new->address);
686			free (new);
687			return;
688		}
689		memcpy(new->name, auth->name, (int) auth->name_length);
690	} else
691		new->name = NULL;
692	new->family = auth->family;
693	new->next = addrs;
694	addrs = new;
695}
696
697static int
698checkEntry (Xauth *auth)
699{
700	struct addrList	*a;
701
702	for (a = addrs; a; a = a->next) {
703		if (a->family == auth->family &&
704		    a->address_length == auth->address_length &&
705		    binaryEqual (a->address, auth->address, auth->address_length) &&
706		    a->number_length == auth->number_length &&
707		    binaryEqual (a->number, auth->number, auth->number_length) &&
708		    a->name_length == auth->name_length &&
709		    binaryEqual (a->name, auth->name, auth->name_length))
710		{
711			return 1;
712		}
713	}
714	return 0;
715}
716
717static int  doWrite;
718
719static void
720writeAuth (FILE *file, Xauth *auth)
721{
722    if (debugLevel >= 15) {	/* normally too verbose */
723        Debug ("writeAuth: doWrite = %d\n", doWrite);
724	dumpAuth (auth);	/* does Debug only */
725    }
726	if (doWrite)
727	    XauWriteAuth (file, auth);
728}
729
730static void
731writeAddr (
732    int		family,
733    int		addr_length,
734    char	*addr,
735    FILE	*file,
736    Xauth	*auth)
737{
738	auth->family = (unsigned short) family;
739	auth->address_length = addr_length;
740	auth->address = addr;
741	Debug ("writeAddr: writing and saving an entry\n");
742	writeAuth (file, auth);
743	saveEntry (auth);
744}
745
746static void
747DefineLocal (FILE *file, Xauth *auth)
748{
749	char	displayname[100];
750	int	len = _XGetHostname (displayname, sizeof(displayname));
751
752/* Make sure this produces the same string as _XGetHostname in lib/X/XlibInt.c.
753 * Otherwise, Xau will not be able to find your cookies in the Xauthority file.
754 *
755 * Note: POSIX says that the ``nodename'' member of utsname does _not_ have
756 *       to have sufficient information for interfacing to the network,
757 *       and so, you may be better off using gethostname (if it exists).
758 */
759
760
761	writeAddr (FamilyLocal, len, displayname, file, auth);
762}
763
764#ifdef HAVE_GETIFADDRS
765# include <ifaddrs.h>
766
767static void
768DefineSelf(int fd, FILE *file, Xauth *auth)
769{
770    struct ifaddrs *ifap, *ifr;
771    char *addr;
772    int family, len;
773
774    Debug("DefineSelf\n");
775    if (getifaddrs(&ifap) < 0)
776	return;
777    for (ifr = ifap; ifr != NULL; ifr = ifr->ifa_next) {
778	len = sizeof(*(ifr->ifa_addr));
779	family = ConvertAddr((XdmcpNetaddr)(ifr->ifa_addr), &len, &addr);
780	if (family == -1 || family == FamilyLocal)
781	    continue;
782	/*
783	 * don't write out 'localhost' entries, as
784	 * they may conflict with other local entries.
785	 * DefineLocal will always be called to add
786	 * the local entry anyway, so this one can
787	 * be tossed.
788	 */
789	if (family == FamilyInternet && len == 4 && addr[0] == 127)
790	{
791	    Debug ("Skipping localhost address\n");
792	    continue;
793	}
794# ifdef IPv6
795	if(family == FamilyInternet6) {
796	    if (IN6_IS_ADDR_LOOPBACK(((struct in6_addr *)addr))) {
797		Debug ("Skipping IPv6 localhost address\n");
798		continue;
799	    }
800	    /* Also skip XDM-AUTHORIZATION-1 */
801	    if (auth->name_length == 19 &&
802		strcmp(auth->name, "XDM-AUTHORIZATION-1") == 0) {
803		Debug ("Skipping IPv6 XDM-AUTHORIZATION-1\n");
804		continue;
805	    }
806	}
807# endif
808	writeAddr(family, len, addr, file, auth);
809    }
810    freeifaddrs(ifap);
811    Debug("DefineSelf done\n");
812}
813#else  /* GETIFADDRS */
814
815# if defined(SIOCGIFCONF) || defined (USE_SIOCGLIFCONF)
816
817#  ifdef USE_SIOCGLIFCONF
818#   define ifr_type    struct lifreq
819#  else
820#   define ifr_type    struct ifreq
821#  endif
822
823/* Handle variable length ifreq in BNR2 and later */
824#  ifdef VARIABLE_IFREQ
825#   define ifr_size(p) (sizeof (struct ifreq) + \
826		     (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
827		      p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
828#  else
829#   define ifr_size(p) (sizeof (ifr_type))
830#  endif
831
832/* Define this host for access control.  Find all the hosts the OS knows about
833 * for this fd and add them to the selfhosts list.
834 */
835static void
836DefineSelf (int fd, FILE *file, Xauth *auth)
837{
838    char		buf[2048], *cp, *cplim;
839    int 		len;
840    char 		*addr;
841    int 		family;
842    register ifr_type  *ifr;
843#  ifdef USE_SIOCGLIFCONF
844    void *		bufptr = buf;
845    size_t		buflen = sizeof(buf);
846    struct lifconf	ifc;
847#   ifdef SIOCGLIFNUM
848    struct lifnum	ifn;
849#   endif
850#  else
851    struct ifconf	ifc;
852#  endif
853
854#  if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF)
855    ifn.lifn_family = AF_UNSPEC;
856    ifn.lifn_flags = 0;
857    if (ioctl (fd, (int) SIOCGLIFNUM, (char *) &ifn) < 0)
858	LogError ("Failed getting interface count");
859    if (buflen < (ifn.lifn_count * sizeof(struct lifreq))) {
860	buflen = ifn.lifn_count * sizeof(struct lifreq);
861	bufptr = malloc(buflen);
862    }
863#  endif
864
865#  ifdef USE_SIOCGLIFCONF
866    ifc.lifc_family = AF_UNSPEC;
867    ifc.lifc_flags = 0;
868    ifc.lifc_len = buflen;
869    ifc.lifc_buf = bufptr;
870
871#   define IFC_IOCTL_REQ SIOCGLIFCONF
872#   define IFC_IFC_REQ ifc.lifc_req
873#   define IFC_IFC_LEN ifc.lifc_len
874#   define IFR_IFR_ADDR ifr->lifr_addr
875#   define IFR_IFR_NAME ifr->lifr_name
876
877#  else
878    ifc.ifc_len = sizeof (buf);
879    ifc.ifc_buf = buf;
880
881#   define IFC_IOCTL_REQ SIOCGIFCONF
882#   define IFC_IFC_REQ ifc.ifc_req
883#   define IFC_IFC_LEN ifc.ifc_len
884#   define IFR_IFR_ADDR ifr->ifr_addr
885#   define IFR_IFR_NAME ifr->ifr_name
886#  endif
887
888    if (ioctl (fd, IFC_IOCTL_REQ, (char *) &ifc) < 0) {
889        LogError ("Trouble getting network interface configuration");
890
891#  ifdef USE_SIOCGLIFCONF
892	if (bufptr != buf) {
893	    free(bufptr);
894	}
895#  endif
896	return;
897    }
898
899    cplim = (char *) IFC_IFC_REQ + IFC_IFC_LEN;
900
901    for (cp = (char *) IFC_IFC_REQ; cp < cplim; cp += ifr_size (ifr))
902    {
903	ifr = (ifr_type *) cp;
904	family = ConvertAddr ((XdmcpNetaddr) &IFR_IFR_ADDR, &len, &addr);
905	if (family < 0)
906	    continue;
907
908	if (len == 0)
909	{
910	    Debug ("Skipping zero length address\n");
911	    continue;
912	}
913	/*
914	 * don't write out 'localhost' entries, as
915	 * they may conflict with other local entries.
916	 * DefineLocal will always be called to add
917	 * the local entry anyway, so this one can
918	 * be tossed.
919	 */
920	if (family == FamilyInternet && len == 4 &&
921	    addr[0] == 127 && addr[1] == 0 &&
922	    addr[2] == 0 && addr[3] == 1)
923	{
924	    Debug ("Skipping localhost address\n");
925	    continue;
926	}
927#  ifdef IPv6
928	if (family == FamilyInternet6) {
929	    if (IN6_IS_ADDR_LOOPBACK(((struct in6_addr *)addr))) {
930		Debug ("Skipping IPv6 localhost address\n");
931		continue;
932	    }
933	    /* Also skip XDM-AUTHORIZATION-1 */
934	    if (auth->name_length == 19 &&
935		strcmp(auth->name, "XDM-AUTHORIZATION-1") == 0) {
936		Debug ("Skipping IPv6 XDM-AUTHORIZATION-1\n");
937		continue;
938	    }
939	}
940#  endif
941	Debug ("DefineSelf: write network address, length %d\n", len);
942	writeAddr (family, len, addr, file, auth);
943    }
944}
945
946# else /* SIOCGIFCONF */
947
948/* Define this host for access control.  Find all the hosts the OS knows about
949 * for this fd and add them to the selfhosts list.
950 */
951static void
952DefineSelf (int fd, int file, int auth)
953{
954    register int n;
955    int	len;
956    caddr_t	addr;
957    int		family;
958
959    struct utsname name;
960    register struct hostent  *hp;
961
962    union {
963	struct  sockaddr   sa;
964	struct  sockaddr_in  in;
965    } saddr;
966
967    struct	sockaddr_in	*inetaddr;
968
969    /* hpux:
970     * Why not use gethostname()?  Well, at least on my system, I've had to
971     * make an ugly kernel patch to get a name longer than 8 characters, and
972     * uname() lets me access to the whole string (it smashes release, you
973     * see), whereas gethostname() kindly truncates it for me.
974     */
975    uname(&name);
976    hp = gethostbyname (name.nodename);
977    if (hp != NULL) {
978	saddr.sa.sa_family = hp->h_addrtype;
979	inetaddr = (struct sockaddr_in *) (&(saddr.sa));
980	memcpy(&(inetaddr->sin_addr), hp->h_addr, hp->h_length);
981	family = ConvertAddr ( &(saddr.sa), &len, &addr);
982	if ( family >= 0) {
983	    writeAddr (FamilyInternet, sizeof (inetaddr->sin_addr),
984			(char *) (&inetaddr->sin_addr), file, auth);
985	}
986    }
987}
988
989
990# endif /* SIOCGIFCONF else */
991#endif /* HAVE_GETIFADDRS */
992
993static void
994setAuthNumber (Xauth *auth, char *name)
995{
996    char	*colon;
997    char	*dot, *number;
998
999    Debug ("setAuthNumber %s\n", name);
1000    colon = strrchr(name, ':');
1001    if (colon) {
1002	++colon;
1003	dot = strchr(colon, '.');
1004	if (dot)
1005	    auth->number_length = dot - colon;
1006	else
1007	    auth->number_length = strlen (colon);
1008	number = malloc (auth->number_length + 1);
1009	if (number) {
1010	    strncpy (number, colon, auth->number_length);
1011	    number[auth->number_length] = '\0';
1012	} else {
1013	    LogOutOfMem ("setAuthNumber");
1014	    auth->number_length = 0;
1015	}
1016	auth->number = number;
1017	Debug ("setAuthNumber: %s\n", number);
1018    }
1019}
1020
1021static void
1022writeLocalAuth (FILE *file, Xauth *auth, char *name)
1023{
1024    int	fd;
1025
1026    Debug ("writeLocalAuth: %s %.*s\n", name, auth->name_length, auth->name);
1027    setAuthNumber (auth, name);
1028#ifdef TCPCONN
1029# ifdef IPv6
1030    fd = socket (AF_INET6, SOCK_STREAM, 0);
1031    if (fd < 0)
1032# endif
1033    fd = socket (AF_INET, SOCK_STREAM, 0);
1034    DefineSelf (fd, file, auth);
1035    close (fd);
1036#endif
1037    DefineLocal (file, auth);
1038}
1039
1040#ifdef XDMCP
1041
1042static void
1043writeRemoteAuth (FILE *file, Xauth *auth, XdmcpNetaddr peer, int peerlen, char *name)
1044{
1045    int	    family = FamilyLocal;
1046    char    *addr;
1047
1048    Debug ("writeRemoteAuth: %s %.*s\n", name, auth->name_length, auth->name);
1049    if (!peer || peerlen < 2)
1050	return;
1051    setAuthNumber (auth, name);
1052    family = ConvertAddr (peer, &peerlen, &addr);
1053    Debug ("writeRemoteAuth: family %d\n", family);
1054    if (family != FamilyLocal)
1055    {
1056	Debug ("writeRemoteAuth: %d, %d, %x\n",
1057		family, peerlen, *(int *)addr);
1058	writeAddr (family, peerlen, addr, file, auth);
1059    }
1060    else
1061    {
1062	writeLocalAuth (file, auth, name);
1063    }
1064}
1065
1066#endif /* XDMCP */
1067
1068void
1069SetUserAuthorization (struct display *d, struct verify_info *verify)
1070{
1071    FILE	*old = NULL, *new;
1072    char	home_name[1024], backup_name[1024], new_name[1024];
1073    char	*name = NULL;
1074    char	*home;
1075    char	*envname = NULL;
1076    int	lockStatus;
1077    Xauth	*entry, **auths;
1078    int		setenv = 0;
1079    struct stat	statb;
1080    int		i;
1081    int		magicCookie;
1082    int		data_len;
1083#ifdef HAVE_MKSTEMP
1084    int		fd;
1085#endif
1086
1087    Debug ("SetUserAuthorization\n");
1088    auths = d->authorizations;
1089    if (auths) {
1090	home = getEnv (verify->userEnviron, "HOME");
1091	lockStatus = LOCK_ERROR;
1092	if (home) {
1093	    snprintf (home_name, sizeof(home_name), "%s/.Xauthority", home);
1094	    Debug ("XauLockAuth %s\n", home_name);
1095	    lockStatus = XauLockAuth (home_name, 1, 2, 10);
1096	    Debug ("Lock is %d\n", lockStatus);
1097	    if (lockStatus == LOCK_SUCCESS) {
1098		if (openFiles (home_name, new_name, &old, &new)
1099		    && (old != NULL) && (new != NULL)) {
1100		    name = home_name;
1101		    setenv = 0;
1102		} else {
1103		    Debug ("openFiles failed\n");
1104		    XauUnlockAuth (home_name);
1105		    lockStatus = LOCK_ERROR;
1106		    if (old != NULL) {
1107			(void) fclose (old);
1108			old = NULL;
1109		    }
1110		    if (new != NULL)
1111			(void) fclose (new);
1112		}
1113	    }
1114	}
1115	if (lockStatus != LOCK_SUCCESS) {
1116	    snprintf (backup_name, sizeof(backup_name),
1117		      "%s/.XauthXXXXXX", d->userAuthDir);
1118#ifdef HAVE_MKSTEMP
1119	    fd = mkstemp (backup_name);
1120	    if (fd >= 0) {
1121		old = fdopen (fd, "r");
1122		if (old == NULL)
1123		    (void) close(fd);
1124	    }
1125
1126	    if (old != NULL)
1127#else
1128	    (void) mktemp (backup_name);
1129#endif
1130	    {
1131		lockStatus = XauLockAuth (backup_name, 1, 2, 10);
1132		Debug ("backup lock is %d\n", lockStatus);
1133		if (lockStatus == LOCK_SUCCESS) {
1134		    if (openFiles (backup_name, new_name, &old, &new)
1135			&& (old != NULL) && (new != NULL)) {
1136			name = backup_name;
1137			setenv = 1;
1138		    } else {
1139			XauUnlockAuth (backup_name);
1140			lockStatus = LOCK_ERROR;
1141			if (old != NULL) {
1142			    (void) fclose (old);
1143			    old = NULL;
1144			}
1145			if (new != NULL)
1146			    (void) fclose (new);
1147		    }
1148#ifdef HAVE_MKSTEMP
1149		} else {
1150		    (void) fclose (old);
1151#endif
1152		}
1153	    }
1154	}
1155	if (lockStatus != LOCK_SUCCESS) {
1156	    Debug ("can't lock auth file %s or backup %s\n",
1157			    home_name, backup_name);
1158	    LogError ("can't lock authorization file %s or backup %s\n",
1159			    home_name, backup_name);
1160	    return;
1161	}
1162	initAddrs ();
1163	doWrite = 1;
1164	Debug ("%d authorization protocols for %s\n", d->authNum, d->name);
1165	/*
1166	 * Write MIT-MAGIC-COOKIE-1 authorization first, so that
1167	 * R4 clients which only knew that, and used the first
1168	 * matching entry will continue to function
1169	 */
1170	magicCookie = -1;
1171	for (i = 0; i < d->authNum; i++)
1172	{
1173	    if (auths[i]->name_length == 18 &&
1174		!strncmp (auths[i]->name, "MIT-MAGIC-COOKIE-1", 18))
1175	    {
1176		magicCookie = i;
1177		if (d->displayType.location == Local)
1178		    writeLocalAuth (new, auths[i], d->name);
1179#ifdef XDMCP
1180		else
1181		    writeRemoteAuth (new, auths[i], d->peer, d->peerlen, d->name);
1182#endif
1183		break;
1184	    }
1185	}
1186	/* now write other authorizations */
1187	for (i = 0; i < d->authNum; i++)
1188	{
1189	    if (i != magicCookie)
1190	    {
1191		data_len = auths[i]->data_length;
1192		/* client will just use default Kerberos cache, so don't
1193		 * even write cache info into the authority file.
1194		 */
1195		if (auths[i]->name_length == 14 &&
1196		    !strncmp (auths[i]->name, "MIT-KERBEROS-5", 14))
1197		    auths[i]->data_length = 0;
1198		if (d->displayType.location == Local)
1199		    writeLocalAuth (new, auths[i], d->name);
1200#ifdef XDMCP
1201		else
1202		    writeRemoteAuth (new, auths[i], d->peer, d->peerlen, d->name);
1203#endif
1204		auths[i]->data_length = data_len;
1205	    }
1206	}
1207	if (old) {
1208	    if (fstat (fileno (old), &statb) != -1)
1209		chmod (new_name, (int) (statb.st_mode & 0777));
1210	    /*SUPPRESS 560*/
1211	    while ((entry = XauReadAuth (old))) {
1212		if (!checkEntry (entry))
1213		{
1214		    Debug ("Writing an entry\n");
1215		    writeAuth (new, entry);
1216		}
1217		XauDisposeAuth (entry);
1218	    }
1219	    fclose (old);
1220	}
1221	doneAddrs ();
1222	fclose (new);
1223	if (unlink (name) == -1)
1224	    if (errno != ENOENT)
1225		LogError ("cannot remove old authorization file %s: %s\n",
1226			  name, _SysErrorMsg (errno));
1227	envname = name;
1228	if (link (new_name, name) == -1) {
1229	    LogError ("cannot link temporary authorization file %s to old "
1230		      "location %s: %s\n", new_name, name,
1231		      _SysErrorMsg (errno));
1232	    setenv = 1;
1233	    envname = new_name;
1234	} else {
1235	    Debug ("authorization file %s successfully updated\n", name);
1236	    if (unlink (new_name))
1237		if (errno != ENOENT)
1238		    LogError ("cannot remove new authorization file %s:"
1239			      " %s\n", new_name, _SysErrorMsg (errno));
1240	}
1241	if (setenv) {
1242	    verify->userEnviron = setEnv (verify->userEnviron,
1243				    "XAUTHORITY", envname);
1244	    verify->systemEnviron = setEnv (verify->systemEnviron,
1245				    "XAUTHORITY", envname);
1246	}
1247	XauUnlockAuth (name);
1248	if (envname)
1249	    chown (envname, verify->uid, verify->gid);
1250    }
1251    Debug ("done SetUserAuthorization\n");
1252}
1253
1254void
1255RemoveUserAuthorization (struct display *d, struct verify_info *verify)
1256{
1257    char    *home;
1258    Xauth   **auths, *entry;
1259    char    name[1024], new_name[1024];
1260    int	    lockStatus;
1261    FILE    *old, *new;
1262    struct stat	statb;
1263    int	    i;
1264
1265    if (!(auths = d->authorizations))
1266	return;
1267    home = getEnv (verify->userEnviron, "HOME");
1268    if (!home)
1269	return;
1270    Debug ("RemoveUserAuthorization\n");
1271    snprintf(name, sizeof(name), "%s/.Xauthority", home);
1272    Debug ("XauLockAuth %s\n", name);
1273    lockStatus = XauLockAuth (name, 1, 2, 10);
1274    Debug ("Lock is %d\n", lockStatus);
1275    if (lockStatus != LOCK_SUCCESS)
1276	return;
1277    old = NULL;
1278    if (openFiles (name, new_name, &old, &new))
1279    {
1280	initAddrs ();
1281	doWrite = 0;
1282	for (i = 0; i < d->authNum; i++)
1283	{
1284	    if (d->displayType.location == Local)
1285		writeLocalAuth (new, auths[i], d->name);
1286#ifdef XDMCP
1287	    else
1288		writeRemoteAuth (new, auths[i], d->peer, d->peerlen, d->name);
1289#endif
1290	}
1291	doWrite = 1;
1292	if (old) {
1293	    if (fstat (fileno (old), &statb) != -1)
1294		chmod (new_name, (int) (statb.st_mode & 0777));
1295	    /*SUPPRESS 560*/
1296	    while ((entry = XauReadAuth (old))) {
1297		if (!checkEntry (entry))
1298		{
1299		    Debug ("Writing an entry\n");
1300		    writeAuth (new, entry);
1301		}
1302		XauDisposeAuth (entry);
1303	    }
1304	    fclose (old);
1305	}
1306	doneAddrs ();
1307	fclose (new);
1308	if (unlink (name) == -1)
1309	    if (errno != ENOENT)
1310		LogError ("cannot remove new authorization file %s: %s\n",
1311			  name, _SysErrorMsg (errno));
1312	if (link (new_name, name) == -1) {
1313	    LogError ("cannot link temporary authorization file %s to old "
1314		      "location %s: %s\n", new_name, name,
1315		      _SysErrorMsg (errno));
1316	} else {
1317	    Debug ("authorization file %s successfully updated\n", name);
1318	    if (unlink (new_name))
1319		if (errno != ENOENT)
1320		    LogError ("cannot remove new authorization file %s:"
1321			      " %s\n", new_name, _SysErrorMsg (errno));
1322	}
1323    }
1324    XauUnlockAuth (name);
1325}
1326