1/*
2
3Copyright 1989, 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 * Author:  Jim Fulton, MIT X Consortium
31 */
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36
37#include "xauth.h"
38#include <ctype.h>
39#include <errno.h>
40#include <stdint.h>
41#include <sys/stat.h>
42#ifndef WIN32
43#include <sys/socket.h>
44#else
45#include <X11/Xwinsock.h>
46#endif
47
48#include <signal.h>
49#include <X11/X.h>			/* for Family constants */
50
51#include <X11/Xlib.h>
52#include <X11/extensions/security.h>
53
54#ifndef DEFAULT_PROTOCOL_ABBREV		/* to make add command easier */
55#define DEFAULT_PROTOCOL_ABBREV "."
56#endif
57#ifndef DEFAULT_PROTOCOL		/* for protocol abbreviation */
58#define DEFAULT_PROTOCOL "MIT-MAGIC-COOKIE-1"
59#endif
60
61#define SECURERPC "SUN-DES-1"
62#define K5AUTH "MIT-KERBEROS-5"
63
64#define XAUTH_DEFAULT_RETRIES 10	/* number of competitors we expect */
65#define XAUTH_DEFAULT_TIMEOUT 2		/* in seconds, be quick */
66#define XAUTH_DEFAULT_DEADTIME 600L	/* 10 minutes in seconds */
67
68typedef struct _AuthList {		/* linked list of entries */
69    struct _AuthList *next;
70    Xauth *auth;
71} AuthList;
72
73typedef int (*ProcessFunc)(const char *, int, int, const char**);
74
75#define add_to_list(h,t,e) {if (t) (t)->next = (e); else (h) = (e); (t) = (e);}
76
77typedef struct _CommandTable {		/* commands that are understood */
78    const char *name;			/* full name */
79    int minlen;				/* unique prefix */
80    int maxlen;				/* strlen(name) */
81    ProcessFunc processfunc;		/* handler */
82    const char *helptext;		/* what to print for help */
83} CommandTable;
84
85struct _extract_data {			/* for iterating */
86    FILE *fp;				/* input source */
87    const char *filename;		/* name of input */
88    Bool used_stdout;			/* whether or not need to close */
89    Bool numeric;			/* format in which to write */
90    int nwritten;			/* number of entries written */
91    const char *cmd;			/* for error messages */
92};
93
94struct _list_data {			/* for iterating */
95    FILE *fp;				/* output file */
96    Bool numeric;			/* format in which to write */
97};
98
99
100/*
101 * private data
102 */
103static const char *stdin_filename = "(stdin)";		/* for messages */
104static const char *stdout_filename = "(stdout)";	/* for messages */
105static const char *Yes = "yes";		/* for messages */
106static const char *No = "no";		/* for messages */
107
108static int do_help ( const char *inputfilename, int lineno, int argc, const char **argv );
109static int do_questionmark ( const char *inputfilename, int lineno, int argc, const char **argv );
110static int do_list ( const char *inputfilename, int lineno, int argc, const char **argv );
111static int do_merge ( const char *inputfilename, int lineno, int argc, const char **argv );
112static int do_extract ( const char *inputfilename, int lineno, int argc, const char **argv );
113static int do_add ( const char *inputfilename, int lineno, int argc, const char **argv );
114static int do_remove ( const char *inputfilename, int lineno, int argc, const char **argv );
115static int do_info ( const char *inputfilename, int lineno, int argc, const char **argv );
116static int do_exit ( const char *inputfilename, int lineno, int argc, const char **argv );
117static int do_quit ( const char *inputfilename, int lineno, int argc, const char **argv );
118static int do_source ( const char *inputfilename, int lineno, int argc, const char **argv );
119static int do_generate ( const char *inputfilename, int lineno, int argc, const char **argv );
120static int do_version ( const char *inputfilename, int lineno, int argc, const char **argv );
121
122static CommandTable command_table[] = {	/* table of known commands */
123    { "add",      2, 3, do_add,
124	"add dpyname protoname hexkey   add entry" },
125    { "exit",     3, 4, do_exit,
126	"exit                           save changes and exit program" },
127    { "extract",  3, 7, do_extract,
128	"extract filename dpyname...    extract entries into file" },
129    { "help",     1, 4, do_help,
130	"help [topic]                   print help" },
131    { "info",     1, 4, do_info,
132	"info                           print information about entries" },
133    { "list",     1, 4, do_list,
134	"list [dpyname...]              list entries" },
135    { "merge",    1, 5, do_merge,
136	"merge filename...              merge entries from files" },
137    { "nextract", 2, 8, do_extract,
138	"nextract filename dpyname...   numerically extract entries" },
139    { "nlist",    2, 5, do_list,
140	"nlist [dpyname...]             numerically list entries" },
141    { "nmerge",   2, 6, do_merge,
142	"nmerge filename...             numerically merge entries" },
143    { "quit",     1, 4, do_quit,
144	"quit                           abort changes and exit program" },
145    { "remove",   1, 6, do_remove,
146	"remove dpyname...              remove entries" },
147    { "source",   1, 6, do_source,
148	"source filename                read commands from file" },
149    { "version",  1, 7, do_version,
150	"version                        show version number of xauth" },
151    { "?",        1, 1, do_questionmark,
152	"?                              list available commands" },
153    { "generate", 1, 8, do_generate,
154	"generate dpyname protoname [options]  use server to generate entry\n"
155        "    options are:\n"
156        "      timeout n    authorization expiration time in seconds\n"
157        "      trusted      clients using this entry are trusted\n"
158        "      untrusted    clients using this entry are untrusted\n"
159        "      group n      clients using this entry belong to application group n\n"
160        "      data hexkey  auth protocol specific data needed to generate the entry\n"
161    },
162    { NULL,       0, 0, NULL, NULL },
163};
164
165#define COMMAND_NAMES_PADDED_WIDTH 10	/* wider than anything above */
166
167
168static Bool okay_to_use_stdin = True;	/* set to false after using */
169
170static const char *hex_table[] = {	/* for printing hex digits */
171    "00", "01", "02", "03", "04", "05", "06", "07",
172    "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
173    "10", "11", "12", "13", "14", "15", "16", "17",
174    "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
175    "20", "21", "22", "23", "24", "25", "26", "27",
176    "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
177    "30", "31", "32", "33", "34", "35", "36", "37",
178    "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
179    "40", "41", "42", "43", "44", "45", "46", "47",
180    "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
181    "50", "51", "52", "53", "54", "55", "56", "57",
182    "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
183    "60", "61", "62", "63", "64", "65", "66", "67",
184    "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
185    "70", "71", "72", "73", "74", "75", "76", "77",
186    "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
187    "80", "81", "82", "83", "84", "85", "86", "87",
188    "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
189    "90", "91", "92", "93", "94", "95", "96", "97",
190    "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
191    "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
192    "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
193    "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7",
194    "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
195    "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
196    "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
197    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
198    "d8", "d9", "da", "db", "dc", "dd", "de", "df",
199    "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7",
200    "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
201    "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
202    "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
203};
204
205static unsigned int hexvalues[256];	/* for parsing hex input */
206
207static int original_umask = 0;		/* for restoring */
208
209
210/*
211 * private utility procedures
212 */
213
214static void
215prefix(const char *fn, int n)
216{
217    fprintf (stderr, "%s: %s:%d:  ", ProgramName, fn, n);
218}
219
220static void
221baddisplayname(const char *dpy, const char *cmd)
222{
223    fprintf (stderr, "bad display name \"%s\" in \"%s\" command\n",
224	     dpy, cmd);
225}
226
227static void
228badcommandline(const char *cmd)
229{
230    fprintf (stderr, "bad \"%s\" command line\n", cmd);
231}
232
233static char *
234skip_space(register char *s)
235{
236    if (!s) return NULL;
237
238    for ( ; *s && isascii(*s) && isspace(*s); s++)
239	;
240    return s;
241}
242
243
244static char *
245skip_nonspace(register char *s)
246{
247    if (!s) return NULL;
248
249    /* put quoting into loop if need be */
250    for ( ; *s && isascii(*s) && !isspace(*s); s++)
251	;
252    return s;
253}
254
255#ifndef HAVE_REALLOCARRAY
256static inline void *
257reallocarray(void *optr, size_t nmemb, size_t size)
258{
259    if ((nmemb > 0) && (SIZE_MAX / nmemb < size)) {
260        errno = ENOMEM;
261        return NULL;
262    }
263    return realloc(optr, size * nmemb);
264}
265#endif
266
267static const char **
268split_into_words(char *src, int *argcp)  /* argvify string */
269{
270    char *jword;
271    const char **argv;
272    int cur, total;
273
274    *argcp = 0;
275#define WORDSTOALLOC 4			/* most lines are short */
276    argv = malloc (WORDSTOALLOC * sizeof (char *));
277    if (!argv) return NULL;
278    cur = 0;
279    total = WORDSTOALLOC;
280
281    /*
282     * split the line up into separate, nul-terminated tokens; the last
283     * "token" will point to the empty string so that it can be bashed into
284     * a null pointer.
285     */
286
287    do {
288	char savec;
289
290	jword = skip_space (src);
291	src = skip_nonspace (jword);
292	savec = *src;
293	*src = '\0';
294	if (cur == total) {
295	    const char **new_argv;
296	    total += WORDSTOALLOC;
297	    new_argv = reallocarray (argv, total, sizeof (char *));
298	    if (new_argv != NULL) {
299		argv = new_argv;
300	    } else {
301		free(argv);
302		return NULL;
303	    }
304	}
305	argv[cur++] = jword;
306	if (savec) src++;		/* if not last on line advance */
307    } while (jword != src);
308
309    argv[--cur] = NULL;			/* smash empty token to end list */
310    *argcp = cur;
311    return argv;
312}
313
314
315static FILE *
316open_file(const char **filenamep,
317	  const char *mode,
318	  Bool *usedstdp,
319	  const char *srcfn,
320	  int srcln,
321	  const char *cmd)
322{
323    FILE *fp;
324
325    if (strcmp (*filenamep, "-") == 0) {
326	*usedstdp = True;
327					/* select std descriptor to use */
328	if (mode[0] == 'r') {
329	    if (okay_to_use_stdin) {
330		okay_to_use_stdin = False;
331		*filenamep = stdin_filename;
332		return stdin;
333	    } else {
334		prefix (srcfn, srcln);
335		fprintf (stderr, "%s:  stdin already in use\n", cmd);
336		return NULL;
337	    }
338	} else {
339	    *filenamep = stdout_filename;
340	    return stdout;		/* always okay to use stdout */
341	}
342    }
343
344    fp = fopen (*filenamep, mode);
345    if (!fp) {
346	prefix (srcfn, srcln);
347	fprintf (stderr, "%s:  unable to open file %s\n", cmd, *filenamep);
348    }
349    return fp;
350}
351
352static int
353getinput(FILE *fp)
354{
355    register int c;
356
357    while ((c = getc (fp)) != EOF && isascii(c) && c != '\n' && isspace(c)) ;
358    return c;
359}
360
361static int
362get_short(FILE *fp, unsigned short *sp)	/* for reading numeric input */
363{
364    unsigned short us = 0;
365
366    /*
367     * read family:  written with %04x
368     */
369    for (int i = 0; i < 4; i++) {
370	int c;
371
372	switch (c = getinput (fp)) {
373	  case EOF:
374	  case '\n':
375	    return 0;
376	}
377	if (c < 0 || c > 255) return 0;
378	us = (us * 16) + hexvalues[c];	/* since msb */
379    }
380    *sp = us;
381    return 1;
382}
383
384static int
385get_bytes(FILE *fp, unsigned int n, char **ptr)	/* for reading numeric input */
386{
387    char *s;
388    register char *cp;
389
390    cp = s = malloc (n);
391    if (!cp) return 0;
392
393    while (n > 0) {
394	int c1, c2;
395
396	if ((c1 = getinput (fp)) == EOF || c1 == '\n' ||
397	    (c2 = getinput (fp)) == EOF || c2 == '\n') {
398	    free (s);
399	    return 0;
400	}
401	*cp = (char) ((hexvalues[c1] * 16) + hexvalues[c2]);
402	cp++;
403	n--;
404    }
405
406    *ptr = s;
407    return 1;
408}
409
410
411static Xauth *
412read_numeric(FILE *fp)
413{
414    Xauth *auth;
415
416    auth = malloc (sizeof (Xauth));
417    if (!auth) goto bad;
418    auth->family = 0;
419    auth->address = NULL;
420    auth->address_length = 0;
421    auth->number = NULL;
422    auth->number_length = 0;
423    auth->name = NULL;
424    auth->name_length = 0;
425    auth->data = NULL;
426    auth->data_length = 0;
427
428    if (!get_short (fp, (unsigned short *) &auth->family))
429      goto bad;
430    if (!get_short (fp, (unsigned short *) &auth->address_length))
431      goto bad;
432    if (!get_bytes (fp, (unsigned int) auth->address_length, &auth->address))
433      goto bad;
434    if (!get_short (fp, (unsigned short *) &auth->number_length))
435      goto bad;
436    if (!get_bytes (fp, (unsigned int) auth->number_length, &auth->number))
437      goto bad;
438    if (!get_short (fp, (unsigned short *) &auth->name_length))
439      goto bad;
440    if (!get_bytes (fp, (unsigned int) auth->name_length, &auth->name))
441      goto bad;
442    if (!get_short (fp, (unsigned short *) &auth->data_length))
443      goto bad;
444    if (!get_bytes (fp, (unsigned int) auth->data_length, &auth->data))
445      goto bad;
446
447    switch (getinput (fp)) {		/* get end of line */
448      case EOF:
449      case '\n':
450	return auth;
451    }
452
453  bad:
454    if (auth) XauDisposeAuth (auth);	/* won't free null pointers */
455    return NULL;
456}
457
458typedef Xauth *(*ReadFunc)(FILE *);
459
460static int
461read_auth_entries(FILE *fp, Bool numeric, AuthList **headp, AuthList **tailp)
462{
463    ReadFunc readfunc = (numeric ? read_numeric : XauReadAuth);
464    Xauth *auth;
465    AuthList *head, *tail;
466    int n;
467
468    head = tail = NULL;
469    n = 0;
470					/* put all records into linked list */
471    while ((auth = ((*readfunc) (fp))) != NULL) {
472	AuthList *l = malloc (sizeof (AuthList));
473	if (!l) {
474	    fprintf (stderr,
475		     "%s:  unable to alloc entry reading auth file\n",
476		     ProgramName);
477	    exit (1);
478	}
479	l->next = NULL;
480	l->auth = auth;
481	if (tail) 			/* if not first time through append */
482	  tail->next = l;
483	else
484	  head = l;			/* first time through, so assign */
485	tail = l;
486	n++;
487    }
488    *headp = head;
489    *tailp = tail;
490    return n;
491}
492
493static Bool
494get_displayname_auth(const char *displayname, AuthList **authl)
495{
496    int family;
497    char *host = NULL, *rest = NULL;
498    int dpynum, scrnum;
499    char *cp;
500    int prelen = 0;
501    struct addrlist *addrlist_head, *addrlist_cur;
502    AuthList *authl_cur = NULL;
503
504    *authl = NULL;
505    /*
506     * check to see if the display name is of the form "host/unix:"
507     * which is how the list routine prints out local connections
508     */
509    cp = strchr(displayname, '/');
510    if (cp && strncmp (cp, "/unix:", 6) == 0)
511      prelen = (cp - displayname);
512
513    if (!parse_displayname (displayname + ((prelen > 0) ? prelen + 1 : 0),
514			    &family, &host, &dpynum, &scrnum, &rest)) {
515	return False;
516    }
517
518    addrlist_head = get_address_info(family, displayname, prelen, host);
519    if (addrlist_head) {
520	char buf[40];			/* want to hold largest display num */
521	unsigned short dpylen;
522
523	buf[0] = '\0';
524	snprintf (buf, sizeof(buf), "%d", dpynum);
525	dpylen = strlen (buf);
526	if (dpylen > 0) {
527	    for (addrlist_cur = addrlist_head; addrlist_cur != NULL;
528		 addrlist_cur = addrlist_cur->next) {
529		AuthList *newal = malloc(sizeof(AuthList));
530		Xauth *auth = malloc(sizeof(Xauth));
531
532		if ((newal == NULL) || (auth == NULL)) {
533		    if (newal != NULL) free(newal);
534		    if (auth != NULL) free(auth);
535		    break;
536		}
537
538		if (authl_cur == NULL) {
539		    *authl = authl_cur = newal;
540		} else {
541		    authl_cur->next = newal;
542		    authl_cur = newal;
543		}
544
545		newal->next = NULL;
546		newal->auth = auth;
547
548		auth->family = addrlist_cur->family;
549		auth->address = addrlist_cur->address;
550		auth->address_length = addrlist_cur->len;
551		auth->number = copystring(buf, dpylen);
552		auth->number_length = dpylen;
553		auth->name = NULL;
554		auth->name_length = 0;
555		auth->data = NULL;
556		auth->data_length = 0;
557	    }
558	}
559    }
560
561    if (host) free (host);
562    if (rest) free (rest);
563
564    if (*authl != NULL) {
565	return True;
566    } else {
567	return False;
568    }
569}
570
571static int
572cvthexkey(const char *hexstr, char **ptrp) /* turn hex key string into octets */
573{
574    int i;
575    int len = 0;
576    char *retval;
577    unsigned char *us;
578    char savec = '\0';
579
580    /* count */
581    for (const char *s = hexstr; *s; s++) {
582	if (!isascii(*s)) return -1;
583	if (isspace(*s)) continue;
584	if (!isxdigit(*s)) return -1;
585	len++;
586    }
587
588    /* if 0 or odd, then there was an error */
589    if (len == 0 || (len & 1) == 1) return -1;
590
591
592    /* now we know that the input is good */
593    len >>= 1;
594    retval = malloc (len);
595    if (!retval) {
596	fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
597		 ProgramName, len);
598	return -1;
599    }
600
601    for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
602	char c = *hexstr;
603	if (isspace(c)) continue;	 /* already know it is ascii */
604	if (isupper(c))
605	    c = tolower(c);
606	if (savec) {
607#define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
608	    *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
609#undef atoh
610	    savec = 0;		/* ready for next character */
611	    us++;
612	    i--;
613	} else {
614	    savec = c;
615	}
616    }
617    *ptrp = retval;
618    return len;
619}
620
621static int
622dispatch_command(const char *inputfilename,
623		 int lineno,
624		 int argc,
625		 const char **argv,
626		 CommandTable *tab,
627		 int *statusp)
628{
629    const char *cmd;
630    int n;
631					/* scan table for command */
632    cmd = argv[0];
633    n = strlen (cmd);
634    for (CommandTable *ct = tab; ct->name; ct++) {
635					/* look for unique prefix */
636	if (n >= ct->minlen && n <= ct->maxlen &&
637	    strncmp (cmd, ct->name, n) == 0) {
638	    *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
639	    return 1;
640	}
641    }
642
643    *statusp = 1;
644    return 0;
645}
646
647
648static AuthList *xauth_head = NULL;	/* list of auth entries */
649static Bool xauth_existed = False;	/* if was present at initialize */
650static Bool xauth_modified = False;	/* if added, removed, or merged */
651static Bool xauth_allowed = True;	/* if allowed to write auth file */
652static Bool xauth_locked = False;     /* if has been locked */
653static const char *xauth_filename = NULL;
654static volatile Bool dying = False;
655
656
657/* poor man's puts(), for under signal handlers,
658   extended to ignore warn_unused_result */
659#define WRITES(fd, S) {if(write((fd), (S), strlen((S)))){}}
660
661/* ARGSUSED */
662_X_NORETURN
663static void
664die(int sig)
665{
666    dying = True;
667    _exit (auth_finalize ());
668    /* NOTREACHED */
669}
670
671_X_NORETURN
672static void
673catchsig(int sig)
674{
675#ifdef SYSV
676    if (sig > 0) signal (sig, die);	/* re-establish signal handler */
677#endif
678    /*
679     * fileno() might not be reentrant, avoid it if possible, and use
680     * stderr instead of stdout
681     */
682#ifdef STDERR_FILENO
683    if (verbose && xauth_modified) WRITES(STDERR_FILENO, "\r\n");
684#else
685    if (verbose && xauth_modified) WRITES(fileno(stderr), "\r\n");
686#endif
687    die (sig);
688    /* NOTREACHED */
689}
690
691static void
692register_signals(void)
693{
694    signal (SIGINT, catchsig);
695    signal (SIGTERM, catchsig);
696#ifdef SIGHUP
697    signal (SIGHUP, catchsig);
698#endif
699#ifdef SIGPIPE
700    signal (SIGPIPE, catchsig);
701#endif
702    return;
703}
704
705
706/*
707 * public procedures for parsing lines of input
708 */
709
710int
711auth_initialize(const char *authfilename)
712{
713    int n;
714    FILE *authfp;
715    Bool exists;
716
717    /*
718     * XauLockAuth in libXau limits file names to 1022 characters so it
719     * has room to append two characters in its 1025 character buffers.
720     */
721    if (strlen(authfilename) > 1022) {
722	fprintf (stderr, "%s: authority file name \"%s\" too long\n",
723		 ProgramName, authfilename);
724	exit (1);
725    }
726    xauth_filename = authfilename;    /* used in cleanup, prevent race with
727                                         signals */
728    register_signals ();
729
730    bzero ((char *) hexvalues, sizeof hexvalues);
731    hexvalues['0'] = 0;
732    hexvalues['1'] = 1;
733    hexvalues['2'] = 2;
734    hexvalues['3'] = 3;
735    hexvalues['4'] = 4;
736    hexvalues['5'] = 5;
737    hexvalues['6'] = 6;
738    hexvalues['7'] = 7;
739    hexvalues['8'] = 8;
740    hexvalues['9'] = 9;
741    hexvalues['a'] = hexvalues['A'] = 0xa;
742    hexvalues['b'] = hexvalues['B'] = 0xb;
743    hexvalues['c'] = hexvalues['C'] = 0xc;
744    hexvalues['d'] = hexvalues['D'] = 0xd;
745    hexvalues['e'] = hexvalues['E'] = 0xe;
746    hexvalues['f'] = hexvalues['F'] = 0xf;
747
748    if (break_locks && verbose) {
749	printf ("Attempting to break locks on authority file %s\n",
750		authfilename);
751    }
752
753    if (ignore_locks) {
754	if (break_locks) XauUnlockAuth (authfilename);
755    } else {
756	n = XauLockAuth (authfilename, XAUTH_DEFAULT_RETRIES,
757			 XAUTH_DEFAULT_TIMEOUT,
758			 (break_locks ? 0L : XAUTH_DEFAULT_DEADTIME));
759	if (n != LOCK_SUCCESS) {
760	    const char *reason = "unknown error";
761	    switch (n) {
762	      case LOCK_ERROR:
763		reason = "error";
764		break;
765	      case LOCK_TIMEOUT:
766		reason = "timeout";
767		break;
768	    }
769	    fprintf (stderr, "%s:  %s in locking authority file %s\n",
770		     ProgramName, reason, authfilename);
771	    return -1;
772	} else
773	    xauth_locked = True;
774    }
775
776    /* these checks can only be done reliably after the file is locked */
777    exists = (access (authfilename, F_OK) == 0);
778    if (exists && access (authfilename, W_OK) != 0) {
779	fprintf (stderr,
780	 "%s:  %s not writable, changes will be ignored\n",
781		 ProgramName, authfilename);
782	xauth_allowed = False;
783    }
784
785    original_umask = umask (0077);	/* disallow non-owner access */
786
787    authfp = fopen (authfilename, "rb");
788    if (!authfp) {
789	int olderrno = errno;
790
791					/* if file there then error */
792	if (access (authfilename, F_OK) == 0) {	 /* then file does exist! */
793	    errno = olderrno;
794	    return -1;
795	}				/* else ignore it */
796	fprintf (stderr,
797		 "%s:  file %s does not exist\n",
798		 ProgramName, authfilename);
799    } else {
800	AuthList *head, *tail;
801
802	xauth_existed = True;
803	n = read_auth_entries (authfp, False, &head, &tail);
804	(void) fclose (authfp);
805	if (n < 0) {
806	    fprintf (stderr,
807		     "%s:  unable to read auth entries from file \"%s\"\n",
808		     ProgramName, authfilename);
809	    return -1;
810	}
811	xauth_head = head;
812    }
813
814    xauth_filename = strdup(authfilename);
815    if (!xauth_filename) {
816	fprintf(stderr,"cannot allocate memory\n");
817	return -1;
818    }
819
820    xauth_modified = False;
821
822    if (verbose) {
823	printf ("%s authority file %s\n",
824		ignore_locks ? "Ignoring locks on" : "Using", authfilename);
825    }
826    return 0;
827}
828
829static int
830write_auth_file(char *tmp_nam, size_t tmp_nam_size)
831{
832    FILE *fp = NULL;
833    int fd;
834
835    /* Append "-n" for "new" */
836    int ret = snprintf(tmp_nam, tmp_nam_size, "%s-n", xauth_filename);
837    if (ret < 0 || ret >= tmp_nam_size) {
838        fprintf(stderr, "Error constructing filename: %s\n",
839                (ret < 0) ? "snprintf failed" : "buffer size is too small");
840        return -1;
841    }
842    (void) remove (tmp_nam);
843    /* CPhipps 2000/02/12 - fix file unlink/fopen race */
844    fd = open(tmp_nam, O_WRONLY | O_CREAT | O_EXCL, 0600);
845    if (fd != -1) fp = fdopen (fd, "wb");
846    if (!fp) {
847        if (fd != -1) close(fd);
848	fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
849		 ProgramName, tmp_nam);
850	return -1;
851    }
852
853    /*
854     * Write MIT-MAGIC-COOKIE-1 first, because R4 Xlib knows
855     * only that and uses the first authorization it finds.
856     */
857    for (AuthList *list = xauth_head; list; list = list->next) {
858	if (list->auth->name_length == 18
859	    && strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) == 0) {
860	    if (!XauWriteAuth(fp, list->auth)) {
861		(void) fclose(fp);
862		return -1;
863	    }
864	}
865    }
866    for (AuthList *list = xauth_head; list; list = list->next) {
867	if (list->auth->name_length != 18
868	    || strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) != 0) {
869	    if (!XauWriteAuth(fp, list->auth)) {
870		(void) fclose(fp);
871		return -1;
872	    }
873	}
874    }
875
876    if (fclose(fp)) {
877	return -1;
878    }
879
880    return 0;
881}
882
883int
884auth_finalize(void)
885{
886    if (xauth_modified) {
887	if (dying) {
888	    if (verbose) {
889		/*
890		 * called from a signal handler -- printf is *not* reentrant; also
891		 * fileno() might not be reentrant, avoid it if possible, and use
892		 * stderr instead of stdout
893		 */
894#ifdef STDERR_FILENO
895		WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
896		WRITES(STDERR_FILENO, xauth_filename);
897		WRITES(STDERR_FILENO, "\n");
898#else
899		WRITES(fileno(stderr), "\nAborting changes to authority file ");
900		WRITES(fileno(stderr), xauth_filename);
901		WRITES(fileno(stderr), "\n");
902#endif
903	    }
904	} else if (!xauth_allowed) {
905	    fprintf (stderr,
906		     "%s:  %s not writable, changes ignored\n",
907		     ProgramName, xauth_filename);
908	} else {
909	    char temp_name[1025];	/* large filename size */
910
911	    if (verbose) {
912		printf ("%s authority file %s\n",
913			ignore_locks ? "Ignoring locks and writing" :
914			"Writing", xauth_filename);
915	    }
916	    temp_name[0] = '\0';
917	    if (write_auth_file (temp_name, sizeof(temp_name)) == -1) {
918		fprintf (stderr,
919			 "%s:  unable to write authority file %s\n",
920			 ProgramName, temp_name);
921	    } else {
922		if (rename(temp_name, xauth_filename) == -1) {
923		    fprintf (stderr,
924		     "%s:  unable to rename authority file %s, use %s\n",
925			     ProgramName, xauth_filename, temp_name);
926                    remove(temp_name);
927		}
928	    }
929	}
930    }
931
932    if (xauth_locked) {
933	XauUnlockAuth (xauth_filename);
934    }
935    (void) umask (original_umask);
936    return 0;
937}
938
939int
940process_command(const char *inputfilename, int lineno, int argc, const char **argv)
941{
942    int status;
943
944    if (argc < 1 || !argv || !argv[0]) return 1;
945
946    if (dispatch_command (inputfilename, lineno, argc, argv,
947			  command_table, &status))
948      return status;
949
950    prefix (inputfilename, lineno);
951    fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
952    return 1;
953}
954
955
956/*
957 * utility routines
958 */
959
960static char *
961bintohex(unsigned int len, const char *bindata)
962{
963    char *hexdata, *starthex;
964
965    /* two chars per byte, plus null termination */
966    starthex = hexdata = malloc((2 * len) + 1);
967    if (!hexdata)
968	return NULL;
969
970    for (; len > 0; len--, bindata++) {
971	register const char *s = hex_table[(unsigned char)*bindata];
972	*hexdata++ = s[0];
973	*hexdata++ = s[1];
974    }
975    *hexdata = '\0';
976    return starthex;
977}
978
979static void
980fprintfhex(register FILE *fp, int len, char *cp)
981{
982    char *hex;
983
984    hex = bintohex(len, cp);
985    fprintf(fp, "%s", hex);
986    free(hex);
987}
988
989static int
990dump_numeric(register FILE *fp, register Xauth *auth)
991{
992    fprintf (fp, "%04x", auth->family);  /* unsigned short */
993    fprintf (fp, " %04x ", auth->address_length);  /* short */
994    fprintfhex (fp, auth->address_length, auth->address);
995    fprintf (fp, " %04x ", auth->number_length);  /* short */
996    fprintfhex (fp, auth->number_length, auth->number);
997    fprintf (fp, " %04x ", auth->name_length);  /* short */
998    fprintfhex (fp, auth->name_length, auth->name);
999    fprintf (fp, " %04x ", auth->data_length);  /* short */
1000    fprintfhex (fp, auth->data_length, auth->data);
1001    putc ('\n', fp);
1002    return 1;
1003}
1004
1005/* ARGSUSED */
1006static int
1007dump_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
1008{
1009    struct _list_data *ld = (struct _list_data *) data;
1010    FILE *fp = ld->fp;
1011
1012    if (ld->numeric) {
1013	dump_numeric (fp, auth);
1014    } else {
1015	const char *dpyname = NULL;
1016
1017	switch (auth->family) {
1018	  case FamilyLocal:
1019	    fwrite (auth->address, sizeof (char), auth->address_length, fp);
1020	    fprintf (fp, "/unix");
1021	    break;
1022	  case FamilyInternet:
1023#ifdef IPv6
1024	  case FamilyInternet6:
1025#endif
1026	  case FamilyDECnet:
1027	    dpyname = get_hostname (auth);
1028	    if (dpyname) {
1029		fprintf (fp, "%s", dpyname);
1030		break;
1031	    }
1032	    /* else fall through */
1033	  default:
1034	    fprintf (fp, "#%04x#", auth->family);
1035	    fprintfhex (fp, auth->address_length, auth->address);
1036	    putc ('#', fp);
1037	}
1038	putc (':', fp);
1039	fwrite (auth->number, sizeof (char), auth->number_length, fp);
1040	putc (' ', fp);
1041	putc (' ', fp);
1042	fwrite (auth->name, sizeof (char), auth->name_length, fp);
1043	putc (' ', fp);
1044	putc (' ', fp);
1045	if (!strncmp(auth->name, SECURERPC, auth->name_length) ||
1046	    !strncmp(auth->name, K5AUTH, auth->name_length))
1047            fwrite (auth->data, sizeof (char), auth->data_length, fp);
1048	else
1049	    fprintfhex (fp, auth->data_length, auth->data);
1050	putc ('\n', fp);
1051    }
1052    return 0;
1053}
1054
1055static int
1056extract_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
1057{
1058    struct _extract_data *ed = (struct _extract_data *) data;
1059
1060    if (!ed->fp) {
1061	ed->fp = open_file (&ed->filename,
1062			    ed->numeric ? "w" : "wb",
1063			    &ed->used_stdout,
1064			    inputfilename, lineno, ed->cmd);
1065	if (!ed->fp) {
1066	    prefix (inputfilename, lineno);
1067	    fprintf (stderr,
1068		     "unable to open extraction file \"%s\"\n",
1069		     ed->filename);
1070	    return -1;
1071	}
1072    }
1073    (*(ed->numeric ? dump_numeric : XauWriteAuth)) (ed->fp, auth);
1074    ed->nwritten++;
1075
1076    return 0;
1077}
1078
1079
1080static int
1081eq_auth_dpy_and_name(Xauth *a, Xauth *b)
1082{
1083    return((a->family == b->family &&
1084	    a->address_length == b->address_length &&
1085	    a->number_length == b->number_length &&
1086	    a->name_length == b->name_length &&
1087	    memcmp(a->address, b->address, a->address_length) == 0 &&
1088	    memcmp(a->number, b->number, a->number_length) == 0 &&
1089	    memcmp(a->name, b->name, a->name_length) == 0) ? 1 : 0);
1090}
1091
1092static int
1093eq_auth(Xauth *a, Xauth *b)
1094{
1095    return((eq_auth_dpy_and_name(a, b) &&
1096	    a->data_length == b->data_length &&
1097	    memcmp(a->data, b->data, a->data_length) == 0) ? 1 : 0);
1098}
1099
1100static int
1101match_auth_dpy(register Xauth *a, register Xauth *b)
1102{
1103    if (a->family != FamilyWild && b->family != FamilyWild) {
1104        /* Both "a" and "b" are not FamilyWild, they are "normal" families. */
1105
1106	/* Make sure, that both families match: */
1107	if (a->family != b->family)
1108            return 0;
1109
1110	/* By looking at 'man Xsecurity' and the code in
1111	 * GetAuthByAddr() and XauGetBestAuthByAddr() in libXau, we
1112	 * decided, that the address is only relevant for "normal"
1113	 * families and therefore should be ignored for
1114	 * "FamilyWild". */
1115	if (a->address_length != b->address_length ||
1116            memcmp(a->address, b->address, a->address_length) != 0)
1117            return 0;
1118    }
1119
1120    if (a->number_length != 0 && b->number_length != 0) {
1121	/* Both "a" and "b" have a number, make sure they match: */
1122	if (a->number_length != b->number_length ||
1123	    memcmp(a->number, b->number, a->number_length) != 0)
1124            return 0;
1125    }
1126
1127    return 1;
1128}
1129
1130static int
1131merge_entries(AuthList **firstp, AuthList *second, int *nnewp, int *nreplp)
1132{
1133    AuthList *first, *tail;
1134    int n = 0, nnew = 0, nrepl = 0;
1135
1136    if (!second) return 0;
1137
1138    if (!*firstp) {			/* if nothing to merge into */
1139	*firstp = second;
1140	for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
1141	*nnewp = n;
1142	*nreplp = 0;
1143	return n;
1144    }
1145
1146    first = *firstp;
1147    /*
1148     * find end of first list and stick second list on it
1149     */
1150    for (tail = first; tail->next; tail = tail->next) ;
1151    tail->next = second;
1152
1153    /*
1154     * run down list freeing duplicate entries; if an entry is okay, then
1155     * bump the tail up to include it, otherwise, cut the entry out of
1156     * the chain.
1157     */
1158    for (AuthList *b = second; b; ) {
1159	AuthList *next = b->next;	/* in case we free it */
1160	AuthList *a = first;
1161
1162	for (;;) {
1163	    if (eq_auth_dpy_and_name (a->auth, b->auth)) {  /* found a duplicate */
1164		AuthList tmp;		/* swap it in for old one */
1165		tmp = *a;
1166		*a = *b;
1167		*b = tmp;
1168		a->next = b->next;
1169		XauDisposeAuth (b->auth);
1170		free ((char *) b);
1171		b = NULL;
1172		tail->next = next;
1173		nrepl++;
1174		nnew--;
1175		break;
1176	    }
1177	    if (a == tail) break;	/* if have looked at left side */
1178	    a = a->next;
1179	}
1180	if (b) {			/* if we didn't remove it */
1181	    tail = b;			/* bump end of first list */
1182	}
1183	b = next;
1184	n++;
1185	nnew++;
1186    }
1187
1188    *nnewp = nnew;
1189    *nreplp = nrepl;
1190    return n;
1191
1192}
1193
1194static void
1195sort_entries(AuthList **firstp)
1196{
1197    /* Insert sort, in each pass it removes auth records of certain */
1198    /* cathegory from the given list and inserts them into the sorted list. */
1199
1200    AuthList *sorted = NULL, *sorted_tail = NULL;
1201
1202    #define SORT_OUT(EXPRESSION) { \
1203	AuthList *prev = NULL, *next; \
1204	for (AuthList *iter = *firstp; iter; iter = next) { \
1205	    next = iter->next; \
1206	    if (EXPRESSION) { \
1207		if (prev) \
1208		    prev->next = next; \
1209		else \
1210		    *firstp = next; \
1211		if (sorted_tail == NULL) { \
1212		    sorted = sorted_tail = iter; \
1213		} else { \
1214		    sorted_tail->next = iter; \
1215		    sorted_tail = iter; \
1216		} \
1217		iter->next = NULL; \
1218	    } else { \
1219		prev = iter; \
1220	    } \
1221	} \
1222    }
1223
1224    SORT_OUT(iter->auth->family != FamilyWild && iter->auth->number_length != 0);
1225    SORT_OUT(iter->auth->family != FamilyWild && iter->auth->number_length == 0);
1226    SORT_OUT(iter->auth->family == FamilyWild && iter->auth->number_length != 0);
1227    SORT_OUT(iter->auth->family == FamilyWild && iter->auth->number_length == 0);
1228
1229    *firstp = sorted;
1230}
1231
1232static Xauth *
1233copyAuth(Xauth *auth)
1234{
1235    Xauth *a;
1236
1237    a = malloc(sizeof(Xauth));
1238    if (a == NULL) {
1239	return NULL;
1240    }
1241    memset(a, 0, sizeof(Xauth));
1242    a->family = auth->family;
1243    if (auth->address_length != 0) {
1244	a->address = malloc(auth->address_length);
1245	if (a->address == NULL) {
1246	    free(a);
1247	    return NULL;
1248	}
1249	memcpy(a->address, auth->address, auth->address_length);
1250	a->address_length = auth->address_length;
1251    }
1252    if (auth->number_length != 0) {
1253	a->number = malloc(auth->number_length);
1254	if (a->number == NULL) {
1255	    free(a->address);
1256	    free(a);
1257	    return NULL;
1258	}
1259	memcpy(a->number, auth->number, auth->number_length);
1260	a->number_length = auth->number_length;
1261    }
1262    if (auth->name_length != 0) {
1263	a->name = malloc(auth->name_length);
1264	if (a->name == NULL) {
1265	    free(a->address);
1266	    free(a->number);
1267	    free(a);
1268	    return NULL;
1269	}
1270	memcpy(a->name, auth->name, auth->name_length);
1271	a->name_length = auth->name_length;
1272    }
1273    if (auth->data_length != 0) {
1274	a->data = malloc(auth->data_length);
1275	if (a->data == NULL) {
1276	    free(a->address);
1277	    free(a->number);
1278	    free(a->name);
1279	    free(a);
1280	    return NULL;
1281	}
1282	memcpy(a->data, auth->data, auth->data_length);
1283	a->data_length = auth->data_length;
1284    }
1285    return a;
1286}
1287
1288typedef int (*YesNoFunc)(const char *, int, Xauth *, char *);
1289
1290static int
1291iterdpy (const char *inputfilename, int lineno, int start,
1292	 int argc, const char *argv[],
1293	 YesNoFunc yfunc, YesNoFunc nfunc, char *data)
1294{
1295    int errors = 0;
1296
1297    /*
1298     * iterate
1299     */
1300    for (int i = start; i < argc; i++) {
1301	const char *displayname = argv[i];
1302	AuthList *proto_head;
1303	AuthList *next;
1304	int status;
1305
1306	if (!get_displayname_auth (displayname, &proto_head)) {
1307	    prefix (inputfilename, lineno);
1308	    baddisplayname (displayname, argv[0]);
1309	    errors++;
1310	    continue;
1311	}
1312	status = 0;
1313	for (AuthList *l = xauth_head; l; l = next) {
1314	    Bool matched = False;
1315	    Xauth *tmp_auth;
1316
1317	    /* l may be freed by remove_entry below. so save its contents */
1318	    next = l->next;
1319	    tmp_auth = copyAuth(l->auth);
1320	    for (AuthList *proto = proto_head; proto; proto = proto->next) {
1321		if (match_auth_dpy (proto->auth, tmp_auth)) {
1322		    matched = True;
1323		    if (yfunc) {
1324			status = (*yfunc) (inputfilename, lineno,
1325					   tmp_auth, data);
1326			if (status < 0) break;
1327		    }
1328		}
1329	    }
1330	    XauDisposeAuth(tmp_auth);
1331	    if (matched == False) {
1332		if (nfunc) {
1333		    status = (*nfunc) (inputfilename, lineno,
1334				       l->auth, data);
1335		}
1336	    }
1337	    if (status < 0) break;
1338	}
1339	for (AuthList *proto = proto_head; proto ; proto = next) {
1340	    next = proto->next;
1341	    if (proto->auth->address) free (proto->auth->address);
1342	    if (proto->auth->number) free (proto->auth->number);
1343	    free (proto->auth);
1344	    free (proto);
1345	}
1346	if (status < 0) {
1347	    errors -= status;		/* since status is negative */
1348	    break;
1349	}
1350    }
1351
1352    return errors;
1353}
1354
1355/* ARGSUSED */
1356static int
1357remove_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
1358{
1359    int *nremovedp = (int *) data;
1360    AuthList **listp = &xauth_head;
1361    AuthList *list;
1362
1363    /*
1364     * unlink the auth we were asked to
1365     */
1366    while (!eq_auth((list = *listp)->auth, auth)) {
1367        listp = &list->next;
1368        if (!*listp)
1369            return 0;
1370    }
1371    *listp = list->next;
1372    XauDisposeAuth (list->auth);                    /* free the auth */
1373    free (list);				    /* free the link */
1374    xauth_modified = True;
1375    (*nremovedp)++;
1376    return 1;
1377}
1378
1379/*
1380 * action routines
1381 */
1382
1383/*
1384 * help
1385 */
1386int
1387print_help(FILE *fp, const char *cmd, const char *line_prefix)
1388{
1389    int n = 0;
1390
1391    if (!line_prefix) line_prefix = "";
1392
1393    if (!cmd) {				/* if no cmd, print all help */
1394	for (CommandTable *ct = command_table; ct->name; ct++) {
1395	    fprintf (fp, "%s%s\n", line_prefix, ct->helptext);
1396	    n++;
1397	}
1398    } else {
1399	int len = strlen (cmd);
1400	for (CommandTable *ct = command_table; ct->name; ct++) {
1401	    if (strncmp (cmd, ct->name, len) == 0) {
1402		fprintf (fp, "%s%s\n", line_prefix, ct->helptext);
1403		n++;
1404	    }
1405	}
1406    }
1407
1408    return n;
1409}
1410
1411static int
1412do_help(const char *inputfilename, int lineno, int argc, const char **argv)
1413{
1414    const char *cmd = (argc > 1 ? argv[1] : NULL);
1415    int n;
1416
1417    n = print_help (stdout, cmd, "    ");  /* a nice amount */
1418
1419    if (n < 0 || (n == 0 && !cmd)) {
1420	prefix (inputfilename, lineno);
1421	fprintf (stderr, "internal error with help");
1422	if (cmd) {
1423	    fprintf (stderr, " on command \"%s\"", cmd);
1424	}
1425	fprintf (stderr, "\n");
1426	return 1;
1427    }
1428
1429    if (n == 0) {
1430	prefix (inputfilename, lineno);
1431	/* already know that cmd is set in this case */
1432	fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1433    }
1434
1435    return 0;
1436}
1437
1438/*
1439 * questionmark
1440 */
1441/* ARGSUSED */
1442static int
1443do_questionmark(const char *inputfilename, int lineno, int argc, const char **argv)
1444{
1445#define WIDEST_COLUMN 72
1446    int col = WIDEST_COLUMN;
1447
1448    printf ("Commands:\n");
1449    for (CommandTable *ct = command_table; ct->name; ct++) {
1450	if ((col + ct->maxlen) > WIDEST_COLUMN) {
1451	    if (ct != command_table) {
1452		putc ('\n', stdout);
1453	    }
1454	    fputs ("        ", stdout);
1455	    col = 8;			/* length of string above */
1456	}
1457	fputs (ct->name, stdout);
1458	col += ct->maxlen;
1459	for (int i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1460	    putc (' ', stdout);
1461	    col++;
1462	}
1463    }
1464    if (col != 0) {
1465	putc ('\n', stdout);
1466    }
1467
1468    /* allow bad lines since this is help */
1469    return 0;
1470}
1471
1472/*
1473 * version
1474 */
1475/* ARGSUSED */
1476static int
1477do_version(const char *inputfilename, int lineno, int argc, const char **argv)
1478{
1479    puts (PACKAGE_VERSION);
1480    return 0;
1481}
1482
1483/*
1484 * list [displayname ...]
1485 */
1486static int
1487do_list (const char *inputfilename, int lineno, int argc, const char **argv)
1488{
1489    struct _list_data ld;
1490
1491    ld.fp = stdout;
1492    ld.numeric = (argv[0][0] == 'n');
1493
1494    if (argc == 1) {
1495	if (xauth_head) {
1496	    for (AuthList *l = xauth_head; l; l = l->next) {
1497		dump_entry (inputfilename, lineno, l->auth, (char *) &ld);
1498	    }
1499	}
1500	return 0;
1501    }
1502
1503    return iterdpy (inputfilename, lineno, 1, argc, argv,
1504		    dump_entry, NULL, (char *) &ld);
1505}
1506
1507/*
1508 * merge filename [filename ...]
1509 */
1510static int
1511do_merge(const char *inputfilename, int lineno, int argc, const char **argv)
1512{
1513    int errors = 0;
1514    AuthList *listhead, *listtail;
1515    Bool numeric = False;
1516
1517    if (argc < 2) {
1518	prefix (inputfilename, lineno);
1519	badcommandline (argv[0]);
1520	return 1;
1521    }
1522
1523    if (argv[0][0] == 'n') numeric = True;
1524    listhead = listtail = NULL;
1525
1526    for (int i = 1; i < argc; i++) {
1527	const char *filename = argv[i];
1528	FILE *fp;
1529	Bool used_stdin = False;
1530	int nentries;
1531	AuthList *head, *tail;
1532
1533	fp = open_file (&filename,
1534			numeric ? "r" : "rb",
1535			&used_stdin, inputfilename, lineno,
1536			argv[0]);
1537	if (!fp) {
1538	    errors++;
1539	    continue;
1540	}
1541
1542	head = tail = NULL;
1543	nentries = read_auth_entries (fp, numeric, &head, &tail);
1544	if (nentries == 0) {
1545	    prefix (inputfilename, lineno);
1546	    fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1547		     filename);
1548	    errors++;
1549	} else {			/* link it in */
1550	    add_to_list (listhead, listtail, head);
1551 	}
1552
1553	if (!used_stdin) (void) fclose (fp);
1554    }
1555
1556    /*
1557     * if we have new entries, merge them in (freeing any duplicates)
1558     */
1559    if (listhead) {
1560	int nentries, nnew, nrepl;
1561
1562	nentries = merge_entries (&xauth_head, listhead, &nnew, &nrepl);
1563	if (verbose)
1564	  printf ("%d entries read in:  %d new, %d replacement%s\n",
1565	  	  nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1566	if (nentries > 0) xauth_modified = True;
1567	sort_entries(&xauth_head);
1568    }
1569
1570    return 0;
1571}
1572
1573/*
1574 * extract filename displayname [displayname ...]
1575 */
1576static int
1577do_extract(const char *inputfilename, int lineno, int argc, const char **argv)
1578{
1579    int errors;
1580    struct _extract_data ed;
1581
1582    if (argc < 3) {
1583	prefix (inputfilename, lineno);
1584	badcommandline (argv[0]);
1585	return 1;
1586    }
1587
1588    ed.fp = NULL;
1589    ed.filename = argv[1];
1590    ed.used_stdout = False;
1591    ed.numeric = (argv[0][0] == 'n');
1592    ed.nwritten = 0;
1593    ed.cmd = argv[0];
1594
1595    errors = iterdpy (inputfilename, lineno, 2, argc, argv,
1596		      extract_entry, NULL, (char *) &ed);
1597
1598    if (!ed.fp) {
1599	fprintf (stderr,
1600		 "No matches found, authority file \"%s\" not written\n",
1601		 ed.filename);
1602    } else {
1603	if (verbose) {
1604	    printf ("%d entries written to \"%s\"\n",
1605		    ed.nwritten, ed.filename);
1606	}
1607	if (!ed.used_stdout) {
1608	    (void) fclose (ed.fp);
1609	}
1610    }
1611
1612    return errors;
1613}
1614
1615
1616/*
1617 * add displayname protocolname hexkey
1618 */
1619
1620static int
1621do_add(const char *inputfilename, int lineno, int argc, const char **argv)
1622{
1623    int n, nnew, nrepl;
1624    int len;
1625    const char *dpyname;
1626    const char *protoname;
1627    const char *hexkey;
1628    char *key;
1629    AuthList *list, *list_cur, *list_next;
1630
1631    if (argc != 4 || !argv[1] || !argv[2] || !argv[3]) {
1632	prefix (inputfilename, lineno);
1633	badcommandline (argv[0]);
1634	return 1;
1635    }
1636
1637    dpyname = argv[1];
1638    protoname = argv[2];
1639    hexkey = argv[3];
1640
1641    len = strlen(hexkey);
1642    if (len > 1 && hexkey[0] == '"' && hexkey[len-1] == '"') {
1643	key = malloc(len-1);
1644	if (!key) {
1645	    fprintf(stderr, "unable to allocate memory\n");
1646	    return 1;
1647	}
1648	strncpy(key, hexkey+1, len-2);
1649	key[len-2] = '\0';
1650	len -= 2;
1651    } else if (!strcmp(protoname, SECURERPC) ||
1652	       !strcmp(protoname, K5AUTH)) {
1653	key = malloc(len+1);
1654	if (!key) {
1655	    fprintf(stderr, "unable to allocate memory\n");
1656	    return 1;
1657	}
1658	strcpy(key, hexkey);
1659    } else {
1660	len = cvthexkey (hexkey, &key);
1661	if (len < 0) {
1662	    prefix (inputfilename, lineno);
1663	    fprintf (stderr,
1664		     "key contains odd number of or non-hex characters\n");
1665	    return 1;
1666	}
1667    }
1668
1669    if (!get_displayname_auth (dpyname, &list)) {
1670	prefix (inputfilename, lineno);
1671	baddisplayname (dpyname, argv[0]);
1672	free (key);
1673	return 1;
1674    }
1675
1676    /*
1677     * allow an abbreviation for common protocol names
1678     */
1679    if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
1680	protoname = DEFAULT_PROTOCOL;
1681    }
1682
1683    for (list_cur = list;  list_cur != NULL; list_cur = list_cur->next) {
1684	Xauth *auth = list_cur->auth;
1685
1686	auth->name_length = strlen (protoname);
1687	auth->name = copystring (protoname, auth->name_length);
1688	if (!auth->name) {
1689	    prefix (inputfilename, lineno);
1690	    fprintf (stderr, "unable to allocate %d character protocol name\n",
1691		     auth->name_length);
1692	    for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1693		list_next = list_cur->next;
1694		XauDisposeAuth(list_cur->auth);
1695		free(list_cur);
1696	    }
1697	    free (key);
1698	    return 1;
1699	}
1700	auth->data_length = len;
1701	auth->data = malloc(len);
1702	if (!auth->data) {
1703		prefix(inputfilename, lineno);
1704		fprintf(stderr, "unable to allocate %d bytes for key\n", len);
1705		for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1706			list_next = list_cur->next;
1707			XauDisposeAuth(list_cur->auth);
1708			free(list_cur);
1709		}
1710		free(key);
1711		return 1;
1712	}
1713	memcpy(auth->data, key, len);
1714    }
1715    free(key);
1716    /*
1717     * merge it in; note that merge will deal with allocation
1718     */
1719    n = merge_entries (&xauth_head, list, &nnew, &nrepl);
1720    if (n <= 0) {
1721	prefix (inputfilename, lineno);
1722	fprintf (stderr, "unable to merge in added record\n");
1723	return 1;
1724    }
1725    sort_entries(&xauth_head);
1726
1727    xauth_modified = True;
1728    return 0;
1729}
1730
1731/*
1732 * remove displayname
1733 */
1734static int
1735do_remove(const char *inputfilename, int lineno, int argc, const char **argv)
1736{
1737    int nremoved = 0;
1738    int errors;
1739
1740    if (argc < 2) {
1741	prefix (inputfilename, lineno);
1742	badcommandline (argv[0]);
1743	return 1;
1744    }
1745
1746    errors = iterdpy (inputfilename, lineno, 1, argc, argv,
1747		      remove_entry, NULL, (char *) &nremoved);
1748    if (verbose) printf ("%d entries removed\n", nremoved);
1749    return errors;
1750}
1751
1752/*
1753 * info
1754 */
1755static int
1756do_info(const char *inputfilename, int lineno, int argc, const char **argv)
1757{
1758    int n;
1759    AuthList *l;
1760
1761    if (argc != 1) {
1762	prefix (inputfilename, lineno);
1763	badcommandline (argv[0]);
1764	return 1;
1765    }
1766
1767    for (l = xauth_head, n = 0; l; l = l->next, n++) ;
1768
1769    printf ("Authority file:       %s\n",
1770	    xauth_filename ? xauth_filename : "(none)");
1771    printf ("File new:             %s\n", xauth_existed ? No : Yes);
1772    printf ("File locked:          %s\n", xauth_locked ? No : Yes);
1773    printf ("Number of entries:    %d\n", n);
1774    printf ("Changes honored:      %s\n", xauth_allowed ? Yes : No);
1775    printf ("Changes made:         %s\n", xauth_modified ? Yes : No);
1776    printf ("Current input:        %s:%d\n", inputfilename, lineno);
1777    return 0;
1778}
1779
1780
1781/*
1782 * exit
1783 */
1784static Bool alldone = False;
1785
1786/* ARGSUSED */
1787static int
1788do_exit(const char *inputfilename, int lineno, int argc, const char **argv)
1789{
1790    /* allow bogus stuff */
1791    alldone = True;
1792    return 0;
1793}
1794
1795/*
1796 * quit
1797 */
1798/* ARGSUSED */
1799static int
1800do_quit(const char *inputfilename, int lineno, int argc, const char **argv)
1801{
1802    /* allow bogus stuff */
1803    die (0);
1804    /* NOTREACHED */
1805    return -1;				/* for picky compilers */
1806}
1807
1808
1809/*
1810 * source filename
1811 */
1812static int
1813do_source(const char *inputfilename, int lineno, int argc, const char **argv)
1814{
1815    const char *script;
1816    FILE *fp;
1817    Bool used_stdin = False;
1818    int errors = 0;
1819    int sublineno = 0;
1820    Bool prompt = False;		/* only true if reading from tty */
1821
1822    if (argc != 2 || !argv[1]) {
1823	prefix (inputfilename, lineno);
1824	badcommandline (argv[0]);
1825	return 1;
1826    }
1827
1828    script = argv[1];
1829
1830    fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1831    if (!fp) {
1832	return 1;
1833    }
1834
1835    if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1836
1837    while (!alldone) {
1838	char buf[BUFSIZ];
1839	int len;
1840	const char **subargv;
1841	int subargc;
1842
1843	buf[0] = '\0';
1844	if (prompt) {
1845	    printf ("xauth> ");
1846	    fflush (stdout);
1847	}
1848	if (fgets (buf, sizeof buf, fp) == NULL) break;
1849	sublineno++;
1850	len = strlen (buf);
1851	if (len == 0 || buf[0] == '#') continue;
1852	if (buf[len-1] != '\n') {
1853	    prefix (script, sublineno);
1854	    fprintf (stderr, "line too long\n");
1855	    errors++;
1856	    break;
1857	}
1858	buf[--len] = '\0';		/* remove new line */
1859	subargv = (const char **) split_into_words (buf, &subargc);
1860	if (subargv) {
1861	    int status = process_command (script, sublineno, subargc, subargv);
1862	    errors += status;
1863	    free (subargv);
1864	} else {
1865	    prefix (script, sublineno);
1866	    fprintf (stderr, "unable to break line into words\n");
1867	    errors++;
1868	}
1869    }
1870
1871    if (!used_stdin) {
1872	(void) fclose (fp);
1873    }
1874    return errors;
1875}
1876
1877static int x_protocol_error;
1878static int
1879catch_x_protocol_error(Display *dpy, XErrorEvent *errevent)
1880{
1881    char buf[80];
1882    XGetErrorText(dpy, errevent->error_code, buf, sizeof (buf));
1883    fprintf(stderr, "%s\n", buf);
1884    x_protocol_error = errevent->error_code;
1885    return 1;
1886}
1887
1888/*
1889 * generate
1890 */
1891static int
1892do_generate(const char *inputfilename, int lineno, int argc, const char **argv)
1893{
1894    const char *displayname;
1895    int major_version, minor_version;
1896    XSecurityAuthorization id_return;
1897    Xauth *auth_in = NULL, *auth_return = NULL;
1898    XSecurityAuthorizationAttributes attributes;
1899    unsigned long attrmask = 0;
1900    Display *dpy = NULL;
1901    int status;
1902    const char *args[4];
1903    const char *protoname = ".";
1904    int authdatalen = 0;
1905    const char *hexdata;
1906    char *authdata = NULL;
1907    char *hex = NULL;
1908
1909    if (argc < 2 || !argv[1]) {
1910	prefix (inputfilename, lineno);
1911	badcommandline (argv[0]);
1912	return 1;
1913    }
1914
1915    displayname = argv[1];
1916
1917    if (argc > 2) {
1918	protoname = argv[2];
1919    }
1920
1921    for (int i = 3; i < argc; i++) {
1922	if (0 == strcmp(argv[i], "timeout")) {
1923	    if (++i == argc) {
1924		prefix (inputfilename, lineno);
1925		badcommandline (argv[i-1]);
1926		status = 1;
1927		goto exit_generate;
1928	    }
1929	    attributes.timeout = atoi(argv[i]);
1930	    attrmask |= XSecurityTimeout;
1931
1932	} else if (0 == strcmp(argv[i], "trusted")) {
1933	    attributes.trust_level = XSecurityClientTrusted;
1934	    attrmask |= XSecurityTrustLevel;
1935
1936	} else if (0 == strcmp(argv[i], "untrusted")) {
1937	    attributes.trust_level = XSecurityClientUntrusted;
1938	    attrmask |= XSecurityTrustLevel;
1939
1940	} else if (0 == strcmp(argv[i], "group")) {
1941	    if (++i == argc) {
1942		prefix (inputfilename, lineno);
1943		badcommandline (argv[i-1]);
1944		status = 1;
1945		goto exit_generate;
1946	    }
1947	    attributes.group = atoi(argv[i]);
1948	    attrmask |= XSecurityGroup;
1949
1950	} else if (0 == strcmp(argv[i], "data")) {
1951	    if (++i == argc) {
1952		prefix (inputfilename, lineno);
1953		badcommandline (argv[i-1]);
1954		status = 1;
1955		goto exit_generate;
1956	    }
1957	    hexdata = argv[i];
1958	    authdatalen = strlen(hexdata);
1959	    if (hexdata[0] == '"' && hexdata[authdatalen-1] == '"') {
1960		authdata = malloc(authdatalen-1);
1961		if (!authdata) {
1962		    fprintf(stderr, "unable to allocate memory\n");
1963		    status = 1;
1964		    goto exit_generate;
1965		}
1966		strncpy(authdata, hexdata+1, authdatalen-2);
1967		authdata[authdatalen-2] = '\0';
1968		authdatalen -= 2;
1969	    } else {
1970		authdatalen = cvthexkey (hexdata, &authdata);
1971		if (authdatalen < 0) {
1972		    prefix (inputfilename, lineno);
1973		    fprintf (stderr,
1974			     "data contains odd number of or non-hex characters\n");
1975		    status = 1;
1976		    goto exit_generate;
1977		}
1978	    }
1979	} else {
1980	    prefix (inputfilename, lineno);
1981	    badcommandline (argv[i]);
1982	    status = 1;
1983	    goto exit_generate;
1984	}
1985    }
1986
1987    /* generate authorization using the Security extension */
1988
1989    dpy = XOpenDisplay (displayname);
1990    if (!dpy) {
1991	prefix (inputfilename, lineno);
1992	fprintf (stderr, "unable to open display \"%s\".\n", displayname);
1993	status = 1;
1994	goto exit_generate;
1995    }
1996
1997    status = XSecurityQueryExtension(dpy, &major_version, &minor_version);
1998    if (!status)
1999    {
2000	prefix (inputfilename, lineno);
2001	fprintf (stderr, "couldn't query Security extension on display \"%s\"\n",
2002		 displayname);
2003	status = 1;
2004	goto exit_generate;
2005    }
2006
2007    /* fill in input Xauth struct */
2008
2009    auth_in = XSecurityAllocXauth();
2010    if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
2011	 auth_in->name = copystring(DEFAULT_PROTOCOL, strlen(DEFAULT_PROTOCOL));
2012    }
2013    else
2014	auth_in->name = copystring (protoname, strlen(protoname));
2015    auth_in->name_length = strlen(auth_in->name);
2016    auth_in->data = authdata;
2017    auth_in->data_length = authdatalen;
2018
2019    x_protocol_error = 0;
2020    XSetErrorHandler(catch_x_protocol_error);
2021    auth_return = XSecurityGenerateAuthorization(dpy, auth_in, attrmask,
2022						 &attributes, &id_return);
2023    XSync(dpy, False);
2024
2025    if (!auth_return || x_protocol_error)
2026    {
2027	prefix (inputfilename, lineno);
2028	fprintf (stderr, "couldn't generate authorization\n");
2029	status = 1;
2030	goto exit_generate;
2031    }
2032
2033    if (verbose)
2034	printf("authorization id is %ld\n", id_return);
2035
2036    /* create a fake input line to give to do_add */
2037    hex = bintohex(auth_return->data_length, auth_return->data);
2038    args[0] = "add";
2039    args[1] = displayname;
2040    args[2] = auth_in->name;
2041    args[3] = hex;
2042
2043    status = do_add(inputfilename, lineno, 4, args);
2044
2045  exit_generate:
2046    free(authdata);
2047    XSecurityFreeXauth(auth_in);
2048    XSecurityFreeXauth(auth_return);
2049    free(hex);
2050    if (dpy != NULL)
2051	XCloseDisplay(dpy);
2052    return status;
2053}
2054