process.c revision 26472619
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    (void) fclose (fp);
848    return 0;
849}
850
851int
852auth_finalize(void)
853{
854    char temp_name[1024];	/* large filename size */
855
856    if (xauth_modified) {
857	if (dieing) {
858	    if (verbose) {
859		/*
860		 * called from a signal handler -- printf is *not* reentrant; also
861		 * fileno() might not be reentrant, avoid it if possible, and use
862		 * stderr instead of stdout
863		 */
864#ifdef STDERR_FILENO
865		WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
866		WRITES(STDERR_FILENO, xauth_filename);
867		WRITES(STDERR_FILENO, "\n");
868#else
869		WRITES(fileno(stderr), "\nAborting changes to authority file ");
870		WRITES(fileno(stderr), xauth_filename);
871		WRITES(fileno(stderr), "\n");
872#endif
873	    }
874	} else if (!xauth_allowed) {
875	    fprintf (stderr,
876		     "%s:  %s not writable, changes ignored\n",
877		     ProgramName, xauth_filename);
878	} else {
879	    if (verbose) {
880		printf ("%s authority file %s\n",
881			ignore_locks ? "Ignoring locks and writing" :
882			"Writing", xauth_filename);
883	    }
884	    temp_name[0] = '\0';
885	    if (write_auth_file (temp_name) == -1) {
886		fprintf (stderr,
887			 "%s:  unable to write authority file %s\n",
888			 ProgramName, temp_name);
889	    } else {
890		(void) unlink (xauth_filename);
891#if defined(WIN32) || defined(__UNIXOS2__)
892		if (rename(temp_name, xauth_filename) == -1)
893#else
894		/* Attempt to rename() if link() fails, since this may be on a FS that does not support hard links */
895		if (link (temp_name, xauth_filename) == -1 && rename(temp_name, xauth_filename) == -1)
896#endif
897		{
898		    fprintf (stderr,
899		     "%s:  unable to link authority file %s, use %s\n",
900			     ProgramName, xauth_filename, temp_name);
901		} else {
902		    (void) unlink (temp_name);
903		}
904	    }
905	}
906    }
907
908    if (xauth_locked) {
909	XauUnlockAuth (xauth_filename);
910    }
911    (void) umask (original_umask);
912    return 0;
913}
914
915int
916process_command(const char *inputfilename, int lineno, int argc, const char **argv)
917{
918    int status;
919
920    if (argc < 1 || !argv || !argv[0]) return 1;
921
922    if (dispatch_command (inputfilename, lineno, argc, argv,
923			  command_table, &status))
924      return status;
925
926    prefix (inputfilename, lineno);
927    fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
928    return 1;
929}
930
931
932/*
933 * utility routines
934 */
935
936static char *
937bintohex(unsigned int len, const char *bindata)
938{
939    char *hexdata, *starthex;
940
941    /* two chars per byte, plus null termination */
942    starthex = hexdata = (char *)malloc(2*len + 1);
943    if (!hexdata)
944	return NULL;
945
946    for (; len > 0; len--, bindata++) {
947	register const char *s = hex_table[(unsigned char)*bindata];
948	*hexdata++ = s[0];
949	*hexdata++ = s[1];
950    }
951    *hexdata = '\0';
952    return starthex;
953}
954
955static void
956fprintfhex(register FILE *fp, int len, char *cp)
957{
958    char *hex;
959
960    hex = bintohex(len, cp);
961    fprintf(fp, "%s", hex);
962    free(hex);
963}
964
965static int
966dump_numeric(register FILE *fp, register Xauth *auth)
967{
968    fprintf (fp, "%04x", auth->family);  /* unsigned short */
969    fprintf (fp, " %04x ", auth->address_length);  /* short */
970    fprintfhex (fp, auth->address_length, auth->address);
971    fprintf (fp, " %04x ", auth->number_length);  /* short */
972    fprintfhex (fp, auth->number_length, auth->number);
973    fprintf (fp, " %04x ", auth->name_length);  /* short */
974    fprintfhex (fp, auth->name_length, auth->name);
975    fprintf (fp, " %04x ", auth->data_length);  /* short */
976    fprintfhex (fp, auth->data_length, auth->data);
977    putc ('\n', fp);
978    return 1;
979}
980
981/* ARGSUSED */
982static int
983dump_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
984{
985    struct _list_data *ld = (struct _list_data *) data;
986    FILE *fp = ld->fp;
987
988    if (ld->numeric) {
989	dump_numeric (fp, auth);
990    } else {
991	const char *dpyname = NULL;
992
993	switch (auth->family) {
994	  case FamilyLocal:
995	    fwrite (auth->address, sizeof (char), auth->address_length, fp);
996	    fprintf (fp, "/unix");
997	    break;
998	  case FamilyInternet:
999#if defined(IPv6) && defined(AF_INET6)
1000	  case FamilyInternet6:
1001#endif
1002	  case FamilyDECnet:
1003	    dpyname = get_hostname (auth);
1004	    if (dpyname) {
1005		fprintf (fp, "%s", dpyname);
1006		break;
1007	    }
1008	    /* else fall through to default */
1009	  default:
1010	    fprintf (fp, "#%04x#", auth->family);
1011	    fprintfhex (fp, auth->address_length, auth->address);
1012	    putc ('#', fp);
1013	}
1014	putc (':', fp);
1015	fwrite (auth->number, sizeof (char), auth->number_length, fp);
1016	putc (' ', fp);
1017	putc (' ', fp);
1018	fwrite (auth->name, sizeof (char), auth->name_length, fp);
1019	putc (' ', fp);
1020	putc (' ', fp);
1021	if (!strncmp(auth->name, SECURERPC, auth->name_length) ||
1022	    !strncmp(auth->name, K5AUTH, auth->name_length))
1023            fwrite (auth->data, sizeof (char), auth->data_length, fp);
1024	else
1025	    fprintfhex (fp, auth->data_length, auth->data);
1026	putc ('\n', fp);
1027    }
1028    return 0;
1029}
1030
1031static int
1032extract_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
1033{
1034    struct _extract_data *ed = (struct _extract_data *) data;
1035
1036    if (!ed->fp) {
1037	ed->fp = open_file (&ed->filename,
1038			    ed->numeric ? "w" : "wb",
1039			    &ed->used_stdout,
1040			    inputfilename, lineno, ed->cmd);
1041	if (!ed->fp) {
1042	    prefix (inputfilename, lineno);
1043	    fprintf (stderr,
1044		     "unable to open extraction file \"%s\"\n",
1045		     ed->filename);
1046	    return -1;
1047	}
1048    }
1049    (*(ed->numeric ? dump_numeric : XauWriteAuth)) (ed->fp, auth);
1050    ed->nwritten++;
1051
1052    return 0;
1053}
1054
1055
1056static int
1057eq_auth(Xauth *a, Xauth *b)
1058{
1059    return((a->family == b->family &&
1060	    a->address_length == b->address_length &&
1061	    a->number_length == b->number_length &&
1062	    a->name_length == b->name_length &&
1063	    a->data_length == b->data_length &&
1064	    memcmp(a->address, b->address, a->address_length) == 0 &&
1065	    memcmp(a->number, b->number, a->number_length) == 0 &&
1066	    memcmp(a->name, b->name, a->name_length) == 0 &&
1067	    memcmp(a->data, b->data, a->data_length) == 0) ? 1 : 0);
1068}
1069
1070static int
1071match_auth_dpy(register Xauth *a, register Xauth *b)
1072{
1073    if (a->family != FamilyWild && b->family != FamilyWild) {
1074        /* Both "a" and "b" are not FamilyWild, they are "normal" families. */
1075
1076	/* Make sure, that both families match: */
1077	if (a->family != b->family)
1078            return 0;
1079
1080	/* By looking at 'man Xsecurity' and the code in
1081	 * GetAuthByAddr() and XauGetBestAuthByAddr() in libXau, we
1082	 * decided, that the address is only relevant for "normal"
1083	 * families and therefore should be ignored for
1084	 * "FamilyWild". */
1085	if (a->address_length != b->address_length ||
1086            memcmp(a->address, b->address, a->address_length) != 0)
1087            return 0;
1088    }
1089
1090    if (a->number_length != 0 && b->number_length != 0) {
1091	/* Both "a" and "b" have a number, make sure they match: */
1092	if (a->number_length != b->number_length ||
1093	    memcmp(a->number, b->number, a->number_length) != 0)
1094            return 0;
1095    }
1096
1097    return 1;
1098}
1099
1100/* return non-zero iff display and authorization type are the same */
1101
1102static int
1103match_auth(register Xauth *a, register Xauth *b)
1104{
1105    return ((match_auth_dpy(a, b)
1106	     && a->name_length == b->name_length
1107	     && memcmp(a->name, b->name, a->name_length) == 0) ? 1 : 0);
1108}
1109
1110
1111static int
1112merge_entries(AuthList **firstp, AuthList *second, int *nnewp, int *nreplp)
1113{
1114    AuthList *a, *b, *first, *tail;
1115    int n = 0, nnew = 0, nrepl = 0;
1116
1117    if (!second) return 0;
1118
1119    if (!*firstp) {			/* if nothing to merge into */
1120	*firstp = second;
1121	for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
1122	*nnewp = n;
1123	*nreplp = 0;
1124	return n;
1125    }
1126
1127    first = *firstp;
1128    /*
1129     * find end of first list and stick second list on it
1130     */
1131    for (tail = first; tail->next; tail = tail->next) ;
1132    tail->next = second;
1133
1134    /*
1135     * run down list freeing duplicate entries; if an entry is okay, then
1136     * bump the tail up to include it, otherwise, cut the entry out of
1137     * the chain.
1138     */
1139    for (b = second; b; ) {
1140	AuthList *next = b->next;	/* in case we free it */
1141
1142	a = first;
1143	for (;;) {
1144	    if (match_auth (a->auth, b->auth)) {  /* found a duplicate */
1145		AuthList tmp;		/* swap it in for old one */
1146		tmp = *a;
1147		*a = *b;
1148		*b = tmp;
1149		a->next = b->next;
1150		XauDisposeAuth (b->auth);
1151		free ((char *) b);
1152		b = NULL;
1153		tail->next = next;
1154		nrepl++;
1155		nnew--;
1156		break;
1157	    }
1158	    if (a == tail) break;	/* if have looked at left side */
1159	    a = a->next;
1160	}
1161	if (b) {			/* if we didn't remove it */
1162	    tail = b;			/* bump end of first list */
1163	}
1164	b = next;
1165	n++;
1166	nnew++;
1167    }
1168
1169    *nnewp = nnew;
1170    *nreplp = nrepl;
1171    return n;
1172
1173}
1174
1175static Xauth *
1176copyAuth(Xauth *auth)
1177{
1178    Xauth *a;
1179
1180    a = (Xauth *)malloc(sizeof(Xauth));
1181    if (a == NULL) {
1182	return NULL;
1183    }
1184    memset(a, 0, sizeof(Xauth));
1185    a->family = auth->family;
1186    if (auth->address_length != 0) {
1187	a->address = malloc(auth->address_length);
1188	if (a->address == NULL) {
1189	    free(a);
1190	    return NULL;
1191	}
1192	memcpy(a->address, auth->address, auth->address_length);
1193	a->address_length = auth->address_length;
1194    }
1195    if (auth->number_length != 0) {
1196	a->number = malloc(auth->number_length);
1197	if (a->number == NULL) {
1198	    free(a->address);
1199	    free(a);
1200	    return NULL;
1201	}
1202	memcpy(a->number, auth->number, auth->number_length);
1203	a->number_length = auth->number_length;
1204    }
1205    if (auth->name_length != 0) {
1206	a->name = malloc(auth->name_length);
1207	if (a->name == NULL) {
1208	    free(a->address);
1209	    free(a->number);
1210	    free(a);
1211	    return NULL;
1212	}
1213	memcpy(a->name, auth->name, auth->name_length);
1214	a->name_length = auth->name_length;
1215    }
1216    if (auth->data_length != 0) {
1217	a->data = malloc(auth->data_length);
1218	if (a->data == NULL) {
1219	    free(a->address);
1220	    free(a->number);
1221	    free(a->name);
1222	    free(a);
1223	    return NULL;
1224	}
1225	memcpy(a->data, auth->data, auth->data_length);
1226	a->data_length = auth->data_length;
1227    }
1228    return a;
1229}
1230
1231typedef int (*YesNoFunc)(const char *, int, Xauth *, char *);
1232
1233static int
1234iterdpy (const char *inputfilename, int lineno, int start,
1235	 int argc, const char *argv[],
1236	 YesNoFunc yfunc, YesNoFunc nfunc, char *data)
1237{
1238    int i;
1239    int status;
1240    int errors = 0;
1241    Xauth *tmp_auth;
1242    AuthList *proto_head, *proto;
1243    AuthList *l, *next;
1244
1245    /*
1246     * iterate
1247     */
1248    for (i = start; i < argc; i++) {
1249	const char *displayname = argv[i];
1250	if (!get_displayname_auth (displayname, &proto_head)) {
1251	    prefix (inputfilename, lineno);
1252	    baddisplayname (displayname, argv[0]);
1253	    errors++;
1254	    continue;
1255	}
1256	status = 0;
1257	for (l = xauth_head; l; l = next) {
1258	    Bool matched = False;
1259
1260	    /* l may be freed by remove_entry below. so save its contents */
1261	    next = l->next;
1262	    tmp_auth = copyAuth(l->auth);
1263	    for (proto = proto_head; proto; proto = proto->next) {
1264		if (match_auth_dpy (proto->auth, tmp_auth)) {
1265		    matched = True;
1266		    if (yfunc) {
1267			status = (*yfunc) (inputfilename, lineno,
1268					   tmp_auth, data);
1269			if (status < 0) break;
1270		    }
1271		}
1272	    }
1273	    XauDisposeAuth(tmp_auth);
1274	    if (matched == False) {
1275		if (nfunc) {
1276		    status = (*nfunc) (inputfilename, lineno,
1277				       l->auth, data);
1278		}
1279	    }
1280	    if (status < 0) break;
1281	}
1282	for (proto = proto_head; proto ; proto = next) {
1283	    next = proto->next;
1284	    if (proto->auth->address) free (proto->auth->address);
1285	    if (proto->auth->number) free (proto->auth->number);
1286	    free (proto->auth);
1287	    free (proto);
1288	}
1289	if (status < 0) {
1290	    errors -= status;		/* since status is negative */
1291	    break;
1292	}
1293    }
1294
1295    return errors;
1296}
1297
1298/* ARGSUSED */
1299static int
1300remove_entry(const char *inputfilename, int lineno, Xauth *auth, char *data)
1301{
1302    int *nremovedp = (int *) data;
1303    AuthList **listp = &xauth_head;
1304    AuthList *list;
1305
1306    /*
1307     * unlink the auth we were asked to
1308     */
1309    while (!eq_auth((list = *listp)->auth, auth)) {
1310        listp = &list->next;
1311        if (!*listp)
1312            return 0;
1313    }
1314    *listp = list->next;
1315    XauDisposeAuth (list->auth);                    /* free the auth */
1316    free (list);				    /* free the link */
1317    xauth_modified = True;
1318    (*nremovedp)++;
1319    return 1;
1320}
1321
1322/*
1323 * action routines
1324 */
1325
1326/*
1327 * help
1328 */
1329int
1330print_help(FILE *fp, const char *cmd, const char *line_prefix)
1331{
1332    CommandTable *ct;
1333    int n = 0;
1334
1335    if (!line_prefix) line_prefix = "";
1336
1337    if (!cmd) {				/* if no cmd, print all help */
1338	for (ct = command_table; ct->name; ct++) {
1339	    fprintf (fp, "%s%s\n", line_prefix, ct->helptext);
1340	    n++;
1341	}
1342    } else {
1343	int len = strlen (cmd);
1344	for (ct = command_table; ct->name; ct++) {
1345	    if (strncmp (cmd, ct->name, len) == 0) {
1346		fprintf (fp, "%s%s\n", line_prefix, ct->helptext);
1347		n++;
1348	    }
1349	}
1350    }
1351
1352    return n;
1353}
1354
1355static int
1356do_help(const char *inputfilename, int lineno, int argc, const char **argv)
1357{
1358    const char *cmd = (argc > 1 ? argv[1] : NULL);
1359    int n;
1360
1361    n = print_help (stdout, cmd, "    ");  /* a nice amount */
1362
1363    if (n < 0 || (n == 0 && !cmd)) {
1364	prefix (inputfilename, lineno);
1365	fprintf (stderr, "internal error with help");
1366	if (cmd) {
1367	    fprintf (stderr, " on command \"%s\"", cmd);
1368	}
1369	fprintf (stderr, "\n");
1370	return 1;
1371    }
1372
1373    if (n == 0) {
1374	prefix (inputfilename, lineno);
1375	/* already know that cmd is set in this case */
1376	fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1377    }
1378
1379    return 0;
1380}
1381
1382/*
1383 * questionmark
1384 */
1385/* ARGSUSED */
1386static int
1387do_questionmark(const char *inputfilename, int lineno, int argc, const char **argv)
1388{
1389    CommandTable *ct;
1390    int i;
1391#define WIDEST_COLUMN 72
1392    int col = WIDEST_COLUMN;
1393
1394    printf ("Commands:\n");
1395    for (ct = command_table; ct->name; ct++) {
1396	if ((col + ct->maxlen) > WIDEST_COLUMN) {
1397	    if (ct != command_table) {
1398		putc ('\n', stdout);
1399	    }
1400	    fputs ("        ", stdout);
1401	    col = 8;			/* length of string above */
1402	}
1403	fputs (ct->name, stdout);
1404	col += ct->maxlen;
1405	for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1406	    putc (' ', stdout);
1407	    col++;
1408	}
1409    }
1410    if (col != 0) {
1411	putc ('\n', stdout);
1412    }
1413
1414    /* allow bad lines since this is help */
1415    return 0;
1416}
1417
1418/*
1419 * version
1420 */
1421/* ARGSUSED */
1422static int
1423do_version(const char *inputfilename, int lineno, int argc, const char **argv)
1424{
1425    puts (PACKAGE_VERSION);
1426    return 0;
1427}
1428
1429/*
1430 * list [displayname ...]
1431 */
1432static int
1433do_list (const char *inputfilename, int lineno, int argc, const char **argv)
1434{
1435    struct _list_data ld;
1436
1437    ld.fp = stdout;
1438    ld.numeric = (argv[0][0] == 'n');
1439
1440    if (argc == 1) {
1441	register AuthList *l;
1442
1443	if (xauth_head) {
1444	    for (l = xauth_head; l; l = l->next) {
1445		dump_entry (inputfilename, lineno, l->auth, (char *) &ld);
1446	    }
1447	}
1448	return 0;
1449    }
1450
1451    return iterdpy (inputfilename, lineno, 1, argc, argv,
1452		    dump_entry, NULL, (char *) &ld);
1453}
1454
1455/*
1456 * merge filename [filename ...]
1457 */
1458static int
1459do_merge(const char *inputfilename, int lineno, int argc, const char **argv)
1460{
1461    int i;
1462    int errors = 0;
1463    AuthList *head, *tail, *listhead, *listtail;
1464    int nentries, nnew, nrepl;
1465    Bool numeric = False;
1466
1467    if (argc < 2) {
1468	prefix (inputfilename, lineno);
1469	badcommandline (argv[0]);
1470	return 1;
1471    }
1472
1473    if (argv[0][0] == 'n') numeric = True;
1474    listhead = listtail = NULL;
1475
1476    for (i = 1; i < argc; i++) {
1477	const char *filename = argv[i];
1478	FILE *fp;
1479	Bool used_stdin = False;
1480
1481	fp = open_file (&filename,
1482			numeric ? "r" : "rb",
1483			&used_stdin, inputfilename, lineno,
1484			argv[0]);
1485	if (!fp) {
1486	    errors++;
1487	    continue;
1488	}
1489
1490	head = tail = NULL;
1491	nentries = read_auth_entries (fp, numeric, &head, &tail);
1492	if (nentries == 0) {
1493	    prefix (inputfilename, lineno);
1494	    fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1495		     filename);
1496	    errors++;
1497	} else {			/* link it in */
1498	    add_to_list (listhead, listtail, head);
1499 	}
1500
1501	if (!used_stdin) (void) fclose (fp);
1502    }
1503
1504    /*
1505     * if we have new entries, merge them in (freeing any duplicates)
1506     */
1507    if (listhead) {
1508	nentries = merge_entries (&xauth_head, listhead, &nnew, &nrepl);
1509	if (verbose)
1510	  printf ("%d entries read in:  %d new, %d replacement%s\n",
1511	  	  nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1512	if (nentries > 0) xauth_modified = True;
1513    }
1514
1515    return 0;
1516}
1517
1518/*
1519 * extract filename displayname [displayname ...]
1520 */
1521static int
1522do_extract(const char *inputfilename, int lineno, int argc, const char **argv)
1523{
1524    int errors;
1525    struct _extract_data ed;
1526
1527    if (argc < 3) {
1528	prefix (inputfilename, lineno);
1529	badcommandline (argv[0]);
1530	return 1;
1531    }
1532
1533    ed.fp = NULL;
1534    ed.filename = argv[1];
1535    ed.used_stdout = False;
1536    ed.numeric = (argv[0][0] == 'n');
1537    ed.nwritten = 0;
1538    ed.cmd = argv[0];
1539
1540    errors = iterdpy (inputfilename, lineno, 2, argc, argv,
1541		      extract_entry, NULL, (char *) &ed);
1542
1543    if (!ed.fp) {
1544	fprintf (stderr,
1545		 "No matches found, authority file \"%s\" not written\n",
1546		 ed.filename);
1547    } else {
1548	if (verbose) {
1549	    printf ("%d entries written to \"%s\"\n",
1550		    ed.nwritten, ed.filename);
1551	}
1552	if (!ed.used_stdout) {
1553	    (void) fclose (ed.fp);
1554	}
1555    }
1556
1557    return errors;
1558}
1559
1560
1561/*
1562 * add displayname protocolname hexkey
1563 */
1564
1565static int
1566do_add(const char *inputfilename, int lineno, int argc, const char **argv)
1567{
1568    int n, nnew, nrepl;
1569    int len;
1570    const char *dpyname;
1571    const char *protoname;
1572    const char *hexkey;
1573    char *key;
1574    AuthList *list, *list_cur, *list_next;
1575
1576    if (argc != 4 || !argv[1] || !argv[2] || !argv[3]) {
1577	prefix (inputfilename, lineno);
1578	badcommandline (argv[0]);
1579	return 1;
1580    }
1581
1582    dpyname = argv[1];
1583    protoname = argv[2];
1584    hexkey = argv[3];
1585
1586    len = strlen(hexkey);
1587    if (hexkey[0] == '"' && hexkey[len-1] == '"') {
1588	key = malloc(len-1);
1589	strncpy(key, hexkey+1, len-2);
1590	len -= 2;
1591    } else if (!strcmp(protoname, SECURERPC) ||
1592	       !strcmp(protoname, K5AUTH)) {
1593	key = malloc(len+1);
1594	strcpy(key, hexkey);
1595    } else {
1596	len = cvthexkey (hexkey, &key);
1597	if (len < 0) {
1598	    prefix (inputfilename, lineno);
1599	    fprintf (stderr,
1600		     "key contains odd number of or non-hex characters\n");
1601	    return 1;
1602	}
1603    }
1604
1605    if (!get_displayname_auth (dpyname, &list)) {
1606	prefix (inputfilename, lineno);
1607	baddisplayname (dpyname, argv[0]);
1608	free (key);
1609	return 1;
1610    }
1611
1612    /*
1613     * allow an abbreviation for common protocol names
1614     */
1615    if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
1616	protoname = DEFAULT_PROTOCOL;
1617    }
1618
1619    for (list_cur = list;  list_cur != NULL; list_cur = list_cur->next) {
1620	Xauth *auth = list_cur->auth;
1621
1622	auth->name_length = strlen (protoname);
1623	auth->name = copystring (protoname, auth->name_length);
1624	if (!auth->name) {
1625	    prefix (inputfilename, lineno);
1626	    fprintf (stderr, "unable to allocate %d character protocol name\n",
1627		     auth->name_length);
1628	    for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1629		list_next = list_cur->next;
1630		XauDisposeAuth(list_cur->auth);
1631		free(list_cur);
1632	    }
1633	    free (key);
1634	    return 1;
1635	}
1636	auth->data_length = len;
1637	auth->data = malloc(len);
1638	if (!auth->data) {
1639		prefix(inputfilename, lineno);
1640		fprintf(stderr, "unable to allocate %d bytes for key\n", len);
1641		for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1642			list_next = list_cur->next;
1643			XauDisposeAuth(list_cur->auth);
1644			free(list_cur);
1645		}
1646		free(key);
1647		return 1;
1648	}
1649	memcpy(auth->data, key, len);
1650    }
1651    free(key);
1652    /*
1653     * merge it in; note that merge will deal with allocation
1654     */
1655    n = merge_entries (&xauth_head, list, &nnew, &nrepl);
1656    if (n <= 0) {
1657	prefix (inputfilename, lineno);
1658	fprintf (stderr, "unable to merge in added record\n");
1659	return 1;
1660    }
1661
1662    xauth_modified = True;
1663    return 0;
1664}
1665
1666/*
1667 * remove displayname
1668 */
1669static int
1670do_remove(const char *inputfilename, int lineno, int argc, const char **argv)
1671{
1672    int nremoved = 0;
1673    int errors;
1674
1675    if (argc < 2) {
1676	prefix (inputfilename, lineno);
1677	badcommandline (argv[0]);
1678	return 1;
1679    }
1680
1681    errors = iterdpy (inputfilename, lineno, 1, argc, argv,
1682		      remove_entry, NULL, (char *) &nremoved);
1683    if (verbose) printf ("%d entries removed\n", nremoved);
1684    return errors;
1685}
1686
1687/*
1688 * info
1689 */
1690static int
1691do_info(const char *inputfilename, int lineno, int argc, const char **argv)
1692{
1693    int n;
1694    AuthList *l;
1695
1696    if (argc != 1) {
1697	prefix (inputfilename, lineno);
1698	badcommandline (argv[0]);
1699	return 1;
1700    }
1701
1702    for (l = xauth_head, n = 0; l; l = l->next, n++) ;
1703
1704    printf ("Authority file:       %s\n",
1705	    xauth_filename ? xauth_filename : "(none)");
1706    printf ("File new:             %s\n", xauth_existed ? No : Yes);
1707    printf ("File locked:          %s\n", xauth_locked ? No : Yes);
1708    printf ("Number of entries:    %d\n", n);
1709    printf ("Changes honored:      %s\n", xauth_allowed ? Yes : No);
1710    printf ("Changes made:         %s\n", xauth_modified ? Yes : No);
1711    printf ("Current input:        %s:%d\n", inputfilename, lineno);
1712    return 0;
1713}
1714
1715
1716/*
1717 * exit
1718 */
1719static Bool alldone = False;
1720
1721/* ARGSUSED */
1722static int
1723do_exit(const char *inputfilename, int lineno, int argc, const char **argv)
1724{
1725    /* allow bogus stuff */
1726    alldone = True;
1727    return 0;
1728}
1729
1730/*
1731 * quit
1732 */
1733/* ARGSUSED */
1734static int
1735do_quit(const char *inputfilename, int lineno, int argc, const char **argv)
1736{
1737    /* allow bogus stuff */
1738    die (0);
1739    /* NOTREACHED */
1740    return -1;				/* for picky compilers */
1741}
1742
1743
1744/*
1745 * source filename
1746 */
1747static int
1748do_source(const char *inputfilename, int lineno, int argc, const char **argv)
1749{
1750    const char *script;
1751    char buf[BUFSIZ];
1752    FILE *fp;
1753    Bool used_stdin = False;
1754    int len;
1755    int errors = 0, status;
1756    int sublineno = 0;
1757    const char **subargv;
1758    int subargc;
1759    Bool prompt = False;		/* only true if reading from tty */
1760
1761    if (argc != 2 || !argv[1]) {
1762	prefix (inputfilename, lineno);
1763	badcommandline (argv[0]);
1764	return 1;
1765    }
1766
1767    script = argv[1];
1768
1769    fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1770    if (!fp) {
1771	return 1;
1772    }
1773
1774    if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1775
1776    while (!alldone) {
1777	buf[0] = '\0';
1778	if (prompt) {
1779	    printf ("xauth> ");
1780	    fflush (stdout);
1781	}
1782	if (fgets (buf, sizeof buf, fp) == NULL) break;
1783	sublineno++;
1784	len = strlen (buf);
1785	if (len == 0 || buf[0] == '#') continue;
1786	if (buf[len-1] != '\n') {
1787	    prefix (script, sublineno);
1788	    fprintf (stderr, "line too long\n");
1789	    errors++;
1790	    break;
1791	}
1792	buf[--len] = '\0';		/* remove new line */
1793	subargv = (const char **) split_into_words (buf, &subargc);
1794	if (subargv) {
1795	    status = process_command (script, sublineno, subargc, subargv);
1796	    free (subargv);
1797	    errors += status;
1798	} else {
1799	    prefix (script, sublineno);
1800	    fprintf (stderr, "unable to break line into words\n");
1801	    errors++;
1802	}
1803    }
1804
1805    if (!used_stdin) {
1806	(void) fclose (fp);
1807    }
1808    return errors;
1809}
1810
1811static int x_protocol_error;
1812static int
1813catch_x_protocol_error(Display *dpy, XErrorEvent *errevent)
1814{
1815    char buf[80];
1816    XGetErrorText(dpy, errevent->error_code, buf, sizeof (buf));
1817    fprintf(stderr, "%s\n", buf);
1818    x_protocol_error = errevent->error_code;
1819    return 1;
1820}
1821
1822/*
1823 * generate
1824 */
1825static int
1826do_generate(const char *inputfilename, int lineno, int argc, const char **argv)
1827{
1828    const char *displayname;
1829    int major_version, minor_version;
1830    XSecurityAuthorization id_return;
1831    Xauth *auth_in, *auth_return;
1832    XSecurityAuthorizationAttributes attributes;
1833    unsigned long attrmask = 0;
1834    Display *dpy;
1835    int status;
1836    const char *args[4];
1837    const char *protoname = ".";
1838    int i;
1839    int authdatalen = 0;
1840    const char *hexdata;
1841    char *authdata = NULL;
1842    char *hex;
1843
1844    if (argc < 2 || !argv[1]) {
1845	prefix (inputfilename, lineno);
1846	badcommandline (argv[0]);
1847	return 1;
1848    }
1849
1850    displayname = argv[1];
1851
1852    if (argc > 2) {
1853	protoname = argv[2];
1854    }
1855
1856    for (i = 3; i < argc; i++) {
1857	if (0 == strcmp(argv[i], "timeout")) {
1858	    if (++i == argc) {
1859		prefix (inputfilename, lineno);
1860		badcommandline (argv[i-1]);
1861		return 1;
1862	    }
1863	    attributes.timeout = atoi(argv[i]);
1864	    attrmask |= XSecurityTimeout;
1865
1866	} else if (0 == strcmp(argv[i], "trusted")) {
1867	    attributes.trust_level = XSecurityClientTrusted;
1868	    attrmask |= XSecurityTrustLevel;
1869
1870	} else if (0 == strcmp(argv[i], "untrusted")) {
1871	    attributes.trust_level = XSecurityClientUntrusted;
1872	    attrmask |= XSecurityTrustLevel;
1873
1874	} else if (0 == strcmp(argv[i], "group")) {
1875	    if (++i == argc) {
1876		prefix (inputfilename, lineno);
1877		badcommandline (argv[i-1]);
1878		return 1;
1879	    }
1880	    attributes.group = atoi(argv[i]);
1881	    attrmask |= XSecurityGroup;
1882
1883	} else if (0 == strcmp(argv[i], "data")) {
1884	    if (++i == argc) {
1885		prefix (inputfilename, lineno);
1886		badcommandline (argv[i-1]);
1887		return 1;
1888	    }
1889	    hexdata = argv[i];
1890	    authdatalen = strlen(hexdata);
1891	    if (hexdata[0] == '"' && hexdata[authdatalen-1] == '"') {
1892		authdata = malloc(authdatalen-1);
1893		strncpy(authdata, hexdata+1, authdatalen-2);
1894		authdatalen -= 2;
1895	    } else {
1896		authdatalen = cvthexkey (hexdata, &authdata);
1897		if (authdatalen < 0) {
1898		    prefix (inputfilename, lineno);
1899		    fprintf (stderr,
1900			     "data contains odd number of or non-hex characters\n");
1901		    return 1;
1902		}
1903	    }
1904	} else {
1905	    prefix (inputfilename, lineno);
1906	    badcommandline (argv[i]);
1907	    return 1;
1908	}
1909    }
1910
1911    /* generate authorization using the Security extension */
1912
1913    dpy = XOpenDisplay (displayname);
1914    if (!dpy) {
1915	prefix (inputfilename, lineno);
1916	fprintf (stderr, "unable to open display \"%s\".\n", displayname);
1917	return 1;
1918    }
1919
1920    status = XSecurityQueryExtension(dpy, &major_version, &minor_version);
1921    if (!status)
1922    {
1923	prefix (inputfilename, lineno);
1924	fprintf (stderr, "couldn't query Security extension on display \"%s\"\n",
1925		 displayname);
1926        return 1;
1927    }
1928
1929    /* fill in input Xauth struct */
1930
1931    auth_in = XSecurityAllocXauth();
1932    if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
1933	 auth_in->name = copystring(DEFAULT_PROTOCOL, strlen(DEFAULT_PROTOCOL));
1934    }
1935    else
1936	auth_in->name = copystring (protoname, strlen(protoname));
1937    auth_in->name_length = strlen(auth_in->name);
1938    auth_in->data = authdata;
1939    auth_in->data_length = authdatalen;
1940
1941    x_protocol_error = 0;
1942    XSetErrorHandler(catch_x_protocol_error);
1943    auth_return = XSecurityGenerateAuthorization(dpy, auth_in, attrmask,
1944						 &attributes, &id_return);
1945    XSync(dpy, False);
1946
1947    if (!auth_return || x_protocol_error)
1948    {
1949	prefix (inputfilename, lineno);
1950	fprintf (stderr, "couldn't generate authorization\n");
1951	return 1;
1952    }
1953
1954    if (verbose)
1955	printf("authorization id is %ld\n", id_return);
1956
1957    /* create a fake input line to give to do_add */
1958    hex = bintohex(auth_return->data_length, auth_return->data);
1959    args[0] = "add";
1960    args[1] = displayname;
1961    args[2] = auth_in->name;
1962    args[3] = hex;
1963
1964    status = do_add(inputfilename, lineno, 4, args);
1965
1966    if (authdata) free(authdata);
1967    XSecurityFreeXauth(auth_in);
1968    XSecurityFreeXauth(auth_return);
1969    free(hex);
1970    XCloseDisplay(dpy);
1971    return status;
1972}
1973