2 * Copyright 2000, International Business Machines Corporation and others.
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
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. */
15 #include <afsconfig.h>
16 #include <afs/param.h>
22 #include <afs/debug.h>
26 /* These two needed for rxgen output to work */
27 #include <sys/types.h>
38 #include <afs/cellconfig.h>
40 #include <afs/com_err.h>
41 #include <afs/afsutil.h>
47 #define CMD_PARSER_AMBIG_FIX 1 /* allow ambiguous aliases */
49 #define KA_SIXHOURS (6*3600)
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 : */
59 static char **zero_argv;
60 afs_uint32 ka_islocked();
69 code = ka_ExpandCell(0, cell, 0 /*local */ );
71 com_err(whoami, code, "Can't expand cell name");
76 /* These are the command operation procedures. */
79 DumpUser(char *user, char *arock, int showadmin, int showkey, char *inst)
81 char name[MAXKTCNAMELEN];
82 char instance[MAXKTCNAMELEN];
85 char bob[KA_TIMESTR_LEN];
87 struct kaentryinfo tentry;
89 code = ka_ParseLoginName(user, name, instance, 0);
91 com_err(whoami, code, "parsing user's name '%s'", user);
98 ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION, &tentry);
100 com_err(whoami, code, "getting information for %s.%s", name, inst);
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))
108 ka_PrintUserID("\nUser data for ", name, inst, "");
111 #define NEWPREFIX "+"
112 if (tentry.flags & KAFADMIN) {
113 printf("%sADMIN", prefix);
116 if (tentry.flags & KAFNOTGS) {
117 printf("%sNOTGS", prefix);
120 if (tentry.flags & KAFNOCPW) {
121 printf("%sNOCPW", prefix);
124 if (tentry.flags & KAFNOSEAL) {
125 printf("%sNOSEAL", prefix);
128 if (tentry.flags & KAFNEWASSOC) {
129 printf("%sNEWASSOC", prefix);
132 if (tentry.flags & KAFASSOCROOT) {
133 printf("%sASSOCROOT", prefix);
136 if (tentry.flags & KAFASSOC) {
137 printf("%sASSOC", prefix);
140 if (tentry.user_expiration <= now) {
141 printf("%sexpired", prefix);
144 if (strcmp(prefix, NEWPREFIX) == 0)
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));
153 if (tentry.keyCheckSum == 0)
154 printf(" key version is %d", tentry.key_version);
156 printf(" key (%d) cksum is %u", tentry.key_version,
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");
164 (" An unlimited number of unsuccessful authentications is permitted.\n");
166 unsigned char misc_stuff[4];
169 temp = tentry.misc_auth_bytes;
171 temp = ntohl(tentry.misc_auth_bytes);
173 unpack_long(temp, misc_stuff);
175 if (!misc_stuff[0]) {
176 printf(" password will never expire.\n");
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);
185 (" An unlimited number of unsuccessful authentications is permitted.\n");
188 (" %d consecutive unsuccessful authentications are permitted.\n",
192 printf(" The lock time for this user is not limited.\n");
194 printf(" The lock time for this user is %4.1f minutes.\n",
195 (float)((unsigned int)misc_stuff[3] << 9) / 60.0);
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");
203 ka_timestr(temp, bob, KA_TIMESTR_LEN);
204 printf(" User is locked until %s\n", bob);
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.");
217 printf(" entry expires on %s.", exp);
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;
228 printf(" permit password reuse\n");
230 printf(" don't permit password reuse\n");
237 ListUsers(struct cmd_syndesc *as, char *arock)
242 afs_int32 next_index;
243 int code, all = 0, showa = 0;
244 int showkey = (as->parms[2].items != NULL);
246 if (as->parms[0].items)
248 if (as->parms[1].items) {
252 for (index = 0; 1; index = next_index) {
254 ubik_Call(KAM_ListEntry, conn, 0, index, &next_index, &count,
257 com_err(whoami, code, "calling KAM_ListEntry");
263 printf("next_index (%d) is negative: ", next_index);
264 if (strlen(name.name) == 0)
265 printf("name is zero length: ");
267 DumpUser(name.name, NULL, showa, showkey, name.instance);
269 ka_PrintUserID("", name.name, name.instance, "\n");
276 ExamineUser(struct cmd_syndesc *as, char *arock)
278 int showkey = (as->parms[1].items != NULL);
279 return DumpUser(as->parms[0].items->data, arock, 0, showkey, NULL);
289 handle_errors(int code, /* error code to handle */
290 struct OKerrors OKlist[], /* list of errors & messages that should be ignored */
292 { /* set this if we should retry, clear otherwise */
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 */
303 printf(" : [%s] %s", error_table_name(code), error_message(code));
306 printf(", wait one second\n");
310 case RX_CALL_TIMEOUT:
311 printf(" (retrying)\n");
316 *persist = 0; /* don't retry these errors */
321 CreateUser(struct cmd_syndesc *as, char *arock)
324 char name[MAXKTCNAMELEN];
325 char instance[MAXKTCNAMELEN];
326 struct ktc_encryptionKey key;
329 struct OKerrors OKlist[2];
332 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
334 com_err(whoami, code, "parsing user's name '%s'",
335 as->parms[0].items->data);
338 ka_StringToKey(as->parms[1].items->data, cell, &key);
341 code = ubik_Call(KAM_CreateUser, conn, 0, name, instance, key);
344 ka_PrintUserID("Creating user ", name, instance, " ");
345 code = handle_errors(code, OKlist, &persist);
351 DeleteUser(struct cmd_syndesc *as, char *arock)
354 char name[MAXKTCNAMELEN];
355 char instance[MAXKTCNAMELEN];
358 struct OKerrors OKlist[2];
360 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
362 com_err(whoami, code, "parsing user's name '%s'",
363 as->parms[0].items->data);
368 code = ubik_Call(KAM_DeleteUser, conn, 0, name, instance);
371 ka_PrintUserID("Deleting user ", name, instance, " ");
372 code = handle_errors(code, OKlist, &persist);
378 read_time_interval(char *str, afs_int32 * seconds)
384 str = strncpy(buf, str, sizeof(buf));
385 s = strchr(str, ':');
389 *s++ = '\0'; /* separate hours and minutes */
390 sec = atoi(str) * 3600 + atoi(s) * 60;
397 parse_flags(char *name, char *inst, char *str, afs_int32 * flags)
399 struct kaentryinfo tentry;
405 int addop; /* 1=add bit; 0=remove bit */
409 str = lcstring(bitspec, str, sizeof(bitspec));
411 if (strncmp(str, "0x", 2) == 0) /* 0x => hex */
412 sscanf(str, "0x%lx", &f);
413 else if (*str == '0') /* assume octal */
414 sscanf(str, "%lo", &f);
415 else /* just assume hex */
416 sscanf(str, "%lx", &f);
423 if (strchr("+-", *str))
424 addop = (*str++ == '+');
425 else if (*str == '_') {
431 ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION,
434 com_err(whoami, code,
435 "could get current flag value for %s.%s", name, inst);
452 if (strcmp(bit, "admin") == 0)
454 else if (strcmp(bit, "noadmin") == 0)
455 flag = KAFADMIN, addop = !addop;
456 else if (strcmp(bit, "notgs") == 0)
458 else if (strcmp(bit, "tgs") == 0)
459 flag = KAFNOTGS, addop = !addop;
460 else if (strcmp(bit, "noseal") == 0)
462 else if (strcmp(bit, "seal") == 0)
463 flag = KAFNOSEAL, addop = !addop;
464 else if (strcmp(bit, "nocpw") == 0)
466 else if (strcmp(bit, "cpw") == 0)
467 flag = KAFNOCPW, addop = !addop;
468 else if (strcmp(bit, "newassoc") == 0)
470 else if (strcmp(bit, "nonewassoc") == 0)
471 flag = KAFNEWASSOC, addop = !addop;
474 ("Illegal bit name: %s; choices are: [no]admin, [no]tgs, [no]seal, [no]cpw\n",
487 addop = 1; /* get next op */
488 else if ((*str == '-') || (*str == '_'))
491 printf("Illegal combination operator: %c\n", *str);
497 *flags = (f & KAF_SETTABLE_FLAGS) | KAFNORMAL;
501 #define seriouserror(code) ((code <0) || ((code != UNOSERVERS) && (code != UNOQUORUM) && code != UNOTSYNC))
503 /* return MAXLONG if locked forever */
505 ka_islocked(char *name, char *instance, afs_uint32 * when)
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);
519 if (seriouserror(code))
520 com_err(whoami, code, "");
521 } else if (tempwhen) { /* user is locked */
522 if (!*when || tempwhen < *when) {
526 } else /* ! tempwhen ==> user is not locked */
529 } while (code != UNOSERVERS);
535 Unlock(struct cmd_syndesc *as, char *arock)
537 afs_int32 code, rcode = 0;
540 char name[MAXKTCNAMELEN];
541 char instance[MAXKTCNAMELEN];
543 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
545 com_err(whoami, code, "parsing user's name '%s'",
546 as->parms[0].items->data);
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 = "<unknown>";
556 if (conn && conn->conns[count - 1]
557 && conn->conns[count - 1]->peer) {
558 server = rx_AddrStringOf(conn->conns[count - 1]->peer);
560 com_err(whoami, code,
561 "so %s.%s may still be locked (on server %s)",
562 name, instance, server);
568 } while (code != UNOSERVERS);
574 SetFields(struct cmd_syndesc *as, char *arock)
577 char name[MAXKTCNAMELEN];
578 char instance[MAXKTCNAMELEN];
581 afs_int32 lifetime = 0;
582 afs_int32 maxAssociates = -1;
583 afs_int32 pwexpiry = 0;
584 afs_int32 was_spare = 0;
585 char misc_auth_bytes[4];
588 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
590 com_err(whoami, code, "parsing user's name '%s'",
591 as->parms[0].items->data);
595 if (as->parms[1].items) {
596 code = parse_flags(name, instance, as->parms[1].items->data, &flags);
599 ("Illegal flag specification: %s, should be of the form <'='|'+'|'-'|'_'>bitname{<'+'|'-'>bitname}*\n",
600 as->parms[1].items->data);
604 if (as->parms[2].items) {
606 char *s = strncpy(buf, as->parms[2].items->data, sizeof(buf));
607 code = ktime_DateToInt32(s, &expiration);
609 printf("Illegal time format %s: %s\n", as->parms[2].items->data,
610 ktime_GetDateUsage());
613 if (expiration == 0) {
614 fprintf(stderr, "Expiration time must be after (about) 1970.\n");
617 if (expiration < time(0)) {
619 "Warning: expiration being set into the past, account will be disabled.\n");
625 if (as->parms[3].items) {
626 code = read_time_interval(as->parms[3].items->data, &lifetime);
631 /* no point in doing this any sooner than necessary */
632 for (i = 0; i < 4; misc_auth_bytes[i++] = 0);
634 if (as->parms[4].items) {
635 if (util_isint(as->parms[4].items->data))
636 pwexpiry = atoi(as->parms[4].items->data);
639 "Password lifetime specified must be a non-negative decimal integer.\n");
642 if (pwexpiry < 0 || pwexpiry > 254) {
644 "Password lifetime range must be [0..254] days.\n");
645 fprintf(stderr, "Zero represents an unlimited lifetime.\n");
648 misc_auth_bytes[0] = pwexpiry + 1;
652 if (as->parms[5].items) {
654 reuse = (as->parms[5].items->data);
656 if (!strcmp(reuse, "yes")) {
657 misc_auth_bytes[1] = KA_REUSEPW;
658 } else if (strcmp(reuse, "no")) {
660 "must specify \"yes\" or \"no\": \"yes\" assumed\n");
661 misc_auth_bytes[1] = KA_REUSEPW;
663 misc_auth_bytes[1] = KA_NOREUSEPW;
667 if (as->parms[6].items) {
671 if (util_isint(as->parms[6].items->data)
672 && ((nfailures = atoi(as->parms[6].items->data)) < 255)) {
673 misc_auth_bytes[2] = nfailures + 1;
675 fprintf(stderr, "Failure limit must be in [0..254].\n");
676 fprintf(stderr, "Zero represents unlimited login attempts.\n");
681 if (as->parms[7].items) {
682 int locktime, hrs, mins;
686 s = as->parms[7].items->data;
688 sscanf(s, "%d:%d", &hrs, &mins);
690 sscanf(s, "%d", &mins);
692 locktime = hrs * 60 + mins;
693 if (hrs < 0 || hrs > 36 || mins < 0) {
695 "Lockout times must be either minutes or hh:mm.\n");
696 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
698 } else if (locktime > 36 * 60) {
700 "Lockout times must be either minutes or hh:mm.\n");
701 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
703 "Continuing with lock time of exactly 36 hours...\n");
706 locktime = (locktime * 60 + 511) >> 9; /* ceil(l*60/512) */
707 misc_auth_bytes[3] = locktime + 1; /* will be 1 if user said 0 */
710 if (as->parms[8].items) {
711 maxAssociates = atoi(as->parms[6].items->data);
712 if (maxAssociates < 0) {
713 printf("Illegal maximum number of associates\n");
718 was_spare = pack_long(misc_auth_bytes);
720 if (was_spare || flags || expiration || lifetime || (maxAssociates >= 0))
722 ubik_Call(KAM_SetFields, conn, 0, name, instance, flags,
723 expiration, lifetime, maxAssociates, was_spare,
726 printf("Must specify one of the optional parameters\n");
730 com_err(whoami, code, "calling KAM_SetFields for %s.%s", name,
736 StringToKey(struct cmd_syndesc *as, char *arock)
739 char realm[MAXKTCREALMLEN];
740 struct ktc_encryptionKey key;
742 if (as->parms[1].items) {
743 code = ka_ExpandCell(as->parms[1].items->data, realm, 0 /*local */ );
745 com_err(whoami, code,
746 "expanding %s as cell name, attempting to continue",
747 as->parms[1].items->data);
749 ucstring(realm, realm, sizeof(realm));
751 if (code = DefaultCell())
753 ucstring(realm, cell, sizeof(realm));
755 ka_StringToKey(as->parms[0].items->data, realm, &key);
757 printf("Converting %s in realm '%s' yields key='",
758 as->parms[0].items->data, realm);
759 ka_PrintBytes((char *)&key, sizeof(key));
762 des_string_to_key(as->parms[0].items->data, &key);
764 printf("Converting %s with the DES string to key yields key='",
765 as->parms[0].items->data);
766 ka_PrintBytes((char *)&key, sizeof(key));
773 SetPassword(struct cmd_syndesc *as, char *arock)
776 char name[MAXKTCNAMELEN];
777 char instance[MAXKTCNAMELEN];
778 char realm[MAXKTCREALMLEN];
779 struct ktc_encryptionKey key;
782 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, realm);
784 com_err(whoami, code, "parsing user's name '%s'",
785 as->parms[0].items->data);
789 if (strlen(realm) == 0)
790 ucstring(realm, cell, sizeof(realm));
792 if (as->parms[1].items && as->parms[2].items) {
793 printf("Can't specify both a password and a key\n");
795 } else if (as->parms[1].items) {
796 (void)init_child(myName);
797 (void)give_to_child(passwd); /* old password */
798 code = password_bad(as->parms[1].items->data);
799 (void)terminate_child();
802 ka_StringToKey(as->parms[1].items->data, realm, &key);
803 } else if (as->parms[2].items) {
804 if (ka_ReadBytes(as->parms[2].items->data, (char *)&key, sizeof(key))
806 printf("Key must be 8 bytes: '%s' was too long\n",
807 as->parms[2].items->data);
811 printf("Must specify new password or key\n");
816 if (as->parms[3].items)
817 sscanf(as->parms[3].items->data, "%d", &kvno);
819 #if defined(AFS_S390_LINUX20_ENV) && !defined(AFS_S390X_LINUX20_ENV)
820 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, 0, key);
822 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, key);
825 com_err(whoami, code, "so can't set password for %s.%s", name,
830 #define PrintPrincipal(p,n,l) \
831 PrintName((p)->name, (p)->instance, (p)->cell, l, n)
834 PrintName(char *name, char *inst, char *acell, int buflen, char *buf)
837 int left; /* if ConvertBytes stops early */
846 left = ka_ConvertBytes(buf, buflen, name, strlen(name));
850 com_err(whoami, code, "PrintName: principal name was '%s'.'%s'@'%s'",
857 if (nlen + len + 1 >= buflen)
860 left = ka_ConvertBytes(buf + nlen, buflen - nlen, inst, len);
868 char *lcell = ka_LocalCell();
871 if (strcmp(acell, lcell) != 0) {
872 /* only append cell if not the local cell */
873 if (nlen + len + 1 >= buflen)
876 left = ka_ConvertBytes(buf + nlen, buflen - nlen, acell, len);
885 #define PrintedPrincipal(p) PrintedName ((p)->name, (p)->instance, (p)->cell)
887 /* PrintedName - returned a pointer to a static string in which the formated
888 * name has been stored. */
891 PrintedName(char *name, char *inst, char *cell)
893 static char printedName[128];
895 code = PrintName(name, inst, cell, sizeof(printedName), printedName);
899 strncpy(printedName, name, sizeof(printedName));
900 printedName[sizeof(printedName) - 8] = 0;
901 strcat(printedName, "<error>");
907 ListTicket(struct ktc_principal *server, int verbose)
910 struct ktc_token token; /* the token we're printing */
911 struct ktc_principal client;
912 char UserName[sizeof(struct ktc_principal)];
913 char ServerName[sizeof(struct ktc_principal)];
914 afs_int32 now = time(0);
915 char bob[KA_TIMESTR_LEN];
917 /* get the ticket info itself */
918 code = ktc_GetToken(server, &token, sizeof(token), &client);
920 com_err(whoami, code, "failed to get token info for server %s",
921 PrintedPrincipal(server));
924 code = PrintPrincipal(&client, UserName, sizeof(UserName));
927 /* spaces are printed as "\040" */
928 if (UserName[0] == 0)
930 else if (strncmp(UserName, "AFS\\040ID\\040", 13) == 0) {
931 printf("User's (AFS ID %s) tokens", UserName + 13);
932 } else if (strncmp(UserName, "Unix\\040UID\\040", 15) == 0) {
935 printf("User %s's tokens", UserName);
937 code = PrintPrincipal(server, ServerName, sizeof(ServerName));
940 printf(" for %s ", ServerName);
942 if (token.startTime > now) {
943 ka_timestr(token.startTime, bob, KA_TIMESTR_LEN);
944 printf("[>> POSTDATED 'till %s <<]", bob);
947 if (token.endTime <= now)
948 printf("[>> Expired <<]\n");
950 ka_timestr(token.endTime, bob, KA_TIMESTR_LEN);
951 printf("[Expires %s]\n", bob);
954 printf("SessionKey: ");
955 ka_PrintBytes((char *)&token.sessionKey, sizeof(token.sessionKey));
956 printf("\nTicket (kvno = %d, len = %d): ", token.kvno,
958 ka_PrintBytes((char *)token.ticket, token.ticketLen);
965 GetTicket(struct cmd_syndesc *as, char *arock)
968 struct ktc_principal server;
969 struct ktc_token token;
970 afs_int32 life = KA_SIXHOURS;
972 if (as->parms[1].items) {
973 code = read_time_interval(as->parms[1].items->data, &life);
978 ka_ParseLoginName(as->parms[0].items->data, server.name,
979 server.instance, server.cell);
981 com_err(whoami, code, "parsing user's name '%s'",
982 as->parms[0].items->data);
985 if (server.cell[0] == 0) {
986 if (code = DefaultCell())
988 strcpy(server.cell, cell);
990 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
992 com_err(whoami, code, "Can't expand cell name");
997 token.ticketLen = 0; /* in case there are no tokens */
999 ka_GetServerToken(server.name, server.instance, server.cell, life,
1000 &token, /*new */ 1, /*dosetpag */ 0);
1002 com_err(whoami, code, "getting ticket for %s",
1003 PrintedPrincipal(&server));
1005 code = ListTicket(&server, /*verbose */ 1);
1011 GetPassword(struct cmd_syndesc *as, char *arock)
1014 char name[MAXKTCNAMELEN];
1015 struct ktc_encryptionKey key;
1016 static struct ubik_client *lpbkConn = 0;
1018 /* no instance allowed */
1019 code = ka_ParseLoginName(as->parms[0].items->data, name, 0, 0);
1022 com_err(whoami, code,
1023 "getting %s's password via loopback connection to GetPassword",
1025 /* if we got a timeout, print a clarification, too */
1028 "%s: please note that this command must be run locally on a database server machine.\n",
1033 if (lpbkConn == 0) {
1034 struct rx_connection *conns[2];
1035 struct rx_securityClass *sc;
1036 int si; /* security class index */
1041 sc = rxnull_NewClientSecurityObject();
1042 si = RX_SCINDEX_NULL;
1044 rx_NewConnection(htonl(INADDR_LOOPBACK), htons(AFSCONF_KAUTHPORT),
1045 KA_MAINTENANCE_SERVICE, sc, si);
1047 code = ubik_ClientInit(conns, &lpbkConn);
1051 code = ubik_Call(KAM_GetPassword, lpbkConn, 0, name, &key);
1052 /* Lets close down the ubik_Client connection now */
1053 ubik_ClientDestroy(lpbkConn);
1057 ka_PrintBytes((char *)&key, sizeof(key));
1063 GetRandomKey(struct cmd_syndesc *as, char *arock)
1066 struct ktc_encryptionKey key;
1068 code = ubik_Call(KAM_GetRandomKey, conn, 0, &key);
1070 com_err(whoami, code, "so can't get random key");
1074 ka_PrintBytes((char *)&key, sizeof(key));
1076 for (i = 0; i < sizeof(key); i++) {
1077 printf("%0.2x", ((char *)&key)[i] & 0xff);
1089 Statistics(struct cmd_syndesc *as, char *arock)
1095 char bob[KA_TIMESTR_LEN];
1098 ubik_Call(KAM_GetStats, conn, 0, KAMAJORVERSION, &admins, &statics,
1101 printf("call to GetStats failed: %s\n", ka_ErrorString(code));
1104 if (statics.minor_version != KAMINORVERSION)
1105 printf("Minor version number mismatch: got %d, expected %d\n",
1106 statics.minor_version, KAMINORVERSION);
1107 printf("%d allocs, %d frees, %d password changes\n", statics.allocs,
1108 statics.frees, statics.cpws);
1109 printf("Hash table utilization = %f%%\n",
1110 (double)dynamics.hashTableUtilization / 100.0);
1111 ka_timestr(dynamics.start_time, bob, KA_TIMESTR_LEN);
1112 printf("From host %lx started at %s:\n", dynamics.host, bob);
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);
1128 print_stat(UAuthenticate);
1129 print_stat(UGetTicket);
1131 #if (KAMAJORVERSION>5)
1132 print cpu stats printf("%d string checks\n", dynamics.string_checks);
1134 printf("Used %.3f seconds of CPU time.\n",
1135 dynamics.string_checks / 1000.0);
1137 printf("%d admin accounts\n", admins);
1142 DebugInfo(struct cmd_syndesc *as, char *arock)
1145 struct ka_debugInfo info;
1149 char bob[KA_TIMESTR_LEN];
1152 if (as->parms[0].items) {
1153 struct ubik_client *iConn;
1155 ka_SingleServerConn(cell, as->parms[0].items->data,
1156 KA_MAINTENANCE_SERVICE, 0, &iConn);
1158 struct afsconf_cell cellinfo;
1160 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);
1164 com_err(whoami, code, "getting servers in cell %s", cell);
1166 printf("Servers in cell %s, are:\n", cell);
1167 for (i = 0; i < cellinfo.numServers; i++)
1168 printf(" %s\n", cellinfo.hostName[i]);
1172 code = ubik_Call(KAM_Debug, iConn, 0, KAMAJORVERSION, 0, &info);
1173 ubik_ClientDestroy(iConn);
1175 code = ubik_Call(KAM_Debug, conn, 0, KAMAJORVERSION, 0, &info);
1178 com_err(whoami, code, "call to Debug failed");
1183 if (info.minorVersion != KAMINORVERSION)
1184 printf("Minor version number mismatch: got %d, expected %d\n",
1185 info.minorVersion, KAMINORVERSION);
1188 #if (KAMAJORVERSION>5)
1195 timeOffset = -timeOffset;
1196 if (timeOffset > 60) {
1198 ("WARNING: Large server client clock skew: %d seconds. Call itself took %d seconds.\n",
1199 timeOffset, now - start);
1201 ka_timestr(info.startTime, bob, KA_TIMESTR_LEN);
1202 printf("From host %lx started %sat %s:\n", info.host,
1203 (info.noAuth ? "w/o authorization " : ""), bob);
1204 ka_timestr(info.lastTrans, bob, KA_TIMESTR_LEN);
1205 printf("Last trans was %s at %s\n", info.lastOperation, bob);
1206 ka_timestr(info.dbHeaderRead, bob, KA_TIMESTR_LEN);
1207 printf("Header last read %s.\n", bob);
1208 printf("db version=%d, keyVersion=%d, key cache version=%d\n",
1209 info.dbVersion, info.dbSpecialKeysVersion, info.kcVersion);
1210 printf("db ptrs: free %d, eof %d, kvno %d.\n", info.dbFreePtr,
1211 info.dbEofPtr, info.dbKvnoPtr);
1212 ka_timestr(info.nextAutoCPW, bob, KA_TIMESTR_LEN);
1213 printf("Next autoCPW at %s or in %d updates.\n", bob,
1214 info.updatesRemaining);
1215 if (info.cheader_lock || info.keycache_lock)
1216 printf("locks: cheader %08lx, keycache %08lx\n", info.cheader_lock,
1217 info.keycache_lock);
1218 printf("Last authentication for %s, last admin user was %s\n",
1219 info.lastAuth, info.lastAdmin);
1220 printf("Last TGS op was a %s ticket was for %s\n", info.lastTGSServer,
1222 printf("Last UDP TGS was a %s ticket for %s. UDP Authenticate for %s\n",
1223 info.lastUTGSServer, info.lastUTGS, info.lastUAuth);
1224 printf("key cache size %d, used %d.\n", info.kcSize, info.kcUsed);
1225 if (info.kcUsed > KADEBUGKCINFOSIZE) {
1226 printf("insufficient room to return all key cache entries!\n");
1227 info.kcUsed = KADEBUGKCINFOSIZE;
1229 for (i = 0; i < info.kcUsed; i++)
1230 ka_timestr(info.kcInfo[i].used, bob, KA_TIMESTR_LEN);
1231 printf("%32s %c %2x(%2x) used %s\n", info.kcInfo[i].principal,
1232 (info.kcInfo[i].primary ? '*' : ' '), info.kcInfo[i].kvno,
1233 info.kcInfo[i].keycksum, bob);
1238 Interactive(struct cmd_syndesc *as, char *arock)
1245 Quit(struct cmd_syndesc *as, char *arock)
1252 MyAfterProc(struct cmd_syndesc *as)
1254 if (!strcmp(as->name, "help"))
1257 /* Determine if we need to destory the ubik connection.
1258 * Closing it avoids resends of packets.
1261 ubik_ClientDestroy(conn);
1268 int init = 0, noauth;
1269 char name[MAXKTCNAMELEN];
1270 char instance[MAXKTCNAMELEN];
1271 char newCell[MAXKTCREALMLEN];
1272 afs_int32 serverList[MAXSERVERS];
1275 NoAuth(struct cmd_syndesc *as, char *arock)
1282 MyBeforeProc(struct cmd_syndesc *as, char *arock)
1284 extern struct passwd *getpwuid();
1285 struct ktc_encryptionKey key;
1286 struct ktc_principal auth_server, auth_token, client;
1287 char realm[MAXKTCREALMLEN];
1289 struct ktc_token token, *pToken;
1290 int i, acode, code = 0;
1293 char *ws = strrchr(as->a0name, '/');
1295 ws++; /* skip everything before the "/" */
1298 if (strlen(ws) > 0) {
1299 strncpy(whoami, ws, sizeof(whoami));
1300 if (strlen(whoami) + 1 >= sizeof(whoami))
1301 strcpy(whoami, "kas:");
1303 strcat(whoami, ":");
1306 /* append sub-command name */
1307 strncat(whoami, as->name, sizeof(whoami) - strlen(whoami) - 1);
1310 if (as->parms[12].name == 0)
1313 assert(as->parms[13].name && as->parms[14].name && as->parms[15].name
1314 && as->parms[16].name);
1316 /* MyAfterProc() destroys the conn, but just to be sure */
1318 code = ubik_ClientDestroy(conn);
1322 if (!init || as->parms[12].items || as->parms[13].items
1323 || as->parms[14].items || as->parms[15].items
1324 || as->parms[16].items) {
1325 strcpy(instance, "");
1326 strcpy(newCell, "");
1328 if (as->parms[12].items) { /* -admin_username */
1330 ka_ParseLoginName(as->parms[12].items->data, name, instance,
1333 com_err(whoami, code, "parsing user's name '%s'",
1334 as->parms[12].items->data);
1339 DWORD len = MAXKTCNAMELEN;
1340 if (!GetUserName((LPTSTR) name, &len)) {
1341 printf("Can't get user name \n");
1345 /* No explicit name provided: use Unix uid. */
1346 struct passwd *pw = getpwuid(getuid());
1348 printf("Can't figure out your name from your user id.\n");
1351 strncpy(name, pw->pw_name, sizeof(name));
1355 if (as->parms[14].items) { /* -cell */
1356 if (strlen(newCell) > 0) {
1357 printf("Duplicate cell specification not allowed\n");
1359 strncpy(newCell, as->parms[14].items->data, sizeof(newCell));
1362 code = ka_ExpandCell(newCell, newCell, 0 /*local */ );
1364 com_err(whoami, code, "Can't expand cell name");
1367 strcpy(cell, newCell);
1369 if (as->parms[15].items) { /* -servers */
1370 struct cmd_item *ip;
1371 char *ap[MAXSERVERS + 2];
1375 for (ip = as->parms[15].items, i = 2; ip; ip = ip->next, i++)
1377 code = ubik_ParseClientList(i, ap, serverList);
1379 com_err(whoami, code, "could not parse server list");
1382 ka_ExplicitCell(cell, serverList);
1385 noauth = (as->parms[16].items ? 1 : 0); /* -noauth */
1390 token.ticketLen = 0; /* in case there are no tokens */
1391 if (!noauth) { /* Will prompt for a password */
1392 /* first see if there's already an admin ticket */
1394 ka_GetAdminToken(0, 0, cell, 0, KA_SIXHOURS, &token,
1396 if (code) { /* if not then get key and try again */
1397 if (as->parms[13].items) { /* if password specified */
1398 strncpy(passwd, as->parms[13].items->data, sizeof(passwd));
1399 memset(as->parms[13].items->data, 0,
1400 strlen(as->parms[13].items->data));
1402 char msg[MAXKTCNAMELEN + 50];
1403 if (as->parms[12].items)
1404 sprintf(msg, "Administrator's (%s) Password: ", name);
1406 sprintf(msg, "Password for %s: ", name);
1407 code = read_pw_string(passwd, sizeof(passwd), msg, 0);
1410 else if (strlen(passwd) == 0)
1411 code = KANULLPASSWORD;
1413 com_err(whoami, code, "reading password");
1417 ka_StringToKey(passwd, cell, &key);
1419 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1420 &token, 0 /* !new */ );
1421 if (code == KABADREQUEST) {
1422 des_string_to_key(passwd, &key);
1424 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1425 &token, 0 /* !new */ );
1427 if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
1428 /* try with only the first 8 characters incase they set
1429 * their password with an old style passwd program. */
1431 ka_StringToKey(passwd, cell, &key);
1433 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1434 &token, 0 /* !new */ );
1437 "Warning: you have typed a password longer than 8 characters, but only the\n");
1439 "first 8 characters were actually significant. If you change your password\n");
1441 "again this warning message will go away.\n");
1448 reason = "password was incorrect";
1451 reason = "Authentication Server was unavailable";
1454 reason = (char *)error_message(code);
1457 "%s: Auth. as %s to AuthServer failed: %s\nProceeding w/o authentication\n",
1458 whoami, PrintedName(name, instance, cell), reason);
1460 /* get an Authentication token while were at it. */
1461 if (ka_CellToRealm(cell, realm, 0) != 0)
1463 strcpy(auth_server.name, KA_TGS_NAME);
1464 strcpy(auth_server.instance, realm);
1465 strcpy(auth_server.cell, cell);
1467 (&auth_server, &auth_token, sizeof(struct ktc_token),
1470 ka_GetAuthToken(name, instance, cell, &key,
1471 MAXKTCTICKETLIFETIME, (afs_int32 *) 0
1472 /*Don't need pwd expiration info here */
1474 if (acode && (acode != code)) /* codes are usually the same */
1475 com_err(whoami, code,
1476 "getting Authentication token for %s",
1477 PrintedName(name, instance, cell));
1479 memset(&key, 0, sizeof(key));
1483 pToken = ((token.ticketLen == 0) ? 0 : &token);
1484 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, pToken, &conn);
1485 if (code && pToken) {
1486 com_err(whoami, code,
1487 "connecting to AuthServer: now trying w/o authentication");
1488 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, 0, &conn);
1490 com_err(whoami, code,
1491 "making unauthenticated connection to AuthServer");
1494 com_err(whoami, code,
1495 "Couldn't establish connection to Authentication Server");
1499 /* now default unspecified password by prompting from terminal */
1500 if (as->nParms >= 12)
1501 for (i = 0; i < 12; i++)
1502 if (as->parms[i].name && (as->parms[i].items == 0)) {
1503 char *p = as->parms[i].name; /* parameter name */
1504 int l = strlen(p); /* length of name */
1505 /* does parameter end in "password" */
1506 if (strcmp(p + (l - 8), "password") == 0) {
1508 char password[BUFSIZ];
1509 struct cmd_item *ip;
1513 code = read_pw_string(password, sizeof(password), msg, 1);
1516 else if (strlen(password) == 0)
1517 code = KANULLPASSWORD;
1519 com_err(whoami, code, "prompting for %s", p + 1);
1522 ip = (struct cmd_item *)malloc(sizeof(struct cmd_item));
1523 ip->data = (char *)malloc(strlen(password) + 1);
1525 strcpy(ip->data, password);
1526 as->parms[i].items = ip;
1529 if (!conn) { /* if all else fails... */
1530 code = NoAuth(0, 0); /* get unauthenticated conn */
1537 /* These are some helpful command that deal with the cache managers tokens. */
1540 ForgetTicket(struct cmd_syndesc *as, char *arock)
1545 struct ktc_principal server;
1547 if (as->parms[0].items) {
1548 char *name = as->parms[0].items->data;
1550 ka_ParseLoginName(name, server.name, server.instance,
1553 com_err(whoami, code, "couldn't interpret name '%s'", name);
1556 if (server.cell[0] == 0) {
1557 if (code = DefaultCell())
1559 strcpy(server.cell, cell);
1561 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1563 com_err(whoami, code, "Can't expand cell name");
1567 code = ktc_ForgetToken(&server);
1569 com_err(whoami, code, "couldn't remove tokens for %s",
1570 PrintedPrincipal(&server));
1574 if (!as->parms[1].items) {
1575 fprintf(stderr, "Must specify server name or -all\n");
1578 code = ktc_ForgetAllTokens();
1580 com_err(whoami, code, "couldn't delete all tokens");
1585 code = ktc_ForgetAllTokens();
1587 com_err(whoami, code, "couldn't delete all tokens");
1594 ListTickets(struct cmd_syndesc *as, char *arock)
1597 int index, newIndex;
1598 struct ktc_principal server;
1601 if (as->parms[1].items)
1603 if (as->parms[0].items) {
1604 char *name = as->parms[0].items->data;
1606 ka_ParseLoginName(name, server.name, server.instance,
1609 com_err(whoami, code, "couldn't interpret name '%s'", name);
1612 if (server.cell[0] == 0) {
1613 if (code = DefaultCell())
1615 strcpy(server.cell, cell);
1617 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1619 com_err(whoami, code, "Can't expand cell name");
1623 code = ListTicket(&server, verbose);
1625 for (index = 0;; index = newIndex) {
1626 code = ktc_ListTokens(index, &newIndex, &server);
1628 if (code == KTC_NOENT)
1629 code = 0; /* end of list */
1632 code = ListTicket(&server, verbose);
1638 add_std_args(register struct cmd_syndesc *ts)
1641 /* 12 */ cmd_AddParm(ts, "-admin_username", CMD_SINGLE, CMD_OPTIONAL,
1642 "admin principal to use for authentication");
1643 /* 13 */ cmd_AddParm(ts, "-password_for_admin", CMD_SINGLE, CMD_OPTIONAL,
1645 /* 14 */ cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1646 /* 15 */ cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
1647 "explicit list of authentication servers");
1648 /* 16 */ cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL,
1649 "don't authenticate");
1653 ka_AdminInteractive(int cmd_argc, char *cmd_argv[])
1656 register struct cmd_syndesc *ts;
1662 strncpy(myName, *cmd_argv, 509);
1664 cmd_SetBeforeProc(MyBeforeProc, NULL);
1665 cmd_SetAfterProc(MyAfterProc, NULL);
1667 ts = cmd_CreateSyntax("interactive", Interactive, 0,
1668 "enter interactive mode");
1671 ts = cmd_CreateSyntax("noauthentication", NoAuth, 0,
1672 "connect to AuthServer w/o using token");
1674 ts = cmd_CreateSyntax("list", ListUsers, 0, "list all users in database");
1675 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1676 "show detailed info about each user");
1677 cmd_AddParm(ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL,
1678 "show all cell administrators");
1679 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1680 "show the user's actual key rather than the checksum");
1682 cmd_CreateAlias(ts, "ls");
1684 ts = cmd_CreateSyntax("examine", ExamineUser, 0,
1685 "examine the entry for a user");
1686 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1687 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1688 "show the user's actual key rather than the checksum");
1691 ts = cmd_CreateSyntax("create", CreateUser, 0,
1692 "create an entry for a user");
1693 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1694 cmd_AddParm(ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL,
1695 "initial password");
1698 ts = cmd_CreateSyntax("delete", DeleteUser, 0, "delete a user");
1699 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1701 cmd_CreateAlias(ts, "rm");
1703 ts = cmd_CreateSyntax("setfields", SetFields, 0,
1704 "set various fields in a user's entry");
1705 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1706 cmd_AddParm(ts, "-flags", CMD_SINGLE, CMD_OPTIONAL,
1707 "hex flag value or flag name expression");
1708 cmd_AddParm(ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL,
1709 "date of account expiration");
1710 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
1711 "maximum ticket lifetime");
1712 cmd_AddParm(ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL,
1713 "number days password is valid ([0..254])");
1714 cmd_AddParm(ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL,
1715 "permit password reuse (yes/no)");
1716 cmd_AddParm(ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL,
1717 "maximum successive failed login tries ([0..254])");
1718 cmd_AddParm(ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL,
1719 "failure penalty [hh:mm or minutes]");
1721 cmd_AddParm(ts, "-associates", CMD_SINGLE, CMD_OPTIONAL,
1722 "maximum associate instances");
1725 cmd_CreateAlias(ts, "sf");
1728 ts = cmd_CreateSyntax("unlock", Unlock, 0,
1729 "Enable authentication ID after max failed attempts exceeded");
1730 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "authentication ID");
1734 ts = cmd_CreateSyntax("stringtokey", StringToKey, 0,
1735 "convert a string to a key");
1736 cmd_AddParm(ts, "-string", CMD_SINGLE, 0, "password string");
1737 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1739 ts = cmd_CreateSyntax("setpassword", SetPassword, 0,
1740 "set a user's password");
1741 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1742 cmd_AddParm(ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL,
1745 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1747 cmd_CreateAlias(ts, "sp");
1748 #ifdef CMD_PARSER_AMBIG_FIX
1749 cmd_CreateAlias(ts, "setpasswd");
1752 /* set a user's key */
1753 ts = cmd_CreateSyntax("setkey", SetPassword, 0, (char *)CMD_HIDDEN);
1754 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1756 cmd_AddParm(ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1758 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1761 /* get a user's password */
1762 ts = cmd_CreateSyntax("getpassword", GetPassword, 0, (char *)CMD_HIDDEN);
1763 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1764 /* don't take standard args */
1765 /* add_std_args (ts); */
1766 #ifdef CMD_PARSER_AMBIG_FIX
1767 cmd_CreateAlias(ts, "getpasswd");
1770 /* get a random key */
1771 ts = cmd_CreateSyntax("getrandomkey", GetRandomKey, 0,
1772 (char *)CMD_HIDDEN);
1775 /* get a ticket for a specific server */
1776 ts = cmd_CreateSyntax("getticket", GetTicket, 0, (char *)CMD_HIDDEN);
1777 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of server");
1778 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1781 ts = cmd_CreateSyntax("statistics", Statistics, 0,
1782 "show statistics for AuthServer");
1785 /* show debugging info from AuthServer */
1786 ts = cmd_CreateSyntax("debuginfo", DebugInfo, 0, (char *)CMD_HIDDEN);
1787 cmd_AddParm(ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL,
1788 "authentication server host name");
1791 ts = cmd_CreateSyntax("forgetticket", ForgetTicket, 0,
1792 "delete user's tickets");
1794 cmd_AddParm(ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1797 cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1799 ts = cmd_CreateSyntax("listtickets", ListTickets, 0,
1800 "show all cache manager tickets");
1801 cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1802 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1803 "show session key and ticket");
1805 ts = cmd_CreateSyntax("quit", Quit, 0, "exit program");
1808 conn = 0; /* no connection yet */
1809 zero_argc = cmd_argc;
1810 zero_argv = cmd_argv;
1812 strcpy(whoami, "kas");
1814 if (code = cmd_Dispatch(cmd_argc, cmd_argv)) {
1823 s = fgets(line, sizeof(line), stdin);
1825 return 0; /* EOF on input */
1826 for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--)
1829 continue; /* blank line */
1832 cmd_ParseLine(line, argv, &argc, sizeof(argv) / sizeof(argv[0]));
1834 com_err(whoami, code, "parsing line: '%s'", line);
1837 code = cmd_Dispatch(argc, argv);