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