rx: Hide struct rx_peer from the rest of the tree
[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 <lock.h>
32 #define UBIK_LEGACY_CALLITER 1
33 #include <ubik.h>
34 #include <afs/auth.h>
35 #include <afs/cellconfig.h>
36 #include <afs/cmd.h>
37 #include <afs/com_err.h>
38 #include <afs/afsutil.h>
39
40 #include "kauth.h"
41 #include "kauth_internal.h"
42 #include "kautils.h"
43 #include "kaport.h"
44 #include "kkids.h"
45
46 #define CMD_PARSER_AMBIG_FIX 1  /* allow ambiguous aliases */
47
48 #define KA_SIXHOURS     (6*3600)
49
50 static struct ubik_client *conn;
51 static char cell[MAXKTCREALMLEN] = "";
52 static char whoami[32];
53 static char passwd[BUFSIZ];
54 static char myName[510];        /* almost like whoami save with path and without : */
55
56 static int finished;
57 static int zero_argc;
58 static char **zero_argv;
59 afs_uint32 ka_islocked(char *, char *, afs_uint32 *);
60
61 afs_int32
62 DefaultCell(void)
63 {
64     afs_int32 code;
65
66     if (cell[0] != 0)
67         return 0;
68     code = ka_ExpandCell(0, cell, 0 /*local */ );
69     if (code) {
70         afs_com_err(whoami, code, "Can't expand cell name");
71     }
72     return code;
73 }
74
75 /* These are the command operation procedures. */
76
77 int
78 DumpUser(char *user, char *arock, int showadmin, int showkey, char *inst)
79 {
80     char name[MAXKTCNAMELEN];
81     char instance[MAXKTCNAMELEN];
82     Date now = time(0);
83     int code;
84     char bob[KA_TIMESTR_LEN];
85
86     struct kaentryinfo tentry;
87
88     code = ka_ParseLoginName(user, name, instance, 0);
89     if (code) {
90         afs_com_err(whoami, code, "parsing user's name '%s'", user);
91         return KABADCMD;
92     }
93
94     if (!inst)
95         inst = instance;
96     code =
97         ubik_KAM_GetEntry(conn, 0, name, inst, KAMAJORVERSION, &tentry);
98     if (code) {
99         afs_com_err(whoami, code, "getting information for %s.%s", name, inst);
100         return code;
101     }
102     if (tentry.minor_version != KAMINORVERSION)
103         printf("Minor version number mismatch: got %d, expected %d\n",
104                tentry.minor_version, KAMINORVERSION);
105     if (showadmin && !(tentry.flags & KAFADMIN))
106         return 0;
107     ka_PrintUserID("\nUser data for ", name, inst, "");
108     {
109         char *prefix = " (";
110 #define NEWPREFIX "+"
111         if (tentry.flags & KAFADMIN) {
112             printf("%sADMIN", prefix);
113             prefix = NEWPREFIX;
114         }
115         if (tentry.flags & KAFNOTGS) {
116             printf("%sNOTGS", prefix);
117             prefix = NEWPREFIX;
118         }
119         if (tentry.flags & KAFNOCPW) {
120             printf("%sNOCPW", prefix);
121             prefix = NEWPREFIX;
122         }
123         if (tentry.flags & KAFNOSEAL) {
124             printf("%sNOSEAL", prefix);
125             prefix = NEWPREFIX;
126         }
127         if (tentry.flags & KAFNEWASSOC) {
128             printf("%sNEWASSOC", prefix);
129             prefix = NEWPREFIX;
130         }
131         if (tentry.flags & KAFASSOCROOT) {
132             printf("%sASSOCROOT", prefix);
133             prefix = NEWPREFIX;
134         }
135         if (tentry.flags & KAFASSOC) {
136             printf("%sASSOC", prefix);
137             prefix = NEWPREFIX;
138         }
139         if (tentry.user_expiration <= now) {
140             printf("%sexpired", prefix);
141             prefix = NEWPREFIX;
142         }
143         if (strcmp(prefix, NEWPREFIX) == 0)
144             printf(")\n");
145         else
146             printf("\n");
147     }
148     if ((!ka_KeyIsZero((char *)&tentry.key, sizeof(tentry.key))) && (showkey)) {
149         printf("  key (%d):", tentry.key_version);
150         ka_PrintBytes((char *)&tentry.key, sizeof(tentry.key));
151     } else {
152         if (tentry.keyCheckSum == 0)
153             printf("  key version is %d", tentry.key_version);
154         else
155             printf("  key (%d) cksum is %u", tentry.key_version,
156                    tentry.keyCheckSum);
157     }
158     ka_timestr(tentry.change_password_time, bob, KA_TIMESTR_LEN);
159     printf(", last cpw: %s\n", bob);
160     if (!tentry.misc_auth_bytes) {
161         printf("  password will never expire.\n");
162         printf
163             ("  An unlimited number of unsuccessful authentications is permitted.\n");
164     } else {
165         unsigned char misc_stuff[4];
166         afs_uint32 temp;
167
168         temp = tentry.misc_auth_bytes;
169 /*
170       temp = ntohl(tentry.misc_auth_bytes);
171 */
172         unpack_long(temp, misc_stuff);
173
174         if (!misc_stuff[0]) {
175             printf("  password will never expire.\n");
176         } else {
177             ka_timestr((tentry.change_password_time +
178                         misc_stuff[0] * 24 * 60 * 60), bob, KA_TIMESTR_LEN);
179             printf("  password will expire: %s\n", bob);
180         }
181
182         if (!misc_stuff[2])
183             printf
184                 ("  An unlimited number of unsuccessful authentications is permitted.\n");
185         else {
186             printf
187                 ("  %d consecutive unsuccessful authentications are permitted.\n",
188                  misc_stuff[2]);
189
190             if (!misc_stuff[3])
191                 printf("  The lock time for this user is not limited.\n");
192             else
193                 printf("  The lock time for this user is %4.1f minutes.\n",
194                        (float)((unsigned int)misc_stuff[3] << 9) / 60.0);
195
196             if (!(misc_stuff[1] & KA_ISLOCKED)
197                 || !ka_islocked(name, instance, &temp))
198                 printf("  User is not locked.\n");
199             else if (temp == (afs_uint32) (-1L))
200                 printf("  User is locked forever.\n");
201             else {
202                 ka_timestr(temp, bob, KA_TIMESTR_LEN);
203                 printf("  User is locked until %s\n", bob);
204             }
205         }
206
207     }
208     {
209         char exp[KA_TIMESTR_LEN];
210         ka_timestr(tentry.user_expiration, exp, KA_TIMESTR_LEN);
211         if (tentry.user_expiration < now)
212             printf("  DISABLED entry at %s.", exp);
213         else if (tentry.user_expiration == NEVERDATE)
214             printf("  entry never expires.");
215         else
216             printf("  entry expires on %s.", exp);
217     }
218     printf("  Max ticket lifetime %.2f hours.\n",
219            tentry.max_ticket_lifetime / 3600.0);
220     ka_timestr(tentry.modification_time, bob, KA_TIMESTR_LEN);
221     printf("  last mod on %s by ", bob);
222     ka_PrintUserID("", tentry.modification_user.name,
223                    tentry.modification_user.instance, "\n");
224     if ((tentry.reserved3 & 0xffff0000) == 0x12340000) {
225         int short reused = (short)tentry.reserved3;
226         if (!reused) {
227             printf("  permit password reuse\n");
228         } else {
229             printf("  don't permit password reuse\n");
230         }
231     }
232     return 0;
233 }
234
235 int
236 ListUsers(struct cmd_syndesc *as, void *arock)
237 {
238     struct kaident name;
239     afs_int32 index;
240     afs_int32 count;
241     afs_int32 next_index;
242     int code, all = 0, showa = 0;
243     int showkey = (as->parms[2].items != NULL);
244
245     if (as->parms[0].items)
246         all = 1;
247     if (as->parms[1].items) {
248         all = 1;
249         showa = 1;
250     }
251     for (index = 0; 1; index = next_index) {
252         code =
253             ubik_KAM_ListEntry(conn, 0, index, &next_index, &count,
254                       &name);
255         if (code) {
256             afs_com_err(whoami, code, "calling KAM_ListEntry");
257             break;
258         }
259         if (!next_index)
260             break;
261         if (next_index < 0)
262             printf("next_index (%d) is negative: ", next_index);
263         if (strlen(name.name) == 0)
264             printf("name is zero length: ");
265         if (all)
266             DumpUser(name.name, NULL, showa, showkey, name.instance);
267         else
268             ka_PrintUserID("", name.name, name.instance, "\n");
269     }
270     return code;
271 }
272
273
274 int
275 ExamineUser(struct cmd_syndesc *as, void *arock)
276 {
277     int showkey = (as->parms[1].items != NULL);
278     return DumpUser(as->parms[0].items->data, arock, 0, showkey, NULL);
279 }
280
281
282 struct OKerrors {
283     int code;
284     char *msg;
285 };
286
287 int
288 handle_errors(int code,         /* error code to handle */
289               struct OKerrors OKlist[], /* list of errors & messages that should be ignored */
290               int *persist)
291 {                               /* set this if we should retry, clear otherwise */
292     int i;
293
294     for (i = 0; OKlist[i].code; i++) {
295         if (OKlist[i].code == code) {
296             printf("%s\n", OKlist[i].msg);
297             *persist = 0;       /* we're done */
298             return 0;
299         }
300     }
301
302     printf(" : [%s] %s", afs_error_table_name(code), afs_error_message(code));
303     switch (code) {
304     case UNOQUORUM:
305         printf(", wait one second\n");
306         IOMGR_Sleep(1);
307         return 0;
308     case KAEMPTY:
309     case RX_CALL_TIMEOUT:
310         printf(" (retrying)\n");
311         return 0;
312     }
313     printf("\n");
314
315     *persist = 0;               /* don't retry these errors */
316     return code;
317 }
318
319 int
320 CreateUser(struct cmd_syndesc *as, void *arock)
321 {
322     int code;
323     char name[MAXKTCNAMELEN];
324     char instance[MAXKTCNAMELEN];
325     struct ktc_encryptionKey key;
326
327     int persist = 1;
328     struct OKerrors OKlist[2];
329     OKlist[0].code = 0;
330
331     code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
332     if (code) {
333         afs_com_err(whoami, code, "parsing user's name '%s'",
334                 as->parms[0].items->data);
335         return KABADCMD;
336     }
337     ka_StringToKey(as->parms[1].items->data, cell, &key);
338
339     do {
340         code = ubik_KAM_CreateUser(conn, 0, name, instance,
341                                    *ktc_to_EncryptionKey(&key));
342         if (!code)
343             return 0;
344         ka_PrintUserID("Creating user ", name, instance, " ");
345         code = handle_errors(code, OKlist, &persist);
346     } while (persist);
347     return code;
348 }
349
350 int
351 DeleteUser(struct cmd_syndesc *as, void *arock)
352 {
353     int code;
354     char name[MAXKTCNAMELEN];
355     char instance[MAXKTCNAMELEN];
356
357     int persist = 1;
358     struct OKerrors OKlist[2];
359     OKlist[0].code = 0;
360     code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
361     if (code) {
362         afs_com_err(whoami, code, "parsing user's name '%s'",
363                 as->parms[0].items->data);
364         return KABADCMD;
365     }
366
367     do {
368         code = ubik_KAM_DeleteUser(conn, 0, name, instance);
369         if (!code)
370             return 0;
371         ka_PrintUserID("Deleting user ", name, instance, " ");
372         code = handle_errors(code, OKlist, &persist);
373     } while (persist);
374     return code;
375 }
376
377 static int
378 read_time_interval(char *str, afs_int32 * seconds)
379 {
380     char *s;
381     int sec = 0;
382     char buf[32];
383
384     str = strncpy(buf, str, sizeof(buf));
385     s = strchr(str, ':');
386     if (s == 0)
387         sec = atoi(str);
388     else {
389         *s++ = '\0';            /* separate hours and minutes */
390         sec = atoi(str) * 3600 + atoi(s) * 60;
391     }
392     *seconds = sec;
393     return 0;
394 }
395
396 int
397 parse_flags(char *name, char *inst, char *str, afs_int32 * flags)
398 {
399     struct kaentryinfo tentry;
400     int code;
401     char bitspec[100];
402     afs_int32 f;
403     char bit[25];
404     char c;
405     int addop;                  /* 1=add bit; 0=remove bit */
406     int flag;
407     int i;
408
409     str = lcstring(bitspec, str, sizeof(bitspec));
410     if (isdigit(*str)) {
411         if (strncmp(str, "0x", 2) == 0) /* 0x => hex */
412             sscanf(str, "0x%lx", (long unsigned int *) &f);
413         else if (*str == '0')   /* assume octal */
414             sscanf(str, "%lo", (long unsigned int *) &f);
415         else                    /* just assume hex */
416             sscanf(str, "%lx", (long unsigned int *) &f);
417     } else {
418         if (*str == '=') {
419             str++;
420             f = 0;
421             addop = 1;
422         } else {
423             if (strchr("+-", *str))
424                 addop = (*str++ == '+');
425             else if (*str == '_') {
426                 addop = 0;
427                 str++;
428             } else
429                 addop = 1;
430             code =
431                 ubik_KAM_GetEntry(conn, 0, name, inst, KAMAJORVERSION,
432                           &tentry);
433             if (code) {
434                 afs_com_err(whoami, code,
435                         "could get current flag value for %s.%s", name, inst);
436                 return -1;
437             }
438             f = tentry.flags;
439         }
440         while (*str) {
441             i = 0;
442             while (1) {
443                 c = *str;
444                 if (isupper(c))
445                     c = tolower(c);
446                 if (!islower(c))
447                     break;
448                 bit[i++] = c;
449                 str++;
450             }
451             bit[i] = '\0';
452             if (strcmp(bit, "admin") == 0)
453                 flag = KAFADMIN;
454             else if (strcmp(bit, "noadmin") == 0)
455                 flag = KAFADMIN, addop = !addop;
456             else if (strcmp(bit, "notgs") == 0)
457                 flag = KAFNOTGS;
458             else if (strcmp(bit, "tgs") == 0)
459                 flag = KAFNOTGS, addop = !addop;
460             else if (strcmp(bit, "noseal") == 0)
461                 flag = KAFNOSEAL;
462             else if (strcmp(bit, "seal") == 0)
463                 flag = KAFNOSEAL, addop = !addop;
464             else if (strcmp(bit, "nocpw") == 0)
465                 flag = KAFNOCPW;
466             else if (strcmp(bit, "cpw") == 0)
467                 flag = KAFNOCPW, addop = !addop;
468             else if (strcmp(bit, "newassoc") == 0)
469                 flag = KAFNEWASSOC;
470             else if (strcmp(bit, "nonewassoc") == 0)
471                 flag = KAFNEWASSOC, addop = !addop;
472             else {
473                 printf
474                     ("Illegal bit name: %s; choices are: [no]admin, [no]tgs, [no]seal, [no]cpw\n",
475                      bit);
476                 return -1;
477             }
478
479             if (addop)
480                 f |= flag;
481             else
482                 f &= ~flag;
483
484             if (*str == 0)
485                 break;
486             if (*str == '+')
487                 addop = 1;      /* get next op */
488             else if ((*str == '-') || (*str == '_'))
489                 addop = 0;
490             else {
491                 printf("Illegal combination operator: %c\n", *str);
492                 return -1;
493             }
494             str++;
495         }
496     }
497     *flags = (f & KAF_SETTABLE_FLAGS) | KAFNORMAL;
498     return 0;
499 }
500
501 #define seriouserror(code) ((code <0) || ((code != UNOSERVERS) && (code != UNOQUORUM) && code != UNOTSYNC))
502
503 /* return MAXLONG if locked forever */
504 afs_uint32
505 ka_islocked(char *name, char *instance, afs_uint32 * when)
506 {
507     int count, code;
508     afs_uint32 tempwhen;
509
510     count = 0;
511     *when = 0;
512     do {
513         tempwhen = 0;
514         code =
515             ubik_CallIter(KAM_LockStatus, conn, UPUBIKONLY, &count, (long) name,
516                           (long) instance, (long) &tempwhen, 0, 0, 0,
517                           0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
518         if (code) {
519             if (seriouserror(code))
520                 afs_com_err(whoami, code, NULL);
521         } else if (tempwhen) {  /* user is locked */
522             if (!*when || tempwhen < *when) {
523                 *when = tempwhen;
524                 return (*when);
525             }
526         } else                  /* ! tempwhen ==> user is not locked */
527             return 0;
528
529     } while (code != UNOSERVERS);
530
531     return (*when);
532 }
533
534 int
535 Unlock(struct cmd_syndesc *as, void *arock)
536 {
537     afs_int32 code, rcode = 0;
538     afs_int32 count;
539     afs_int32 server;
540     char name[MAXKTCNAMELEN];
541     char instance[MAXKTCNAMELEN];
542
543     code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
544     if (code) {
545         afs_com_err(whoami, code, "parsing user's name '%s'",
546                 as->parms[0].items->data);
547         return KABADCMD;
548     }
549
550     count = 0;
551     do {
552         code = ubik_CallIter(KAM_Unlock, conn, 0, &count, (long) name, (long) instance,
553                              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
554         if (code && (code != UNOSERVERS)) {
555             server = 0;
556             if (conn && conn->conns[count - 1]
557                 && rx_PeerOf(conn->conns[count - 1])) {
558                 server = rx_HostOf(rx_PeerOf(conn->conns[count - 1]));
559             }
560             afs_com_err(whoami, code,
561                     "so %s.%s may still be locked (on server %d.%d.%d.%d)",
562                     name, instance, ((server >> 24) & 0xFF),
563                     ((server >> 16) & 0xFF), ((server >> 8) & 0xFF),
564                     (server & 0xFF));
565
566             if (!rcode) {
567                 rcode = code;
568             }
569         }
570     } while (code != UNOSERVERS);
571
572     return rcode;
573 }
574
575 int
576 SetFields(struct cmd_syndesc *as, void *arock)
577 {
578     int code;
579     char name[MAXKTCNAMELEN];
580     char instance[MAXKTCNAMELEN];
581     char *end;
582     afs_int32 flags = 0;
583     Date expiration = 0;
584     afs_int32 lifetime = 0;
585     afs_int32 maxAssociates = -1;
586     afs_int32 pwexpiry = 0;
587     afs_int32 was_spare = 0;
588     char misc_auth_bytes[4];
589     int i;
590
591     code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
592     if (code) {
593         afs_com_err(whoami, code, "parsing user's name '%s'",
594                 as->parms[0].items->data);
595         return KABADCMD;
596     }
597
598     if (as->parms[1].items) {
599         code = parse_flags(name, instance, as->parms[1].items->data, &flags);
600         if (code) {
601             printf
602                 ("Illegal flag specification: %s, should be of the form <'='|'+'|'-'|'_'>bitname{<'+'|'-'>bitname}*\n",
603                  as->parms[1].items->data);
604             return KABADCMD;
605         }
606     }
607     if (as->parms[2].items) {
608         char buf[32];
609         char *s = strncpy(buf, as->parms[2].items->data, sizeof(buf));
610         code = ktime_DateToInt32(s, &expiration);
611         if (code) {
612             printf("Illegal time format %s: %s\n", as->parms[2].items->data,
613                    ktime_GetDateUsage());
614             return KABADCMD;
615         }
616         if (expiration == 0) {
617             fprintf(stderr, "Expiration time must be after (about) 1970.\n");
618             return KABADCMD;
619         }
620         if (expiration < time(0)) {
621             fprintf(stderr,
622                     "Warning: expiration being set into the past, account will be disabled.\n");
623         }
624     }
625     /*
626      * TICKET lifetime...
627      */
628     if (as->parms[3].items) {
629         code = read_time_interval(as->parms[3].items->data, &lifetime);
630         if (code)
631             return KABADCMD;
632     }
633
634     /*  no point in doing this any sooner than necessary */
635     for (i = 0; i < 4; misc_auth_bytes[i++] = 0);
636
637     if (as->parms[4].items) {
638         pwexpiry = strtol(as->parms[4].items->data, &end, 10);
639         if (*end != '\0') {
640             fprintf(stderr,
641                     "Password lifetime specified must be a non-negative decimal integer.\n");
642             pwexpiry = -1;
643         }
644         if (pwexpiry < 0 || pwexpiry > 254) {
645             fprintf(stderr,
646                     "Password lifetime range must be [0..254] days.\n");
647             fprintf(stderr, "Zero represents an unlimited lifetime.\n");
648             return KABADCMD;
649         }
650
651         misc_auth_bytes[0] = pwexpiry + 1;
652     }
653
654     if (as->parms[5].items) {
655         char *reuse;
656         reuse = (as->parms[5].items->data);
657
658         if (!strcmp(reuse, "yes")) {
659             misc_auth_bytes[1] = KA_REUSEPW;
660         } else if (strcmp(reuse, "no")) {
661             fprintf(stderr,
662                     "must specify \"yes\" or \"no\": \"yes\" assumed\n");
663             misc_auth_bytes[1] = KA_REUSEPW;
664         } else {
665             misc_auth_bytes[1] = KA_NOREUSEPW;
666         }
667     }
668
669     if (as->parms[6].items) {
670         int nfailures;
671
672         nfailures = strtol(as->parms[6].items->data, &end, 10);
673
674         if (*end != '\0' || nfailures < 0 || nfailures > 254) {
675             fprintf(stderr, "Failure limit must be in [0..254].\n");
676             fprintf(stderr, "Zero represents unlimited login attempts.\n");
677             return KABADCMD;
678         }
679         misc_auth_bytes[2] = nfailures + 1;
680     }
681
682     if (as->parms[7].items) {
683         int locktime, hrs, mins;
684         char *s;
685
686         hrs = 0;
687         s = as->parms[7].items->data;
688         if (strchr(s, ':'))
689             sscanf(s, "%d:%d", &hrs, &mins);
690         else
691             sscanf(s, "%d", &mins);
692
693         locktime = hrs * 60 + mins;
694         if (hrs < 0 || hrs > 36 || mins < 0) {
695             fprintf(stderr,
696                     "Lockout times must be either minutes or hh:mm.\n");
697             fprintf(stderr, "Lockout times must be less than 36 hours.\n");
698             return KABADCMD;
699         } else if (locktime > 36 * 60) {
700             fprintf(stderr,
701                     "Lockout times must be either minutes or hh:mm.\n");
702             fprintf(stderr, "Lockout times must be less than 36 hours.\n");
703             fprintf(stderr,
704                     "Continuing with lock time of exactly 36 hours...\n");
705             locktime = 36 * 60;
706         }
707         locktime = (locktime * 60 + 511) >> 9;  /* ceil(l*60/512) */
708         misc_auth_bytes[3] = locktime + 1;      /* will be 1 if user said 0 */
709     }
710 #if ASSOCIATES
711     if (as->parms[8].items) {
712         maxAssociates = atoi(as->parms[6].items->data);
713         if (maxAssociates < 0) {
714             printf("Illegal maximum number of associates\n");
715             return KABADCMD;
716         }
717     }
718 #endif
719     was_spare = pack_long(misc_auth_bytes);
720
721     if (was_spare || flags || expiration || lifetime || (maxAssociates >= 0))
722         code =
723             ubik_KAM_SetFields(conn, 0, name, instance, flags,
724                       expiration, lifetime, maxAssociates, was_spare,
725                       /* spare */ 0);
726     else {
727         printf("Must specify one of the optional parameters\n");
728         return KABADCMD;
729     }
730     if (code)
731         afs_com_err(whoami, code, "calling KAM_SetFields for %s.%s", name,
732                 instance);
733     return code;
734 }
735
736 int
737 StringToKey(struct cmd_syndesc *as, void *arock)
738 {
739     afs_int32 code;
740     char realm[MAXKTCREALMLEN];
741     struct ktc_encryptionKey key;
742
743     if (as->parms[1].items) {
744         code = ka_ExpandCell(as->parms[1].items->data, realm, 0 /*local */ );
745         if (code) {
746             afs_com_err(whoami, code,
747                     "expanding %s as cell name, attempting to continue",
748                     as->parms[1].items->data);
749         }
750         ucstring(realm, realm, sizeof(realm));
751     } else {
752         if ((code = DefaultCell()))
753             return code;
754         ucstring(realm, cell, sizeof(realm));
755     }
756     ka_StringToKey(as->parms[0].items->data, realm, &key);
757
758     printf("Converting %s in realm '%s' yields key='",
759            as->parms[0].items->data, realm);
760     ka_PrintBytes((char *)&key, sizeof(key));
761     printf("'.\n");
762
763     DES_string_to_key(as->parms[0].items->data, ktc_to_cblockptr(&key));
764
765     printf("Converting %s with the DES string to key yields key='",
766            as->parms[0].items->data);
767     ka_PrintBytes((char *)&key, sizeof(key));
768     printf("'.\n");
769
770     return 0;
771 }
772
773 int
774 SetPassword(struct cmd_syndesc *as, void *arock)
775 {
776     int code;
777     char name[MAXKTCNAMELEN];
778     char instance[MAXKTCNAMELEN];
779     char realm[MAXKTCREALMLEN];
780     struct ktc_encryptionKey key;
781     afs_int32 kvno = 0;
782
783     code = ka_ParseLoginName(as->parms[0].items->data, name, instance, realm);
784     if (code) {
785         afs_com_err(whoami, code, "parsing user's name '%s'",
786                 as->parms[0].items->data);
787         return KABADCMD;
788     }
789
790     if (strlen(realm) == 0)
791         ucstring(realm, cell, sizeof(realm));
792
793     if (as->parms[1].items && as->parms[2].items) {
794         printf("Can't specify both a password and a key\n");
795         return KABADCMD;
796     } else if (as->parms[1].items) {
797         (void)init_child(myName);
798         (void)give_to_child(passwd);    /* old password */
799         code = password_bad(as->parms[1].items->data);
800         (void)terminate_child();
801         if (code)
802             return KABADCMD;
803         ka_StringToKey(as->parms[1].items->data, realm, &key);
804     } else if (as->parms[2].items) {
805         if (ka_ReadBytes(as->parms[2].items->data, (char *)&key, sizeof(key))
806             != 8) {
807             printf("Key must be 8 bytes: '%s' was too long\n",
808                    as->parms[2].items->data);
809             return KABADCMD;
810         }
811     } else {
812         printf("Must specify new password or key\n");
813         return KABADCMD;
814     }
815
816
817     if (as->parms[3].items)
818         sscanf(as->parms[3].items->data, "%d", &kvno);
819
820     code = ubik_KAM_SetPassword(conn, 0, name, instance, kvno,
821                                 *ktc_to_EncryptionKey(&key));
822     if (code)
823         afs_com_err(whoami, code, "so can't set password for %s.%s", name,
824                 instance);
825     return code;
826 }
827
828 #define PrintPrincipal(p,n,l) \
829     PrintName((p)->name, (p)->instance, (p)->cell, l, n)
830
831 static afs_int32
832 PrintName(char *name, char *inst, char *acell, int buflen, char *buf)
833 {
834     int nlen, len;
835     int left;                   /* if ConvertBytes stops early */
836     afs_int32 code;
837
838     if (name == 0)
839         name = "";
840     if (inst == 0)
841         inst = "";
842     if (acell == 0)
843         acell = "";
844     left = ka_ConvertBytes(buf, buflen, name, strlen(name));
845     if (left) {
846       bad_name:
847         code = KABADNAME;
848         afs_com_err(whoami, code, "PrintName: principal name was '%s'.'%s'@'%s'",
849                 name, inst, acell);
850         return code;
851     }
852     nlen = strlen(buf);
853     len = strlen(inst);
854     if (len) {
855         if (nlen + len + 1 >= buflen)
856             goto bad_name;
857         buf[nlen++] = '.';
858         left = ka_ConvertBytes(buf + nlen, buflen - nlen, inst, len);
859         if (left)
860             goto bad_name;
861         nlen += len;
862     }
863
864     len = strlen(acell);
865     if (len) {
866         char *lcell = ka_LocalCell();
867         if (lcell == 0)
868             lcell = "";
869         if (strcmp(acell, lcell) != 0) {
870             /* only append cell if not the local cell */
871             if (nlen + len + 1 >= buflen)
872                 goto bad_name;
873             buf[nlen++] = '@';
874             left = ka_ConvertBytes(buf + nlen, buflen - nlen, acell, len);
875             if (left)
876                 goto bad_name;
877             nlen += len;
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         code = 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 = (struct cmd_item *)malloc(sizeof(struct cmd_item));
1525                     ip->data = (char *)malloc(strlen(password) + 1);
1526                     ip->next = 0;
1527                     strcpy(ip->data, password);
1528                     as->parms[i].items = ip;
1529                 }
1530             }
1531     if (!conn) {                /* if all else fails... */
1532         code = NoAuth(0, 0);    /* get unauthenticated conn */
1533         if (code)
1534             return code;
1535     }
1536     return 0;
1537 }
1538
1539 /* These are some helpful command that deal with the cache managers tokens. */
1540
1541 static int
1542 ForgetTicket(struct cmd_syndesc *as, void *arock)
1543 {
1544     afs_int32 code;
1545
1546 #ifdef notdef
1547     struct ktc_principal server;
1548
1549     if (as->parms[0].items) {
1550         char *name = as->parms[0].items->data;
1551         code =
1552             ka_ParseLoginName(name, server.name, server.instance,
1553                               server.cell);
1554         if (code) {
1555             afs_com_err(whoami, code, "couldn't interpret name '%s'", name);
1556             return code;
1557         }
1558         if (server.cell[0] == 0) {
1559             if (code = DefaultCell())
1560                 return code;
1561             strcpy(server.cell, cell);
1562         } else {
1563             code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1564             if (code) {
1565                 afs_com_err(whoami, code, "Can't expand cell name");
1566                 return code;
1567             }
1568         }
1569         code = ktc_ForgetToken(&server);
1570         if (code) {
1571             afs_com_err(whoami, code, "couldn't remove tokens for %s",
1572                     PrintedPrincipal(&server));
1573             return code;
1574         }
1575     } else {
1576         if (!as->parms[1].items) {
1577             fprintf(stderr, "Must specify server name or -all\n");
1578             return KABADCMD;
1579         }
1580         code = ktc_ForgetAllTokens();
1581         if (code) {
1582             afs_com_err(whoami, code, "couldn't delete all tokens");
1583             return code;
1584         }
1585     }
1586 #endif
1587     code = ktc_ForgetAllTokens();
1588     if (code) {
1589         afs_com_err(whoami, code, "couldn't delete all tokens");
1590         return code;
1591     }
1592     return 0;
1593 }
1594
1595 static int
1596 ListTickets(struct cmd_syndesc *as, void *arock)
1597 {
1598     afs_int32 code = 0;
1599     int index, newIndex;
1600     struct ktc_principal server;
1601     int verbose = 0;
1602
1603     if (as->parms[1].items)
1604         verbose = 1;
1605     if (as->parms[0].items) {
1606         char *name = as->parms[0].items->data;
1607         code =
1608             ka_ParseLoginName(name, server.name, server.instance,
1609                               server.cell);
1610         if (code) {
1611             afs_com_err(whoami, code, "couldn't interpret name '%s'", name);
1612             return code;
1613         }
1614         if (server.cell[0] == 0) {
1615             if ((code = DefaultCell()))
1616                 return code;
1617             strcpy(server.cell, cell);
1618         } else {
1619             code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1620             if (code) {
1621                 afs_com_err(whoami, code, "Can't expand cell name");
1622                 return code;
1623             }
1624         }
1625         code = ListTicket(&server, verbose);
1626     } else
1627         for (index = 0;; index = newIndex) {
1628             code = ktc_ListTokens(index, &newIndex, &server);
1629             if (code) {
1630                 if (code == KTC_NOENT)
1631                     code = 0;   /* end of list */
1632                 break;
1633             }
1634             code = ListTicket(&server, verbose);
1635         }
1636     return code;
1637 }
1638
1639 static void
1640 add_std_args(struct cmd_syndesc *ts)
1641 {
1642     cmd_Seek(ts, 12);
1643     /* 12 */ cmd_AddParm(ts, "-admin_username", CMD_SINGLE, CMD_OPTIONAL,
1644                          "admin principal to use for authentication");
1645     /* 13 */ cmd_AddParm(ts, "-password_for_admin", CMD_SINGLE, CMD_OPTIONAL,
1646                          "admin password");
1647     /* 14 */ cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1648     /* 15 */ cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
1649                          "explicit list of authentication servers");
1650     /* 16 */ cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL,
1651                          "don't authenticate");
1652 }
1653
1654 afs_int32
1655 ka_AdminInteractive(int cmd_argc, char *cmd_argv[])
1656 {
1657     int code;
1658     struct cmd_syndesc *ts;
1659
1660     char line[BUFSIZ];
1661     afs_int32 argc;
1662     char *argv[32];
1663
1664     strncpy(myName, *cmd_argv, 509);
1665
1666     cmd_SetBeforeProc(MyBeforeProc, NULL);
1667     cmd_SetAfterProc(MyAfterProc, NULL);
1668
1669     ts = cmd_CreateSyntax("interactive", Interactive, NULL,
1670                           "enter interactive mode");
1671     add_std_args(ts);
1672
1673     ts = cmd_CreateSyntax("noauthentication", NoAuth, NULL,
1674                           "connect to AuthServer w/o using token");
1675
1676     ts = cmd_CreateSyntax("list", ListUsers, NULL,
1677                           "list all users in database");
1678     cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1679                 "show detailed info about each user");
1680     cmd_AddParm(ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL,
1681                 "show all cell administrators");
1682     cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1683                 "show the user's actual key rather than the checksum");
1684     add_std_args(ts);
1685     cmd_CreateAlias(ts, "ls");
1686
1687     ts = cmd_CreateSyntax("examine", ExamineUser, NULL,
1688                           "examine the entry for a user");
1689     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1690     cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1691                 "show the user's actual key rather than the checksum");
1692     add_std_args(ts);
1693
1694     ts = cmd_CreateSyntax("create", CreateUser, NULL,
1695                           "create an entry for a user");
1696     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1697     cmd_AddParm(ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL,
1698                 "initial password");
1699     add_std_args(ts);
1700
1701     ts = cmd_CreateSyntax("delete", DeleteUser, NULL, "delete a user");
1702     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1703     add_std_args(ts);
1704     cmd_CreateAlias(ts, "rm");
1705
1706     ts = cmd_CreateSyntax("setfields", SetFields, NULL,
1707                           "set various fields in a user's entry");
1708     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1709     cmd_AddParm(ts, "-flags", CMD_SINGLE, CMD_OPTIONAL,
1710                 "hex flag value or flag name expression");
1711     cmd_AddParm(ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL,
1712                 "date of account expiration");
1713     cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
1714                 "maximum ticket lifetime");
1715     cmd_AddParm(ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL,
1716                 "number days password is valid ([0..254])");
1717     cmd_AddParm(ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL,
1718                 "permit password reuse (yes/no)");
1719     cmd_AddParm(ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL,
1720                 "maximum successive failed login tries ([0..254])");
1721     cmd_AddParm(ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL,
1722                 "failure penalty [hh:mm or minutes]");
1723 #if ASSOCIATES
1724     cmd_AddParm(ts, "-associates", CMD_SINGLE, CMD_OPTIONAL,
1725                 "maximum associate instances");
1726 #endif
1727     add_std_args(ts);
1728     cmd_CreateAlias(ts, "sf");
1729
1730
1731     ts = cmd_CreateSyntax("unlock", Unlock, NULL,
1732                           "Enable authentication ID after max failed attempts exceeded");
1733     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "authentication ID");
1734     add_std_args(ts);
1735
1736
1737     ts = cmd_CreateSyntax("stringtokey", StringToKey, NULL,
1738                           "convert a string to a key");
1739     cmd_AddParm(ts, "-string", CMD_SINGLE, 0, "password string");
1740     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1741
1742     ts = cmd_CreateSyntax("setpassword", SetPassword, NULL,
1743                           "set a user's password");
1744     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1745     cmd_AddParm(ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL,
1746                 "new password");
1747     cmd_Seek(ts, 3);
1748     cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1749     add_std_args(ts);
1750     cmd_CreateAlias(ts, "sp");
1751 #ifdef CMD_PARSER_AMBIG_FIX
1752     cmd_CreateAlias(ts, "setpasswd");
1753 #endif
1754
1755     /* set a user's key */
1756     ts = cmd_CreateSyntax("setkey", SetPassword, NULL, (char *)CMD_HIDDEN);
1757     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1758     cmd_Seek(ts, 2);
1759     cmd_AddParm(ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1760     cmd_Seek(ts, 3);
1761     cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1762     add_std_args(ts);
1763
1764     /* get a user's password */
1765     ts = cmd_CreateSyntax("getpassword", GetPassword, NULL, (char *)CMD_HIDDEN);
1766     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1767     /* don't take standard args */
1768     /* add_std_args (ts); */
1769 #ifdef CMD_PARSER_AMBIG_FIX
1770     cmd_CreateAlias(ts, "getpasswd");
1771 #endif
1772
1773     /* get a random key */
1774     ts = cmd_CreateSyntax("getrandomkey", GetRandomKey, NULL,
1775                           (char *)CMD_HIDDEN);
1776     add_std_args(ts);
1777
1778     /* get a ticket for a specific server */
1779     ts = cmd_CreateSyntax("getticket", GetTicket, NULL, (char *)CMD_HIDDEN);
1780     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of server");
1781     cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1782     add_std_args(ts);
1783
1784     ts = cmd_CreateSyntax("statistics", Statistics, NULL,
1785                           "show statistics for AuthServer");
1786     add_std_args(ts);
1787
1788     /* show debugging info from AuthServer */
1789     ts = cmd_CreateSyntax("debuginfo", DebugInfo, NULL, (char *)CMD_HIDDEN);
1790     cmd_AddParm(ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL,
1791                 "authentication server host name");
1792     add_std_args(ts);
1793
1794     ts = cmd_CreateSyntax("forgetticket", ForgetTicket, NULL,
1795                           "delete user's tickets");
1796 #ifdef notdef
1797     cmd_AddParm(ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1798                 "name of server");
1799 #endif
1800     cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1801
1802     ts = cmd_CreateSyntax("listtickets", ListTickets, NULL,
1803                           "show all cache manager tickets");
1804     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1805     cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1806                 "show session key and ticket");
1807
1808     ts = cmd_CreateSyntax("quit", Quit, NULL, "exit program");
1809
1810     finished = 1;
1811     conn = 0;                   /* no connection yet */
1812     zero_argc = cmd_argc;
1813     zero_argv = cmd_argv;
1814
1815     strcpy(whoami, "kas");
1816
1817     if ((code = cmd_Dispatch(cmd_argc, cmd_argv))) {
1818         return code;
1819     }
1820
1821     while (!finished) {
1822         char *s;
1823         int i;
1824
1825         printf("ka> ");
1826         s = fgets(line, sizeof(line), stdin);
1827         if (s == NULL)
1828             return 0;           /* EOF on input */
1829         for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--)
1830             line[i] = 0;
1831         if (i < 0)
1832             continue;           /* blank line */
1833
1834         code =
1835             cmd_ParseLine(line, argv, &argc, sizeof(argv) / sizeof(argv[0]));
1836         if (code) {
1837             afs_com_err(whoami, code, "parsing line: '%s'", line);
1838             return code;
1839         }
1840         code = cmd_Dispatch(argc, argv);
1841         cmd_FreeArgv(argv);
1842     }
1843     return code;
1844 }