Increment CDE version number to 2.2.0
[oweals/cde.git] / cde / lib / tt / bin / ttauth / process.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * $TOG: process.c /main/1 1999/08/30 10:45:37 mgreess $
25  *
26  * 
27 Copyright 1989, 1998  The Open Group
28
29 All Rights Reserved.
30
31 The above copyright notice and this permission notice shall be included in
32 all copies or substantial portions of the Software.
33
34 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
37 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
38 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40
41 Except as contained in this notice, the name of The Open Group shall not be
42 used in advertising or otherwise to promote the sale, use or other dealings
43 in this Software without prior written authorization from The Open Group.
44  * *
45  * Original Author of "xauth" : Jim Fulton, MIT X Consortium
46  * Modified into "ttauth"    : Ralph Mor, X Consortium
47  * Modified into "ttauth"     : Mitch Greess, Solutions Atlantic
48  */
49
50 #include <ctype.h>
51 #include <errno.h>
52 #ifdef X_NOT_STDC_ENV
53 extern int errno;
54 #endif
55 #include "ttauth.h"
56 #include "api/c/api_auth.h"
57 #include "api/c/tt_c.h"
58
59 #include <signal.h>
60
61 #define SECURERPC "SUN-DES-1"
62 #define K5AUTH "KERBEROS-V5-1"
63
64 #define TTAUTH_DEFAULT_RETRIES 10       /* number of competitors we expect */
65 #define TTAUTH_DEFAULT_TIMEOUT 2        /* in seconds, be quick */
66 #define TTAUTH_DEFAULT_DEADTIME 600L    /* 10 minutes in seconds */
67
68 #define add_to_list(h,t,e) {if (t) (t)->next = (e); else (h) = (e); (t) = (e);}
69
70 typedef struct _CommandTable {          /* commands that are understood */
71     char *name;                         /* full name */
72     int minlen;                         /* unique prefix */
73     int maxlen;                         /* strlen(name) */
74     int (*processfunc)();               /* handler */
75     char *helptext;                     /* what to print for help */
76 } CommandTable;
77
78 struct _extract_data {                  /* for iterating */
79     FILE *fp;                           /* input source */
80     char *filename;                     /* name of input */
81     Bool used_stdout;                   /* whether or not need to close */
82     int nwritten;                       /* number of entries written */
83     char *cmd;                          /* for error messages */
84 };
85
86 struct _list_data {                     /* for iterating */
87     FILE *fp;                           /* output file */
88 };
89
90
91 /*
92  * private data
93  */
94 static char *stdin_filename = "(stdin)";  /* for messages */
95 static char *stdout_filename = "(stdout)";  /* for messages */
96 static char *Yes = "yes";               /* for messages */
97 static char *No = "no";                 /* for messages */
98
99 static int do_list(), do_merge(), do_extract(), do_add(), do_remove();
100 static int do_help(), do_source(), do_info(), do_exit();
101 static int do_quit(), do_questionmark();
102
103 static CommandTable command_table[] = { /* table of known commands */
104 { "add", 2, 3, do_add,
105 "\
106 add       add an entry\n\
107           add protoname protodata netid authname authdata"
108 },
109
110 { "exit", 3, 4, do_exit,
111 "\
112 exit      save changes and exit program"
113 },
114
115 { "extract", 3, 7, do_extract,
116 "\
117 extract   extract entries into file\n\
118           extract filename <protoname=$> <protodata=$> <netid=$> <authname=$>"
119 },
120
121 { "help", 1, 4, do_help,
122 "\
123 help      print help\n\
124           help <topic>"
125 },
126
127 { "info", 1, 4, do_info,
128 "\
129 info      print information about entries"
130 },
131
132 { "list", 1, 4, do_list,
133 "\
134 list      list entries\n\
135           list <protoname=$> <protodata=$> <netid=$> <authname=$>"
136 },
137
138 { "merge", 1, 5, do_merge,
139 "\
140 merge     merge entries from files\n\
141           merge filename1 <filename2> <filename3> ..."
142 },
143
144 { "quit", 1, 4, do_quit,
145 "\
146 quit      abort changes and exit program" },
147
148 { "remove", 1, 6, do_remove,
149 "\
150 remove    remove entries\n\
151           remove <protoname=$> <protodata=$> <netid=$> <authname=$>"
152 },
153
154 { "source", 1, 6, do_source,
155 "\
156 source    read commands from file\n\
157           source filename"
158 },
159
160 { "?", 1, 1, do_questionmark,
161 "\
162 ?         list available commands" },
163
164 { NULL, 0, 0, NULL, NULL },
165 };
166
167 #define COMMAND_NAMES_PADDED_WIDTH 10   /* wider than anything above */
168
169
170 static Bool okay_to_use_stdin = True;   /* set to false after using */
171
172 static char *hex_table[] = {            /* for printing hex digits */
173     "00", "01", "02", "03", "04", "05", "06", "07", 
174     "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
175     "10", "11", "12", "13", "14", "15", "16", "17", 
176     "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
177     "20", "21", "22", "23", "24", "25", "26", "27", 
178     "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
179     "30", "31", "32", "33", "34", "35", "36", "37", 
180     "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
181     "40", "41", "42", "43", "44", "45", "46", "47", 
182     "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
183     "50", "51", "52", "53", "54", "55", "56", "57", 
184     "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
185     "60", "61", "62", "63", "64", "65", "66", "67", 
186     "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
187     "70", "71", "72", "73", "74", "75", "76", "77", 
188     "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
189     "80", "81", "82", "83", "84", "85", "86", "87", 
190     "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
191     "90", "91", "92", "93", "94", "95", "96", "97", 
192     "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
193     "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", 
194     "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
195     "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", 
196     "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
197     "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", 
198     "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
199     "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 
200     "d8", "d9", "da", "db", "dc", "dd", "de", "df", 
201     "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", 
202     "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
203     "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", 
204     "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 
205 };
206
207 static unsigned int hexvalues[256];     /* for parsing hex input */
208
209 static int original_umask = 0;          /* for restoring */
210
211
212 /*
213  * private utility procedures
214  */
215
216 static char *copystring (src)
217     char *src;
218 {
219     int len = strlen (src);
220     char *cp;
221
222     if (!src) return NULL;
223     cp = malloc (len + 1);
224     if (cp)
225         strcpy (cp, src);
226     return cp;
227 }
228
229 static int
230 binaryEqual (a, b, len)
231
232 register char           *a, *b;
233 register unsigned       len;
234
235 {
236     while (len--)
237         if (*a++ != *b++)
238             return 0;
239     return 1;
240 }
241
242 static void prefix (fn, n)
243     char *fn;
244     int n;
245 {
246     fprintf (stderr, "%s: %s:%d:  ", ProgramName, fn, n);
247 }
248
249 static void badcommandline (cmd)
250     char *cmd;
251 {
252     fprintf (stderr, "bad \"%s\" command line\n", cmd);
253 }
254
255 static char *skip_space (s)
256     register char *s;
257 {
258     if (!s) return NULL;
259
260     for ( ; *s && isascii(*s) && isspace(*s); s++)
261         ;
262     return s;
263 }
264
265
266 static char *skip_nonspace (s)
267     register char *s;
268 {
269     if (!s) return NULL;
270
271     /* put quoting into loop if need be */
272     for ( ; *s && isascii(*s) && !isspace(*s); s++)
273         ;
274     return s;
275 }
276
277 static char **split_into_words (src, argcp)  /* argvify string */
278     char *src;
279     int *argcp;
280 {
281     char *jword;
282     char savec;
283     char **argv;
284     int cur, total;
285
286     *argcp = 0;
287 #define WORDSTOALLOC 4                  /* most lines are short */
288     argv = (char **) malloc (WORDSTOALLOC * sizeof (char *));
289     if (!argv) return NULL;
290     cur = 0;
291     total = WORDSTOALLOC;
292
293     /*
294      * split the line up into separate, nul-terminated tokens; the last
295      * "token" will point to the empty string so that it can be bashed into
296      * a null pointer.
297      */
298
299     do {
300         jword = skip_space (src);
301         src = skip_nonspace (jword);
302         savec = *src;
303         *src = '\0';
304         if (cur == total) {
305             total += WORDSTOALLOC;
306             argv = (char **) realloc (argv, total * sizeof (char *));
307             if (!argv) return NULL;
308         }
309         argv[cur++] = jword;
310         if (savec) src++;               /* if not last on line advance */
311     } while (jword != src);
312
313     argv[--cur] = NULL;                 /* smash empty token to end list */
314     *argcp = cur;
315     return argv;
316 }
317
318
319 static FILE *open_file (filenamep, mode, usedstdp, srcfn, srcln, cmd)
320     char **filenamep;
321     char *mode;
322     Bool *usedstdp;
323     char *srcfn;
324     int srcln;
325     char *cmd;
326 {
327     FILE *fp;
328
329     if (strcmp (*filenamep, "-") == 0) {
330         *usedstdp = True;
331                                         /* select std descriptor to use */
332         if (mode[0] == 'r') {
333             if (okay_to_use_stdin) {
334                 okay_to_use_stdin = False;
335                 *filenamep = stdin_filename;
336                 return stdin;
337             } else {
338                 prefix (srcfn, srcln);
339                 fprintf (stderr, "%s:  stdin already in use\n", cmd);
340                 return NULL;
341             }
342         } else {
343             *filenamep = stdout_filename;
344             return stdout;              /* always okay to use stdout */
345         }
346     }
347
348     fp = fopen (*filenamep, mode);
349     if (!fp) {
350         prefix (srcfn, srcln);
351         fprintf (stderr, "%s:  unable to open file %s\n", cmd, *filenamep);
352     }
353     return fp;
354 }
355
356
357 static int read_auth_entries (fp, headp, tailp)
358     FILE *fp;
359     _tt_AuthFileEntryList **headp, **tailp;
360 {
361     _tt_AuthFileEntry *entry;
362     _tt_AuthFileEntryList *head, *tail;
363     int n;
364
365     head = tail = NULL;
366     n = 0;
367                                         /* put all records into linked list */
368     while ((entry = tt_ReadAuthFileEntry (fp)) != NULL) {
369         _tt_AuthFileEntryList *l =
370           (_tt_AuthFileEntryList *) malloc (sizeof (_tt_AuthFileEntryList));
371         if (!l) {
372             fprintf (stderr,
373                      "%s:  unable to alloc entry reading auth file\n",
374                      ProgramName);
375             exit (1);
376         }
377         l->next = NULL;
378         l->entry = entry;
379         if (tail)                       /* if not first time through append */
380           tail->next = l;
381         else
382           head = l;                     /* first time through, so assign */
383         tail = l;
384         n++;
385     }
386     *headp = head;
387     *tailp = tail;
388     return n;
389 }
390
391
392 static int cvthexkey (hexstr, ptrp)     /* turn hex key string into octets */
393     char *hexstr;
394     char **ptrp;
395 {
396     int i;
397     int len = 0;
398     char *retval, *s;
399     unsigned char *us;
400     char c;
401     char savec = '\0';
402
403     /* count */
404     for (s = hexstr; *s; s++) {
405         if (!isascii(*s)) return -1;
406         if (isspace(*s)) continue;
407         if (!isxdigit(*s)) return -1;
408         len++;
409     }
410
411     /* if odd then there was an error */
412     if ((len & 1) == 1) return -1;
413
414
415     /* now we know that the input is good */
416     len >>= 1;
417     retval = malloc (len);
418     if (!retval) {
419         fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
420                  ProgramName, len);
421         return -1;
422     }
423
424     for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
425         c = *hexstr;
426         if (isspace(c)) continue;        /* already know it is ascii */
427         if (isupper(c))
428             c = tolower(c);
429         if (savec) {
430 #define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
431             *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
432 #undef atoh
433             savec = 0;          /* ready for next character */
434             us++;
435             i--;
436         } else {
437             savec = c;
438         }
439     }
440     *ptrp = retval;
441     return len;
442 }
443
444 static int dispatch_command (inputfilename, lineno, argc, argv, tab, statusp)
445     char *inputfilename;
446     int lineno;
447     int argc;
448     char **argv;
449     CommandTable *tab;
450     int *statusp;
451 {
452     CommandTable *ct;
453     char *cmd;
454     int n;
455                                         /* scan table for command */
456     cmd = argv[0];
457     n = strlen (cmd);
458     for (ct = tab; ct->name; ct++) {
459                                         /* look for unique prefix */
460         if (n >= ct->minlen && n <= ct->maxlen &&
461             strncmp (cmd, ct->name, n) == 0) {
462             *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
463             return 1;
464         }
465     }
466
467     *statusp = 1;
468     return 0;
469 }
470
471
472 static _tt_AuthFileEntryList *ttauth_head = NULL; /* list of auth entries */
473 static Bool ttauth_existed = False;     /* if was present at initialize */
474 static Bool ttauth_modified = False;    /* if added, removed, or merged */
475 static Bool ttauth_allowed = True;      /* if allowed to write auth file */
476 static char *ttauth_filename = NULL;
477 static Bool dieing = False;
478
479 #ifdef SIGNALRETURNSINT
480 #define _signal_t int
481 #else
482 #define _signal_t void
483 #endif
484
485 /* ARGSUSED */
486 static _signal_t die (sig)
487     int sig;
488 {
489     dieing = True;
490     exit (auth_finalize ());
491     /* NOTREACHED */
492 #ifdef SIGNALRETURNSINT
493     return -1;                          /* for picky compilers */
494 #endif
495 }
496
497 static _signal_t catchsig (sig)
498     int sig;
499 {
500 #ifdef SYSV
501     if (sig > 0) signal (sig, die);     /* re-establish signal handler */
502 #endif
503     if (verbose && ttauth_modified) printf ("\r\n");
504     die (sig);
505     /* NOTREACHED */
506 #ifdef SIGNALRETURNSINT
507     return -1;                          /* for picky compilers */
508 #endif
509 }
510
511 static void register_signals ()
512 {
513     signal (SIGINT, catchsig);
514     signal (SIGTERM, catchsig);
515 #ifdef SIGHUP
516     signal (SIGHUP, catchsig);
517 #endif
518     return;
519 }
520
521
522 /*
523  * public procedures for parsing lines of input
524  */
525
526 int auth_initialize (authfilename)
527     char *authfilename;
528 {
529     int n;
530     _tt_AuthFileEntryList *head, *tail;
531     FILE *authfp;
532     Bool exists;
533
534     register_signals ();
535
536     bzero ((char *) hexvalues, sizeof hexvalues);
537     hexvalues['0'] = 0;
538     hexvalues['1'] = 1;
539     hexvalues['2'] = 2;
540     hexvalues['3'] = 3;
541     hexvalues['4'] = 4;
542     hexvalues['5'] = 5;
543     hexvalues['6'] = 6;
544     hexvalues['7'] = 7;
545     hexvalues['8'] = 8;
546     hexvalues['9'] = 9;
547     hexvalues['a'] = hexvalues['A'] = 0xa;
548     hexvalues['b'] = hexvalues['B'] = 0xb;
549     hexvalues['c'] = hexvalues['C'] = 0xc;
550     hexvalues['d'] = hexvalues['D'] = 0xd;
551     hexvalues['e'] = hexvalues['E'] = 0xe;
552     hexvalues['f'] = hexvalues['F'] = 0xf;
553
554     if (break_locks && verbose) {
555         printf ("Attempting to break locks on authority file %s\n",
556                 authfilename);
557     }
558
559     if (ignore_locks) {
560         if (break_locks) tt_UnlockAuthFile (authfilename);
561     } else {
562         n = tt_LockAuthFile (authfilename, TTAUTH_DEFAULT_RETRIES,
563                          TTAUTH_DEFAULT_TIMEOUT, 
564                          (break_locks ? 0L : TTAUTH_DEFAULT_DEADTIME));
565         if (n != _tt_AuthLockSuccess) {
566             char *reason = "unknown error";
567             switch (n) {
568               case _tt_AuthLockError:
569                 reason = "error";
570                 break;
571               case _tt_AuthLockTimeout:
572                 reason = "timeout";
573                 break;
574             }
575             fprintf (stderr, "%s:  %s in locking authority file %s\n",
576                      ProgramName, reason, authfilename);
577             return -1;
578         }
579     }
580
581     /* these checks can only be done reliably after the file is locked */
582     exists = (access (authfilename, F_OK) == 0);
583     if (exists && access (authfilename, W_OK) != 0) {
584         fprintf (stderr,
585          "%s:  %s not writable, changes will be ignored\n",
586                  ProgramName, authfilename);
587         ttauth_allowed = False;
588     }
589
590     original_umask = umask (0077);      /* disallow non-owner access */
591
592     authfp = fopen (authfilename, "rb");
593     if (!authfp) {
594         int olderrno = errno;
595
596                                         /* if file there then error */
597         if (access (authfilename, F_OK) == 0) {  /* then file does exist! */
598             errno = olderrno;
599             return -1;
600         }                               /* else ignore it */
601         fprintf (stderr, 
602                  "%s:  creating new authority file %s\n",
603                  ProgramName, authfilename);
604     } else {
605         ttauth_existed = True;
606         n = read_auth_entries (authfp, &head, &tail);
607         (void) fclose (authfp);
608         if (n < 0) {
609             fprintf (stderr,
610                      "%s:  unable to read auth entries from file \"%s\"\n",
611                      ProgramName, authfilename);
612             return -1;
613         }
614         ttauth_head = head;
615     }
616
617     n = strlen (authfilename);
618     ttauth_filename = malloc (n + 1);
619     if (ttauth_filename) strcpy (ttauth_filename, authfilename);
620     ttauth_modified = False;
621
622     if (verbose) {
623         printf ("%s authority file %s\n", 
624                 ignore_locks ? "Ignoring locks on" : "Using", authfilename);
625     }
626     return 0;
627 }
628
629 static int write_auth_file (tmp_nam)
630     char *tmp_nam;
631 {
632     FILE *fp;
633     _tt_AuthFileEntryList *list;
634
635     strcpy (tmp_nam, ttauth_filename);
636     strcat (tmp_nam, "-n");             /* for new */
637     (void) unlink (tmp_nam);
638     fp = fopen (tmp_nam, "wb");         /* umask is still set to 0077 */
639     if (!fp) {
640         fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
641                  ProgramName, tmp_nam);
642         return -1;
643     } 
644
645     for (list = ttauth_head; list; list = list->next)
646         tt_WriteAuthFileEntry (fp, list->entry);
647
648     (void) fclose (fp);
649     return 0;
650 }
651
652 int auth_finalize ()
653 {
654     char temp_name[1024];                       /* large filename size */
655
656     if (ttauth_modified) {
657         if (dieing) {
658             if (verbose) {
659                 printf ("Aborting changes to authority file %s\n",
660                         ttauth_filename);
661             }
662         } else if (!ttauth_allowed) {
663             fprintf (stderr, 
664                      "%s:  %s not writable, changes ignored\n",
665                      ProgramName, ttauth_filename);
666         } else {
667             if (verbose) {
668                 printf ("%s authority file %s\n", 
669                         ignore_locks ? "Ignoring locks and writing" :
670                         "Writing", ttauth_filename);
671             }
672             temp_name[0] = '\0';
673             if (write_auth_file (temp_name) == -1) {
674                 fprintf (stderr,
675                          "%s:  unable to write authority file %s\n",
676                          ProgramName, temp_name);
677             } else {
678                 (void) unlink (ttauth_filename);
679 #ifdef WIN32
680                 if (rename(temp_name, ttauth_filename) == -1)
681 #else
682                 if (link (temp_name, ttauth_filename) == -1)
683 #endif
684                 {
685                     fprintf (stderr,
686                      "%s:  unable to link authority file %s, use %s\n",
687                              ProgramName, ttauth_filename, temp_name);
688                 } else {
689                     (void) unlink (temp_name);
690                 }
691             }
692         }
693     }
694
695     if (!ignore_locks) {
696         tt_UnlockAuthFile (ttauth_filename);
697     }
698     (void) umask (original_umask);
699     return 0;
700 }
701
702 int process_command (inputfilename, lineno, argc, argv)
703     char *inputfilename;
704     int lineno;
705     int argc;
706     char **argv;
707 {
708     int status;
709
710     if (argc < 1 || !argv || !argv[0]) return 1;
711
712     if (dispatch_command (inputfilename, lineno, argc, argv,
713                           command_table, &status))
714       return status;
715
716     prefix (inputfilename, lineno);
717     fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
718     return 1;
719 }
720
721
722 /*
723  * utility routines
724  */
725
726 static void fprintfhex (fp, len, cp)
727     register FILE *fp;
728     unsigned int len;
729     char *cp;
730 {
731     unsigned char *ucp = (unsigned char *) cp;
732
733     for (; len > 0; len--, ucp++) {
734         register char *s = hex_table[*ucp];
735         putc (s[0], fp);
736         putc (s[1], fp);
737     }
738     return;
739 }
740
741 /* ARGSUSED */
742 static int dump_entry (inputfilename, lineno, auth, data)
743     char *inputfilename;
744     int lineno;
745     _tt_AuthFileEntry *auth;
746     char *data;
747 {
748     struct _list_data *ld = (struct _list_data *) data;
749     FILE *fp = ld->fp;
750
751     fprintf (fp, "%s", auth->protocol_name);
752     putc (' ', fp);
753     if (auth->protocol_data_length > 0)
754         fprintfhex (fp, auth->protocol_data_length, auth->protocol_data);
755     else
756         fprintf (fp, "\"\"");
757     putc (' ', fp);
758     fprintf (fp, "%s", auth->network_id);
759     putc (' ', fp);
760     fprintf (fp, "%s", auth->auth_name);
761     putc (' ', fp);
762
763     if (auth->auth_data_length == 0)
764         fprintf (fp, "\"\"");
765     else if (!strcmp(auth->auth_name, SECURERPC) ||
766         !strcmp(auth->auth_name, K5AUTH))
767         fwrite (auth->auth_data, sizeof (char), auth->auth_data_length, fp);
768     else
769         fprintfhex (fp, auth->auth_data_length, auth->auth_data);
770     putc ('\n', fp);
771
772     return 0;
773 }
774
775 static int extract_entry (inputfilename, lineno, auth, data)
776     char *inputfilename;
777     int lineno;
778     _tt_AuthFileEntry *auth;
779     char *data;
780 {
781     struct _extract_data *ed = (struct _extract_data *) data;
782
783     if (!ed->fp) {
784         ed->fp = open_file (&ed->filename, "wb",
785                             &ed->used_stdout,
786                             inputfilename, lineno, ed->cmd);
787         if (!ed->fp) {
788             prefix (inputfilename, lineno);
789             fprintf (stderr,
790                      "unable to open extraction file \"%s\"\n",
791                      ed->filename);
792             return -1;
793         }
794     }
795     tt_WriteAuthFileEntry (ed->fp, auth);
796     ed->nwritten++;
797
798     return 0;
799 }
800
801
802 static int match_auth (a, b, authDataSame)
803     register _tt_AuthFileEntry *a, *b;
804     int *authDataSame;
805 {
806     int match = strcmp (a->protocol_name, b->protocol_name) == 0 &&
807             strcmp (a->network_id, b->network_id) == 0 &&
808             strcmp (a->auth_name, b->auth_name) == 0;
809
810     if (match)
811     {
812         *authDataSame = (a->auth_data_length == b->auth_data_length &&
813             binaryEqual (a->auth_data, b->auth_data, a->auth_data_length));
814     }
815     else
816         *authDataSame = 0;
817
818     return (match);
819 }
820
821
822 static int merge_entries (firstp, second, nnewp, nreplp, ndupp)
823     _tt_AuthFileEntryList **firstp, *second;
824     int *nnewp, *nreplp, *ndupp;
825 {
826     _tt_AuthFileEntryList *a, *b, *first, *tail;
827     int n = 0, nnew = 0, nrepl = 0, ndup = 0;
828
829     if (!second) return 0;
830
831     if (!*firstp) {                     /* if nothing to merge into */
832         *firstp = second;
833         for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
834         *nnewp = n;
835         *nreplp = 0;
836         *ndupp = 0;
837         return n;
838     }
839
840     first = *firstp;
841     /*
842      * find end of first list and stick second list on it
843      */
844     for (tail = first; tail->next; tail = tail->next) ;
845     tail->next = second;
846
847     /*
848      * run down list freeing duplicate entries; if an entry is okay, then
849      * bump the tail up to include it, otherwise, cut the entry out of
850      * the chain.
851      */
852     for (b = second; b; ) {
853         _tt_AuthFileEntryList *next = b->next;  /* in case we free it */
854         int duplicate;
855
856         duplicate = 0;
857         a = first;
858         for (;;) {
859             int authDataSame;
860             if (match_auth (a->entry, b->entry, &authDataSame)) {
861                 if (authDataSame)
862                 {
863                     /* found a complete duplicate, ignore */
864                     duplicate = 1;
865                     break;
866                 }
867                 else
868                 {
869                     /* found a duplicate, but auth data differs */
870
871                     _tt_AuthFileEntryList tmp;  /* swap it in for old one */
872                     tmp = *a;
873                     *a = *b;
874                     *b = tmp;
875                     a->next = b->next;
876                     tt_FreeAuthFileEntry (b->entry);
877                     free ((char *) b);
878                     b = NULL;
879                     tail->next = next;
880                     nrepl++;
881                     nnew--;
882                     break;
883                 }
884             }
885             if (a == tail) break;       /* if have looked at left side */
886             a = a->next;
887         }
888         if (!duplicate && b) {          /* if we didn't remove it */
889             tail = b;                   /* bump end of first list */
890         }
891         b = next;
892
893         if (duplicate)
894             ndup++;
895         else
896         {
897             n++;
898             nnew++;
899         }
900     }
901
902     *nnewp = nnew;
903     *nreplp = nrepl;
904     *ndupp = ndup;
905     return n;
906
907 }
908
909
910 static int search_and_do (inputfilename, lineno, start,
911                     argc, argv, do_func, data)
912     char *inputfilename;
913     int lineno;
914     int start;
915     int argc;
916     char *argv[];
917     int (*do_func)();
918     char *data;
919 {
920     int i;
921     int status;
922     int errors = 0;
923     _tt_AuthFileEntryList *l, *next;
924     char *protoname, *protodata, *netid, *authname;
925
926     for (l = ttauth_head; l; l = next)
927     {
928         next = l->next;
929
930         protoname = protodata = netid = authname = NULL;
931
932         for (i = start; i < argc; i++)
933         {
934             if (!strncmp ("protoname=", argv[i], 10))
935                 protoname = argv[i] + 10;
936             else if (!strncmp ("protodata=", argv[i], 10))
937                 protodata = argv[i] + 10;
938             else if (!strncmp ("netid=", argv[i], 6))
939                 netid = argv[i] + 6;
940             else if (!strncmp ("authname=", argv[i], 9))
941                 authname = argv[i] + 9;
942         }
943
944         status = 0;
945
946         if (protoname || protodata || netid || authname)
947         {
948             if (protoname && strcmp (protoname, l->entry->protocol_name))
949                 continue;
950
951             if (protodata && !binaryEqual (protodata,
952                 l->entry->protocol_data, l->entry->protocol_data_length))
953                 continue;
954
955             if (netid && strcmp (netid, l->entry->network_id))
956                 continue;
957
958             if (authname && strcmp (authname, l->entry->auth_name))
959                 continue;
960
961             status = (*do_func) (inputfilename, lineno, l->entry, data);
962
963             if (status < 0)
964                 break;
965         }
966     }
967
968     if (status < 0)
969         errors -= status;               /* since status is negative */
970
971     return (errors);
972 }
973
974
975 /* ARGSUSED */
976 static int remove_entry (inputfilename, lineno, entry, data)
977     char *inputfilename;
978     int lineno;
979     _tt_AuthFileEntry *entry;
980     char *data;
981 {
982     int *nremovedp = (int *) data;
983     _tt_AuthFileEntryList **listp = &ttauth_head;
984     _tt_AuthFileEntryList *list;
985
986     /*
987      * unlink the auth we were asked to
988      */
989     while ((list = *listp)->entry != entry)
990         listp = &list->next;
991     *listp = list->next;
992     tt_FreeAuthFileEntry (list->entry);                    /* free the auth */
993     free (list);                                    /* free the link */
994     ttauth_modified = True;
995     (*nremovedp)++;
996     return 1;
997 }
998
999 /*
1000  * action routines
1001  */
1002
1003 /*
1004  * help
1005  */
1006 int print_help (fp, cmd)
1007     FILE *fp;
1008     char *cmd;
1009 {
1010     CommandTable *ct;
1011     int n = 0;
1012
1013     fprintf (fp, "\n");
1014     if (!cmd) {                         /* if no cmd, print all help */
1015         for (ct = command_table; ct->name; ct++) {
1016             fprintf (fp, "%s\n\n", ct->helptext);
1017             n++;
1018         }
1019     } else {
1020         int len = strlen (cmd);
1021         for (ct = command_table; ct->name; ct++) {
1022             if (strncmp (cmd, ct->name, len) == 0) {
1023                 fprintf (fp, "%s\n\n", ct->helptext);
1024                 n++;
1025             }
1026         }
1027     }
1028         
1029     return n;
1030 }
1031
1032 static int do_help (inputfilename, lineno, argc, argv)
1033     char *inputfilename;
1034     int lineno;
1035     int argc;
1036     char **argv;
1037 {
1038     char *cmd = (argc > 1 ? argv[1] : NULL);
1039     int n;
1040
1041     n = print_help (stdout, cmd);
1042
1043     if (n < 0 || (n == 0 && !cmd)) {
1044         prefix (inputfilename, lineno);
1045         fprintf (stderr, "internal error with help");
1046         if (cmd) {
1047             fprintf (stderr, " on command \"%s\"", cmd);
1048         }
1049         fprintf (stderr, "\n");
1050         return 1;
1051     }
1052
1053     if (n == 0) {
1054         prefix (inputfilename, lineno);
1055         /* already know that cmd is set in this case */
1056         fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1057     }
1058
1059     return 0;
1060 }
1061
1062 /*
1063  * questionmark
1064  */
1065 /* ARGSUSED */
1066 static int do_questionmark (inputfilename, lineno, argc, argv)
1067     char *inputfilename;
1068     int lineno;
1069     int argc;
1070     char **argv;
1071 {
1072     CommandTable *ct;
1073     int i;
1074 #define WIDEST_COLUMN 72
1075     int col = WIDEST_COLUMN;
1076
1077     printf ("Commands:\n");
1078     for (ct = command_table; ct->name; ct++) {
1079         if ((col + ct->maxlen) > WIDEST_COLUMN) {
1080             if (ct != command_table) {
1081                 putc ('\n', stdout);
1082             }
1083             fputs ("        ", stdout);
1084             col = 8;                    /* length of string above */
1085         }
1086         fputs (ct->name, stdout);
1087         col += ct->maxlen;
1088         for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1089             putc (' ', stdout);
1090             col++;
1091         }
1092     }
1093     if (col != 0) {
1094         putc ('\n', stdout);
1095     }
1096
1097     /* allow bad lines since this is help */
1098     return 0;
1099 }
1100
1101 /*
1102  * list [displayname ...]
1103  */
1104 static int do_list (inputfilename, lineno, argc, argv)
1105     char *inputfilename;
1106     int lineno;
1107     int argc;
1108     char **argv;
1109 {
1110     struct _list_data ld;
1111
1112     ld.fp = stdout;
1113
1114     if (argc == 1) {
1115         register _tt_AuthFileEntryList *l;
1116
1117         if (ttauth_head) {
1118             for (l = ttauth_head; l; l = l->next) {
1119                 dump_entry (inputfilename, lineno, l->entry, (char *) &ld);
1120             }
1121         }
1122         return 0;
1123     }
1124     else
1125     {
1126         return (search_and_do (inputfilename, lineno, 1, argc, argv,
1127             dump_entry, (char *) &ld));
1128     }
1129 }
1130
1131 /*
1132  * merge filename [filename ...]
1133  */
1134 static int do_merge (inputfilename, lineno, argc, argv)
1135     char *inputfilename;
1136     int lineno;
1137     int argc;
1138     char **argv;
1139 {
1140     int i;
1141     int errors = 0;
1142     _tt_AuthFileEntryList *head, *tail, *listhead, *listtail;
1143     int nentries, nnew, nrepl, ndup;
1144
1145     if (argc < 2) {
1146         prefix (inputfilename, lineno);
1147         badcommandline (argv[0]);
1148         return 1;
1149     }
1150
1151     listhead = listtail = NULL;
1152
1153     for (i = 1; i < argc; i++) {
1154         char *filename = argv[i];
1155         FILE *fp;
1156         Bool used_stdin = False;
1157
1158         fp = open_file (&filename, "rb",
1159                         &used_stdin, inputfilename, lineno,
1160                         argv[0]);
1161         if (!fp) {
1162             errors++;
1163             continue;
1164         }
1165
1166         head = tail = NULL;
1167         nentries = read_auth_entries (fp, &head, &tail);
1168         if (nentries == 0) {
1169             prefix (inputfilename, lineno);
1170             fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1171                      filename);
1172             errors++;
1173         } else {                        /* link it in */
1174             add_to_list (listhead, listtail, head);
1175         }
1176
1177         if (!used_stdin) (void) fclose (fp);
1178     }
1179
1180     /*
1181      * if we have new entries, merge them in (freeing any duplicates)
1182      */
1183     if (listhead) {
1184         nentries = merge_entries (&ttauth_head, listhead,
1185             &nnew, &nrepl, &ndup);
1186         if (verbose) 
1187           printf ("%d entries read in:  %d new, %d replacement%s\n", 
1188                   nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1189         if (nentries > 0) ttauth_modified = True;
1190     }
1191
1192     return 0;
1193 }
1194
1195 /*
1196  * extract filename displayname [displayname ...]
1197  */
1198 static int do_extract (inputfilename, lineno, argc, argv)
1199     char *inputfilename;
1200     int lineno;
1201     int argc;
1202     char **argv;
1203 {
1204     int errors;
1205     struct _extract_data ed;
1206
1207     if (argc < 3) {
1208         prefix (inputfilename, lineno);
1209         badcommandline (argv[0]);
1210         return 1;
1211     }
1212
1213     ed.fp = NULL;
1214     ed.filename = argv[1];
1215     ed.nwritten = 0;
1216     ed.cmd = argv[0];
1217
1218     errors = search_and_do (inputfilename, lineno, 2, argc, argv, 
1219         extract_entry, (char *) &ed);
1220
1221     if (!ed.fp) {
1222         fprintf (stderr, 
1223                  "No matches found, authority file \"%s\" not written\n",
1224                  ed.filename);
1225     } else {
1226         if (verbose) {
1227             printf ("%d entries written to \"%s\"\n", 
1228                     ed.nwritten, ed.filename);
1229         }
1230         if (!ed.used_stdout) {
1231             (void) fclose (ed.fp);
1232         }
1233     }
1234
1235     return errors;
1236 }
1237
1238
1239 /*
1240  * add protoname protodata netid authname authdata
1241  */
1242 static int do_add (inputfilename, lineno, argc, argv)
1243     char *inputfilename;
1244     int lineno;
1245     int argc;
1246     char **argv;
1247
1248     int n, nnew, nrepl, ndup;
1249     char *protoname;
1250     char *protodata_hex;
1251     char *protodata = NULL; /* not required */
1252     char *netid;
1253     char *authname;
1254     char *authdata_hex;
1255     char *authdata;
1256     int protodata_len, authdata_len;
1257     _tt_AuthFileEntry *entry;
1258     _tt_AuthFileEntryList *list;
1259     int status = 0;
1260
1261     if (argc != 6 || !argv[1] || !argv[2] ||
1262         !argv[3] || !argv[4] || !argv[5])
1263     {
1264         prefix (inputfilename, lineno);
1265         badcommandline (argv[0]);
1266         return 1;
1267     }
1268
1269     protoname = argv[1];
1270     protodata_hex = argv[2];
1271     netid = argv[3];
1272     authname = argv[4];
1273     authdata_hex = argv[5];
1274
1275     protodata_len = strlen (protodata_hex);
1276     if (protodata_len > 0)
1277     {
1278         if (protodata_hex[0] == '"' && protodata_hex[protodata_len - 1] == '"')
1279         {
1280             protodata = malloc (protodata_len - 1);
1281             if (protodata)
1282             {
1283                 strncpy (protodata, protodata_hex + 1, protodata_len - 2);
1284                 protodata_len -= 2;
1285             }
1286             else
1287                 goto add_bad_malloc;
1288         }
1289         else
1290         {
1291             protodata_len = cvthexkey (protodata_hex, &protodata);
1292             if (protodata_len < 0)
1293             {
1294                 prefix (inputfilename, lineno);
1295                 fprintf (stderr,
1296                "protodata_hex contains odd number of or non-hex characters\n");
1297                 return (1);
1298             }
1299         }
1300     }
1301
1302     authdata_len = strlen (authdata_hex);
1303     if (authdata_hex[0] == '"' && authdata_hex[authdata_len - 1] == '"')
1304     {
1305         authdata = malloc (authdata_len - 1);
1306         if (authdata)
1307         {
1308             strncpy (authdata, authdata_hex + 1, authdata_len - 2);
1309             authdata_len -= 2;
1310         }
1311         else
1312             goto add_bad_malloc;
1313     }
1314     else if (!strcmp (protoname, SECURERPC) || !strcmp (protoname, K5AUTH))
1315     {
1316         authdata = malloc (authdata_len + 1);
1317         if (authdata)
1318             strcpy (authdata, authdata_hex);
1319         else
1320             goto add_bad_malloc;
1321     }
1322     else
1323     {
1324         authdata_len = cvthexkey (authdata_hex, &authdata);
1325         if (authdata_len < 0)
1326         {
1327             prefix (inputfilename, lineno);
1328             fprintf (stderr,
1329                "authdata_hex contains odd number of or non-hex characters\n");
1330             free (protodata);
1331             return (1);
1332         }
1333     }
1334
1335     entry = (_tt_AuthFileEntry *) malloc (sizeof (_tt_AuthFileEntry));
1336
1337     if (!entry)
1338         goto add_bad_malloc;
1339
1340     entry->protocol_name = copystring (protoname);
1341     entry->protocol_data_length = protodata_len;
1342     entry->protocol_data = protodata;
1343     entry->network_id = copystring (netid);
1344     entry->auth_name = copystring (authname);
1345     entry->auth_data_length = authdata_len;
1346     entry->auth_data = authdata;
1347
1348     if (!entry->protocol_name ||
1349         (!entry->protocol_data && entry->protocol_data_length > 0) ||
1350         !entry->network_id || !entry->auth_name ||
1351         (!entry->auth_data && entry->auth_data_length > 0))
1352     {
1353         goto add_bad_malloc;
1354     }
1355
1356     list = (_tt_AuthFileEntryList *) malloc (sizeof (_tt_AuthFileEntryList));
1357
1358     if (!list)
1359         goto add_bad_malloc;
1360
1361     list->next = NULL;
1362     list->entry = entry;
1363
1364     /*
1365      * merge it in; note that merge will deal with allocation
1366      */
1367
1368     n = merge_entries (&ttauth_head, list, &nnew, &nrepl, &ndup);
1369
1370     if (n > 0)
1371         ttauth_modified = True;
1372     else
1373     {
1374         prefix (inputfilename, lineno);
1375         if (ndup > 0)
1376         {
1377             status = 0;
1378             fprintf (stderr, "no records added - all duplicate\n");
1379         }
1380         else
1381         {
1382             status = 1;
1383             fprintf (stderr, "unable to merge in added record\n");
1384         }
1385         goto cant_add;
1386     }
1387
1388     return 0;
1389
1390
1391 add_bad_malloc:
1392
1393     status = 1;
1394     prefix (inputfilename, lineno);
1395     fprintf (stderr, "unable to allocate memory to add an entry\n");
1396
1397 cant_add:
1398
1399     if (protodata)
1400         free (protodata);
1401     if (authdata)
1402         free (authdata);
1403     if (entry)
1404     {
1405         if (entry->protocol_name)
1406             free (entry->protocol_name);
1407         if (entry->protocol_data)
1408             free (entry->protocol_data);
1409         if (entry->network_id)
1410             free (entry->network_id);
1411         if (entry->auth_name)
1412             free (entry->auth_name);
1413         if (entry->auth_data)
1414             free (entry->auth_data);
1415         free ((char *) entry);
1416     }
1417
1418     return status;
1419 }
1420
1421 /*
1422  * remove displayname
1423  */
1424 static int do_remove (inputfilename, lineno, argc, argv)
1425     char *inputfilename;
1426     int lineno;
1427     int argc;
1428     char **argv;
1429 {
1430     int nremoved = 0;
1431     int errors;
1432
1433     if (argc < 2) {
1434         prefix (inputfilename, lineno);
1435         badcommandline (argv[0]);
1436         return 1;
1437     }
1438
1439     errors = search_and_do (inputfilename, lineno, 1, argc, argv,
1440         remove_entry, (char *) &nremoved);
1441     if (verbose) printf ("%d entries removed\n", nremoved);
1442     return errors;
1443 }
1444
1445 /*
1446  * info
1447  */
1448 static int do_info (inputfilename, lineno, argc, argv)
1449     char *inputfilename;
1450     int lineno;
1451     int argc;
1452     char **argv;
1453 {
1454     int n;
1455     _tt_AuthFileEntryList *l;
1456
1457     if (argc != 1) {
1458         prefix (inputfilename, lineno);
1459         badcommandline (argv[0]);
1460         return 1;
1461     }
1462
1463     for (l = ttauth_head, n = 0; l; l = l->next, n++) ;
1464
1465     printf ("Authority file:       %s\n",
1466             ttauth_filename ? ttauth_filename : "(none)");
1467     printf ("File new:             %s\n", ttauth_existed ? No : Yes);
1468     printf ("File locked:          %s\n", ignore_locks ? No : Yes);
1469     printf ("Number of entries:    %d\n", n);
1470     printf ("Changes honored:      %s\n", ttauth_allowed ? Yes : No);
1471     printf ("Changes made:         %s\n", ttauth_modified ? Yes : No);
1472     printf ("Current input:        %s:%d\n", inputfilename, lineno);
1473     return 0;
1474 }
1475
1476
1477 /*
1478  * exit
1479  */
1480 static Bool alldone = False;
1481
1482 /* ARGSUSED */
1483 static int do_exit (inputfilename, lineno, argc, argv)
1484     char *inputfilename;
1485     int lineno;
1486     int argc;
1487     char **argv;
1488 {
1489     /* allow bogus stuff */
1490     alldone = True;
1491     return 0;
1492 }
1493
1494 /*
1495  * quit
1496  */
1497 /* ARGSUSED */
1498 static int do_quit (inputfilename, lineno, argc, argv)
1499     char *inputfilename;
1500     int lineno;
1501     int argc;
1502     char **argv;
1503 {
1504     /* allow bogus stuff */
1505     die (0);
1506     /* NOTREACHED */
1507     return -1;                          /* for picky compilers */
1508 }
1509
1510
1511 /*
1512  * source filename
1513  */
1514 static int do_source (inputfilename, lineno, argc, argv)
1515     char *inputfilename;
1516     int lineno;
1517     int argc;
1518     char **argv;
1519 {
1520     char *script;
1521     char buf[BUFSIZ];
1522     FILE *fp;
1523     Bool used_stdin = False;
1524     int len;
1525     int errors = 0, status;
1526     int sublineno = 0;
1527     char **subargv;
1528     int subargc;
1529     Bool prompt = False;                /* only true if reading from tty */
1530
1531     if (argc != 2 || !argv[1]) {
1532         prefix (inputfilename, lineno);
1533         badcommandline (argv[0]);
1534         return 1;
1535     }
1536
1537     script = argv[1];
1538
1539     fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1540     if (!fp) {
1541         return 1;
1542     }
1543
1544     if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1545
1546     while (!alldone) {
1547         buf[0] = '\0';
1548         if (prompt) {
1549             printf ("ttauth> ");
1550             fflush (stdout);
1551         }
1552         if (fgets (buf, sizeof buf, fp) == NULL) break;
1553         sublineno++;
1554         len = strlen (buf);
1555         if (len == 0 || buf[0] == '#') continue;
1556         if (buf[len-1] != '\n') {
1557             prefix (script, sublineno);
1558             fprintf (stderr, "line too long\n");
1559             errors++;
1560             break;
1561         }
1562         buf[--len] = '\0';              /* remove new line */
1563         subargv = split_into_words (buf, &subargc);
1564         if (subargv) {
1565             status = process_command (script, sublineno, subargc, subargv);
1566             free ((char *) subargv);
1567             errors += status;
1568         } else {
1569             prefix (script, sublineno);
1570             fprintf (stderr, "unable to break line into words\n");
1571             errors++;
1572         }
1573     }
1574
1575     if (!used_stdin) {
1576         (void) fclose (fp);
1577     }
1578     return errors;
1579 }