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();
1291 struct ktc_encryptionKey key;
1292 struct ktc_principal auth_server, auth_token, client;
1293 char realm[MAXKTCREALMLEN];
1295 struct ktc_token token, *pToken;
1296 int i, acode, code = 0;
1299 char *ws = strrchr(as->a0name, '/');
1301 ws++; /* skip everything before the "/" */
1304 if (strlen(ws) > 0) {
1305 strncpy(whoami, ws, sizeof(whoami));
1306 if (strlen(whoami) + 1 >= sizeof(whoami))
1307 strcpy(whoami, "kas:");
1309 strcat(whoami, ":");
1312 /* append sub-command name */
1313 strncat(whoami, as->name, sizeof(whoami) - strlen(whoami) - 1);
1316 if (as->parms[12].name == 0)
1319 assert(as->parms[13].name && as->parms[14].name && as->parms[15].name
1320 && as->parms[16].name);
1322 /* MyAfterProc() destroys the conn, but just to be sure */
1324 code = ubik_ClientDestroy(conn);
1328 if (!init || as->parms[12].items || as->parms[13].items
1329 || as->parms[14].items || as->parms[15].items
1330 || as->parms[16].items) {
1331 strcpy(instance, "");
1332 strcpy(newCell, "");
1334 if (as->parms[12].items) { /* -admin_username */
1336 ka_ParseLoginName(as->parms[12].items->data, name, instance,
1339 com_err(whoami, code, "parsing user's name '%s'",
1340 as->parms[12].items->data);
1345 DWORD len = MAXKTCNAMELEN;
1346 if (!GetUserName((LPTSTR) name, &len)) {
1347 printf("Can't get user name \n");
1351 /* No explicit name provided: use Unix uid. */
1352 pw = getpwuid(getuid());
1354 printf("Can't figure out your name from your user id.\n");
1357 strncpy(name, pw->pw_name, sizeof(name));
1361 if (as->parms[14].items) { /* -cell */
1362 if (strlen(newCell) > 0) {
1363 printf("Duplicate cell specification not allowed\n");
1365 strncpy(newCell, as->parms[14].items->data, sizeof(newCell));
1368 code = ka_ExpandCell(newCell, newCell, 0 /*local */ );
1370 com_err(whoami, code, "Can't expand cell name");
1373 strcpy(cell, newCell);
1375 if (as->parms[15].items) { /* -servers */
1376 struct cmd_item *ip;
1377 char *ap[MAXSERVERS + 2];
1381 for (ip = as->parms[15].items, i = 2; ip; ip = ip->next, i++)
1383 code = ubik_ParseClientList(i, ap, serverList);
1385 com_err(whoami, code, "could not parse server list");
1388 ka_ExplicitCell(cell, serverList);
1391 noauth = (as->parms[16].items ? 1 : 0); /* -noauth */
1396 token.ticketLen = 0; /* in case there are no tokens */
1397 if (!noauth) { /* Will prompt for a password */
1398 /* first see if there's already an admin ticket */
1400 ka_GetAdminToken(0, 0, cell, 0, KA_SIXHOURS, &token,
1402 if (code) { /* if not then get key and try again */
1403 if (as->parms[13].items) { /* if password specified */
1404 strncpy(passwd, as->parms[13].items->data, sizeof(passwd));
1405 memset(as->parms[13].items->data, 0,
1406 strlen(as->parms[13].items->data));
1408 char msg[MAXKTCNAMELEN + 50];
1409 if (as->parms[12].items)
1410 sprintf(msg, "Administrator's (%s) Password: ", name);
1412 sprintf(msg, "Password for %s: ", name);
1413 code = read_pw_string(passwd, sizeof(passwd), msg, 0);
1416 else if (strlen(passwd) == 0)
1417 code = KANULLPASSWORD;
1419 com_err(whoami, code, "reading password");
1423 ka_StringToKey(passwd, cell, &key);
1425 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1426 &token, 0 /* !new */ );
1427 if (code == KABADREQUEST) {
1428 des_string_to_key(passwd, &key);
1430 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1431 &token, 0 /* !new */ );
1433 if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
1434 /* try with only the first 8 characters incase they set
1435 * their password with an old style passwd program. */
1437 ka_StringToKey(passwd, cell, &key);
1439 ka_GetAdminToken(name, instance, cell, &key, KA_SIXHOURS,
1440 &token, 0 /* !new */ );
1443 "Warning: you have typed a password longer than 8 characters, but only the\n");
1445 "first 8 characters were actually significant. If you change your password\n");
1447 "again this warning message will go away.\n");
1454 reason = "password was incorrect";
1457 reason = "Authentication Server was unavailable";
1460 reason = (char *)error_message(code);
1463 "%s: Auth. as %s to AuthServer failed: %s\nProceeding w/o authentication\n",
1464 whoami, PrintedName(name, instance, cell), reason);
1466 /* get an Authentication token while were at it. */
1467 if (ka_CellToRealm(cell, realm, 0) != 0)
1469 strcpy(auth_server.name, KA_TGS_NAME);
1470 strcpy(auth_server.instance, realm);
1471 strcpy(auth_server.cell, cell);
1473 (&auth_server, &auth_token, sizeof(struct ktc_token),
1476 ka_GetAuthToken(name, instance, cell, &key,
1477 MAXKTCTICKETLIFETIME, (afs_int32 *) 0
1478 /*Don't need pwd expiration info here */
1480 if (acode && (acode != code)) /* codes are usually the same */
1481 com_err(whoami, code,
1482 "getting Authentication token for %s",
1483 PrintedName(name, instance, cell));
1485 memset(&key, 0, sizeof(key));
1489 pToken = ((token.ticketLen == 0) ? 0 : &token);
1490 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, pToken, &conn);
1491 if (code && pToken) {
1492 com_err(whoami, code,
1493 "connecting to AuthServer: now trying w/o authentication");
1494 code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, 0, &conn);
1496 com_err(whoami, code,
1497 "making unauthenticated connection to AuthServer");
1500 com_err(whoami, code,
1501 "Couldn't establish connection to Authentication Server");
1505 /* now default unspecified password by prompting from terminal */
1506 if (as->nParms >= 12)
1507 for (i = 0; i < 12; i++)
1508 if (as->parms[i].name && (as->parms[i].items == 0)) {
1509 char *p = as->parms[i].name; /* parameter name */
1510 int l = strlen(p); /* length of name */
1511 /* does parameter end in "password" */
1512 if (strcmp(p + (l - 8), "password") == 0) {
1514 char password[BUFSIZ];
1515 struct cmd_item *ip;
1519 code = read_pw_string(password, sizeof(password), msg, 1);
1522 else if (strlen(password) == 0)
1523 code = KANULLPASSWORD;
1525 com_err(whoami, code, "prompting for %s", p + 1);
1528 ip = (struct cmd_item *)malloc(sizeof(struct cmd_item));
1529 ip->data = (char *)malloc(strlen(password) + 1);
1531 strcpy(ip->data, password);
1532 as->parms[i].items = ip;
1535 if (!conn) { /* if all else fails... */
1536 code = NoAuth(0, 0); /* get unauthenticated conn */
1543 /* These are some helpful command that deal with the cache managers tokens. */
1546 ForgetTicket(struct cmd_syndesc *as, char *arock)
1551 struct ktc_principal server;
1553 if (as->parms[0].items) {
1554 char *name = as->parms[0].items->data;
1556 ka_ParseLoginName(name, server.name, server.instance,
1559 com_err(whoami, code, "couldn't interpret name '%s'", name);
1562 if (server.cell[0] == 0) {
1563 if (code = DefaultCell())
1565 strcpy(server.cell, cell);
1567 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1569 com_err(whoami, code, "Can't expand cell name");
1573 code = ktc_ForgetToken(&server);
1575 com_err(whoami, code, "couldn't remove tokens for %s",
1576 PrintedPrincipal(&server));
1580 if (!as->parms[1].items) {
1581 fprintf(stderr, "Must specify server name or -all\n");
1584 code = ktc_ForgetAllTokens();
1586 com_err(whoami, code, "couldn't delete all tokens");
1591 code = ktc_ForgetAllTokens();
1593 com_err(whoami, code, "couldn't delete all tokens");
1600 ListTickets(struct cmd_syndesc *as, char *arock)
1603 int index, newIndex;
1604 struct ktc_principal server;
1607 if (as->parms[1].items)
1609 if (as->parms[0].items) {
1610 char *name = as->parms[0].items->data;
1612 ka_ParseLoginName(name, server.name, server.instance,
1615 com_err(whoami, code, "couldn't interpret name '%s'", name);
1618 if (server.cell[0] == 0) {
1619 if (code = DefaultCell())
1621 strcpy(server.cell, cell);
1623 code = ka_ExpandCell(server.cell, server.cell, 0 /*local */ );
1625 com_err(whoami, code, "Can't expand cell name");
1629 code = ListTicket(&server, verbose);
1631 for (index = 0;; index = newIndex) {
1632 code = ktc_ListTokens(index, &newIndex, &server);
1634 if (code == KTC_NOENT)
1635 code = 0; /* end of list */
1638 code = ListTicket(&server, verbose);
1644 add_std_args(register struct cmd_syndesc *ts)
1647 /* 12 */ cmd_AddParm(ts, "-admin_username", CMD_SINGLE, CMD_OPTIONAL,
1648 "admin principal to use for authentication");
1649 /* 13 */ cmd_AddParm(ts, "-password_for_admin", CMD_SINGLE, CMD_OPTIONAL,
1651 /* 14 */ cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1652 /* 15 */ cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
1653 "explicit list of authentication servers");
1654 /* 16 */ cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL,
1655 "don't authenticate");
1659 ka_AdminInteractive(int cmd_argc, char *cmd_argv[])
1662 register struct cmd_syndesc *ts;
1668 strncpy(myName, *cmd_argv, 509);
1670 cmd_SetBeforeProc(MyBeforeProc, NULL);
1671 cmd_SetAfterProc(MyAfterProc, NULL);
1673 ts = cmd_CreateSyntax("interactive", Interactive, 0,
1674 "enter interactive mode");
1677 ts = cmd_CreateSyntax("noauthentication", NoAuth, 0,
1678 "connect to AuthServer w/o using token");
1680 ts = cmd_CreateSyntax("list", ListUsers, 0, "list all users in database");
1681 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1682 "show detailed info about each user");
1683 cmd_AddParm(ts, "-showadmin", CMD_FLAG, CMD_OPTIONAL,
1684 "show all cell administrators");
1685 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1686 "show the user's actual key rather than the checksum");
1688 cmd_CreateAlias(ts, "ls");
1690 ts = cmd_CreateSyntax("examine", ExamineUser, 0,
1691 "examine the entry for a user");
1692 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1693 cmd_AddParm(ts, "-showkey", CMD_FLAG, CMD_OPTIONAL,
1694 "show the user's actual key rather than the checksum");
1697 ts = cmd_CreateSyntax("create", CreateUser, 0,
1698 "create an entry for a user");
1699 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1700 cmd_AddParm(ts, "-initial_password", CMD_SINGLE, CMD_OPTIONAL,
1701 "initial password");
1704 ts = cmd_CreateSyntax("delete", DeleteUser, 0, "delete a user");
1705 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1707 cmd_CreateAlias(ts, "rm");
1709 ts = cmd_CreateSyntax("setfields", SetFields, 0,
1710 "set various fields in a user's entry");
1711 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1712 cmd_AddParm(ts, "-flags", CMD_SINGLE, CMD_OPTIONAL,
1713 "hex flag value or flag name expression");
1714 cmd_AddParm(ts, "-expiration", CMD_SINGLE, CMD_OPTIONAL,
1715 "date of account expiration");
1716 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
1717 "maximum ticket lifetime");
1718 cmd_AddParm(ts, "-pwexpires", CMD_SINGLE, CMD_OPTIONAL,
1719 "number days password is valid ([0..254])");
1720 cmd_AddParm(ts, "-reuse", CMD_SINGLE, CMD_OPTIONAL,
1721 "permit password reuse (yes/no)");
1722 cmd_AddParm(ts, "-attempts", CMD_SINGLE, CMD_OPTIONAL,
1723 "maximum successive failed login tries ([0..254])");
1724 cmd_AddParm(ts, "-locktime", CMD_SINGLE, CMD_OPTIONAL,
1725 "failure penalty [hh:mm or minutes]");
1727 cmd_AddParm(ts, "-associates", CMD_SINGLE, CMD_OPTIONAL,
1728 "maximum associate instances");
1731 cmd_CreateAlias(ts, "sf");
1734 ts = cmd_CreateSyntax("unlock", Unlock, 0,
1735 "Enable authentication ID after max failed attempts exceeded");
1736 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "authentication ID");
1740 ts = cmd_CreateSyntax("stringtokey", StringToKey, 0,
1741 "convert a string to a key");
1742 cmd_AddParm(ts, "-string", CMD_SINGLE, 0, "password string");
1743 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1745 ts = cmd_CreateSyntax("setpassword", SetPassword, 0,
1746 "set a user's password");
1747 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1748 cmd_AddParm(ts, "-new_password", CMD_SINGLE, CMD_OPTIONAL,
1751 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1753 cmd_CreateAlias(ts, "sp");
1754 #ifdef CMD_PARSER_AMBIG_FIX
1755 cmd_CreateAlias(ts, "setpasswd");
1758 /* set a user's key */
1759 ts = cmd_CreateSyntax("setkey", SetPassword, 0, (char *)CMD_HIDDEN);
1760 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1762 cmd_AddParm(ts, "-new_key", CMD_SINGLE, 0, "eight byte new key");
1764 cmd_AddParm(ts, "-kvno", CMD_SINGLE, CMD_OPTIONAL, "key version number");
1767 /* get a user's password */
1768 ts = cmd_CreateSyntax("getpassword", GetPassword, 0, (char *)CMD_HIDDEN);
1769 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of user");
1770 /* don't take standard args */
1771 /* add_std_args (ts); */
1772 #ifdef CMD_PARSER_AMBIG_FIX
1773 cmd_CreateAlias(ts, "getpasswd");
1776 /* get a random key */
1777 ts = cmd_CreateSyntax("getrandomkey", GetRandomKey, 0,
1778 (char *)CMD_HIDDEN);
1781 /* get a ticket for a specific server */
1782 ts = cmd_CreateSyntax("getticket", GetTicket, 0, (char *)CMD_HIDDEN);
1783 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of server");
1784 cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime");
1787 ts = cmd_CreateSyntax("statistics", Statistics, 0,
1788 "show statistics for AuthServer");
1791 /* show debugging info from AuthServer */
1792 ts = cmd_CreateSyntax("debuginfo", DebugInfo, 0, (char *)CMD_HIDDEN);
1793 cmd_AddParm(ts, "-hostname", CMD_SINGLE, CMD_OPTIONAL,
1794 "authentication server host name");
1797 ts = cmd_CreateSyntax("forgetticket", ForgetTicket, 0,
1798 "delete user's tickets");
1800 cmd_AddParm(ts, "-name", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1803 cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "delete all tickets");
1805 ts = cmd_CreateSyntax("listtickets", ListTickets, 0,
1806 "show all cache manager tickets");
1807 cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "name of server");
1808 cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL,
1809 "show session key and ticket");
1811 ts = cmd_CreateSyntax("quit", Quit, 0, "exit program");
1814 conn = 0; /* no connection yet */
1815 zero_argc = cmd_argc;
1816 zero_argv = cmd_argv;
1818 strcpy(whoami, "kas");
1820 if (code = cmd_Dispatch(cmd_argc, cmd_argv)) {
1829 s = fgets(line, sizeof(line), stdin);
1831 return 0; /* EOF on input */
1832 for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--)
1835 continue; /* blank line */
1838 cmd_ParseLine(line, argv, &argc, sizeof(argv) / sizeof(argv[0]));
1840 com_err(whoami, code, "parsing line: '%s'", line);
1843 code = cmd_Dispatch(argc, argv);