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