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