rudimentary-support-in-kpasswd-and-kas-for-mit-v4-string-to-keyed-passwords-20010327
[openafs.git] / src / kauth / admin_tools.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* These routines provide administrative tools for managing the AuthServer.
11    There is an interactive routine that can be used to examine the database and
12    make small changes as well as subroutines to permit specialized programs to
13    update the database, change the server passwords, etc. */
14
15 #include <afs/param.h>
16 #include <afs/stds.h>
17 #include <afs/debug.h>
18 #include <ctype.h>
19 #include <string.h>
20
21     /* These two needed for rxgen output to work */
22 #include <sys/types.h>
23 #include <rx/xdr.h>
24
25 #include <stdio.h>
26 #include <rx/rx.h>
27 #include <lock.h>
28 #include <ubik.h>
29 #ifndef AFS_NT40_ENV
30 #include <pwd.h>
31 #endif
32 #include <afs/auth.h>
33 #include <afs/cellconfig.h>
34 #include <afs/cmd.h>
35 #include <afs/com_err.h>
36 #include <afs/afsutil.h>
37
38 #include "kauth.h"
39 #include "kautils.h"
40 #include "kaport.h"
41
42
43 #define CMD_PARSER_AMBIG_FIX 1          /* allow ambiguous aliases */
44
45 extern char *ktime_GetDateUsage();
46
47 #define KA_SIXHOURS     (6*3600)
48
49 static struct ubik_client *conn;
50 static char cell[MAXKTCREALMLEN] = "";
51 static char whoami[32];
52 static char passwd[BUFSIZ];
53 static char myName[510];  /* almost like whoami save with path and without : */
54
55 static int finished;
56 static int zero_argc;
57 static char **zero_argv;
58 afs_uint32 ka_islocked();
59
60 afs_int32 DefaultCell (void)
61 {
62     afs_int32 code;
63
64     if (cell[0] != 0) return 0;
65     code = ka_ExpandCell (0, cell, 0/*local*/);
66     if (code) {
67         com_err (whoami, code, "Can't expand cell name");
68     }
69     return code;
70 }
71
72 /* These are the command operation procedures. */
73
74 int ListUsers (
75   struct cmd_syndesc *as,
76   char *arock)
77 {
78     struct kaident name;
79     afs_int32 index;
80     afs_int32 count;
81     afs_int32 next_index;
82     int  code, all=0, showa = 0;
83     int showkey = (as->parms[2].items != NULL);
84
85     if (as->parms[0].items) 
86         all = 1;
87     if (as->parms[1].items) {
88         all = 1;
89         showa = 1;
90     }
91     for (index = 0; 1; index = next_index) {
92         code = ubik_Call (KAM_ListEntry, conn, 0,
93                           index, &next_index, &count, &name);
94         if (code) {
95             com_err (whoami, code, "calling KAM_ListEntry");
96             break;
97         }
98         if (!next_index) break;
99         if (next_index < 0)
100             printf ("next_index (%d) is negative: ", next_index);
101         if (strlen(name.name) == 0) printf ("name is zero length: ");
102         if (all)
103             DumpUser(name.name, (char *)0, showa, showkey, name.instance);
104         else
105             ka_PrintUserID ("", name.name, name.instance, "\n");
106     }
107     return code;
108 }
109
110
111 int ExamineUser (
112   struct cmd_syndesc *as,
113   char *arock)
114 {
115     int showkey = (as->parms[1].items != NULL);
116     return DumpUser(as->parms[0].items->data, arock, 0, showkey, (char *)0);
117 }
118
119
120 int DumpUser (
121   char *user,
122   char *arock,
123   int showadmin,
124   int showkey,
125   char *inst)
126 {
127     char name[MAXKTCNAMELEN];
128     char instance[MAXKTCNAMELEN];
129     Date now = time(0);
130     int  code;
131     char bob[KA_TIMESTR_LEN];
132
133     struct kaentryinfo tentry;
134
135     code = ka_ParseLoginName (user, name, instance, 0);
136     if (code) {
137         com_err (whoami, code, "parsing user's name '%s'", user);
138         return KABADCMD;
139     }
140
141     if (!inst)
142         inst = instance;
143     code = ubik_Call (KAM_GetEntry, conn, 0, name, inst,
144                       KAMAJORVERSION, &tentry);
145     if (code) {
146         com_err (whoami, code,
147                  "getting information for %s.%s", name, inst);
148         return code;
149     }
150     if (tentry.minor_version != KAMINORVERSION)
151         printf ("Minor version number mismatch: got %d, expected %d\n",
152                 tentry.minor_version, KAMINORVERSION);
153     if (showadmin && !(tentry.flags & KAFADMIN))
154         return 0;
155     ka_PrintUserID ("\nUser data for ", name, inst, "");
156     {   char *prefix = " (";
157 #define NEWPREFIX "+"
158         if (tentry.flags & KAFADMIN) { printf ("%sADMIN", prefix); prefix = NEWPREFIX; }
159         if (tentry.flags & KAFNOTGS) { printf ("%sNOTGS", prefix); prefix = NEWPREFIX; }
160         if (tentry.flags & KAFNOCPW) { printf ("%sNOCPW", prefix); prefix = NEWPREFIX; }
161         if (tentry.flags & KAFNOSEAL) { printf ("%sNOSEAL", prefix); prefix = NEWPREFIX; }
162         if (tentry.flags & KAFNEWASSOC) { printf ("%sNEWASSOC", prefix); prefix = NEWPREFIX; }
163         if (tentry.flags & KAFASSOCROOT) { printf ("%sASSOCROOT", prefix); prefix = NEWPREFIX; }
164         if (tentry.flags & KAFASSOC) { printf ("%sASSOC", prefix); prefix = NEWPREFIX; }
165         if (tentry.user_expiration <= now) { printf ("%sexpired", prefix); prefix = NEWPREFIX; }
166         if (strcmp (prefix, NEWPREFIX) == 0) printf (")\n");
167         else printf ("\n");
168     }
169     if ((!ka_KeyIsZero((char *) &tentry.key, sizeof(tentry.key))) && 
170         (showkey)) {
171         printf ("  key (%d):", tentry.key_version);
172         ka_PrintBytes ((char *)&tentry.key, sizeof(tentry.key));
173     }
174     else {
175         if (tentry.keyCheckSum == 0) 
176             printf ("  key version is %d", tentry.key_version);
177         else
178             printf ("  key (%d) cksum is %u",
179                     tentry.key_version, tentry.keyCheckSum);
180     }
181     ka_timestr(tentry.change_password_time,bob,KA_TIMESTR_LEN);
182     printf (", last cpw: %s\n", bob);
183     if (!tentry.misc_auth_bytes) {
184        printf ("  password will never expire.\n");
185        printf ("  An unlimited number of unsuccessful authentications is permitted.\n");
186      }
187     else {
188       unsigned char misc_stuff[4];
189       afs_uint32 temp;
190
191       temp = tentry.misc_auth_bytes;
192 /*
193       temp = ntohl(tentry.misc_auth_bytes);
194 */
195       unpack_long(temp, misc_stuff);
196
197       if (!misc_stuff[0]) {
198         printf ("  password will never expire.\n");
199       }
200       else {
201         ka_timestr((tentry.change_password_time + misc_stuff[0]*24*60*60), bob, KA_TIMESTR_LEN);
202         printf ("  password will expire: %s\n", bob);
203       } 
204
205       if (!misc_stuff[2])
206          printf ("  An unlimited number of unsuccessful authentications is permitted.\n");
207       else {
208          printf ("  %d consecutive unsuccessful authentications are permitted.\n", misc_stuff[2]);
209
210          if (!misc_stuff[3])
211            printf ("  The lock time for this user is not limited.\n");
212          else 
213            printf ("  The lock time for this user is %4.1f minutes.\n", 
214                    (float) ((unsigned int) misc_stuff[3] << 9) / 60.0);
215
216          if (!(misc_stuff[1] & KA_ISLOCKED) || !ka_islocked(name, instance, &temp))
217            printf ("  User is not locked.\n");
218          else if (temp == (afs_uint32) (-1L))
219            printf ("  User is locked forever.\n");
220          else {
221            ka_timestr(temp,bob,KA_TIMESTR_LEN);
222            printf ("  User is locked until %s\n",bob);
223          }
224        }
225
226     }
227     {   char exp[KA_TIMESTR_LEN];
228         ka_timestr (tentry.user_expiration, exp, KA_TIMESTR_LEN);
229         if (tentry.user_expiration < now)
230             printf ("  DISABLED entry at %s.", exp);
231         else if (tentry.user_expiration == NEVERDATE)
232             printf ("  entry never expires.");
233         else printf ("  entry expires on %s.", exp);
234     }
235     printf ("  Max ticket lifetime %.2f hours.\n",
236             tentry.max_ticket_lifetime / 3600.0);
237     ka_timestr (tentry.modification_time,bob,KA_TIMESTR_LEN);
238     printf ("  last mod on %s by ", bob);
239     ka_PrintUserID ("", tentry.modification_user.name,
240                     tentry.modification_user.instance, "\n");
241     if ((tentry.reserved3 & 0xffff0000) == 0x12340000) {
242         int short reused = (short)tentry.reserved3;
243         if (!reused) {
244             printf("  permit password reuse\n");
245         } else {
246             printf("  don't permit password reuse\n");
247         }
248     }
249     return 0;
250 }
251
252 struct OKerrors {
253     int   code;
254     char *msg;
255 };
256
257 int handle_errors (
258   int              code,                /* error code to handle */
259   struct OKerrors  OKlist[],            /* list of errors & messages that should be ignored */
260   int             *persist)             /* set this if we should retry, clear otherwise */
261 {
262     int        i;  
263
264     for (i=0; OKlist[i].code; i++) {
265         if (OKlist[i].code == code) {
266             printf ("%s\n", OKlist[i].msg);
267             *persist = 0;               /* we're done */
268             return 0;
269         }
270     }
271
272     printf (" : [%s] %s", error_table_name(code), error_message(code));
273     switch (code) {
274       case UNOQUORUM:
275         printf (", wait one second\n");
276         IOMGR_Sleep (1);
277         return 0;
278       case KAEMPTY:
279       case RX_CALL_TIMEOUT:
280         printf (" (retrying)\n");
281         return 0;
282     }
283     printf ("\n");
284
285     *persist = 0;                       /* don't retry these errors */
286     return code;
287 }
288
289 int CreateUser (
290   struct cmd_syndesc *as,
291   char *arock)
292 {
293     int code;
294     char name[MAXKTCNAMELEN];
295     char instance[MAXKTCNAMELEN];
296     struct ktc_encryptionKey key;
297
298     int persist = 1;
299     struct OKerrors OKlist[2];
300     OKlist[0].code=0;
301
302     code = ka_ParseLoginName (as->parms[0].items->data, name, instance, 0);
303     if (code) {
304         com_err (whoami, code, "parsing user's name '%s'",
305                  as->parms[0].items->data);
306         return KABADCMD;
307     }
308     ka_StringToKey (as->parms[1].items->data, cell, &key);
309
310     do {
311         code = ubik_Call (KAM_CreateUser, conn, 0, name, instance, key);
312         if (!code) return 0;
313         ka_PrintUserID ("Creating user ", name, instance, " ");
314         code = handle_errors (code, OKlist, &persist);
315     } while (persist);
316     return code;
317 }
318
319 int DeleteUser (
320   struct cmd_syndesc *as,
321   char *arock)
322 {
323     int code;
324     char name[MAXKTCNAMELEN];
325     char instance[MAXKTCNAMELEN];
326
327     int persist = 1;
328     struct OKerrors OKlist[2];
329     OKlist[0].code=0;
330     code = ka_ParseLoginName (as->parms[0].items->data, name, instance, 0);
331     if (code) {
332         com_err (whoami, code, "parsing user's name '%s'",
333                  as->parms[0].items->data);
334         return KABADCMD;
335     }
336
337     do {
338         code = ubik_Call (KAM_DeleteUser, conn, 0, name, instance);
339         if (!code) return 0;
340         ka_PrintUserID ("Deleting user ", name, instance, " ");
341         code = handle_errors (code, OKlist, &persist);
342     } while (persist);
343     return code;
344 }
345
346 static int read_time_interval (
347   char *str,
348   afs_int32 *seconds)
349 {
350     char *s;
351     int   sec = 0;
352     char  buf[32];
353
354     str = strncpy (buf, str, sizeof(buf));
355     s = strchr (str, ':');
356     if (s == 0) sec = atoi (str);
357     else {
358         *s++ = '\0';                    /* separate hours and minutes */
359         sec = atoi(str)*3600 + atoi(s)*60;
360     }
361     *seconds = sec;
362     return 0;
363 }
364
365 int parse_flags (
366   char *name,
367   char *inst,
368   char *str,
369   afs_int32 *flags)
370 {
371     struct kaentryinfo tentry;
372     int code;
373     char bitspec[100];
374     afs_int32 f;
375     char bit[25];
376     char c;
377     int  addop;                         /* 1=add bit; 0=remove bit */
378     int  flag;
379     int  i;
380
381     str = lcstring (bitspec, str, sizeof(bitspec));
382     if (isdigit(*str)) {
383         if (strncmp(str, "0x", 2) == 0) /* 0x => hex */
384             sscanf (str, "0x%lx", &f);
385         else if (*str == '0')           /* assume octal */
386             sscanf (str, "%lo", &f);
387         else                            /* just assume hex */
388             sscanf (str, "%lx", &f);
389     }
390     else {
391         if (*str == '=') {
392             str++;
393             f = 0;
394             addop = 1;
395         }
396         else {
397             if (strchr ("+-", *str)) addop = (*str++ == '+');
398             else if (*str == '_') {addop = 0; str++;}
399             else addop = 1;
400             code = ubik_Call (KAM_GetEntry, conn, 0,
401                               name, inst, KAMAJORVERSION, &tentry);
402             if (code) {
403                 com_err (whoami, code,
404                          "could get current flag value for %s.%s", name, inst);
405                 return -1;
406             }
407             f = tentry.flags;
408         }
409         while (*str) {
410             i = 0;
411             while (1) {
412                 c = *str;
413                 if (isupper (c)) c = tolower(c);
414                 if (!islower(c)) break;
415                 bit[i++] = c;
416                 str++;
417             }
418             bit[i] = '\0';
419             if (strcmp (bit, "admin") == 0) flag = KAFADMIN;
420             else if (strcmp (bit, "noadmin") == 0) flag = KAFADMIN, addop = !addop;
421             else if (strcmp (bit, "notgs") == 0) flag = KAFNOTGS;
422             else if (strcmp (bit, "tgs") == 0) flag = KAFNOTGS, addop = !addop;
423             else if (strcmp (bit, "noseal") == 0) flag = KAFNOSEAL;
424             else if (strcmp (bit, "seal") == 0) flag = KAFNOSEAL, addop = !addop;
425             else if (strcmp (bit, "nocpw") == 0) flag = KAFNOCPW;
426             else if (strcmp (bit, "cpw") == 0) flag = KAFNOCPW, addop = !addop;
427             else if (strcmp (bit, "newassoc") == 0) flag = KAFNEWASSOC;
428             else if (strcmp (bit, "nonewassoc") == 0) flag = KAFNEWASSOC, addop = !addop;
429             else {
430                 printf ("Illegal bit name: %s; choices are: [no]admin, [no]tgs, [no]seal, [no]cpw\n", bit);
431                 return -1;
432             }
433
434             if (addop) f |= flag;
435             else f &= ~flag;
436
437             if (*str == 0) break;
438             if (*str == '+') addop = 1;                 /* get next op */
439             else if ((*str == '-') || (*str == '_')) addop = 0;
440             else {
441                 printf ("Illegal combination operator: %c\n", *str);
442                 return -1;
443             }
444             str++;
445         }
446     }
447     *flags = (f & KAF_SETTABLE_FLAGS) | KAFNORMAL;
448     return 0;
449 }
450 #define seriouserror(code) ((code <0) || ((code != UNOSERVERS) && (code != UNOQUORUM) && code != UNOTSYNC))
451
452 /* return MAXLONG if locked forever */
453 afs_uint32 ka_islocked (
454   char *name, 
455   char *instance,
456   afs_uint32 *when)
457 {
458   int count, code;
459   afs_uint32 tempwhen;
460
461   count = 0;
462   *when = 0;
463   do {
464     tempwhen = 0;
465     code = ubik_CallIter (KAM_LockStatus, conn, UPUBIKONLY, &count, 
466                           name, instance, &tempwhen, /*spares*/0,0,0,0);
467     if (code) {
468       if (seriouserror(code)) 
469         com_err (whoami, code, "");
470     }
471     else if (tempwhen) { /* user is locked */
472       if (!*when || tempwhen < *when) {
473           *when = tempwhen;
474           return (*when);
475       }
476     }
477     else /* ! tempwhen ==> user is not locked */
478       return 0;
479
480   } while (code != UNOSERVERS);
481   
482   return (*when);
483 }
484
485 int Unlock (
486   struct cmd_syndesc *as,
487   char *arock)
488 {
489   afs_int32 code, rcode=0;
490   afs_int32 count;
491   afs_int32 server;
492   char name[MAXKTCNAMELEN];
493   char instance[MAXKTCNAMELEN];
494
495     code = ka_ParseLoginName (as->parms[0].items->data, name, instance, 0);
496     if (code) {
497         com_err (whoami, code, "parsing user's name '%s'", as->parms[0].items->data);
498         return KABADCMD;
499     }
500   
501     count = 0;
502     do {
503       code = ubik_CallIter (KAM_Unlock, conn, 0, &count, name, instance, 
504                             /*spares*/0,0,0,0);
505       if (code && (code != UNOSERVERS)) {
506           server = 0;
507           if (conn && conn->conns[count-1] && conn->conns[count-1]->peer) {
508               server = conn->conns[count-1]->peer->host;
509           }
510           com_err (whoami, code,
511                    "so %s.%s may still be locked (on server %d.%d.%d.%d)", 
512                    name, instance, ((server>>24)&0xFF), ((server>>16)&0xFF),
513                    ((server>>8)&0xFF), (server&0xFF));
514
515          if (!rcode) {
516             rcode = code;
517          }
518       }
519     } while (code != UNOSERVERS);
520
521     return rcode;
522 }
523
524 int SetFields (
525   struct cmd_syndesc *as,
526   char *arock)
527 {
528     int code;
529     char name[MAXKTCNAMELEN];
530     char instance[MAXKTCNAMELEN];
531     afs_int32 flags = 0;
532     Date expiration = 0;
533     afs_int32 lifetime = 0;
534     afs_int32 maxAssociates = -1;
535     afs_int32 pwexpiry = 0;
536     afs_int32 was_spare = 0;
537     char misc_auth_bytes[4];
538     int i;
539
540     code = ka_ParseLoginName (as->parms[0].items->data, name, instance, 0);
541     if (code) {
542         com_err (whoami, code, "parsing user's name '%s'",
543                  as->parms[0].items->data);
544         return KABADCMD;
545     }
546
547     if (as->parms[1].items) {
548         code = parse_flags (name, instance, as->parms[1].items->data, &flags);
549         if (code) {
550             printf ("Illegal flag specification: %s, should be of the form <'='|'+'|'-'|'_'>bitname{<'+'|'-'>bitname}*\n", as->parms[1].items->data);
551             return KABADCMD;
552         }
553     }
554     if (as->parms[2].items) {
555         char  buf[32];
556         char *s = strncpy (buf, as->parms[2].items->data, sizeof(buf));
557         code = ktime_DateToInt32 (s, &expiration);
558         if (code) {
559             printf ("Illegal time format %s: %s\n",
560                     as->parms[2].items->data, ktime_GetDateUsage());
561             return KABADCMD;
562         }
563         if (expiration == 0) {
564             fprintf (stderr, "Expiration time must be after (about) 1970.\n");
565             return KABADCMD;
566         }
567         if (expiration < time(0)) {
568             fprintf (stderr, "Warning: expiration being set into the past, account will be disabled.\n");
569         }
570     }
571     /*
572      * TICKET lifetime...
573      */
574     if (as->parms[3].items) {
575         code = read_time_interval (as->parms[3].items->data, &lifetime);
576         if (code) return KABADCMD;
577     }
578
579     /*  no point in doing this any sooner than necessary */
580     for (i=0;i<4;misc_auth_bytes[i++] = 0);
581
582     if (as->parms[4].items) {
583         if (isint(as->parms[4].items->data))
584           pwexpiry = atoi (as->parms[4].items->data);
585         else {
586           fprintf(stderr,"Password lifetime specified must be a non-negative decimal integer.\n");
587           pwexpiry = -1;
588         }
589         if (pwexpiry <0 || pwexpiry >254) {
590             fprintf(stderr,"Password lifetime range must be [0..254] days.\n");
591             fprintf(stderr,"Zero represents an unlimited lifetime.\n");
592             return KABADCMD;
593         }
594         else {
595           misc_auth_bytes[0] = pwexpiry+1;
596         }
597     }
598
599     if (as->parms[5].items) {
600       char *reuse;
601       reuse = (as->parms[5].items->data);
602       
603       if (!strcmp(reuse, "yes")) {
604         misc_auth_bytes[1] = KA_REUSEPW;
605       }
606       else if (strcmp(reuse, "no")) {
607         fprintf(stderr,"must specify \"yes\" or \"no\": \"yes\" assumed\n");
608         misc_auth_bytes[1] = KA_REUSEPW;
609       }
610       else {
611         misc_auth_bytes[1] = KA_NOREUSEPW;
612       }
613     }
614
615     if (as->parms[6].items) {
616       int nfailures;
617
618       
619       if (isint(as->parms[6].items->data) && 
620         ((nfailures = atoi(as->parms[6].items->data)) < 255)) {
621         misc_auth_bytes[2] = nfailures+1;
622       }
623       else {
624         fprintf(stderr,"Failure limit must be in [0..254].\n");
625         fprintf(stderr,"Zero represents unlimited login attempts.\n");
626         return KABADCMD;
627       }
628     }
629
630     if (as->parms[7].items) {
631       int locktime, hrs, mins;
632       char *s;
633
634       hrs = 0;
635       s = as->parms[7].items->data;
636       if (index(s, ':'))
637         sscanf(s, "%d:%d", &hrs, &mins);
638       else
639         sscanf(s, "%d", &mins);
640
641       locktime = hrs*60 + mins;
642       if (hrs < 0 || hrs > 36 || mins < 0) { 
643         fprintf(stderr,"Lockout times must be either minutes or hh:mm.\n");
644         fprintf(stderr,"Lockout times must be less than 36 hours.\n");
645         return KABADCMD;
646       }
647       else if (locktime > 36*60 ) {
648         fprintf(stderr,"Lockout times must be either minutes or hh:mm.\n");
649         fprintf(stderr,"Lockout times must be less than 36 hours.\n");
650         fprintf(stderr,"Continuing with lock time of exactly 36 hours...\n");
651         locktime = 36*60;
652       }
653       locktime = (locktime * 60 + 511) >> 9; /* ceil(l*60/512) */
654       misc_auth_bytes[3] = locktime +1;  /* will be 1 if user said 0 */
655     }
656
657 #if ASSOCIATES
658     if (as->parms[8].items) {
659         maxAssociates = atoi (as->parms[6].items->data);
660         if (maxAssociates < 0) {
661             printf ("Illegal maximum number of associates\n");
662             return KABADCMD;
663         }
664     }
665 #endif
666     was_spare = pack_long(misc_auth_bytes);
667
668     if (was_spare || flags || expiration || lifetime || (maxAssociates >= 0))
669         code = ubik_Call
670             (KAM_SetFields, conn, 0,
671              name, instance, flags, expiration, lifetime, maxAssociates,
672              was_spare, /* spare */ 0);
673     else {
674         printf ("Must specify one of the optional parameters\n");
675         return KABADCMD;
676     }
677     if (code) com_err (whoami, code,
678                        "calling KAM_SetFields for %s.%s", name, instance);
679     return code;
680 }
681
682 int StringToKey (
683   struct cmd_syndesc *as,
684   char *arock)
685 {
686     afs_int32 code;
687     char realm[MAXKTCREALMLEN];
688     struct ktc_encryptionKey key;
689
690     if (as->parms[1].items) {
691         code = ka_ExpandCell (as->parms[1].items->data, realm, 0/*local*/);
692         if (code) {
693             com_err (whoami, code,
694                      "expanding %s as cell name, attempting to continue",
695                      as->parms[1].items->data);
696         }
697         ucstring (realm, realm, sizeof(realm));
698     }
699     else {
700         if (code = DefaultCell()) return code;
701         ucstring (realm, cell, sizeof(realm));
702     }
703     ka_StringToKey (as->parms[0].items->data, realm, &key);
704
705     printf ("Converting %s in realm '%s' yields key='",
706             as->parms[0].items->data, realm);
707     ka_PrintBytes ((char *)&key, sizeof(key));
708     printf ("'.\n");
709
710     des_string_to_key (as->parms[0].items->data, &key);
711       
712     printf ("Converting %s with the DES string to key yields key='",
713             as->parms[0].items->data);
714     ka_PrintBytes (&key, sizeof(key));
715     printf ("'.\n");
716
717     return 0;
718 }
719
720 int SetPassword (
721   struct cmd_syndesc *as,
722   char *arock)
723 {
724     int code;
725     char name[MAXKTCNAMELEN];
726     char instance[MAXKTCNAMELEN];
727     char realm[MAXKTCREALMLEN];
728     struct ktc_encryptionKey key;
729     afs_int32 kvno = 0;
730
731     code = ka_ParseLoginName (as->parms[0].items->data, name, instance, realm);
732     if (code) {
733         com_err (whoami, code, "parsing user's name '%s'",
734                  as->parms[0].items->data);
735         return KABADCMD;
736     }
737     
738     if (strlen(realm) == 0) 
739       ucstring (realm, cell, sizeof(realm));
740
741     if (as->parms[1].items && as->parms[2].items) {
742         printf ("Can't specify both a password and a key\n");
743         return KABADCMD;
744       } 
745     else if (as->parms[1].items) {
746       (void) init_child(myName);
747       (void) give_to_child(passwd);  /* old password */
748       code = password_bad(as->parms[1].items->data);
749       (void) terminate_child();
750       if (code)
751         return KABADCMD;
752       ka_StringToKey (as->parms[1].items->data, realm, &key);
753     } 
754     else if (as->parms[2].items) {
755       if (ka_ReadBytes (as->parms[2].items->data, (char *)&key, sizeof(key))
756           != 8) {
757         printf ("Key must be 8 bytes: '%s' was too long\n",
758                 as->parms[2].items->data);
759         return KABADCMD;
760       }
761     } 
762     else {
763         printf ("Must specify new password or key\n");
764         return KABADCMD;
765     }
766
767
768     if (as->parms[3].items) 
769       sscanf (as->parms[3].items->data, "%d", &kvno);
770
771     code = ubik_Call (KAM_SetPassword, conn, 0, name, instance, kvno, key);
772     if (code) com_err (whoami, code,
773                        "so can't set password for %s.%s", name, instance);
774     return code;
775 }
776
777 #define PrintPrincipal(p,n,l) \
778     PrintName((p)->name, (p)->instance, (p)->cell, l, n)
779
780 static afs_int32 PrintName (
781   char *name,
782   char *inst,
783   char *acell,
784   int buflen,
785   char *buf)
786 {
787     int nlen, len;
788     int left;                           /* if ConvertBytes stops early */
789     afs_int32 code;
790
791     if (name == 0) name = "";
792     if (inst == 0) inst = "";
793     if (acell == 0) acell = "";
794     left = ka_ConvertBytes(buf, buflen, name, strlen(name));
795     if (left) {
796       bad_name:
797         code = KABADNAME;
798         com_err (whoami, code,
799                  "PrintName: principal name was '%s'.'%s'@'%s'",
800                  name, inst, acell);
801         return code;
802     }
803     nlen = strlen (buf);
804     len = strlen(inst);
805     if (len) {
806         if (nlen + len + 1 >= buflen) goto bad_name;
807         buf[nlen++] = '.';
808         left = ka_ConvertBytes(buf+nlen, buflen-nlen, inst, len);
809         if (left) goto bad_name;
810         nlen += len;
811     }
812
813     len = strlen(acell);
814     if (len) {
815         char *lcell = ka_LocalCell();
816         if (lcell == 0) lcell = "";
817         if (strcmp (acell, lcell) != 0) {
818             /* only append cell if not the local cell */
819             if (nlen + len + 1 >= buflen) goto bad_name;
820             buf[nlen++] = '@';
821             left = ka_ConvertBytes(buf+nlen, buflen-nlen, acell, len);
822             if (left) goto bad_name;
823             nlen += len;
824         }
825     }
826     return 0;
827 }
828
829 #define PrintedPrincipal(p) PrintedName ((p)->name, (p)->instance, (p)->cell)
830
831 /* PrintedName - returned a pointer to a static string in which the formated
832  * name has been stored. */
833
834 static char *PrintedName (
835   char *name,
836   char *inst,
837   char *cell)
838 {
839     static char printedName[128];
840     afs_int32 code;
841     code = PrintName (name, inst, cell, sizeof(printedName), printedName);
842     if (code) {
843         if (name == 0) name = "";
844         strncpy (printedName, name, sizeof(printedName));
845         printedName[sizeof(printedName)-8] = 0;
846         strcat (printedName, "<error>");
847     }
848     return printedName;
849 }
850
851 static afs_int32 ListTicket (
852   struct ktc_principal *server,
853   int verbose)
854 {
855     afs_int32 code;
856     struct ktc_token token;             /* the token we're printing */
857     struct ktc_principal client;
858     char  UserName[sizeof(struct ktc_principal)];
859     char  ServerName[sizeof(struct ktc_principal)];
860     afs_int32  now = time(0);
861     char bob[KA_TIMESTR_LEN];
862
863     /* get the ticket info itself */
864     code = ktc_GetToken (server, &token, sizeof(token), &client);
865     if (code) {
866         com_err (whoami, code, "failed to get token info for server %s",
867                  PrintedPrincipal (server));
868         return code;
869     }
870     code = PrintPrincipal (&client, UserName, sizeof(UserName));
871     if (code) return code;
872     /* spaces are printed as "\040" */
873     if (UserName[0] == 0)
874         printf("Tokens");
875     else if (strncmp(UserName, "AFS\\040ID\\040", 13) == 0) {
876         printf("User's (AFS ID %s) tokens", UserName+13);
877     }
878     else if (strncmp(UserName, "Unix\\040UID\\040", 15) == 0) {
879         printf("Tokens");
880     }
881     else
882         printf("User %s's tokens", UserName);
883     
884     code = PrintPrincipal (server, ServerName, sizeof(ServerName));
885     if (code) return code;
886     printf(" for %s ", ServerName);
887     
888     if (token.startTime > now) {
889         ka_timestr(token.startTime,bob,KA_TIMESTR_LEN);
890         printf("[>> POSTDATED 'till %s <<]",bob);
891     }
892
893    if (token.endTime <= now)
894         printf("[>> Expired <<]\n");
895     else {
896         ka_timestr(token.endTime,bob,KA_TIMESTR_LEN);
897         printf("[Expires %s]\n", bob);
898     }
899     if (verbose) {
900         printf ("SessionKey: ");
901         ka_PrintBytes ((char *)&token.sessionKey, sizeof(token.sessionKey));
902         printf ("\nTicket (kvno = %d, len = %d): ", token.kvno, 
903                 token.ticketLen);
904         ka_PrintBytes ((char *)token.ticket, token.ticketLen);
905         printf ("\n");
906     }
907     return 0;
908 }
909
910 static GetTicket (
911   struct cmd_syndesc *as,  
912   char *arock)
913 {
914     int code;
915     struct ktc_principal server;
916     struct ktc_token token;
917     afs_int32 life = KA_SIXHOURS;
918
919     if (as->parms[1].items) {
920         code = read_time_interval (as->parms[1].items->data, &life);
921         if (code) return KABADCMD;
922     }
923     code = ka_ParseLoginName (as->parms[0].items->data,
924                               server.name, server.instance, server.cell);
925     if (code) {
926         com_err (whoami, code, "parsing user's name '%s'",
927                  as->parms[0].items->data);
928         return KABADCMD;
929     }
930     if (server.cell[0] == 0) {
931         if (code = DefaultCell()) return code;
932         strcpy (server.cell, cell);
933     } else {
934         code = ka_ExpandCell (server.cell, server.cell, 0/*local*/);
935         if (code) {
936             com_err (whoami, code, "Can't expand cell name");
937             return code;
938         }
939     }
940
941     token.ticketLen = 0;                /* in case there are no tokens */
942     code = ka_GetServerToken (server.name, server.instance, server.cell,
943                               life, &token, /*new*/1, /*dosetpag*/0);
944     if (code) com_err (whoami, code,
945                        "getting ticket for %s", PrintedPrincipal (&server));
946     else {
947         code = ListTicket (&server, /*verbose*/1);
948     }
949     return code;
950 }
951
952 static GetPassword (
953   struct cmd_syndesc *as,
954   char *arock)
955 {
956     int code;
957     char name[MAXKTCNAMELEN];
958     struct ktc_encryptionKey key;
959     static struct ubik_client *lpbkConn = 0;
960
961      /* no instance allowed */
962     code = ka_ParseLoginName (as->parms[0].items->data, name, 0, 0);
963     if (code) {
964       abort:
965         com_err (whoami, code,
966                  "getting %s's password via loopback connection to GetPassword", name);
967         /* if we got a timeout, print a clarification, too */
968         if (code == -1) {
969             fprintf(stderr, "%s: please note that this command must be run locally on a database server machine.\n", whoami);
970         }
971         return code;
972     }
973     if (lpbkConn == 0) {
974         struct rx_connection    *conns[2];
975         struct rx_securityClass *sc;
976         int                      si;    /* security class index */
977
978         code = rx_Init(0);
979         if (code) goto abort;
980         sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject();
981         si = RX_SCINDEX_NULL;
982         conns[0] = rx_NewConnection (htonl(INADDR_LOOPBACK), htons(AFSCONF_KAUTHPORT),
983                                      KA_MAINTENANCE_SERVICE, sc, si);
984         conns[1] = 0;
985         code = ubik_ClientInit(conns, &lpbkConn);
986         if (code) goto abort;
987     }
988     code = ubik_Call (KAM_GetPassword, lpbkConn, 0, name, &key);
989     /* Lets close down the ubik_Client connection now */
990     ubik_ClientDestroy(lpbkConn);
991     if (code) goto abort;
992     printf ("Key: ");
993     ka_PrintBytes ((char *)&key, sizeof(key));
994     printf ("\n");
995     return code;
996 }
997
998 int GetRandomKey (
999   struct cmd_syndesc *as,
1000   char *arock)
1001 {
1002     int code;
1003     struct ktc_encryptionKey key;
1004
1005     code = ubik_Call (KAM_GetRandomKey, conn, 0, &key);
1006     if (code) com_err(whoami, code, "so can't get random key");
1007     else {
1008         int i;
1009         printf ("Key: ");
1010         ka_PrintBytes ((char *)&key, sizeof(key));
1011         printf (" (");
1012         for (i=0; i<sizeof(key); i++) {
1013             printf ("%0.2x", ((char *)&key)[i] & 0xff);
1014             if (i==3) printf (" ");
1015             else if (i!=7) printf (".");
1016         }
1017         printf (")\n");
1018     }
1019     return code;
1020 }
1021
1022 int Statistics (
1023   struct cmd_syndesc *as,
1024   char *arock)
1025 {
1026     int code;
1027     kasstats statics;
1028     kadstats dynamics;
1029     afs_int32     admins;
1030     char bob[KA_TIMESTR_LEN];
1031
1032     code = ubik_Call (KAM_GetStats, conn, 0,
1033                       KAMAJORVERSION, &admins, &statics, &dynamics);
1034     if (code) {
1035         printf ("call to GetStats failed: %s\n", ka_ErrorString(code));
1036         return code;
1037     }
1038     if (statics.minor_version != KAMINORVERSION)
1039         printf ("Minor version number mismatch: got %d, expected %d\n",
1040                 statics.minor_version, KAMINORVERSION);
1041     printf ("%d allocs, %d frees, %d password changes\n",
1042             statics.allocs, statics.frees, statics.cpws);
1043     printf ("Hash table utilization = %f%%\n",
1044             (double)dynamics.hashTableUtilization / 100.0);
1045     ka_timestr(dynamics.start_time,bob,KA_TIMESTR_LEN);
1046     printf ("From host %lx started at %s:\n", dynamics.host, bob);
1047
1048 #define print_stat(name) if (dynamics.name.requests) printf ("  of %d requests for %s, %d were aborted.\n", dynamics.name.requests, # name, dynamics.name.aborts)
1049     print_stat (Authenticate);
1050     print_stat (ChangePassword);
1051     print_stat (GetTicket);
1052     print_stat (CreateUser);
1053     print_stat (SetPassword);
1054     print_stat (SetFields);
1055     print_stat (DeleteUser);
1056     print_stat (GetEntry);
1057     print_stat (ListEntry);
1058     print_stat (GetStats);
1059     print_stat (GetPassword);
1060     print_stat (GetRandomKey);
1061     print_stat (Debug);
1062     print_stat (UAuthenticate);
1063     print_stat (UGetTicket);
1064     
1065 #if (KAMAJORVERSION>5)
1066     print cpu stats
1067     printf ("%d string checks\n", dynamics.string_checks);
1068 #else
1069     printf ("Used %.3f seconds of CPU time.\n", dynamics.string_checks/1000.0);
1070 #endif
1071     printf ("%d admin accounts\n", admins);
1072     return 0;
1073 }
1074
1075 int DebugInfo (
1076   struct cmd_syndesc *as,
1077   char *arock)
1078 {
1079     int code;
1080     struct ka_debugInfo info;
1081     int i;
1082     Date start,now;
1083     int timeOffset;
1084     char bob[KA_TIMESTR_LEN];
1085
1086     start = time(0);
1087     if (as->parms[0].items) {
1088         struct ubik_client *iConn;
1089         code = ka_SingleServerConn (cell, as->parms[0].items->data, KA_MAINTENANCE_SERVICE, 0, &iConn);
1090         if (code) {
1091             struct afsconf_cell cellinfo;
1092
1093             com_err (whoami, code, "couldn't find host %s in cell %s",
1094                      as->parms[0].items->data, cell);
1095             code = ka_GetServers (cell, &cellinfo);
1096             if (code) com_err (whoami, code, "getting servers in cell %s", cell);
1097             else {
1098                 printf ("Servers in cell %s, are:\n", cell);
1099                 for (i=0; i<cellinfo.numServers; i++)
1100                     printf ("  %s\n", cellinfo.hostName[i]);
1101             }
1102             return code;
1103         }
1104         code = ubik_Call (KAM_Debug, iConn, 0, KAMAJORVERSION, 0, &info);
1105         ubik_ClientDestroy (iConn);
1106     }
1107     else code = ubik_Call (KAM_Debug, conn, 0, KAMAJORVERSION, 0, &info);
1108
1109     if (code) {
1110         com_err (whoami, code, "call to Debug failed");
1111         return code;
1112     }
1113     now = time(0);
1114
1115     if (info.minorVersion != KAMINORVERSION)
1116         printf ("Minor version number mismatch: got %d, expected %d\n",
1117                 info.minorVersion, KAMINORVERSION);
1118
1119     timeOffset = info.
1120 #if (KAMAJORVERSION>5)
1121         now
1122 #else
1123         reserved1
1124 #endif
1125         - now;
1126     if (timeOffset < 0) timeOffset = -timeOffset;
1127     if (timeOffset > 60) {
1128         printf ("WARNING: Large server client clock skew: %d seconds. Call itself took %d seconds.\n", timeOffset, now-start);
1129     }
1130     ka_timestr(info.startTime,bob,KA_TIMESTR_LEN);
1131     printf ("From host %lx started %sat %s:\n",
1132             info.host, (info.noAuth ? "w/o authorization " : ""),
1133             bob);
1134     ka_timestr (info.lastTrans,bob,KA_TIMESTR_LEN);
1135     printf ("Last trans was %s at %s\n", info.lastOperation, bob);
1136     ka_timestr (info.dbHeaderRead,bob,KA_TIMESTR_LEN);
1137     printf ("Header last read %s.\n", bob);
1138     printf ("db version=%d, keyVersion=%d, key cache version=%d\n",
1139             info.dbVersion, info.dbSpecialKeysVersion, info.kcVersion);
1140     printf ("db ptrs: free %d, eof %d, kvno %d.\n",
1141             info.dbFreePtr, info.dbEofPtr, info.dbKvnoPtr);
1142     ka_timestr (info.nextAutoCPW,bob,KA_TIMESTR_LEN);
1143     printf ("Next autoCPW at %s or in %d updates.\n",
1144             bob, info.updatesRemaining);
1145     if (info.cheader_lock || info.keycache_lock)
1146         printf ("locks: cheader %08lx, keycache %08lx\n",
1147                 info.cheader_lock, info.keycache_lock);
1148     printf ("Last authentication for %s, last admin user was %s\n",
1149             info.lastAuth, info.lastAdmin);
1150     printf ("Last TGS op was a %s ticket was for %s\n",
1151             info.lastTGSServer, info.lastTGS);
1152     printf ("Last UDP TGS was a %s ticket for %s.  UDP Authenticate for %s\n",
1153             info.lastUTGSServer, info.lastUTGS, info.lastUAuth);
1154     printf ("key cache size %d, used %d.\n",
1155             info.kcSize, info.kcUsed);
1156     if (info.kcUsed > KADEBUGKCINFOSIZE) {
1157         printf("insufficient room to return all key cache entries!\n");
1158         info.kcUsed = KADEBUGKCINFOSIZE;
1159     }
1160     for (i=0; i<info.kcUsed; i++)
1161         ka_timestr(info.kcInfo[i].used,bob,KA_TIMESTR_LEN);
1162         printf ("%32s %c %2x(%2x) used %s\n",
1163                 info.kcInfo[i].principal, (info.kcInfo[i].primary?'*':' '),
1164                 info.kcInfo[i].kvno, info.kcInfo[i].keycksum,
1165                 bob);
1166     return 0;
1167 }
1168
1169 int Interactive (
1170   struct cmd_syndesc *as,
1171   char *arock)
1172 {
1173     finished = 0;
1174     return 0;
1175 }
1176
1177 int Quit (
1178   struct cmd_syndesc *as,
1179   char *arock)
1180 {
1181     finished = 1;
1182     return 0;
1183 }
1184
1185 int MyAfterProc(
1186   struct cmd_syndesc *as)
1187 {
1188     if (!strcmp(as->name,"help")) return;
1189
1190     /* Determine if we need to destory the ubik connection.
1191      * Closing it avoids resends of packets. 
1192      */
1193     if (conn) {
1194         ubik_ClientDestroy(conn);
1195         conn = 0;
1196     }
1197
1198     return 0;
1199 }
1200
1201 int   init = 0, noauth;
1202 char  name[MAXKTCNAMELEN];
1203 char  instance[MAXKTCNAMELEN];
1204 char  newCell[MAXKTCREALMLEN];
1205 afs_int32 serverList[MAXSERVERS];
1206
1207 int NoAuth (
1208    struct cmd_syndesc *as,
1209    char *arock)
1210 {
1211    noauth = 1;
1212    return 0;
1213 }
1214
1215 static int MyBeforeProc(
1216   struct cmd_syndesc *as,
1217   char *arock)
1218 {
1219     extern struct passwd *getpwuid();
1220     struct passwd *pw;
1221     struct ktc_encryptionKey key;
1222     struct ktc_principal auth_server, auth_token, client;
1223     char realm[MAXKTCREALMLEN];
1224
1225     struct ktc_token token, *pToken;
1226     int i, acode, code = 0;
1227
1228     {   char *ws  = strrchr (as->a0name, '/');
1229         if (ws) ws++;                   /* skip everything before the "/" */
1230         else ws = as->a0name;
1231         if (strlen(ws) > 0) {
1232             strncpy (whoami, ws, sizeof(whoami));
1233             if (strlen (whoami)+1 >= sizeof(whoami)) strcpy (whoami, "kas:");
1234             else strcat (whoami, ":");
1235         } else whoami[0] = 0;
1236         /* append sub-command name */
1237         strncat (whoami, as->name, sizeof(whoami) - strlen(whoami) -1);
1238     }
1239
1240     if (as->parms[12].name == 0) return 0;
1241
1242     assert (as->parms[13].name && as->parms[14].name &&
1243             as->parms[15].name && as->parms[16].name);
1244
1245     /* MyAfterProc() destroys the conn, but just to be sure */
1246     if (conn) {
1247        code = ubik_ClientDestroy (conn);
1248        conn = 0;
1249     }
1250
1251     if (!init || as->parms[12].items || as->parms[13].items ||
1252                  as->parms[14].items || as->parms[15].items ||
1253                  as->parms[16].items) {
1254        strcpy (instance, "");
1255        strcpy (newCell,  "");
1256
1257        if (as->parms[12].items) {                     /* -admin_username */
1258           code = ka_ParseLoginName (as->parms[12].items->data, name, instance, newCell);
1259           if (code) {
1260              com_err (whoami, code, "parsing user's name '%s'", as->parms[12].items->data);
1261              return code;
1262           }
1263        } else {
1264 #ifdef AFS_NT40_ENV
1265           DWORD len = MAXKTCNAMELEN;
1266           if (!GetUserName((LPTSTR)name, &len)) {
1267              printf("Can't get user name \n");
1268              return KABADCMD;
1269           }
1270 #else
1271           /* No explicit name provided: use Unix uid. */
1272           pw = getpwuid(getuid());
1273           if (pw == 0) {
1274              printf ("Can't figure out your name from your user id.\n");
1275              return KABADCMD;
1276           }
1277           strncpy (name, pw->pw_name, sizeof(name));
1278 #endif
1279        }
1280
1281        if (as->parms[14].items) {                     /* -cell */
1282           if (strlen(newCell) > 0) {
1283              printf ("Duplicate cell specification not allowed\n");
1284           } else {
1285              strncpy (newCell, as->parms[14].items->data, sizeof(newCell));
1286           }
1287        }
1288        code = ka_ExpandCell (newCell, newCell, 0/*local*/);
1289        if (code) {
1290           com_err (whoami, code, "Can't expand cell name");
1291           return code;
1292        }
1293        strcpy (cell, newCell);
1294
1295        if (as->parms[15].items) {                   /* -servers */
1296           struct cmd_item *ip;
1297           char *ap[MAXSERVERS+2];
1298
1299           ap[0] = "";
1300           ap[1] = "-servers";
1301           for (ip = as->parms[15].items, i=2; ip; ip=ip->next, i++)
1302             ap[i] = ip->data;
1303           code = ubik_ParseClientList(i, ap, serverList);
1304           if (code) {
1305              com_err (whoami, code, "could not parse server list");
1306              return code;
1307           }
1308           ka_ExplicitCell (cell, serverList);
1309        }
1310
1311        noauth = (as->parms[16].items ? 1 : 0);       /* -noauth */
1312
1313        init = 1;
1314     }
1315
1316     token.ticketLen = 0;                /* in case there are no tokens */
1317     if (!noauth) {                      /* Will prompt for a password */
1318        /* first see if there's already an admin ticket */
1319        code = ka_GetAdminToken (0, 0, cell, 0, KA_SIXHOURS, &token, 0/* !new */);
1320        if (code) {                      /* if not then get key and try again */
1321           if (as->parms[13].items) { /* if password specified */
1322              strncpy (passwd, as->parms[13].items->data, sizeof(passwd));
1323              bzero (as->parms[13].items->data, strlen (as->parms[13].items->data));
1324           } else {
1325              char msg[MAXKTCNAMELEN+50];
1326              if (as->parms[12].items) sprintf (msg, "Administrator's (%s) Password: ", name);
1327              else sprintf (msg, "Password for %s: ", name);
1328              code = read_pw_string (passwd, sizeof(passwd), msg, 0);
1329              if (code) code = KAREADPW;
1330              else if (strlen(passwd) == 0) code = KANULLPASSWORD;
1331              if (code) {
1332                 com_err (whoami, code, "reading password");
1333                 return code;
1334              }
1335           }
1336           ka_StringToKey (passwd, cell, &key);
1337           code = ka_GetAdminToken (name, instance, cell, &key, KA_SIXHOURS,
1338                                    &token, 0/* !new */);
1339           if (code == KABADREQUEST) {
1340               des_string_to_key (passwd, &key);
1341               code = ka_GetAdminToken (name, instance, cell, &key, KA_SIXHOURS,
1342                                        &token, 0/* !new */);
1343           }
1344           if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
1345              /* try with only the first 8 characters incase they set
1346               * their password with an old style passwd program. */
1347              passwd[8] = 0;
1348              ka_StringToKey (passwd, cell, &key);
1349              code = ka_GetAdminToken (name, instance, cell, &key,
1350                                       KA_SIXHOURS, &token, 0/* !new */);
1351              if (code == 0) {
1352                 fprintf (stderr, "Warning: you have typed a password longer than 8 characters, but only the\n");
1353                 fprintf (stderr, "first 8 characters were actually significant.  If you change your password\n");
1354                 fprintf (stderr, "again this warning message will go away.\n");
1355              }
1356           }
1357           if (code) {
1358              char *reason;
1359              switch (code) {
1360                 case KABADREQUEST:
1361                    reason = "password was incorrect";
1362                    break;
1363                  case KAUBIKCALL:
1364                    reason = "Authentication Server was unavailable";
1365                    break;
1366                  default:
1367                    reason = (char *)error_message (code);
1368              }
1369              fprintf (stderr, "%s: Auth. as %s to AuthServer failed: %s\nProceeding w/o authentication\n",
1370                       whoami, PrintedName(name,instance,cell), reason);
1371           }
1372           /* get an Authentication token while were at it. */
1373           if (ka_CellToRealm(cell, realm, 0) != 0) realm[0] = '\0';
1374           strcpy(auth_server.name, KA_TGS_NAME);
1375           strcpy(auth_server.instance, realm);
1376           strcpy(auth_server.cell, cell);
1377           if (ktc_GetToken(&auth_server, &auth_token, sizeof(struct ktc_token), &client) != 0) {
1378              acode = ka_GetAuthToken (name, instance, cell, &key,
1379                                       MAXKTCTICKETLIFETIME,
1380                                       (afs_int32 *)0 /*Don't need pwd expiration info here*/);
1381              if (acode && (acode != code)) /* codes are usually the same */
1382                com_err (whoami, code,
1383                         "getting Authentication token for %s",
1384                         PrintedName (name, instance, cell));
1385           }
1386           bzero (&key, sizeof(key));
1387        }
1388     }
1389
1390     pToken = ((token.ticketLen == 0) ? 0 : &token);
1391     code = ka_AuthServerConn (cell, KA_MAINTENANCE_SERVICE, pToken, &conn);
1392     if (code && pToken) {
1393        com_err (whoami, code,
1394                 "connecting to AuthServer: now trying w/o authentication");
1395        code = ka_AuthServerConn (cell, KA_MAINTENANCE_SERVICE, 0, &conn);
1396        if (code) com_err (whoami, code, "making unauthenticated connection to AuthServer");
1397     }
1398     if (code) {
1399        com_err (whoami, code, "Couldn't establish connection to Authentication Server");
1400        return code;
1401     }
1402
1403     /* now default unspecified password by prompting from terminal */
1404     if (as->nParms >= 12) for (i=0; i<12; i++)
1405         if (as->parms[i].name && (as->parms[i].items == 0)) {
1406             char *p = as->parms[i].name; /* parameter name */
1407             int l = strlen (p);         /* length of name */
1408             /* does parameter end in "password"  */
1409             if (strcmp (p+(l-8), "password") == 0) {
1410                 char msg[32];
1411                 char password[BUFSIZ];
1412                 struct cmd_item *ip;
1413
1414                 strcpy (msg, p+1);
1415                 strcat (msg, ": ");
1416                 code = read_pw_string (password, sizeof(password), msg, 1);
1417                 if (code) code = KAREADPW;
1418                 else if (strlen(password) == 0) code = KANULLPASSWORD;
1419                 if (code) {
1420                     com_err (whoami, code, "prompting for %s", p+1);
1421                     return code;
1422                 }
1423                 ip = (struct cmd_item *)malloc (sizeof(struct cmd_item));
1424                 ip->data = (char *)malloc (strlen(password)+1);
1425                 ip->next = 0;
1426                 strcpy (ip->data, password);
1427                 as->parms[i].items = ip;
1428             }
1429         }
1430     if (!conn) {                        /* if all else fails... */
1431         code = NoAuth (0,0);            /* get unauthenticated conn */
1432         if (code) return code;
1433     }
1434     return 0;
1435 }
1436
1437 /* These are some helpful command that deal with the cache managers tokens. */
1438
1439 static ForgetTicket (
1440   struct cmd_syndesc *as,  
1441   char *arock)
1442 {
1443     afs_int32 code;
1444     struct ktc_principal server;
1445
1446 #ifdef notdef
1447     if (as->parms[0].items) {
1448         char *name = as->parms[0].items->data;
1449         code = ka_ParseLoginName
1450             (name, server.name, server.instance, server.cell);
1451         if (code) {
1452             com_err (whoami, code, "couldn't interpret name '%s'", name);
1453             return code;
1454         }
1455         if (server.cell[0] == 0) {
1456             if (code = DefaultCell()) return code;
1457             strcpy (server.cell, cell);
1458         } else {
1459             code = ka_ExpandCell (server.cell, server.cell, 0/*local*/);
1460             if (code) {
1461                 com_err (whoami, code, "Can't expand cell name");
1462                 return code;
1463             }
1464         }
1465         code = ktc_ForgetToken (&server);
1466         if (code) {
1467             com_err (whoami, code, "couldn't remove tokens for %s",
1468                      PrintedPrincipal (&server));
1469             return code;
1470         }
1471     }
1472     else {
1473         if (!as->parms[1].items) {
1474             fprintf (stderr, "Must specify server name or -all\n");
1475             return KABADCMD;
1476         }
1477         code = ktc_ForgetAllTokens();
1478         if (code) {
1479             com_err (whoami, code, "couldn't delete all tokens");
1480             return code;
1481         }
1482     }
1483 #endif
1484     code = ktc_ForgetAllTokens();
1485     if (code) {
1486        com_err (whoami, code, "couldn't delete all tokens");
1487        return code;
1488     }
1489     return 0;
1490 }
1491
1492 static ListTickets (
1493   struct cmd_syndesc *as,
1494   char *arock)
1495 {
1496     afs_int32 code=0;
1497     int   index, newIndex;
1498     struct ktc_principal server;
1499     int  verbose = 0;
1500
1501     if (as->parms[1].items) verbose = 1;
1502     if (as->parms[0].items) {
1503         char *name = as->parms[0].items->data;
1504         code = ka_ParseLoginName
1505             (name, server.name, server.instance, server.cell);
1506         if (code) {
1507             com_err (whoami, code, "couldn't interpret name '%s'", name);
1508             return code;
1509         }
1510         if (server.cell[0] == 0) {
1511             if (code = DefaultCell()) return code;
1512             strcpy (server.cell, cell);
1513         } else {
1514             code = ka_ExpandCell (server.cell, server.cell, 0/*local*/);
1515             if (code) {
1516                 com_err (whoami, code, "Can't expand cell name");
1517                 return code;
1518             }
1519         }
1520         code = ListTicket (&server, verbose);
1521     }
1522     else for (index = 0; ; index = newIndex) {
1523         code = ktc_ListTokens(index, &newIndex, &server);
1524         if (code) {
1525             if (code == KTC_NOENT) code = 0;   /* end of list */
1526             break;
1527         }
1528         code = ListTicket(&server, verbose);
1529     }
1530     return code;
1531 }
1532
1533 static void add_std_args (register struct cmd_syndesc *ts)
1534 {
1535     cmd_Seek(ts, 12);
1536     /* 12 */ cmd_AddParm (ts, "-admin_username", CMD_SINGLE, CMD_OPTIONAL,
1537                           "admin principal to use for authentication");
1538     /* 13 */ cmd_AddParm (ts, "-password_for_admin", CMD_SINGLE, CMD_OPTIONAL,
1539                           "admin password");
1540     /* 14 */ cmd_AddParm (ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1541     /* 15 */ cmd_AddParm (ts, "-servers", CMD_LIST, CMD_OPTIONAL,
1542                           "explicit list of authentication servers");
1543     /* 16 */ cmd_AddParm (ts, "-noauth", CMD_FLAG, CMD_OPTIONAL,
1544                           "don't authenticate");
1545 }
1546
1547 afs_int32 ka_AdminInteractive (
1548   int   cmd_argc,
1549   char *cmd_argv[])
1550 {
1551     register int   code;
1552     register struct cmd_syndesc *ts;
1553
1554     char  line[BUFSIZ];
1555     afs_int32 argc;
1556     char *argv[32];
1557
1558     strncpy(myName, *cmd_argv, 509);
1559
1560     cmd_SetBeforeProc(MyBeforeProc,  (char *) 0);
1561     cmd_SetAfterProc(MyAfterProc, (char *) 0);
1562
1563     ts = cmd_CreateSyntax ("interactive", Interactive, 0, "enter interactive mode");
1564     add_std_args (ts);
1565
1566     ts = cmd_CreateSyntax ("noauthentication", NoAuth, 0, "connect to AuthServer w/o using token");
1567
1568     ts = cmd_CreateSyntax ("list", ListUsers, 0, "list all users in database");
1569     cmd_AddParm (ts, "-long", CMD_FLAG, CMD_OPTIONAL, "show detailed info about each user");
1570     cmd_AddParm (ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL, "show all cell administrators");
1571     cmd_AddParm (ts, "-showkey", CMD_FLAG, CMD_OPTIONAL, "show the user's actual key rather than the checksum");
1572     add_std_args (ts);
1573     cmd_CreateAlias (ts, "ls");
1574
1575     ts = cmd_CreateSyntax ("examine", ExamineUser, 0, "examine the entry for a user");
1576     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1577     cmd_AddParm (ts, "-showkey", CMD_FLAG, CMD_OPTIONAL, "show the user's actual key rather than the checksum");
1578     add_std_args (ts);
1579
1580     ts = cmd_CreateSyntax ("create", CreateUser, 0, "create an entry for a user");
1581     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1582     cmd_AddParm (ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL, "initial password");
1583     add_std_args (ts);
1584
1585     ts = cmd_CreateSyntax ("delete", DeleteUser, 0, "delete a user");
1586     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1587     add_std_args (ts);
1588     cmd_CreateAlias (ts, "rm");
1589
1590     ts = cmd_CreateSyntax ("setfields", SetFields, 0, "set various fields in a user's entry");
1591     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1592     cmd_AddParm (ts, "-flags", CMD_SINGLE, CMD_OPTIONAL, "hex flag value or flag name expression");
1593     cmd_AddParm (ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL, "date of account expiration");
1594     cmd_AddParm (ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "maximum ticket lifetime");
1595     cmd_AddParm (ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL, "number days password is valid ([0..254])");
1596     cmd_AddParm (ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL, "permit password reuse (yes/no)");
1597     cmd_AddParm (ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL, "maximum successive failed login tries ([0..254])");
1598     cmd_AddParm (ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL, "failure penalty [hh:mm or minutes]");
1599 #if ASSOCIATES
1600     cmd_AddParm (ts, "-associates", CMD_SINGLE, CMD_OPTIONAL, "maximum associate instances");
1601 #endif
1602     add_std_args (ts);
1603     cmd_CreateAlias (ts, "sf");
1604
1605
1606     ts = cmd_CreateSyntax ("unlock", Unlock, 0, "Enable authentication ID after max failed attempts exceeded");
1607     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "authentication ID");
1608     add_std_args (ts);
1609
1610
1611     ts = cmd_CreateSyntax ("stringtokey", StringToKey, 0, "convert a string to a key");
1612     cmd_AddParm (ts, "-string", CMD_SINGLE, 0, "password string");
1613     cmd_AddParm (ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1614
1615     ts = cmd_CreateSyntax ("setpassword", SetPassword, 0, "set a user's password");
1616     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1617     cmd_AddParm (ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL, "new password");
1618     cmd_Seek(ts, 3);
1619     cmd_AddParm (ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1620     add_std_args (ts);
1621     cmd_CreateAlias (ts, "sp");
1622 #ifdef CMD_PARSER_AMBIG_FIX
1623     cmd_CreateAlias (ts, "setpasswd");
1624 #endif
1625
1626     /* set a user's key */
1627     ts = cmd_CreateSyntax ("setkey", SetPassword, 0, (char *) CMD_HIDDEN);
1628     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1629     cmd_Seek(ts, 2);
1630     cmd_AddParm (ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1631     cmd_Seek(ts, 3);
1632     cmd_AddParm (ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1633     add_std_args (ts);
1634
1635     /* get a user's password */
1636     ts = cmd_CreateSyntax ("getpassword", GetPassword, 0, (char *) CMD_HIDDEN);
1637     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of user");
1638     /* don't take standard args */
1639     /* add_std_args (ts); */
1640 #ifdef CMD_PARSER_AMBIG_FIX
1641     cmd_CreateAlias (ts, "getpasswd");
1642 #endif
1643
1644     /* get a random key */
1645     ts = cmd_CreateSyntax ("getrandomkey", GetRandomKey, 0, (char *) CMD_HIDDEN);
1646     add_std_args (ts);
1647
1648     /* get a ticket for a specific server */
1649     ts = cmd_CreateSyntax ("getticket", GetTicket, 0, (char *) CMD_HIDDEN);
1650     cmd_AddParm (ts, "-name", CMD_SINGLE, 0, "name of server");
1651     cmd_AddParm (ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1652     add_std_args (ts);
1653
1654     ts = cmd_CreateSyntax ("statistics", Statistics, 0, "show statistics for AuthServer");
1655     add_std_args (ts);
1656
1657     /* show debugging info from AuthServer */
1658     ts = cmd_CreateSyntax ("debuginfo", DebugInfo, 0, (char *) CMD_HIDDEN);
1659     cmd_AddParm (ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL, "authentication server host name");
1660     add_std_args (ts);
1661
1662     ts = cmd_CreateSyntax ("forgetticket", ForgetTicket, 0, "delete user's tickets");
1663 #ifdef notdef
1664     cmd_AddParm (ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE), "name of server");
1665 #endif
1666     cmd_AddParm (ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1667
1668     ts = cmd_CreateSyntax ("listtickets", ListTickets, 0, "show all cache manager tickets");
1669     cmd_AddParm (ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1670     cmd_AddParm (ts, "-long", CMD_FLAG, CMD_OPTIONAL, "show session key and ticket");
1671
1672     ts = cmd_CreateSyntax ("quit", Quit, 0, "exit program");
1673
1674     finished = 1;
1675     conn = 0;                           /* no connection yet */
1676     zero_argc = cmd_argc;
1677     zero_argv = cmd_argv;
1678
1679     strcpy (whoami, "kas");
1680
1681     if (code = cmd_Dispatch(cmd_argc, cmd_argv)) {
1682         return code;
1683     }
1684
1685     while (!finished) {
1686         char *s;
1687         int i;
1688
1689         printf("ka> ");
1690         s = fgets (line, sizeof(line), stdin);
1691         if (s == NULL) return 0;        /* EOF on input */
1692         for (i=strlen(line)-1; i>=0 && isspace(line[i]); i--) line[i]=0;
1693         if (i < 0) continue;            /* blank line */
1694
1695         code = cmd_ParseLine (line, argv, &argc, sizeof(argv)/sizeof(argv[0]));
1696         if (code) {
1697             com_err (whoami, code, "parsing line: '%s'", line);
1698             return code;
1699         }
1700         code = cmd_Dispatch (argc, argv);
1701         cmd_FreeArgv (argv);
1702     }
1703     return code;
1704 }