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>
48 #define CMD_PARSER_AMBIG_FIX 1 /* allow ambiguous aliases */
50 extern char *ktime_GetDateUsage();
52 #define KA_SIXHOURS (6*3600)
54 static struct ubik_client *conn;
55 static char cell[MAXKTCREALMLEN] = "";
56 static char whoami[32];
57 static char passwd[BUFSIZ];
58 static char myName[510]; /* almost like whoami save with path and without : */
62 static char **zero_argv;
63 afs_uint32 ka_islocked();
72 code = ka_ExpandCell(0, cell, 0 /*local */ );
74 com_err(whoami, code, "Can't expand cell name");
79 /* These are the command operation procedures. */
82 ListUsers(struct cmd_syndesc *as, char *arock)
88 int code, all = 0, showa = 0;
89 int showkey = (as->parms[2].items != NULL);
91 if (as->parms[0].items)
93 if (as->parms[1].items) {
97 for (index = 0; 1; index = next_index) {
99 ubik_Call(KAM_ListEntry, conn, 0, index, &next_index, &count,
102 com_err(whoami, code, "calling KAM_ListEntry");
108 printf("next_index (%d) is negative: ", next_index);
109 if (strlen(name.name) == 0)
110 printf("name is zero length: ");
112 DumpUser(name.name, NULL, showa, showkey, name.instance);
114 ka_PrintUserID("", name.name, name.instance, "\n");
121 ExamineUser(struct cmd_syndesc *as, char *arock)
123 int showkey = (as->parms[1].items != NULL);
124 return DumpUser(as->parms[0].items->data, arock, 0, showkey, NULL);
129 DumpUser(char *user, char *arock, int showadmin, int showkey, char *inst)
131 char name[MAXKTCNAMELEN];
132 char instance[MAXKTCNAMELEN];
135 char bob[KA_TIMESTR_LEN];
137 struct kaentryinfo tentry;
139 code = ka_ParseLoginName(user, name, instance, 0);
141 com_err(whoami, code, "parsing user's name '%s'", user);
148 ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION, &tentry);
150 com_err(whoami, code, "getting information for %s.%s", name, inst);
153 if (tentry.minor_version != KAMINORVERSION)
154 printf("Minor version number mismatch: got %d, expected %d\n",
155 tentry.minor_version, KAMINORVERSION);
156 if (showadmin && !(tentry.flags & KAFADMIN))
158 ka_PrintUserID("\nUser data for ", name, inst, "");
161 #define NEWPREFIX "+"
162 if (tentry.flags & KAFADMIN) {
163 printf("%sADMIN", prefix);
166 if (tentry.flags & KAFNOTGS) {
167 printf("%sNOTGS", prefix);
170 if (tentry.flags & KAFNOCPW) {
171 printf("%sNOCPW", prefix);
174 if (tentry.flags & KAFNOSEAL) {
175 printf("%sNOSEAL", prefix);
178 if (tentry.flags & KAFNEWASSOC) {
179 printf("%sNEWASSOC", prefix);
182 if (tentry.flags & KAFASSOCROOT) {
183 printf("%sASSOCROOT", prefix);
186 if (tentry.flags & KAFASSOC) {
187 printf("%sASSOC", prefix);
190 if (tentry.user_expiration <= now) {
191 printf("%sexpired", prefix);
194 if (strcmp(prefix, NEWPREFIX) == 0)
199 if ((!ka_KeyIsZero((char *)&tentry.key, sizeof(tentry.key))) && (showkey)) {
200 printf(" key (%d):", tentry.key_version);
201 ka_PrintBytes((char *)&tentry.key, sizeof(tentry.key));
203 if (tentry.keyCheckSum == 0)
204 printf(" key version is %d", tentry.key_version);
206 printf(" key (%d) cksum is %u", tentry.key_version,
209 ka_timestr(tentry.change_password_time, bob, KA_TIMESTR_LEN);
210 printf(", last cpw: %s\n", bob);
211 if (!tentry.misc_auth_bytes) {
212 printf(" password will never expire.\n");
214 (" An unlimited number of unsuccessful authentications is permitted.\n");
216 unsigned char misc_stuff[4];
219 temp = tentry.misc_auth_bytes;
221 temp = ntohl(tentry.misc_auth_bytes);
223 unpack_long(temp, misc_stuff);
225 if (!misc_stuff[0]) {
226 printf(" password will never expire.\n");
228 ka_timestr((tentry.change_password_time +
229 misc_stuff[0] * 24 * 60 * 60), bob, KA_TIMESTR_LEN);
230 printf(" password will expire: %s\n", bob);
235 (" An unlimited number of unsuccessful authentications is permitted.\n");
238 (" %d consecutive unsuccessful authentications are permitted.\n",
242 printf(" The lock time for this user is not limited.\n");
244 printf(" The lock time for this user is %4.1f minutes.\n",
245 (float)((unsigned int)misc_stuff[3] << 9) / 60.0);
247 if (!(misc_stuff[1] & KA_ISLOCKED)
248 || !ka_islocked(name, instance, &temp))
249 printf(" User is not locked.\n");
250 else if (temp == (afs_uint32) (-1L))
251 printf(" User is locked forever.\n");
253 ka_timestr(temp, bob, KA_TIMESTR_LEN);
254 printf(" User is locked until %s\n", bob);
260 char exp[KA_TIMESTR_LEN];
261 ka_timestr(tentry.user_expiration, exp, KA_TIMESTR_LEN);
262 if (tentry.user_expiration < now)
263 printf(" DISABLED entry at %s.", exp);
264 else if (tentry.user_expiration == NEVERDATE)
265 printf(" entry never expires.");
267 printf(" entry expires on %s.", exp);
269 printf(" Max ticket lifetime %.2f hours.\n",
270 tentry.max_ticket_lifetime / 3600.0);
271 ka_timestr(tentry.modification_time, bob, KA_TIMESTR_LEN);
272 printf(" last mod on %s by ", bob);
273 ka_PrintUserID("", tentry.modification_user.name,
274 tentry.modification_user.instance, "\n");
275 if ((tentry.reserved3 & 0xffff0000) == 0x12340000) {
276 int short reused = (short)tentry.reserved3;
278 printf(" permit password reuse\n");
280 printf(" don't permit password reuse\n");
292 handle_errors(int code, /* error code to handle */
293 struct OKerrors OKlist[], /* list of errors & messages that should be ignored */
295 { /* set this if we should retry, clear otherwise */
298 for (i = 0; OKlist[i].code; i++) {
299 if (OKlist[i].code == code) {
300 printf("%s\n", OKlist[i].msg);
301 *persist = 0; /* we're done */
306 printf(" : [%s] %s", error_table_name(code), error_message(code));
309 printf(", wait one second\n");
313 case RX_CALL_TIMEOUT:
314 printf(" (retrying)\n");
319 *persist = 0; /* don't retry these errors */
324 CreateUser(struct cmd_syndesc *as, char *arock)
327 char name[MAXKTCNAMELEN];
328 char instance[MAXKTCNAMELEN];
329 struct ktc_encryptionKey key;
332 struct OKerrors OKlist[2];
335 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
337 com_err(whoami, code, "parsing user's name '%s'",
338 as->parms[0].items->data);
341 ka_StringToKey(as->parms[1].items->data, cell, &key);
344 code = ubik_Call(KAM_CreateUser, conn, 0, name, instance, key);
347 ka_PrintUserID("Creating user ", name, instance, " ");
348 code = handle_errors(code, OKlist, &persist);
354 DeleteUser(struct cmd_syndesc *as, char *arock)
357 char name[MAXKTCNAMELEN];
358 char instance[MAXKTCNAMELEN];
361 struct OKerrors OKlist[2];
363 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
365 com_err(whoami, code, "parsing user's name '%s'",
366 as->parms[0].items->data);
371 code = ubik_Call(KAM_DeleteUser, conn, 0, name, instance);
374 ka_PrintUserID("Deleting user ", name, instance, " ");
375 code = handle_errors(code, OKlist, &persist);
381 read_time_interval(char *str, afs_int32 * seconds)
387 str = strncpy(buf, str, sizeof(buf));
388 s = strchr(str, ':');
392 *s++ = '\0'; /* separate hours and minutes */
393 sec = atoi(str) * 3600 + atoi(s) * 60;
400 parse_flags(char *name, char *inst, char *str, afs_int32 * flags)
402 struct kaentryinfo tentry;
408 int addop; /* 1=add bit; 0=remove bit */
412 str = lcstring(bitspec, str, sizeof(bitspec));
414 if (strncmp(str, "0x", 2) == 0) /* 0x => hex */
415 sscanf(str, "0x%lx", &f);
416 else if (*str == '0') /* assume octal */
417 sscanf(str, "%lo", &f);
418 else /* just assume hex */
419 sscanf(str, "%lx", &f);
426 if (strchr("+-", *str))
427 addop = (*str++ == '+');
428 else if (*str == '_') {
434 ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION,
437 com_err(whoami, code,
438 "could get current flag value for %s.%s", name, inst);
455 if (strcmp(bit, "admin") == 0)
457 else if (strcmp(bit, "noadmin") == 0)
458 flag = KAFADMIN, addop = !addop;
459 else if (strcmp(bit, "notgs") == 0)
461 else if (strcmp(bit, "tgs") == 0)
462 flag = KAFNOTGS, addop = !addop;
463 else if (strcmp(bit, "noseal") == 0)
465 else if (strcmp(bit, "seal") == 0)
466 flag = KAFNOSEAL, addop = !addop;
467 else if (strcmp(bit, "nocpw") == 0)
469 else if (strcmp(bit, "cpw") == 0)
470 flag = KAFNOCPW, addop = !addop;
471 else if (strcmp(bit, "newassoc") == 0)
473 else if (strcmp(bit, "nonewassoc") == 0)
474 flag = KAFNEWASSOC, addop = !addop;
477 ("Illegal bit name: %s; choices are: [no]admin, [no]tgs, [no]seal, [no]cpw\n",
490 addop = 1; /* get next op */
491 else if ((*str == '-') || (*str == '_'))
494 printf("Illegal combination operator: %c\n", *str);
500 *flags = (f & KAF_SETTABLE_FLAGS) | KAFNORMAL;
504 #define seriouserror(code) ((code <0) || ((code != UNOSERVERS) && (code != UNOQUORUM) && code != UNOTSYNC))
506 /* return MAXLONG if locked forever */
508 ka_islocked(char *name, char *instance, afs_uint32 * when)
518 ubik_CallIter(KAM_LockStatus, conn, UPUBIKONLY, &count, name,
519 instance, &tempwhen, /*spares */ 0, 0, 0,
522 if (seriouserror(code))
523 com_err(whoami, code, "");
524 } else if (tempwhen) { /* user is locked */
525 if (!*when || tempwhen < *when) {
529 } else /* ! tempwhen ==> user is not locked */
532 } while (code != UNOSERVERS);
538 Unlock(struct cmd_syndesc *as, char *arock)
540 afs_int32 code, rcode = 0;
543 char name[MAXKTCNAMELEN];
544 char instance[MAXKTCNAMELEN];
546 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
548 com_err(whoami, code, "parsing user's name '%s'",
549 as->parms[0].items->data);
555 code = ubik_CallIter(KAM_Unlock, conn, 0, &count, name, instance,
556 /*spares */ 0, 0, 0, 0);
557 if (code && (code != UNOSERVERS)) {
559 if (conn && conn->conns[count - 1]
560 && conn->conns[count - 1]->peer) {
561 server = conn->conns[count - 1]->peer->host;
563 com_err(whoami, code,
564 "so %s.%s may still be locked (on server %d.%d.%d.%d)",
565 name, instance, ((server >> 24) & 0xFF),
566 ((server >> 16) & 0xFF), ((server >> 8) & 0xFF),
573 } while (code != UNOSERVERS);
579 SetFields(struct cmd_syndesc *as, char *arock)
582 char name[MAXKTCNAMELEN];
583 char instance[MAXKTCNAMELEN];
586 afs_int32 lifetime = 0;
587 afs_int32 maxAssociates = -1;
588 afs_int32 pwexpiry = 0;
589 afs_int32 was_spare = 0;
590 char misc_auth_bytes[4];
593 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, 0);
595 com_err(whoami, code, "parsing user's name '%s'",
596 as->parms[0].items->data);
600 if (as->parms[1].items) {
601 code = parse_flags(name, instance, as->parms[1].items->data, &flags);
604 ("Illegal flag specification: %s, should be of the form <'='|'+'|'-'|'_'>bitname{<'+'|'-'>bitname}*\n",
605 as->parms[1].items->data);
609 if (as->parms[2].items) {
611 char *s = strncpy(buf, as->parms[2].items->data, sizeof(buf));
612 code = ktime_DateToInt32(s, &expiration);
614 printf("Illegal time format %s: %s\n", as->parms[2].items->data,
615 ktime_GetDateUsage());
618 if (expiration == 0) {
619 fprintf(stderr, "Expiration time must be after (about) 1970.\n");
622 if (expiration < time(0)) {
624 "Warning: expiration being set into the past, account will be disabled.\n");
630 if (as->parms[3].items) {
631 code = read_time_interval(as->parms[3].items->data, &lifetime);
636 /* no point in doing this any sooner than necessary */
637 for (i = 0; i < 4; misc_auth_bytes[i++] = 0);
639 if (as->parms[4].items) {
640 if (util_isint(as->parms[4].items->data))
641 pwexpiry = atoi(as->parms[4].items->data);
644 "Password lifetime specified must be a non-negative decimal integer.\n");
647 if (pwexpiry < 0 || pwexpiry > 254) {
649 "Password lifetime range must be [0..254] days.\n");
650 fprintf(stderr, "Zero represents an unlimited lifetime.\n");
653 misc_auth_bytes[0] = pwexpiry + 1;
657 if (as->parms[5].items) {
659 reuse = (as->parms[5].items->data);
661 if (!strcmp(reuse, "yes")) {
662 misc_auth_bytes[1] = KA_REUSEPW;
663 } else if (strcmp(reuse, "no")) {
665 "must specify \"yes\" or \"no\": \"yes\" assumed\n");
666 misc_auth_bytes[1] = KA_REUSEPW;
668 misc_auth_bytes[1] = KA_NOREUSEPW;
672 if (as->parms[6].items) {
676 if (util_isint(as->parms[6].items->data)
677 && ((nfailures = atoi(as->parms[6].items->data)) < 255)) {
678 misc_auth_bytes[2] = nfailures + 1;
680 fprintf(stderr, "Failure limit must be in [0..254].\n");
681 fprintf(stderr, "Zero represents unlimited login attempts.\n");
686 if (as->parms[7].items) {
687 int locktime, hrs, mins;
691 s = as->parms[7].items->data;
693 sscanf(s, "%d:%d", &hrs, &mins);
695 sscanf(s, "%d", &mins);
697 locktime = hrs * 60 + mins;
698 if (hrs < 0 || hrs > 36 || mins < 0) {
700 "Lockout times must be either minutes or hh:mm.\n");
701 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
703 } else if (locktime > 36 * 60) {
705 "Lockout times must be either minutes or hh:mm.\n");
706 fprintf(stderr, "Lockout times must be less than 36 hours.\n");
708 "Continuing with lock time of exactly 36 hours...\n");
711 locktime = (locktime * 60 + 511) >> 9; /* ceil(l*60/512) */
712 misc_auth_bytes[3] = locktime + 1; /* will be 1 if user said 0 */
715 if (as->parms[8].items) {
716 maxAssociates = atoi(as->parms[6].items->data);
717 if (maxAssociates < 0) {
718 printf("Illegal maximum number of associates\n");
723 was_spare = pack_long(misc_auth_bytes);
725 if (was_spare || flags || expiration || lifetime || (maxAssociates >= 0))
727 ubik_Call(KAM_SetFields, conn, 0, name, instance, flags,
728 expiration, lifetime, maxAssociates, was_spare,
731 printf("Must specify one of the optional parameters\n");
735 com_err(whoami, code, "calling KAM_SetFields for %s.%s", name,
741 StringToKey(struct cmd_syndesc *as, char *arock)
744 char realm[MAXKTCREALMLEN];
745 struct ktc_encryptionKey key;
747 if (as->parms[1].items) {
748 code = ka_ExpandCell(as->parms[1].items->data, realm, 0 /*local */ );
750 com_err(whoami, code,
751 "expanding %s as cell name, attempting to continue",
752 as->parms[1].items->data);
754 ucstring(realm, realm, sizeof(realm));
756 if (code = DefaultCell())
758 ucstring(realm, cell, sizeof(realm));
760 ka_StringToKey(as->parms[0].items->data, realm, &key);
762 printf("Converting %s in realm '%s' yields key='",
763 as->parms[0].items->data, realm);
764 ka_PrintBytes((char *)&key, sizeof(key));
767 des_string_to_key(as->parms[0].items->data, &key);
769 printf("Converting %s with the DES string to key yields key='",
770 as->parms[0].items->data);
771 ka_PrintBytes((char *)&key, sizeof(key));
778 SetPassword(struct cmd_syndesc *as, char *arock)
781 char name[MAXKTCNAMELEN];
782 char instance[MAXKTCNAMELEN];
783 char realm[MAXKTCREALMLEN];
784 struct ktc_encryptionKey key;
787 code = ka_ParseLoginName(as->parms[0].items->data, name, instance, realm);
789 com_err(whoami, code, "parsing user's name '%s'",
790 as->parms[0].items->data);
794 if (strlen(realm) == 0)
795 ucstring(realm, cell, sizeof(realm));
797 if (as->parms[1].items && as->parms[2].items) {
798 printf("Can't specify both a password and a key\n");
800 } else if (as->parms[1].items) {
801 (void)init_child(myName);
802 (void)give_to_child(passwd); /* old password */
803 code = password_bad(as->parms[1].items->data);
804 (void)terminate_child();
807 ka_StringToKey(as->parms[1].items->data, realm, &key);
808 } else if (as->parms[2].items) {
809 if (ka_ReadBytes(as->parms[2].items->data, (char *)&key, sizeof(key))
811 printf("Key must be 8 bytes: '%s' was too long\n",
812 as->parms[2].items->data);
816 printf("Must specify new password or key\n");
821 if (as->parms[3].items)
822 sscanf(as->parms[3].items->data, "%d", &kvno);
824 #ifdef AFS_S390_LINUX20_ENV
825 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, 0, key);
827 code = ubik_Call(KAM_SetPassword, conn, 0, name, instance, kvno, key);
830 com_err(whoami, code, "so can't set password for %s.%s", name,
835 #define PrintPrincipal(p,n,l) \
836 PrintName((p)->name, (p)->instance, (p)->cell, l, n)
839 PrintName(char *name, char *inst, char *acell, int buflen, char *buf)
842 int left; /* if ConvertBytes stops early */
851 left = ka_ConvertBytes(buf, buflen, name, strlen(name));
855 com_err(whoami, code, "PrintName: principal name was '%s'.'%s'@'%s'",
862 if (nlen + len + 1 >= buflen)
865 left = ka_ConvertBytes(buf + nlen, buflen - nlen, inst, len);
873 char *lcell = ka_LocalCell();
876 if (strcmp(acell, lcell) != 0) {
877 /* only append cell if not the local cell */
878 if (nlen + len + 1 >= buflen)
881 left = ka_ConvertBytes(buf + nlen, buflen - nlen, acell, len);
890 #define PrintedPrincipal(p) PrintedName ((p)->name, (p)->instance, (p)->cell)
892 /* PrintedName - returned a pointer to a static string in which the formated
893 * name has been stored. */
896 PrintedName(char *name, char *inst, char *cell)
898 static char printedName[128];
900 code = PrintName(name, inst, cell, sizeof(printedName), printedName);
904 strncpy(printedName, name, sizeof(printedName));
905 printedName[sizeof(printedName) - 8] = 0;
906 strcat(printedName, "<error>");
912 ListTicket(struct ktc_principal *server, int verbose)
915 struct ktc_token token; /* the token we're printing */
916 struct ktc_principal client;
917 char UserName[sizeof(struct ktc_principal)];
918 char ServerName[sizeof(struct ktc_principal)];
919 afs_int32 now = time(0);
920 char bob[KA_TIMESTR_LEN];
922 /* get the ticket info itself */
923 code = ktc_GetToken(server, &token, sizeof(token), &client);
925 com_err(whoami, code, "failed to get token info for server %s",
926 PrintedPrincipal(server));
929 code = PrintPrincipal(&client, UserName, sizeof(UserName));
932 /* spaces are printed as "\040" */
933 if (UserName[0] == 0)
935 else if (strncmp(UserName, "AFS\\040ID\\040", 13) == 0) {
936 printf("User's (AFS ID %s) tokens", UserName + 13);
937 } else if (strncmp(UserName, "Unix\\040UID\\040", 15) == 0) {
940 printf("User %s's tokens", UserName);
942 code = PrintPrincipal(server, ServerName, sizeof(ServerName));
945 printf(" for %s ", ServerName);
947 if (token.startTime > now) {
948 ka_timestr(token.startTime, bob, KA_TIMESTR_LEN);
949 printf("[>> POSTDATED 'till %s <<]", bob);
952 if (token.endTime <= now)
953 printf("[>> Expired <<]\n");
955 ka_timestr(token.endTime, bob, KA_TIMESTR_LEN);
956 printf("[Expires %s]\n", bob);
959 printf("SessionKey: ");
960 ka_PrintBytes((char *)&token.sessionKey, sizeof(token.sessionKey));
961 printf("\nTicket (kvno = %d, len = %d): ", token.kvno,
963 ka_PrintBytes((char *)token.ticket, token.ticketLen);
970 GetTicket(struct cmd_syndesc *as, char *arock)
973 struct ktc_principal server;
974 struct ktc_token token;
975 afs_int32 life = KA_SIXHOURS;
977 if (as->parms[1].items) {
978 code = read_time_interval(as->parms[1].items->data, &life);
983 ka_ParseLoginName(as->parms[0].items->data, server.name,
984 server.instance, server.cell);
986 com_err(whoami, code, "parsing user's name '%s'",
987 as->parms[0].items->data);
990 if (server.cell[0] == 0) {
991 if (code = DefaultCell())
993 strcpy(server.cell, cell);
995 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
997 com_err(whoami, code, "Can't expand cell name");
1002 token.ticketLen = 0; /* in case there are no tokens */
1004 ka_GetServerToken(server.name, server.instance, server.cell, life,
1005 &token, /*new */ 1, /*dosetpag */ 0);
1007 com_err(whoami, code, "getting ticket for %s",
1008 PrintedPrincipal(&server));
1010 code = ListTicket(&server, /*verbose */ 1);
1016 GetPassword(struct cmd_syndesc *as, char *arock)
1019 char name[MAXKTCNAMELEN];
1020 struct ktc_encryptionKey key;
1021 static struct ubik_client *lpbkConn = 0;
1023 /* no instance allowed */
1024 code = ka_ParseLoginName(as->parms[0].items->data, name, 0, 0);
1027 com_err(whoami, code,
1028 "getting %s's password via loopback connection to GetPassword",
1030 /* if we got a timeout, print a clarification, too */
1033 "%s: please note that this command must be run locally on a database server machine.\n",
1038 if (lpbkConn == 0) {
1039 struct rx_connection *conns[2];
1040 struct rx_securityClass *sc;
1041 int si; /* security class index */
1046 sc = rxnull_NewClientSecurityObject();
1047 si = RX_SCINDEX_NULL;
1049 rx_NewConnection(htonl(INADDR_LOOPBACK), htons(AFSCONF_KAUTHPORT),
1050 KA_MAINTENANCE_SERVICE, sc, si);
1052 code = ubik_ClientInit(conns, &lpbkConn);
1056 code = ubik_Call(KAM_GetPassword, lpbkConn, 0, name, &key);
1057 /* Lets close down the ubik_Client connection now */
1058 ubik_ClientDestroy(lpbkConn);
1062 ka_PrintBytes((char *)&key, sizeof(key));
1068 GetRandomKey(struct cmd_syndesc *as, char *arock)
1071 struct ktc_encryptionKey key;
1073 code = ubik_Call(KAM_GetRandomKey, conn, 0, &key);
1075 com_err(whoami, code, "so can't get random key");
1079 ka_PrintBytes((char *)&key, sizeof(key));
1081 for (i = 0; i < sizeof(key); i++) {
1082 printf("%0.2x", ((char *)&key)[i] & 0xff);
1094 Statistics(struct cmd_syndesc *as, char *arock)
1100 char bob[KA_TIMESTR_LEN];
1103 ubik_Call(KAM_GetStats, conn, 0, KAMAJORVERSION, &admins, &statics,
1106 printf("call to GetStats failed: %s\n", ka_ErrorString(code));
1109 if (statics.minor_version != KAMINORVERSION)
1110 printf("Minor version number mismatch: got %d, expected %d\n",
1111 statics.minor_version, KAMINORVERSION);
1112 printf("%d allocs, %d frees, %d password changes\n", statics.allocs,
1113 statics.frees, statics.cpws);
1114 printf("Hash table utilization = %f%%\n",
1115 (double)dynamics.hashTableUtilization / 100.0);
1116 ka_timestr(dynamics.start_time, bob, KA_TIMESTR_LEN);
1117 printf("From host %lx started at %s:\n", dynamics.host, bob);
1119 #define print_stat(name) if (dynamics.name.requests) printf (" of %d requests for %s, %d were aborted.\n", dynamics.name.requests, # name, dynamics.name.aborts)
1120 print_stat(Authenticate);
1121 print_stat(ChangePassword);
1122 print_stat(GetTicket);
1123 print_stat(CreateUser);
1124 print_stat(SetPassword);
1125 print_stat(SetFields);
1126 print_stat(DeleteUser);
1127 print_stat(GetEntry);
1128 print_stat(ListEntry);
1129 print_stat(GetStats);
1130 print_stat(GetPassword);
1131 print_stat(GetRandomKey);
1133 print_stat(UAuthenticate);
1134 print_stat(UGetTicket);
1136 #if (KAMAJORVERSION>5)
1137 print cpu stats printf("%d string checks\n", dynamics.string_checks);
1139 printf("Used %.3f seconds of CPU time.\n",
1140 dynamics.string_checks / 1000.0);
1142 printf("%d admin accounts\n", admins);
1147 DebugInfo(struct cmd_syndesc *as, char *arock)
1150 struct ka_debugInfo info;
1154 char bob[KA_TIMESTR_LEN];
1157 if (as->parms[0].items) {
1158 struct ubik_client *iConn;
1160 ka_SingleServerConn(cell, as->parms[0].items->data,
1161 KA_MAINTENANCE_SERVICE, 0, &iConn);
1163 struct afsconf_cell cellinfo;
1165 com_err(whoami, code, "couldn't find host %s in cell %s",
1166 as->parms[0].items->data, cell);
1167 code = ka_GetServers(cell, &cellinfo);
1169 com_err(whoami, code, "getting servers in cell %s", cell);
1171 printf("Servers in cell %s, are:\n", cell);
1172 for (i = 0; i < cellinfo.numServers; i++)
1173 printf(" %s\n", cellinfo.hostName[i]);
1177 code = ubik_Call(KAM_Debug, iConn, 0, KAMAJORVERSION, 0, &info);
1178 ubik_ClientDestroy(iConn);
1180 code = ubik_Call(KAM_Debug, conn, 0, KAMAJORVERSION, 0, &info);
1183 com_err(whoami, code, "call to Debug failed");
1188 if (info.minorVersion != KAMINORVERSION)
1189 printf("Minor version number mismatch: got %d, expected %d\n",
1190 info.minorVersion, KAMINORVERSION);
1193 #if (KAMAJORVERSION>5)
1200 timeOffset = -timeOffset;
1201 if (timeOffset > 60) {
1203 ("WARNING: Large server client clock skew: %d seconds. Call itself took %d seconds.\n",
1204 timeOffset, now - start);
1206 ka_timestr(info.startTime, bob, KA_TIMESTR_LEN);
1207 printf("From host %lx started %sat %s:\n", info.host,
1208 (info.noAuth ? "w/o authorization " : ""), bob);
1209 ka_timestr(info.lastTrans, bob, KA_TIMESTR_LEN);
1210 printf("Last trans was %s at %s\n", info.lastOperation, bob);
1211 ka_timestr(info.dbHeaderRead, bob, KA_TIMESTR_LEN);
1212 printf("Header last read %s.\n", bob);
1213 printf("db version=%d, keyVersion=%d, key cache version=%d\n",
1214 info.dbVersion, info.dbSpecialKeysVersion, info.kcVersion);
1215 printf("db ptrs: free %d, eof %d, kvno %d.\n", info.dbFreePtr,
1216 info.dbEofPtr, info.dbKvnoPtr);
1217 ka_timestr(info.nextAutoCPW, bob, KA_TIMESTR_LEN);
1218 printf("Next autoCPW at %s or in %d updates.\n", bob,
1219 info.updatesRemaining);
1220 if (info.cheader_lock || info.keycache_lock)
1221 printf("locks: cheader %08lx, keycache %08lx\n", info.cheader_lock,
1222 info.keycache_lock);
1223 printf("Last authentication for %s, last admin user was %s\n",
1224 info.lastAuth, info.lastAdmin);
1225 printf("Last TGS op was a %s ticket was for %s\n", info.lastTGSServer,
1227 printf("Last UDP TGS was a %s ticket for %s. UDP Authenticate for %s\n",
1228 info.lastUTGSServer, info.lastUTGS, info.lastUAuth);
1229 printf("key cache size %d, used %d.\n", info.kcSize, info.kcUsed);
1230 if (info.kcUsed > KADEBUGKCINFOSIZE) {
1231 printf("insufficient room to return all key cache entries!\n");
1232 info.kcUsed = KADEBUGKCINFOSIZE;
1234 for (i = 0; i < info.kcUsed; i++)
1235 ka_timestr(info.kcInfo[i].used, bob, KA_TIMESTR_LEN);
1236 printf("%32s %c %2x(%2x) used %s\n", info.kcInfo[i].principal,
1237 (info.kcInfo[i].primary ? '*' : ' '), info.kcInfo[i].kvno,
1238 info.kcInfo[i].keycksum, bob);
1243 Interactive(struct cmd_syndesc *as, char *arock)
1250 Quit(struct cmd_syndesc *as, char *arock)
1257 MyAfterProc(struct cmd_syndesc *as)
1259 if (!strcmp(as->name, "help"))
1262 /* Determine if we need to destory the ubik connection.
1263 * Closing it avoids resends of packets.
1266 ubik_ClientDestroy(conn);
1273 int init = 0, noauth;
1274 char name[MAXKTCNAMELEN];
1275 char instance[MAXKTCNAMELEN];
1276 char newCell[MAXKTCREALMLEN];
1277 afs_int32 serverList[MAXSERVERS];
1280 NoAuth(struct cmd_syndesc *as, char *arock)
1287 MyBeforeProc(struct cmd_syndesc *as, char *arock)
1289 extern struct passwd *getpwuid();
1290 struct ktc_encryptionKey key;
1291 struct ktc_principal auth_server, auth_token, client;
1292 char realm[MAXKTCREALMLEN];
1294 struct ktc_token token, *pToken;
1295 int i, acode, code = 0;
1298 char *ws = strrchr(as->a0name, '/');
1300 ws++; /* skip everything before the "/" */
1303 if (strlen(ws) > 0) {
1304 strncpy(whoami, ws, sizeof(whoami));
1305 if (strlen(whoami) + 1 >= sizeof(whoami))
1306 strcpy(whoami, "kas:");
1308 strcat(whoami, ":");
1311 /* append sub-command name */
1312 strncat(whoami, as->name, sizeof(whoami) - strlen(whoami) - 1);
1315 if (as->parms[12].name == 0)
1318 assert(as->parms[13].name && as->parms[14].name && as->parms[15].name
1319 && as->parms[16].name);
1321 /* MyAfterProc() destroys the conn, but just to be sure */
1323 code = ubik_ClientDestroy(conn);
1327 if (!init || as->parms[12].items || as->parms[13].items
1328 || as->parms[14].items || as->parms[15].items
1329 || as->parms[16].items) {
1330 strcpy(instance, "");
1331 strcpy(newCell, "");
1333 if (as->parms[12].items) { /* -admin_username */
1335 ka_ParseLoginName(as->parms[12].items->data, name, instance,
1338 com_err(whoami, code, "parsing user's name '%s'",
1339 as->parms[12].items->data);
1344 DWORD len = MAXKTCNAMELEN;
1345 if (!GetUserName((LPTSTR) name, &len)) {
1346 printf("Can't get user name \n");
1350 /* No explicit name provided: use Unix uid. */
1351 struct passwd *pw = getpwuid(getuid());
1353 printf("Can't figure out your name from your user id.\n");
1356 strncpy(name, pw->pw_name, sizeof(name));
1360 if (as->parms[14].items) { /* -cell */
1361 if (strlen(newCell) > 0) {
1362 printf("Duplicate cell specification not allowed\n");
1364 strncpy(newCell, as->parms[14].items->data, sizeof(newCell));
1367 code = ka_ExpandCell(newCell, newCell, 0 /*local */ );
1369 com_err(whoami, code, "Can't expand cell name");
1372 strcpy(cell, newCell);
1374 if (as->parms[15].items) { /* -servers */
1375 struct cmd_item *ip;
1376 char *ap[MAXSERVERS + 2];
1380 for (ip = as->parms[15].items, i = 2; ip; ip = ip->next, i++)
1382 code = ubik_ParseClientList(i, ap, serverList);
1384 com_err(whoami, code, "could not parse server list");
1387 ka_ExplicitCell(cell, serverList);
1390 noauth = (as->parms[16].items ? 1 : 0); /* -noauth */
1395 token.ticketLen = 0; /* in case there are no tokens */
1396 if (!noauth) { /* Will prompt for a password */
1397 /* first see if there's already an admin ticket */
1399 ka_GetAdminToken(0, 0, cell, 0, KA_SIXHOURS, &token,
1401 if (code) { /* if not then get key and try again */
1402 if (as->parms[13].items) { /* if password specified */
1403 strncpy(passwd, as->parms[13].items->data, sizeof(passwd));
1404 memset(as->parms[13].items->data, 0,
1405 strlen(as->parms[13].items->data));
1407 char msg[MAXKTCNAMELEN + 50];
1408 if (as->parms[12].items)
1409 sprintf(msg, "Administrator's (%s) Password: ", name);
1411 sprintf(msg, "Password for %s: ", name);
1412 code = read_pw_string(passwd, sizeof(passwd), msg, 0);
1415 else if (strlen(passwd) == 0)
1416 code = KANULLPASSWORD;
1418 com_err(whoami, code, "reading password");
1422 ka_StringToKey(passwd, cell, &key);
1424 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1425 &token, 0 /* !new */ );
1426 if (code == KABADREQUEST) {
1427 des_string_to_key(passwd, &key);
1429 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1430 &token, 0 /* !new */ );
1432 if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
1433 /* try with only the first 8 characters incase they set
1434 * their password with an old style passwd program. */
1436 ka_StringToKey(passwd, cell, &key);
1438 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1439 &token, 0 /* !new */ );
1442 "Warning: you have typed a password longer than 8 characters, but only the\n");
1444 "first 8 characters were actually significant. If you change your password\n");
1446 "again this warning message will go away.\n");
1453 reason = "password was incorrect";
1456 reason = "Authentication Server was unavailable";
1459 reason = (char *)error_message(code);
1462 "%s: Auth. as %s to AuthServer failed: %s\nProceeding w/o authentication\n",
1463 whoami, PrintedName(name, instance, cell), reason);
1465 /* get an Authentication token while were at it. */
1466 if (ka_CellToRealm(cell, realm, 0) != 0)
1468 strcpy(auth_server.name, KA_TGS_NAME);
1469 strcpy(auth_server.instance, realm);
1470 strcpy(auth_server.cell, cell);
1472 (&auth_server, &auth_token, sizeof(struct ktc_token),
1475 ka_GetAuthToken(name, instance, cell, &key,
1476 MAXKTCTICKETLIFETIME, (afs_int32 *) 0
1477 /*Don't need pwd expiration info here */
1479 if (acode && (acode != code)) /* codes are usually the same */
1480 com_err(whoami, code,
1481 "getting Authentication token for %s",
1482 PrintedName(name, instance, cell));
1484 memset(&key, 0, sizeof(key));
1488 pToken = ((token.ticketLen == 0) ? 0 : &token);
1489 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, pToken, &conn);
1490 if (code && pToken) {
1491 com_err(whoami, code,
1492 "connecting to AuthServer: now trying w/o authentication");
1493 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, 0, &conn);
1495 com_err(whoami, code,
1496 "making unauthenticated connection to AuthServer");
1499 com_err(whoami, code,
1500 "Couldn't establish connection to Authentication Server");
1504 /* now default unspecified password by prompting from terminal */
1505 if (as->nParms >= 12)
1506 for (i = 0; i < 12; i++)
1507 if (as->parms[i].name && (as->parms[i].items == 0)) {
1508 char *p = as->parms[i].name; /* parameter name */
1509 int l = strlen(p); /* length of name */
1510 /* does parameter end in "password" */
1511 if (strcmp(p + (l - 8), "password") == 0) {
1513 char password[BUFSIZ];
1514 struct cmd_item *ip;
1518 code = read_pw_string(password, sizeof(password), msg, 1);
1521 else if (strlen(password) == 0)
1522 code = KANULLPASSWORD;
1524 com_err(whoami, code, "prompting for %s", p + 1);
1527 ip = (struct cmd_item *)malloc(sizeof(struct cmd_item));
1528 ip->data = (char *)malloc(strlen(password) + 1);
1530 strcpy(ip->data, password);
1531 as->parms[i].items = ip;
1534 if (!conn) { /* if all else fails... */
1535 code = NoAuth(0, 0); /* get unauthenticated conn */
1542 /* These are some helpful command that deal with the cache managers tokens. */
1545 ForgetTicket(struct cmd_syndesc *as, char *arock)
1550 struct ktc_principal server;
1552 if (as->parms[0].items) {
1553 char *name = as->parms[0].items->data;
1555 ka_ParseLoginName(name, server.name, server.instance,
1558 com_err(whoami, code, "couldn't interpret name '%s'", name);
1561 if (server.cell[0] == 0) {
1562 if (code = DefaultCell())
1564 strcpy(server.cell, cell);
1566 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1568 com_err(whoami, code, "Can't expand cell name");
1572 code = ktc_ForgetToken(&server);
1574 com_err(whoami, code, "couldn't remove tokens for %s",
1575 PrintedPrincipal(&server));
1579 if (!as->parms[1].items) {
1580 fprintf(stderr, "Must specify server name or -all\n");
1583 code = ktc_ForgetAllTokens();
1585 com_err(whoami, code, "couldn't delete all tokens");
1590 code = ktc_ForgetAllTokens();
1592 com_err(whoami, code, "couldn't delete all tokens");
1599 ListTickets(struct cmd_syndesc *as, char *arock)
1602 int index, newIndex;
1603 struct ktc_principal server;
1606 if (as->parms[1].items)
1608 if (as->parms[0].items) {
1609 char *name = as->parms[0].items->data;
1611 ka_ParseLoginName(name, server.name, server.instance,
1614 com_err(whoami, code, "couldn't interpret name '%s'", name);
1617 if (server.cell[0] == 0) {
1618 if (code = DefaultCell())
1620 strcpy(server.cell, cell);
1622 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1624 com_err(whoami, code, "Can't expand cell name");
1628 code = ListTicket(&server, verbose);
1630 for (index = 0;; index = newIndex) {
1631 code = ktc_ListTokens(index, &newIndex, &server);
1633 if (code == KTC_NOENT)
1634 code = 0; /* end of list */
1637 code = ListTicket(&server, verbose);
1643 add_std_args(register struct cmd_syndesc *ts)
1646 /* 12 */ cmd_AddParm(ts, "-admin_username", CMD_SINGLE, CMD_OPTIONAL,
1647 "admin principal to use for authentication");
1648 /* 13 */ cmd_AddParm(ts, "-password_for_admin", CMD_SINGLE, CMD_OPTIONAL,
1650 /* 14 */ cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1651 /* 15 */ cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
1652 "explicit list of authentication servers");
1653 /* 16 */ cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL,
1654 "don't authenticate");
1658 ka_AdminInteractive(int cmd_argc, char *cmd_argv[])
1661 register struct cmd_syndesc *ts;
1667 strncpy(myName, *cmd_argv, 509);
1669 cmd_SetBeforeProc(MyBeforeProc, NULL);
1670 cmd_SetAfterProc(MyAfterProc, NULL);
1672 ts = cmd_CreateSyntax("interactive", Interactive, 0,
1673 "enter interactive mode");
1676 ts = cmd_CreateSyntax("noauthentication", NoAuth, 0,
1677 "connect to AuthServer w/o using token");
1679 ts = cmd_CreateSyntax("list", ListUsers, 0, "list all users in database");
1680 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1681 "show detailed info about each user");
1682 cmd_AddParm(ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL,
1683 "show all cell administrators");
1684 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1685 "show the user's actual key rather than the checksum");
1687 cmd_CreateAlias(ts, "ls");
1689 ts = cmd_CreateSyntax("examine", ExamineUser, 0,
1690 "examine the entry for a user");
1691 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1692 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1693 "show the user's actual key rather than the checksum");
1696 ts = cmd_CreateSyntax("create", CreateUser, 0,
1697 "create an entry for a user");
1698 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1699 cmd_AddParm(ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL,
1700 "initial password");
1703 ts = cmd_CreateSyntax("delete", DeleteUser, 0, "delete a user");
1704 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1706 cmd_CreateAlias(ts, "rm");
1708 ts = cmd_CreateSyntax("setfields", SetFields, 0,
1709 "set various fields in a user's entry");
1710 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1711 cmd_AddParm(ts, "-flags", CMD_SINGLE, CMD_OPTIONAL,
1712 "hex flag value or flag name expression");
1713 cmd_AddParm(ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL,
1714 "date of account expiration");
1715 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
1716 "maximum ticket lifetime");
1717 cmd_AddParm(ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL,
1718 "number days password is valid ([0..254])");
1719 cmd_AddParm(ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL,
1720 "permit password reuse (yes/no)");
1721 cmd_AddParm(ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL,
1722 "maximum successive failed login tries ([0..254])");
1723 cmd_AddParm(ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL,
1724 "failure penalty [hh:mm or minutes]");
1726 cmd_AddParm(ts, "-associates", CMD_SINGLE, CMD_OPTIONAL,
1727 "maximum associate instances");
1730 cmd_CreateAlias(ts, "sf");
1733 ts = cmd_CreateSyntax("unlock", Unlock, 0,
1734 "Enable authentication ID after max failed attempts exceeded");
1735 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "authentication ID");
1739 ts = cmd_CreateSyntax("stringtokey", StringToKey, 0,
1740 "convert a string to a key");
1741 cmd_AddParm(ts, "-string", CMD_SINGLE, 0, "password string");
1742 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1744 ts = cmd_CreateSyntax("setpassword", SetPassword, 0,
1745 "set a user's password");
1746 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1747 cmd_AddParm(ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL,
1750 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1752 cmd_CreateAlias(ts, "sp");
1753 #ifdef CMD_PARSER_AMBIG_FIX
1754 cmd_CreateAlias(ts, "setpasswd");
1757 /* set a user's key */
1758 ts = cmd_CreateSyntax("setkey", SetPassword, 0, (char *)CMD_HIDDEN);
1759 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1761 cmd_AddParm(ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1763 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1766 /* get a user's password */
1767 ts = cmd_CreateSyntax("getpassword", GetPassword, 0, (char *)CMD_HIDDEN);
1768 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1769 /* don't take standard args */
1770 /* add_std_args (ts); */
1771 #ifdef CMD_PARSER_AMBIG_FIX
1772 cmd_CreateAlias(ts, "getpasswd");
1775 /* get a random key */
1776 ts = cmd_CreateSyntax("getrandomkey", GetRandomKey, 0,
1777 (char *)CMD_HIDDEN);
1780 /* get a ticket for a specific server */
1781 ts = cmd_CreateSyntax("getticket", GetTicket, 0, (char *)CMD_HIDDEN);
1782 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of server");
1783 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1786 ts = cmd_CreateSyntax("statistics", Statistics, 0,
1787 "show statistics for AuthServer");
1790 /* show debugging info from AuthServer */
1791 ts = cmd_CreateSyntax("debuginfo", DebugInfo, 0, (char *)CMD_HIDDEN);
1792 cmd_AddParm(ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL,
1793 "authentication server host name");
1796 ts = cmd_CreateSyntax("forgetticket", ForgetTicket, 0,
1797 "delete user's tickets");
1799 cmd_AddParm(ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1802 cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1804 ts = cmd_CreateSyntax("listtickets", ListTickets, 0,
1805 "show all cache manager tickets");
1806 cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1807 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1808 "show session key and ticket");
1810 ts = cmd_CreateSyntax("quit", Quit, 0, "exit program");
1813 conn = 0; /* no connection yet */
1814 zero_argc = cmd_argc;
1815 zero_argv = cmd_argv;
1817 strcpy(whoami, "kas");
1819 if (code = cmd_Dispatch(cmd_argc, cmd_argv)) {
1828 s = fgets(line, sizeof(line), stdin);
1830 return 0; /* EOF on input */
1831 for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--)
1834 continue; /* blank line */
1837 cmd_ParseLine(line, argv, &argc, sizeof(argv) / sizeof(argv[0]));
1839 com_err(whoami, code, "parsing line: '%s'", line);
1842 code = cmd_Dispatch(argc, argv);