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