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