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