process.c revision 2852888e
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)(char *, int, int, 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    char *name;				/* full name */
74    int minlen;				/* unique prefix */
75    int maxlen;				/* strlen(name) */
76    ProcessFunc processfunc;		/* handler */
77    char *helptext;			/* what to print for help */
78} CommandTable;
79
80struct _extract_data {			/* for iterating */
81    FILE *fp;				/* input source */
82    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    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 char *stdin_filename = "(stdin)";  /* for messages */
99static char *stdout_filename = "(stdout)";  /* for messages */
100static char *Yes = "yes";		/* for messages */
101static char *No = "no";			/* for messages */
102
103static int do_help ( char *inputfilename, int lineno, int argc, char **argv );
104static int do_questionmark ( char *inputfilename, int lineno, int argc, char **argv );
105static int do_list ( char *inputfilename, int lineno, int argc, char **argv );
106static int do_merge ( char *inputfilename, int lineno, int argc, char **argv );
107static int do_extract ( char *inputfilename, int lineno, int argc, char **argv );
108static int do_add ( char *inputfilename, int lineno, int argc, char **argv );
109static int do_remove ( char *inputfilename, int lineno, int argc, char **argv );
110static int do_info ( char *inputfilename, int lineno, int argc, char **argv );
111static int do_exit ( char *inputfilename, int lineno, int argc, char **argv );
112static int do_quit ( char *inputfilename, int lineno, int argc, char **argv );
113static int do_source ( char *inputfilename, int lineno, int argc, char **argv );
114static int do_generate ( char *inputfilename, int lineno, int argc, 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 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(char *fn, int n)
208{
209    fprintf (stderr, "%s: %s:%d:  ", ProgramName, fn, n);
210}
211
212static void
213baddisplayname(char *dpy, char *cmd)
214{
215    fprintf (stderr, "bad display name \"%s\" in \"%s\" command\n",
216	     dpy, cmd);
217}
218
219static void
220badcommandline(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(char **filenamep,
290	  char *mode,
291	  Bool *usedstdp,
292	  char *srcfn,
293	  int srcln,
294	  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(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(char *hexstr, char **ptrp)	/* turn hex key string into octets */
545{
546    int i;
547    int len = 0;
548    char *retval, *s;
549    unsigned char *us;
550    char c;
551    char savec = '\0';
552
553    /* count */
554    for (s = hexstr; *s; s++) {
555	if (!isascii(*s)) return -1;
556	if (isspace(*s)) continue;
557	if (!isxdigit(*s)) return -1;
558	len++;
559    }
560
561    /* if 0 or odd, then there was an error */
562    if (len == 0 || (len & 1) == 1) return -1;
563
564
565    /* now we know that the input is good */
566    len >>= 1;
567    retval = malloc (len);
568    if (!retval) {
569	fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
570		 ProgramName, len);
571	return -1;
572    }
573
574    for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
575	c = *hexstr;
576	if (isspace(c)) continue;	 /* already know it is ascii */
577	if (isupper(c))
578	    c = tolower(c);
579	if (savec) {
580#define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
581	    *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
582#undef atoh
583	    savec = 0;		/* ready for next character */
584	    us++;
585	    i--;
586	} else {
587	    savec = c;
588	}
589    }
590    *ptrp = retval;
591    return len;
592}
593
594static int
595dispatch_command(char *inputfilename,
596		 int lineno,
597		 int argc,
598		 char **argv,
599		 CommandTable *tab,
600		 int *statusp)
601{
602    CommandTable *ct;
603    char *cmd;
604    int n;
605					/* scan table for command */
606    cmd = argv[0];
607    n = strlen (cmd);
608    for (ct = tab; ct->name; ct++) {
609					/* look for unique prefix */
610	if (n >= ct->minlen && n <= ct->maxlen &&
611	    strncmp (cmd, ct->name, n) == 0) {
612	    *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
613	    return 1;
614	}
615    }
616
617    *statusp = 1;
618    return 0;
619}
620
621
622static AuthList *xauth_head = NULL;	/* list of auth entries */
623static Bool xauth_existed = False;	/* if was present at initialize */
624static Bool xauth_modified = False;	/* if added, removed, or merged */
625static Bool xauth_allowed = True;	/* if allowed to write auth file */
626static Bool xauth_locked = False;     /* if has been locked */
627static char *xauth_filename = NULL;
628static volatile Bool dieing = False;
629
630
631/* poor man's puts(), for under signal handlers */
632#define WRITES(fd, S) (void)write((fd), (S), strlen((S)))
633
634/* ARGSUSED */
635static RETSIGTYPE
636die(int sig)
637{
638    dieing = True;
639    _exit (auth_finalize ());
640    /* NOTREACHED */
641#ifdef SIGNALRETURNSINT
642    return -1;				/* for picky compilers */
643#endif
644}
645
646static RETSIGTYPE
647catchsig(int sig)
648{
649#ifdef SYSV
650    if (sig > 0) signal (sig, die);	/* re-establish signal handler */
651#endif
652    /*
653     * fileno() might not be reentrant, avoid it if possible, and use
654     * stderr instead of stdout
655     */
656#ifdef STDERR_FILENO
657    if (verbose && xauth_modified) WRITES(STDERR_FILENO, "\r\n");
658#else
659    if (verbose && xauth_modified) WRITES(fileno(stderr), "\r\n");
660#endif
661    die (sig);
662    /* NOTREACHED */
663#ifdef SIGNALRETURNSINT
664    return -1;				/* for picky compilers */
665#endif
666}
667
668static void
669register_signals(void)
670{
671    signal (SIGINT, catchsig);
672    signal (SIGTERM, catchsig);
673#ifdef SIGHUP
674    signal (SIGHUP, catchsig);
675#endif
676#ifdef SIGPIPE
677    signal (SIGPIPE, catchsig);
678#endif
679    return;
680}
681
682
683/*
684 * public procedures for parsing lines of input
685 */
686
687int
688auth_initialize(char *authfilename)
689{
690    int n;
691    AuthList *head, *tail;
692    FILE *authfp;
693    Bool exists;
694
695    xauth_filename = authfilename;    /* used in cleanup, prevent race with
696                                         signals */
697    register_signals ();
698
699    bzero ((char *) hexvalues, sizeof hexvalues);
700    hexvalues['0'] = 0;
701    hexvalues['1'] = 1;
702    hexvalues['2'] = 2;
703    hexvalues['3'] = 3;
704    hexvalues['4'] = 4;
705    hexvalues['5'] = 5;
706    hexvalues['6'] = 6;
707    hexvalues['7'] = 7;
708    hexvalues['8'] = 8;
709    hexvalues['9'] = 9;
710    hexvalues['a'] = hexvalues['A'] = 0xa;
711    hexvalues['b'] = hexvalues['B'] = 0xb;
712    hexvalues['c'] = hexvalues['C'] = 0xc;
713    hexvalues['d'] = hexvalues['D'] = 0xd;
714    hexvalues['e'] = hexvalues['E'] = 0xe;
715    hexvalues['f'] = hexvalues['F'] = 0xf;
716
717    if (break_locks && verbose) {
718	printf ("Attempting to break locks on authority file %s\n",
719		authfilename);
720    }
721
722    if (ignore_locks) {
723	if (break_locks) XauUnlockAuth (authfilename);
724    } else {
725	n = XauLockAuth (authfilename, XAUTH_DEFAULT_RETRIES,
726			 XAUTH_DEFAULT_TIMEOUT,
727			 (break_locks ? 0L : XAUTH_DEFAULT_DEADTIME));
728	if (n != LOCK_SUCCESS) {
729	    char *reason = "unknown error";
730	    switch (n) {
731	      case LOCK_ERROR:
732		reason = "error";
733		break;
734	      case LOCK_TIMEOUT:
735		reason = "timeout";
736		break;
737	    }
738	    fprintf (stderr, "%s:  %s in locking authority file %s\n",
739		     ProgramName, reason, authfilename);
740	    return -1;
741	} else
742	    xauth_locked = True;
743    }
744
745    /* these checks can only be done reliably after the file is locked */
746    exists = (access (authfilename, F_OK) == 0);
747    if (exists && access (authfilename, W_OK) != 0) {
748	fprintf (stderr,
749	 "%s:  %s not writable, changes will be ignored\n",
750		 ProgramName, authfilename);
751	xauth_allowed = False;
752    }
753
754    original_umask = umask (0077);	/* disallow non-owner access */
755
756    authfp = fopen (authfilename, "rb");
757    if (!authfp) {
758	int olderrno = errno;
759
760					/* if file there then error */
761	if (access (authfilename, F_OK) == 0) {	 /* then file does exist! */
762	    errno = olderrno;
763	    return -1;
764	}				/* else ignore it */
765	fprintf (stderr,
766		 "%s:  file %s does not exist\n",
767		 ProgramName, authfilename);
768    } else {
769	xauth_existed = True;
770	n = read_auth_entries (authfp, False, &head, &tail);
771	(void) fclose (authfp);
772	if (n < 0) {
773	    fprintf (stderr,
774		     "%s:  unable to read auth entries from file \"%s\"\n",
775		     ProgramName, authfilename);
776	    return -1;
777	}
778	xauth_head = head;
779    }
780
781    n = strlen (authfilename);
782    xauth_filename = malloc (n + 1);
783    if (xauth_filename) strcpy (xauth_filename, authfilename);
784    else {
785	fprintf(stderr,"cannot allocate memory\n");
786	return -1;
787    }
788
789    xauth_modified = False;
790
791    if (verbose) {
792	printf ("%s authority file %s\n",
793		ignore_locks ? "Ignoring locks on" : "Using", authfilename);
794    }
795    return 0;
796}
797
798static int
799write_auth_file(char *tmp_nam)
800{
801    FILE *fp = NULL;
802    int fd;
803    AuthList *list;
804
805    /*
806     * xdm and auth spec assumes auth file is 12 or fewer characters
807     */
808    strcpy (tmp_nam, xauth_filename);
809    strcat (tmp_nam, "-n");		/* for new */
810    (void) unlink (tmp_nam);
811    /* CPhipps 2000/02/12 - fix file unlink/fopen race */
812    fd = open(tmp_nam, O_WRONLY | O_CREAT | O_EXCL, 0600);
813    if (fd != -1) fp = fdopen (fd, "wb");
814    if (!fp) {
815        if (fd != -1) close(fd);
816	fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
817		 ProgramName, tmp_nam);
818	return -1;
819    }
820
821    /*
822     * Write MIT-MAGIC-COOKIE-1 first, because R4 Xlib knows
823     * only that and uses the first authorization it finds.
824     */
825    for (list = xauth_head; list; list = list->next) {
826	if (list->auth->name_length == 18
827	    && strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) == 0) {
828	    if (!XauWriteAuth(fp, list->auth)) {
829		(void) fclose(fp);
830		return -1;
831	    }
832	}
833    }
834    for (list = xauth_head; list; list = list->next) {
835	if (list->auth->name_length != 18
836	    || strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) != 0) {
837	    if (!XauWriteAuth(fp, list->auth)) {
838		(void) fclose(fp);
839		return -1;
840	    }
841	}
842    }
843
844    (void) fclose (fp);
845    return 0;
846}
847
848int
849auth_finalize(void)
850{
851    char temp_name[1024];	/* large filename size */
852
853    if (xauth_modified) {
854	if (dieing) {
855	    if (verbose) {
856		/*
857		 * called from a signal handler -- printf is *not* reentrant; also
858		 * fileno() might not be reentrant, avoid it if possible, and use
859		 * stderr instead of stdout
860		 */
861#ifdef STDERR_FILENO
862		WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
863		WRITES(STDERR_FILENO, xauth_filename);
864		WRITES(STDERR_FILENO, "\n");
865#else
866		WRITES(fileno(stderr), "\nAborting changes to authority file ");
867		WRITES(fileno(stderr), xauth_filename);
868		WRITES(fileno(stderr), "\n");
869#endif
870	    }
871	} else if (!xauth_allowed) {
872	    fprintf (stderr,
873		     "%s:  %s not writable, changes ignored\n",
874		     ProgramName, xauth_filename);
875	} else {
876	    if (verbose) {
877		printf ("%s authority file %s\n",
878			ignore_locks ? "Ignoring locks and writing" :
879			"Writing", xauth_filename);
880	    }
881	    temp_name[0] = '\0';
882	    if (write_auth_file (temp_name) == -1) {
883		fprintf (stderr,
884			 "%s:  unable to write authority file %s\n",
885			 ProgramName, temp_name);
886	    } else {
887		(void) unlink (xauth_filename);
888#if defined(WIN32) || defined(__UNIXOS2__)
889		if (rename(temp_name, xauth_filename) == -1)
890#else
891		/* Attempt to rename() if link() fails, since this may be on a FS that does not support hard links */
892		if (link (temp_name, xauth_filename) == -1 && rename(temp_name, xauth_filename) == -1)
893#endif
894		{
895		    fprintf (stderr,
896		     "%s:  unable to link authority file %s, use %s\n",
897			     ProgramName, xauth_filename, temp_name);
898		} else {
899		    (void) unlink (temp_name);
900		}
901	    }
902	}
903    }
904
905    if (xauth_locked) {
906	XauUnlockAuth (xauth_filename);
907    }
908    (void) umask (original_umask);
909    return 0;
910}
911
912int
913process_command(char *inputfilename, int lineno, int argc, char **argv)
914{
915    int status;
916
917    if (argc < 1 || !argv || !argv[0]) return 1;
918
919    if (dispatch_command (inputfilename, lineno, argc, argv,
920			  command_table, &status))
921      return status;
922
923    prefix (inputfilename, lineno);
924    fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
925    return 1;
926}
927
928
929/*
930 * utility routines
931 */
932
933static char *
934bintohex(unsigned int len, char *bindata)
935{
936    char *hexdata, *starthex;
937
938    /* two chars per byte, plus null termination */
939    starthex = hexdata = (char *)malloc(2*len + 1);
940    if (!hexdata)
941	return NULL;
942
943    for (; len > 0; len--, bindata++) {
944	register char *s = hex_table[(unsigned char)*bindata];
945	*hexdata++ = s[0];
946	*hexdata++ = s[1];
947    }
948    *hexdata = '\0';
949    return starthex;
950}
951
952static void
953fprintfhex(register FILE *fp, int len, char *cp)
954{
955    char *hex;
956
957    hex = bintohex(len, cp);
958    fprintf(fp, "%s", hex);
959    free(hex);
960}
961
962static int
963dump_numeric(register FILE *fp, register Xauth *auth)
964{
965    fprintf (fp, "%04x", auth->family);  /* unsigned short */
966    fprintf (fp, " %04x ", auth->address_length);  /* short */
967    fprintfhex (fp, auth->address_length, auth->address);
968    fprintf (fp, " %04x ", auth->number_length);  /* short */
969    fprintfhex (fp, auth->number_length, auth->number);
970    fprintf (fp, " %04x ", auth->name_length);  /* short */
971    fprintfhex (fp, auth->name_length, auth->name);
972    fprintf (fp, " %04x ", auth->data_length);  /* short */
973    fprintfhex (fp, auth->data_length, auth->data);
974    putc ('\n', fp);
975    return 1;
976}
977
978/* ARGSUSED */
979static int
980dump_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
981{
982    struct _list_data *ld = (struct _list_data *) data;
983    FILE *fp = ld->fp;
984
985    if (ld->numeric) {
986	dump_numeric (fp, auth);
987    } else {
988	char *dpyname = NULL;
989
990	switch (auth->family) {
991	  case FamilyLocal:
992	    fwrite (auth->address, sizeof (char), auth->address_length, fp);
993	    fprintf (fp, "/unix");
994	    break;
995	  case FamilyInternet:
996#if defined(IPv6) && defined(AF_INET6)
997	  case FamilyInternet6:
998#endif
999	  case FamilyDECnet:
1000	    dpyname = get_hostname (auth);
1001	    if (dpyname) {
1002		fprintf (fp, "%s", dpyname);
1003		break;
1004	    }
1005	    /* else fall through to default */
1006	  default:
1007	    fprintf (fp, "#%04x#", auth->family);
1008	    fprintfhex (fp, auth->address_length, auth->address);
1009	    putc ('#', fp);
1010	}
1011	putc (':', fp);
1012	fwrite (auth->number, sizeof (char), auth->number_length, fp);
1013	putc (' ', fp);
1014	putc (' ', fp);
1015	fwrite (auth->name, sizeof (char), auth->name_length, fp);
1016	putc (' ', fp);
1017	putc (' ', fp);
1018	if (!strncmp(auth->name, SECURERPC, auth->name_length) ||
1019	    !strncmp(auth->name, K5AUTH, auth->name_length))
1020            fwrite (auth->data, sizeof (char), auth->data_length, fp);
1021	else
1022	    fprintfhex (fp, auth->data_length, auth->data);
1023	putc ('\n', fp);
1024    }
1025    return 0;
1026}
1027
1028static int
1029extract_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
1030{
1031    struct _extract_data *ed = (struct _extract_data *) data;
1032
1033    if (!ed->fp) {
1034	ed->fp = open_file (&ed->filename,
1035			    ed->numeric ? "w" : "wb",
1036			    &ed->used_stdout,
1037			    inputfilename, lineno, ed->cmd);
1038	if (!ed->fp) {
1039	    prefix (inputfilename, lineno);
1040	    fprintf (stderr,
1041		     "unable to open extraction file \"%s\"\n",
1042		     ed->filename);
1043	    return -1;
1044	}
1045    }
1046    (*(ed->numeric ? dump_numeric : XauWriteAuth)) (ed->fp, auth);
1047    ed->nwritten++;
1048
1049    return 0;
1050}
1051
1052
1053static int
1054eq_auth(Xauth *a, Xauth *b)
1055{
1056    return((a->family == b->family &&
1057	    a->address_length == b->address_length &&
1058	    a->number_length == b->number_length &&
1059	    a->name_length == b->name_length &&
1060	    a->data_length == b->data_length &&
1061	    memcmp(a->address, b->address, a->address_length) == 0 &&
1062	    memcmp(a->number, b->number, a->number_length) == 0 &&
1063	    memcmp(a->name, b->name, a->name_length) == 0 &&
1064	    memcmp(a->data, b->data, a->data_length) == 0) ? 1 : 0);
1065}
1066
1067static int
1068match_auth_dpy(register Xauth *a, register Xauth *b)
1069{
1070    return ((a->family == b->family &&
1071	     a->address_length == b->address_length &&
1072	     a->number_length == b->number_length &&
1073	     memcmp(a->address, b->address, a->address_length) == 0 &&
1074	     memcmp(a->number, b->number, a->number_length) == 0) ? 1 : 0);
1075}
1076
1077/* return non-zero iff display and authorization type are the same */
1078
1079static int
1080match_auth(register Xauth *a, register Xauth *b)
1081{
1082    return ((match_auth_dpy(a, b)
1083	     && a->name_length == b->name_length
1084	     && memcmp(a->name, b->name, a->name_length) == 0) ? 1 : 0);
1085}
1086
1087
1088static int
1089merge_entries(AuthList **firstp, AuthList *second, int *nnewp, int *nreplp)
1090{
1091    AuthList *a, *b, *first, *tail;
1092    int n = 0, nnew = 0, nrepl = 0;
1093
1094    if (!second) return 0;
1095
1096    if (!*firstp) {			/* if nothing to merge into */
1097	*firstp = second;
1098	for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
1099	*nnewp = n;
1100	*nreplp = 0;
1101	return n;
1102    }
1103
1104    first = *firstp;
1105    /*
1106     * find end of first list and stick second list on it
1107     */
1108    for (tail = first; tail->next; tail = tail->next) ;
1109    tail->next = second;
1110
1111    /*
1112     * run down list freeing duplicate entries; if an entry is okay, then
1113     * bump the tail up to include it, otherwise, cut the entry out of
1114     * the chain.
1115     */
1116    for (b = second; b; ) {
1117	AuthList *next = b->next;	/* in case we free it */
1118
1119	a = first;
1120	for (;;) {
1121	    if (match_auth (a->auth, b->auth)) {  /* found a duplicate */
1122		AuthList tmp;		/* swap it in for old one */
1123		tmp = *a;
1124		*a = *b;
1125		*b = tmp;
1126		a->next = b->next;
1127		XauDisposeAuth (b->auth);
1128		free ((char *) b);
1129		b = NULL;
1130		tail->next = next;
1131		nrepl++;
1132		nnew--;
1133		break;
1134	    }
1135	    if (a == tail) break;	/* if have looked at left side */
1136	    a = a->next;
1137	}
1138	if (b) {			/* if we didn't remove it */
1139	    tail = b;			/* bump end of first list */
1140	}
1141	b = next;
1142	n++;
1143	nnew++;
1144    }
1145
1146    *nnewp = nnew;
1147    *nreplp = nrepl;
1148    return n;
1149
1150}
1151
1152static Xauth *
1153copyAuth(Xauth *auth)
1154{
1155    Xauth *a;
1156
1157    a = (Xauth *)malloc(sizeof(Xauth));
1158    if (a == NULL) {
1159	return NULL;
1160    }
1161    memset(a, 0, sizeof(Xauth));
1162    a->family = auth->family;
1163    if (auth->address_length != 0) {
1164	a->address = malloc(auth->address_length);
1165	if (a->address == NULL) {
1166	    free(a);
1167	    return NULL;
1168	}
1169	memcpy(a->address, auth->address, auth->address_length);
1170	a->address_length = auth->address_length;
1171    }
1172    if (auth->number_length != 0) {
1173	a->number = malloc(auth->number_length);
1174	if (a->number == NULL) {
1175	    free(a->address);
1176	    free(a);
1177	    return NULL;
1178	}
1179	memcpy(a->number, auth->number, auth->number_length);
1180	a->number_length = auth->number_length;
1181    }
1182    if (auth->name_length != 0) {
1183	a->name = malloc(auth->name_length);
1184	if (a->name == NULL) {
1185	    free(a->address);
1186	    free(a->number);
1187	    free(a);
1188	    return NULL;
1189	}
1190	memcpy(a->name, auth->name, auth->name_length);
1191	a->name_length = auth->name_length;
1192    }
1193    if (auth->data_length != 0) {
1194	a->data = malloc(auth->data_length);
1195	if (a->data == NULL) {
1196	    free(a->address);
1197	    free(a->number);
1198	    free(a->name);
1199	    free(a);
1200	    return NULL;
1201	}
1202	memcpy(a->data, auth->data, auth->data_length);
1203	a->data_length = auth->data_length;
1204    }
1205    return a;
1206}
1207
1208typedef int (*YesNoFunc)(char *, int, Xauth *, char *);
1209
1210static int
1211iterdpy (char *inputfilename, int lineno, int start,
1212	 int argc, char *argv[],
1213	 YesNoFunc yfunc, YesNoFunc nfunc, char *data)
1214{
1215    int i;
1216    int status;
1217    int errors = 0;
1218    Xauth *tmp_auth;
1219    AuthList *proto_head, *proto;
1220    AuthList *l, *next;
1221
1222    /*
1223     * iterate
1224     */
1225    for (i = start; i < argc; i++) {
1226	char *displayname = argv[i];
1227	if (!get_displayname_auth (displayname, &proto_head)) {
1228	    prefix (inputfilename, lineno);
1229	    baddisplayname (displayname, argv[0]);
1230	    errors++;
1231	    continue;
1232	}
1233	status = 0;
1234	for (l = xauth_head; l; l = next) {
1235	    Bool matched = False;
1236
1237	    /* l may be freed by remove_entry below. so save its contents */
1238	    next = l->next;
1239	    tmp_auth = copyAuth(l->auth);
1240	    for (proto = proto_head; proto; proto = proto->next) {
1241		if (match_auth_dpy (proto->auth, tmp_auth)) {
1242		    matched = True;
1243		    if (yfunc) {
1244			status = (*yfunc) (inputfilename, lineno,
1245					   tmp_auth, data);
1246			if (status < 0) break;
1247		    }
1248		}
1249	    }
1250	    XauDisposeAuth(tmp_auth);
1251	    if (matched == False) {
1252		if (nfunc) {
1253		    status = (*nfunc) (inputfilename, lineno,
1254				       l->auth, data);
1255		}
1256	    }
1257	    if (status < 0) break;
1258	}
1259	for (proto = proto_head; proto ; proto = next) {
1260	    next = proto->next;
1261	    if (proto->auth->address) free (proto->auth->address);
1262	    if (proto->auth->number) free (proto->auth->number);
1263	    free (proto->auth);
1264	    free (proto);
1265	}
1266	if (status < 0) {
1267	    errors -= status;		/* since status is negative */
1268	    break;
1269	}
1270    }
1271
1272    return errors;
1273}
1274
1275/* ARGSUSED */
1276static int
1277remove_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
1278{
1279    int *nremovedp = (int *) data;
1280    AuthList **listp = &xauth_head;
1281    AuthList *list;
1282
1283    /*
1284     * unlink the auth we were asked to
1285     */
1286    while (!eq_auth((list = *listp)->auth, auth))
1287	listp = &list->next;
1288    *listp = list->next;
1289    XauDisposeAuth (list->auth);                    /* free the auth */
1290    free (list);				    /* free the link */
1291    xauth_modified = True;
1292    (*nremovedp)++;
1293    return 1;
1294}
1295
1296/*
1297 * action routines
1298 */
1299
1300/*
1301 * help
1302 */
1303int
1304print_help(FILE *fp, char *cmd, char *prefix)
1305{
1306    CommandTable *ct;
1307    int n = 0;
1308
1309    if (!prefix) prefix = "";
1310
1311    if (!cmd) {				/* if no cmd, print all help */
1312	for (ct = command_table; ct->name; ct++) {
1313	    fprintf (fp, "%s%s\n", prefix, ct->helptext);
1314	    n++;
1315	}
1316    } else {
1317	int len = strlen (cmd);
1318	for (ct = command_table; ct->name; ct++) {
1319	    if (strncmp (cmd, ct->name, len) == 0) {
1320		fprintf (fp, "%s%s\n", prefix, ct->helptext);
1321		n++;
1322	    }
1323	}
1324    }
1325
1326    return n;
1327}
1328
1329static int
1330do_help(char *inputfilename, int lineno, int argc, char **argv)
1331{
1332    char *cmd = (argc > 1 ? argv[1] : NULL);
1333    int n;
1334
1335    n = print_help (stdout, cmd, "    ");  /* a nice amount */
1336
1337    if (n < 0 || (n == 0 && !cmd)) {
1338	prefix (inputfilename, lineno);
1339	fprintf (stderr, "internal error with help");
1340	if (cmd) {
1341	    fprintf (stderr, " on command \"%s\"", cmd);
1342	}
1343	fprintf (stderr, "\n");
1344	return 1;
1345    }
1346
1347    if (n == 0) {
1348	prefix (inputfilename, lineno);
1349	/* already know that cmd is set in this case */
1350	fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1351    }
1352
1353    return 0;
1354}
1355
1356/*
1357 * questionmark
1358 */
1359/* ARGSUSED */
1360static int
1361do_questionmark(char *inputfilename, int lineno, int argc, char **argv)
1362{
1363    CommandTable *ct;
1364    int i;
1365#define WIDEST_COLUMN 72
1366    int col = WIDEST_COLUMN;
1367
1368    printf ("Commands:\n");
1369    for (ct = command_table; ct->name; ct++) {
1370	if ((col + ct->maxlen) > WIDEST_COLUMN) {
1371	    if (ct != command_table) {
1372		putc ('\n', stdout);
1373	    }
1374	    fputs ("        ", stdout);
1375	    col = 8;			/* length of string above */
1376	}
1377	fputs (ct->name, stdout);
1378	col += ct->maxlen;
1379	for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1380	    putc (' ', stdout);
1381	    col++;
1382	}
1383    }
1384    if (col != 0) {
1385	putc ('\n', stdout);
1386    }
1387
1388    /* allow bad lines since this is help */
1389    return 0;
1390}
1391
1392/*
1393 * list [displayname ...]
1394 */
1395static int
1396do_list (char *inputfilename, int lineno, int argc, char **argv)
1397{
1398    struct _list_data ld;
1399
1400    ld.fp = stdout;
1401    ld.numeric = (argv[0][0] == 'n');
1402
1403    if (argc == 1) {
1404	register AuthList *l;
1405
1406	if (xauth_head) {
1407	    for (l = xauth_head; l; l = l->next) {
1408		dump_entry (inputfilename, lineno, l->auth, (char *) &ld);
1409	    }
1410	}
1411	return 0;
1412    }
1413
1414    return iterdpy (inputfilename, lineno, 1, argc, argv,
1415		    dump_entry, NULL, (char *) &ld);
1416}
1417
1418/*
1419 * merge filename [filename ...]
1420 */
1421static int
1422do_merge(char *inputfilename, int lineno, int argc, char **argv)
1423{
1424    int i;
1425    int errors = 0;
1426    AuthList *head, *tail, *listhead, *listtail;
1427    int nentries, nnew, nrepl;
1428    Bool numeric = False;
1429
1430    if (argc < 2) {
1431	prefix (inputfilename, lineno);
1432	badcommandline (argv[0]);
1433	return 1;
1434    }
1435
1436    if (argv[0][0] == 'n') numeric = True;
1437    listhead = listtail = NULL;
1438
1439    for (i = 1; i < argc; i++) {
1440	char *filename = argv[i];
1441	FILE *fp;
1442	Bool used_stdin = False;
1443
1444	fp = open_file (&filename,
1445			numeric ? "r" : "rb",
1446			&used_stdin, inputfilename, lineno,
1447			argv[0]);
1448	if (!fp) {
1449	    errors++;
1450	    continue;
1451	}
1452
1453	head = tail = NULL;
1454	nentries = read_auth_entries (fp, numeric, &head, &tail);
1455	if (nentries == 0) {
1456	    prefix (inputfilename, lineno);
1457	    fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1458		     filename);
1459	    errors++;
1460	} else {			/* link it in */
1461	    add_to_list (listhead, listtail, head);
1462 	}
1463
1464	if (!used_stdin) (void) fclose (fp);
1465    }
1466
1467    /*
1468     * if we have new entries, merge them in (freeing any duplicates)
1469     */
1470    if (listhead) {
1471	nentries = merge_entries (&xauth_head, listhead, &nnew, &nrepl);
1472	if (verbose)
1473	  printf ("%d entries read in:  %d new, %d replacement%s\n",
1474	  	  nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1475	if (nentries > 0) xauth_modified = True;
1476    }
1477
1478    return 0;
1479}
1480
1481/*
1482 * extract filename displayname [displayname ...]
1483 */
1484static int
1485do_extract(char *inputfilename, int lineno, int argc, char **argv)
1486{
1487    int errors;
1488    struct _extract_data ed;
1489
1490    if (argc < 3) {
1491	prefix (inputfilename, lineno);
1492	badcommandline (argv[0]);
1493	return 1;
1494    }
1495
1496    ed.fp = NULL;
1497    ed.filename = argv[1];
1498    ed.used_stdout = False;
1499    ed.numeric = (argv[0][0] == 'n');
1500    ed.nwritten = 0;
1501    ed.cmd = argv[0];
1502
1503    errors = iterdpy (inputfilename, lineno, 2, argc, argv,
1504		      extract_entry, NULL, (char *) &ed);
1505
1506    if (!ed.fp) {
1507	fprintf (stderr,
1508		 "No matches found, authority file \"%s\" not written\n",
1509		 ed.filename);
1510    } else {
1511	if (verbose) {
1512	    printf ("%d entries written to \"%s\"\n",
1513		    ed.nwritten, ed.filename);
1514	}
1515	if (!ed.used_stdout) {
1516	    (void) fclose (ed.fp);
1517	}
1518    }
1519
1520    return errors;
1521}
1522
1523
1524/*
1525 * add displayname protocolname hexkey
1526 */
1527static int
1528do_add(char *inputfilename, int lineno, int argc, char **argv)
1529{
1530    int n, nnew, nrepl;
1531    int len;
1532    char *dpyname;
1533    char *protoname;
1534    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(char *inputfilename, int lineno, int argc, 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(char *inputfilename, int lineno, int argc, 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(char *inputfilename, int lineno, int argc, 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(char *inputfilename, int lineno, int argc, 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(char *inputfilename, int lineno, int argc, char **argv)
1711{
1712    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    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 = split_into_words (buf, &subargc);
1756	if (subargv) {
1757	    status = process_command (script, sublineno, subargc, subargv);
1758	    free ((char *) 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(char *inputfilename, int lineno, int argc, char **argv)
1789{
1790    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    char *args[4];
1799    char *protoname = ".";
1800    int i;
1801    int authdatalen = 0;
1802    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(args[3]); /* hex data */
1931    XCloseDisplay(dpy);
1932    return status;
1933}
1934