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