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