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)) {
556 if (conn && conn->conns[count - 1]
557 && conn->conns[count - 1]->peer) {
558 server = conn->conns[count - 1]->peer->host;
560 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),
570 } while (code != UNOSERVERS);
576 SetFields(struct cmd_syndesc *as, char *arock)
579 char name[MAXKTCNAMELEN];
580 char instance[MAXKTCNAMELEN];
583 afs_int32 lifetime = 0;
584 afs_int32 maxAssociates = -1;
585 afs_int32 pwexpiry = 0;
586 afs_int32 was_spare = 0;
587 char misc_auth_bytes[4];
590 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
592 com_err(whoami, code, "parsing user's name '%s'",
593 as->parms[0].items->data);
597 if (as->parms[1].items) {
598 code = parse_flags(name, instance, as->parms[1].items->data, &flags);
601 ("Illegal flag specification: %s, should be of the form <'='|'+'|'-'|'_'>bitname{<'+'|'-'>bitname}*\n",
602 as->parms[1].items->data);
606 if (as->parms[2].items) {
608 char *s = strncpy(buf, as->parms[2].items->data, sizeof(buf));
609 code = ktime_DateToInt32(s, &expiration);
611 printf("Illegal time format %s: %s\n", as->parms[2].items->data,
612 ktime_GetDateUsage());
615 if (expiration == 0) {
616 fprintf(stderr, "Expiration time must be after (about) 1970.\n");
619 if (expiration < time(0)) {
621 "Warning: expiration being set into the past, account will be disabled.\n");
627 if (as->parms[3].items) {
628 code = read_time_interval(as->parms[3].items->data, &lifetime);
633 /* no point in doing this any sooner than necessary */
634 for (i = 0; i < 4; misc_auth_bytes[i++] = 0);
636 if (as->parms[4].items) {
637 if (util_isint(as->parms[4].items->data))
638 pwexpiry = atoi(as->parms[4].items->data);
641 "Password lifetime specified must be a non-negative decimal integer.\n");
644 if (pwexpiry < 0 || pwexpiry > 254) {
646 "Password lifetime range must be [0..254] days.\n");
647 fprintf(stderr, "Zero represents an unlimited lifetime.\n");
650 misc_auth_bytes[0] = pwexpiry + 1;
654 if (as->parms[5].items) {
656 reuse = (as->parms[5].items->data);
658 if (!strcmp(reuse, "yes")) {
659 misc_auth_bytes[1] = KA_REUSEPW;
660 } else if (strcmp(reuse, "no")) {
662 "must specify \"yes\" or \"no\": \"yes\" assumed\n");
663 misc_auth_bytes[1] = KA_REUSEPW;
665 misc_auth_bytes[1] = KA_NOREUSEPW;
669 if (as->parms[6].items) {
673 if (util_isint(as->parms[6].items->data)
674 && ((nfailures = atoi(as->parms[6].items->data)) < 255)) {
675 misc_auth_bytes[2] = nfailures + 1;
677 fprintf(stderr, "Failure limit must be in [0..254].\n");
678 fprintf(stderr, "Zero represents unlimited login attempts.\n");
683 if (as->parms[7].items) {
684 int locktime, hrs, mins;
688 s = as->parms[7].items->data;
690 sscanf(s, "%d:%d", &hrs, &mins);
692 sscanf(s, "%d", &mins);
694 locktime = hrs * 60 + mins;
695 if (hrs < 0 || hrs > 36 || mins < 0) {
697 "Lockout times must be either minutes or hh:mm.\n");
698 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
700 } else if (locktime > 36 * 60) {
702 "Lockout times must be either minutes or hh:mm.\n");
703 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
705 "Continuing with lock time of exactly 36 hours...\n");
708 locktime = (locktime * 60 + 511) >> 9; /* ceil(l*60/512) */
709 misc_auth_bytes[3] = locktime + 1; /* will be 1 if user said 0 */
712 if (as->parms[8].items) {
713 maxAssociates = atoi(as->parms[6].items->data);
714 if (maxAssociates < 0) {
715 printf("Illegal maximum number of associates\n");
720 was_spare = pack_long(misc_auth_bytes);
722 if (was_spare || flags || expiration || lifetime || (maxAssociates >= 0))
724 ubik_Call(KAM_SetFields, conn, 0, name, instance, flags,
725 expiration, lifetime, maxAssociates, was_spare,
728 printf("Must specify one of the optional parameters\n");
732 com_err(whoami, code, "calling KAM_SetFields for %s.%s", name,
738 StringToKey(struct cmd_syndesc *as, char *arock)
741 char realm[MAXKTCREALMLEN];
742 struct ktc_encryptionKey key;
744 if (as->parms[1].items) {
745 code = ka_ExpandCell(as->parms[1].items->data, realm, 0 /*local */ );
747 com_err(whoami, code,
748 "expanding %s as cell name, attempting to continue",
749 as->parms[1].items->data);
751 ucstring(realm, realm, sizeof(realm));
753 if (code = DefaultCell())
755 ucstring(realm, cell, sizeof(realm));
757 ka_StringToKey(as->parms[0].items->data, realm, &key);
759 printf("Converting %s in realm '%s' yields key='",
760 as->parms[0].items->data, realm);
761 ka_PrintBytes((char *)&key, sizeof(key));
764 des_string_to_key(as->parms[0].items->data, &key);
766 printf("Converting %s with the DES string to key yields key='",
767 as->parms[0].items->data);
768 ka_PrintBytes((char *)&key, sizeof(key));
775 SetPassword(struct cmd_syndesc *as, char *arock)
778 char name[MAXKTCNAMELEN];
779 char instance[MAXKTCNAMELEN];
780 char realm[MAXKTCREALMLEN];
781 struct ktc_encryptionKey key;
784 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, realm);
786 com_err(whoami, code, "parsing user's name '%s'",
787 as->parms[0].items->data);
791 if (strlen(realm) == 0)
792 ucstring(realm, cell, sizeof(realm));
794 if (as->parms[1].items && as->parms[2].items) {
795 printf("Can't specify both a password and a key\n");
797 } else if (as->parms[1].items) {
798 (void)init_child(myName);
799 (void)give_to_child(passwd); /* old password */
800 code = password_bad(as->parms[1].items->data);
801 (void)terminate_child();
804 ka_StringToKey(as->parms[1].items->data, realm, &key);
805 } else if (as->parms[2].items) {
806 if (ka_ReadBytes(as->parms[2].items->data, (char *)&key, sizeof(key))
808 printf("Key must be 8 bytes: '%s' was too long\n",
809 as->parms[2].items->data);
813 printf("Must specify new password or key\n");
818 if (as->parms[3].items)
819 sscanf(as->parms[3].items->data, "%d", &kvno);
821 #if defined(AFS_S390_LINUX20_ENV) && !defined(AFS_S390X_LINUX20_ENV)
822 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, 0, key);
824 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, key);
827 com_err(whoami, code, "so can't set password for %s.%s", name,
832 #define PrintPrincipal(p,n,l) \
833 PrintName((p)->name, (p)->instance, (p)->cell, l, n)
836 PrintName(char *name, char *inst, char *acell, int buflen, char *buf)
839 int left; /* if ConvertBytes stops early */
848 left = ka_ConvertBytes(buf, buflen, name, strlen(name));
852 com_err(whoami, code, "PrintName: principal name was '%s'.'%s'@'%s'",
859 if (nlen + len + 1 >= buflen)
862 left = ka_ConvertBytes(buf + nlen, buflen - nlen, inst, len);
870 char *lcell = ka_LocalCell();
873 if (strcmp(acell, lcell) != 0) {
874 /* only append cell if not the local cell */
875 if (nlen + len + 1 >= buflen)
878 left = ka_ConvertBytes(buf + nlen, buflen - nlen, acell, len);
887 #define PrintedPrincipal(p) PrintedName ((p)->name, (p)->instance, (p)->cell)
889 /* PrintedName - returned a pointer to a static string in which the formated
890 * name has been stored. */
893 PrintedName(char *name, char *inst, char *cell)
895 static char printedName[128];
897 code = PrintName(name, inst, cell, sizeof(printedName), printedName);
901 strncpy(printedName, name, sizeof(printedName));
902 printedName[sizeof(printedName) - 8] = 0;
903 strcat(printedName, "<error>");
909 ListTicket(struct ktc_principal *server, int verbose)
912 struct ktc_token token; /* the token we're printing */
913 struct ktc_principal client;
914 char UserName[sizeof(struct ktc_principal)];
915 char ServerName[sizeof(struct ktc_principal)];
916 afs_int32 now = time(0);
917 char bob[KA_TIMESTR_LEN];
919 /* get the ticket info itself */
920 code = ktc_GetToken(server, &token, sizeof(token), &client);
922 com_err(whoami, code, "failed to get token info for server %s",
923 PrintedPrincipal(server));
926 code = PrintPrincipal(&client, UserName, sizeof(UserName));
929 /* spaces are printed as "\040" */
930 if (UserName[0] == 0)
932 else if (strncmp(UserName, "AFS\\040ID\\040", 13) == 0) {
933 printf("User's (AFS ID %s) tokens", UserName + 13);
934 } else if (strncmp(UserName, "Unix\\040UID\\040", 15) == 0) {
937 printf("User %s's tokens", UserName);
939 code = PrintPrincipal(server, ServerName, sizeof(ServerName));
942 printf(" for %s ", ServerName);
944 if (token.startTime > now) {
945 ka_timestr(token.startTime, bob, KA_TIMESTR_LEN);
946 printf("[>> POSTDATED 'till %s <<]", bob);
949 if (token.endTime <= now)
950 printf("[>> Expired <<]\n");
952 ka_timestr(token.endTime, bob, KA_TIMESTR_LEN);
953 printf("[Expires %s]\n", bob);
956 printf("SessionKey: ");
957 ka_PrintBytes((char *)&token.sessionKey, sizeof(token.sessionKey));
958 printf("\nTicket (kvno = %d, len = %d): ", token.kvno,
960 ka_PrintBytes((char *)token.ticket, token.ticketLen);
967 GetTicket(struct cmd_syndesc *as, char *arock)
970 struct ktc_principal server;
971 struct ktc_token token;
972 afs_int32 life = KA_SIXHOURS;
974 if (as->parms[1].items) {
975 code = read_time_interval(as->parms[1].items->data, &life);
980 ka_ParseLoginName(as->parms[0].items->data, server.name,
981 server.instance, server.cell);
983 com_err(whoami, code, "parsing user's name '%s'",
984 as->parms[0].items->data);
987 if (server.cell[0] == 0) {
988 if (code = DefaultCell())
990 strcpy(server.cell, cell);
992 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
994 com_err(whoami, code, "Can't expand cell name");
999 token.ticketLen = 0; /* in case there are no tokens */
1001 ka_GetServerToken(server.name, server.instance, server.cell, life,
1002 &token, /*new */ 1, /*dosetpag */ 0);
1004 com_err(whoami, code, "getting ticket for %s",
1005 PrintedPrincipal(&server));
1007 code = ListTicket(&server, /*verbose */ 1);
1013 GetPassword(struct cmd_syndesc *as, char *arock)
1016 char name[MAXKTCNAMELEN];
1017 struct ktc_encryptionKey key;
1018 static struct ubik_client *lpbkConn = 0;
1020 /* no instance allowed */
1021 code = ka_ParseLoginName(as->parms[0].items->data, name, 0, 0);
1024 com_err(whoami, code,
1025 "getting %s's password via loopback connection to GetPassword",
1027 /* if we got a timeout, print a clarification, too */
1030 "%s: please note that this command must be run locally on a database server machine.\n",
1035 if (lpbkConn == 0) {
1036 struct rx_connection *conns[2];
1037 struct rx_securityClass *sc;
1038 int si; /* security class index */
1043 sc = rxnull_NewClientSecurityObject();
1044 si = RX_SCINDEX_NULL;
1046 rx_NewConnection(htonl(INADDR_LOOPBACK), htons(AFSCONF_KAUTHPORT),
1047 KA_MAINTENANCE_SERVICE, sc, si);
1049 code = ubik_ClientInit(conns, &lpbkConn);
1053 code = ubik_Call(KAM_GetPassword, lpbkConn, 0, name, &key);
1054 /* Lets close down the ubik_Client connection now */
1055 ubik_ClientDestroy(lpbkConn);
1059 ka_PrintBytes((char *)&key, sizeof(key));
1065 GetRandomKey(struct cmd_syndesc *as, char *arock)
1068 struct ktc_encryptionKey key;
1070 code = ubik_Call(KAM_GetRandomKey, conn, 0, &key);
1072 com_err(whoami, code, "so can't get random key");
1076 ka_PrintBytes((char *)&key, sizeof(key));
1078 for (i = 0; i < sizeof(key); i++) {
1079 printf("%0.2x", ((char *)&key)[i] & 0xff);
1091 Statistics(struct cmd_syndesc *as, char *arock)
1097 char bob[KA_TIMESTR_LEN];
1100 ubik_Call(KAM_GetStats, conn, 0, KAMAJORVERSION, &admins, &statics,
1103 printf("call to GetStats failed: %s\n", ka_ErrorString(code));
1106 if (statics.minor_version != KAMINORVERSION)
1107 printf("Minor version number mismatch: got %d, expected %d\n",
1108 statics.minor_version, KAMINORVERSION);
1109 printf("%d allocs, %d frees, %d password changes\n", statics.allocs,
1110 statics.frees, statics.cpws);
1111 printf("Hash table utilization = %f%%\n",
1112 (double)dynamics.hashTableUtilization / 100.0);
1113 ka_timestr(dynamics.start_time, bob, KA_TIMESTR_LEN);
1114 printf("From host %lx started at %s:\n", dynamics.host, bob);
1116 #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)
1117 print_stat(Authenticate);
1118 print_stat(ChangePassword);
1119 print_stat(GetTicket);
1120 print_stat(CreateUser);
1121 print_stat(SetPassword);
1122 print_stat(SetFields);
1123 print_stat(DeleteUser);
1124 print_stat(GetEntry);
1125 print_stat(ListEntry);
1126 print_stat(GetStats);
1127 print_stat(GetPassword);
1128 print_stat(GetRandomKey);
1130 print_stat(UAuthenticate);
1131 print_stat(UGetTicket);
1133 #if (KAMAJORVERSION>5)
1134 print cpu stats printf("%d string checks\n", dynamics.string_checks);
1136 printf("Used %.3f seconds of CPU time.\n",
1137 dynamics.string_checks / 1000.0);
1139 printf("%d admin accounts\n", admins);
1144 DebugInfo(struct cmd_syndesc *as, char *arock)
1147 struct ka_debugInfo info;
1151 char bob[KA_TIMESTR_LEN];
1154 if (as->parms[0].items) {
1155 struct ubik_client *iConn;
1157 ka_SingleServerConn(cell, as->parms[0].items->data,
1158 KA_MAINTENANCE_SERVICE, 0, &iConn);
1160 struct afsconf_cell cellinfo;
1162 com_err(whoami, code, "couldn't find host %s in cell %s",
1163 as->parms[0].items->data, cell);
1164 code = ka_GetServers(cell, &cellinfo);
1166 com_err(whoami, code, "getting servers in cell %s", cell);
1168 printf("Servers in cell %s, are:\n", cell);
1169 for (i = 0; i < cellinfo.numServers; i++)
1170 printf(" %s\n", cellinfo.hostName[i]);
1174 code = ubik_Call(KAM_Debug, iConn, 0, KAMAJORVERSION, 0, &info);
1175 ubik_ClientDestroy(iConn);
1177 code = ubik_Call(KAM_Debug, conn, 0, KAMAJORVERSION, 0, &info);
1180 com_err(whoami, code, "call to Debug failed");
1185 if (info.minorVersion != KAMINORVERSION)
1186 printf("Minor version number mismatch: got %d, expected %d\n",
1187 info.minorVersion, KAMINORVERSION);
1190 #if (KAMAJORVERSION>5)
1197 timeOffset = -timeOffset;
1198 if (timeOffset > 60) {
1200 ("WARNING: Large server client clock skew: %d seconds. Call itself took %d seconds.\n",
1201 timeOffset, now - start);
1203 ka_timestr(info.startTime, bob, KA_TIMESTR_LEN);
1204 printf("From host %lx started %sat %s:\n", info.host,
1205 (info.noAuth ? "w/o authorization " : ""), bob);
1206 ka_timestr(info.lastTrans, bob, KA_TIMESTR_LEN);
1207 printf("Last trans was %s at %s\n", info.lastOperation, bob);
1208 ka_timestr(info.dbHeaderRead, bob, KA_TIMESTR_LEN);
1209 printf("Header last read %s.\n", bob);
1210 printf("db version=%d, keyVersion=%d, key cache version=%d\n",
1211 info.dbVersion, info.dbSpecialKeysVersion, info.kcVersion);
1212 printf("db ptrs: free %d, eof %d, kvno %d.\n", info.dbFreePtr,
1213 info.dbEofPtr, info.dbKvnoPtr);
1214 ka_timestr(info.nextAutoCPW, bob, KA_TIMESTR_LEN);
1215 printf("Next autoCPW at %s or in %d updates.\n", bob,
1216 info.updatesRemaining);
1217 if (info.cheader_lock || info.keycache_lock)
1218 printf("locks: cheader %08lx, keycache %08lx\n", info.cheader_lock,
1219 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,
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;
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);
1240 Interactive(struct cmd_syndesc *as, char *arock)
1247 Quit(struct cmd_syndesc *as, char *arock)
1254 MyAfterProc(struct cmd_syndesc *as)
1256 if (!strcmp(as->name, "help"))
1259 /* Determine if we need to destory the ubik connection.
1260 * Closing it avoids resends of packets.
1263 ubik_ClientDestroy(conn);
1270 int init = 0, noauth;
1271 char name[MAXKTCNAMELEN];
1272 char instance[MAXKTCNAMELEN];
1273 char newCell[MAXKTCREALMLEN];
1274 afs_int32 serverList[MAXSERVERS];
1277 NoAuth(struct cmd_syndesc *as, char *arock)
1284 MyBeforeProc(struct cmd_syndesc *as, char *arock)
1286 extern struct passwd *getpwuid();
1287 struct ktc_encryptionKey key;
1288 struct ktc_principal auth_server, auth_token, client;
1289 char realm[MAXKTCREALMLEN];
1291 struct ktc_token token, *pToken;
1292 int i, acode, code = 0;
1295 char *ws = strrchr(as->a0name, '/');
1297 ws++; /* skip everything before the "/" */
1300 if (strlen(ws) > 0) {
1301 strncpy(whoami, ws, sizeof(whoami));
1302 if (strlen(whoami) + 1 >= sizeof(whoami))
1303 strcpy(whoami, "kas:");
1305 strcat(whoami, ":");
1308 /* append sub-command name */
1309 strncat(whoami, as->name, sizeof(whoami) - strlen(whoami) - 1);
1312 if (as->parms[12].name == 0)
1315 assert(as->parms[13].name && as->parms[14].name && as->parms[15].name
1316 && as->parms[16].name);
1318 /* MyAfterProc() destroys the conn, but just to be sure */
1320 code = ubik_ClientDestroy(conn);
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, "");
1330 if (as->parms[12].items) { /* -admin_username */
1332 ka_ParseLoginName(as->parms[12].items->data, name, instance,
1335 com_err(whoami, code, "parsing user's name '%s'",
1336 as->parms[12].items->data);
1341 DWORD len = MAXKTCNAMELEN;
1342 if (!GetUserName((LPTSTR) name, &len)) {
1343 printf("Can't get user name \n");
1347 /* No explicit name provided: use Unix uid. */
1348 struct passwd *pw = getpwuid(getuid());
1350 printf("Can't figure out your name from your user id.\n");
1353 strncpy(name, pw->pw_name, sizeof(name));
1357 if (as->parms[14].items) { /* -cell */
1358 if (strlen(newCell) > 0) {
1359 printf("Duplicate cell specification not allowed\n");
1361 strncpy(newCell, as->parms[14].items->data, sizeof(newCell));
1364 code = ka_ExpandCell(newCell, newCell, 0 /*local */ );
1366 com_err(whoami, code, "Can't expand cell name");
1369 strcpy(cell, newCell);
1371 if (as->parms[15].items) { /* -servers */
1372 struct cmd_item *ip;
1373 char *ap[MAXSERVERS + 2];
1377 for (ip = as->parms[15].items, i = 2; ip; ip = ip->next, i++)
1379 code = ubik_ParseClientList(i, ap, serverList);
1381 com_err(whoami, code, "could not parse server list");
1384 ka_ExplicitCell(cell, serverList);
1387 noauth = (as->parms[16].items ? 1 : 0); /* -noauth */
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 */
1396 ka_GetAdminToken(0, 0, cell, 0, KA_SIXHOURS, &token,
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));
1404 char msg[MAXKTCNAMELEN + 50];
1405 if (as->parms[12].items)
1406 sprintf(msg, "Administrator's (%s) Password: ", name);
1408 sprintf(msg, "Password for %s: ", name);
1409 code = read_pw_string(passwd, sizeof(passwd), msg, 0);
1412 else if (strlen(passwd) == 0)
1413 code = KANULLPASSWORD;
1415 com_err(whoami, code, "reading password");
1419 ka_StringToKey(passwd, cell, &key);
1421 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1422 &token, 0 /* !new */ );
1423 if (code == KABADREQUEST) {
1424 des_string_to_key(passwd, &key);
1426 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1427 &token, 0 /* !new */ );
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. */
1433 ka_StringToKey(passwd, cell, &key);
1435 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1436 &token, 0 /* !new */ );
1439 "Warning: you have typed a password longer than 8 characters, but only the\n");
1441 "first 8 characters were actually significant. If you change your password\n");
1443 "again this warning message will go away.\n");
1450 reason = "password was incorrect";
1453 reason = "Authentication Server was unavailable";
1456 reason = (char *)error_message(code);
1459 "%s: Auth. as %s to AuthServer failed: %s\nProceeding w/o authentication\n",
1460 whoami, PrintedName(name, instance, cell), reason);
1462 /* get an Authentication token while were at it. */
1463 if (ka_CellToRealm(cell, realm, 0) != 0)
1465 strcpy(auth_server.name, KA_TGS_NAME);
1466 strcpy(auth_server.instance, realm);
1467 strcpy(auth_server.cell, cell);
1469 (&auth_server, &auth_token, sizeof(struct ktc_token),
1472 ka_GetAuthToken(name, instance, cell, &key,
1473 MAXKTCTICKETLIFETIME, (afs_int32 *) 0
1474 /*Don't need pwd expiration info here */
1476 if (acode && (acode != code)) /* codes are usually the same */
1477 com_err(whoami, code,
1478 "getting Authentication token for %s",
1479 PrintedName(name, instance, cell));
1481 memset(&key, 0, sizeof(key));
1485 pToken = ((token.ticketLen == 0) ? 0 : &token);
1486 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, pToken, &conn);
1487 if (code && pToken) {
1488 com_err(whoami, code,
1489 "connecting to AuthServer: now trying w/o authentication");
1490 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, 0, &conn);
1492 com_err(whoami, code,
1493 "making unauthenticated connection to AuthServer");
1496 com_err(whoami, code,
1497 "Couldn't establish connection to Authentication Server");
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) {
1510 char password[BUFSIZ];
1511 struct cmd_item *ip;
1515 code = read_pw_string(password, sizeof(password), msg, 1);
1518 else if (strlen(password) == 0)
1519 code = KANULLPASSWORD;
1521 com_err(whoami, code, "prompting for %s", p + 1);
1524 ip = (struct cmd_item *)malloc(sizeof(struct cmd_item));
1525 ip->data = (char *)malloc(strlen(password) + 1);
1527 strcpy(ip->data, password);
1528 as->parms[i].items = ip;
1531 if (!conn) { /* if all else fails... */
1532 code = NoAuth(0, 0); /* get unauthenticated conn */
1539 /* These are some helpful command that deal with the cache managers tokens. */
1542 ForgetTicket(struct cmd_syndesc *as, char *arock)
1547 struct ktc_principal server;
1549 if (as->parms[0].items) {
1550 char *name = as->parms[0].items->data;
1552 ka_ParseLoginName(name, server.name, server.instance,
1555 com_err(whoami, code, "couldn't interpret name '%s'", name);
1558 if (server.cell[0] == 0) {
1559 if (code = DefaultCell())
1561 strcpy(server.cell, cell);
1563 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1565 com_err(whoami, code, "Can't expand cell name");
1569 code = ktc_ForgetToken(&server);
1571 com_err(whoami, code, "couldn't remove tokens for %s",
1572 PrintedPrincipal(&server));
1576 if (!as->parms[1].items) {
1577 fprintf(stderr, "Must specify server name or -all\n");
1580 code = ktc_ForgetAllTokens();
1582 com_err(whoami, code, "couldn't delete all tokens");
1587 code = ktc_ForgetAllTokens();
1589 com_err(whoami, code, "couldn't delete all tokens");
1596 ListTickets(struct cmd_syndesc *as, char *arock)
1599 int index, newIndex;
1600 struct ktc_principal server;
1603 if (as->parms[1].items)
1605 if (as->parms[0].items) {
1606 char *name = as->parms[0].items->data;
1608 ka_ParseLoginName(name, server.name, server.instance,
1611 com_err(whoami, code, "couldn't interpret name '%s'", name);
1614 if (server.cell[0] == 0) {
1615 if (code = DefaultCell())
1617 strcpy(server.cell, cell);
1619 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1621 com_err(whoami, code, "Can't expand cell name");
1625 code = ListTicket(&server, verbose);
1627 for (index = 0;; index = newIndex) {
1628 code = ktc_ListTokens(index, &newIndex, &server);
1630 if (code == KTC_NOENT)
1631 code = 0; /* end of list */
1634 code = ListTicket(&server, verbose);
1640 add_std_args(register struct cmd_syndesc *ts)
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,
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");
1655 ka_AdminInteractive(int cmd_argc, char *cmd_argv[])
1658 register struct cmd_syndesc *ts;
1664 strncpy(myName, *cmd_argv, 509);
1666 cmd_SetBeforeProc(MyBeforeProc, NULL);
1667 cmd_SetAfterProc(MyAfterProc, NULL);
1669 ts = cmd_CreateSyntax("interactive", Interactive, 0,
1670 "enter interactive mode");
1673 ts = cmd_CreateSyntax("noauthentication", NoAuth, 0,
1674 "connect to AuthServer w/o using token");
1676 ts = cmd_CreateSyntax("list", ListUsers, 0, "list all users in database");
1677 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1678 "show detailed info about each user");
1679 cmd_AddParm(ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL,
1680 "show all cell administrators");
1681 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1682 "show the user's actual key rather than the checksum");
1684 cmd_CreateAlias(ts, "ls");
1686 ts = cmd_CreateSyntax("examine", ExamineUser, 0,
1687 "examine the entry for a user");
1688 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1689 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1690 "show the user's actual key rather than the checksum");
1693 ts = cmd_CreateSyntax("create", CreateUser, 0,
1694 "create an entry for a user");
1695 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1696 cmd_AddParm(ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL,
1697 "initial password");
1700 ts = cmd_CreateSyntax("delete", DeleteUser, 0, "delete a user");
1701 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1703 cmd_CreateAlias(ts, "rm");
1705 ts = cmd_CreateSyntax("setfields", SetFields, 0,
1706 "set various fields in a user's entry");
1707 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1708 cmd_AddParm(ts, "-flags", CMD_SINGLE, CMD_OPTIONAL,
1709 "hex flag value or flag name expression");
1710 cmd_AddParm(ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL,
1711 "date of account expiration");
1712 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
1713 "maximum ticket lifetime");
1714 cmd_AddParm(ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL,
1715 "number days password is valid ([0..254])");
1716 cmd_AddParm(ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL,
1717 "permit password reuse (yes/no)");
1718 cmd_AddParm(ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL,
1719 "maximum successive failed login tries ([0..254])");
1720 cmd_AddParm(ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL,
1721 "failure penalty [hh:mm or minutes]");
1723 cmd_AddParm(ts, "-associates", CMD_SINGLE, CMD_OPTIONAL,
1724 "maximum associate instances");
1727 cmd_CreateAlias(ts, "sf");
1730 ts = cmd_CreateSyntax("unlock", Unlock, 0,
1731 "Enable authentication ID after max failed attempts exceeded");
1732 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "authentication ID");
1736 ts = cmd_CreateSyntax("stringtokey", StringToKey, 0,
1737 "convert a string to a key");
1738 cmd_AddParm(ts, "-string", CMD_SINGLE, 0, "password string");
1739 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1741 ts = cmd_CreateSyntax("setpassword", SetPassword, 0,
1742 "set a user's password");
1743 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1744 cmd_AddParm(ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL,
1747 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1749 cmd_CreateAlias(ts, "sp");
1750 #ifdef CMD_PARSER_AMBIG_FIX
1751 cmd_CreateAlias(ts, "setpasswd");
1754 /* set a user's key */
1755 ts = cmd_CreateSyntax("setkey", SetPassword, 0, (char *)CMD_HIDDEN);
1756 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1758 cmd_AddParm(ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1760 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1763 /* get a user's password */
1764 ts = cmd_CreateSyntax("getpassword", GetPassword, 0, (char *)CMD_HIDDEN);
1765 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1766 /* don't take standard args */
1767 /* add_std_args (ts); */
1768 #ifdef CMD_PARSER_AMBIG_FIX
1769 cmd_CreateAlias(ts, "getpasswd");
1772 /* get a random key */
1773 ts = cmd_CreateSyntax("getrandomkey", GetRandomKey, 0,
1774 (char *)CMD_HIDDEN);
1777 /* get a ticket for a specific server */
1778 ts = cmd_CreateSyntax("getticket", GetTicket, 0, (char *)CMD_HIDDEN);
1779 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of server");
1780 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1783 ts = cmd_CreateSyntax("statistics", Statistics, 0,
1784 "show statistics for AuthServer");
1787 /* show debugging info from AuthServer */
1788 ts = cmd_CreateSyntax("debuginfo", DebugInfo, 0, (char *)CMD_HIDDEN);
1789 cmd_AddParm(ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL,
1790 "authentication server host name");
1793 ts = cmd_CreateSyntax("forgetticket", ForgetTicket, 0,
1794 "delete user's tickets");
1796 cmd_AddParm(ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1799 cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1801 ts = cmd_CreateSyntax("listtickets", ListTickets, 0,
1802 "show all cache manager tickets");
1803 cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1804 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1805 "show session key and ticket");
1807 ts = cmd_CreateSyntax("quit", Quit, 0, "exit program");
1810 conn = 0; /* no connection yet */
1811 zero_argc = cmd_argc;
1812 zero_argv = cmd_argv;
1814 strcpy(whoami, "kas");
1816 if (code = cmd_Dispatch(cmd_argc, cmd_argv)) {
1825 s = fgets(line, sizeof(line), stdin);
1827 return 0; /* EOF on input */
1828 for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--)
1831 continue; /* blank line */
1834 cmd_ParseLine(line, argv, &argc, sizeof(argv) / sizeof(argv[0]));
1836 com_err(whoami, code, "parsing line: '%s'", line);
1839 code = cmd_Dispatch(argc, argv);