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