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 /* RX Authentication Stress test: client side code. */
12 #include <afsconfig.h>
13 #include <afs/param.h>
17 #include <sys/types.h>
23 #include <netinet/in.h>
25 #include <afs/com_err.h>
26 #include <afs/afsutil.h>
30 #include "stress_internal.h"
31 #ifdef AFS_PTHREAD_ENV
33 #define FT_ApproxTime() (int)time(0)
44 register struct hostent *th;
47 th = gethostbyname(aname);
49 fprintf(stderr, "host %s not found\n", aname);
52 memcpy(&addr, th->h_addr, sizeof(addr));
57 GetToken(versionP, session, ticketLenP, ticket, cell)
59 OUT struct ktc_encryptionKey *session;
63 struct ktc_principal sname;
64 struct ktc_token ttoken;
67 strcpy(sname.cell, cell);
68 sname.instance[0] = 0;
69 strcpy(sname.name, "afs");
70 code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
74 *versionP = ttoken.kvno;
75 *ticketLenP = ttoken.ticketLen;
76 memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
77 memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
82 GetTicket(versionP, session, ticketLenP, ticket, cell)
84 OUT struct ktc_encryptionKey *session;
90 /* create random session key, using key for seed to good random */
91 des_init_random_number_generator(&serviceKey);
92 code = des_random_key(session);
96 /* now create the actual ticket */
99 tkt_MakeTicket(ticket, ticketLenP, &serviceKey, RXKST_CLIENT_NAME,
100 RXKST_CLIENT_INST, cell,
101 /*start,end */ 0, 0xffffffff, session, /*host */ 0,
102 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
103 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
104 * instance and cell, start time, end time, session key to seal in ticket,
105 * inet host, server name and server instance */
108 *versionP = serviceKeyVersion;
112 struct rx_connection *conn;
113 u_long sendLen; /* parameters for call to Copious */
115 u_long *fastCalls; /* number of calls to perform */
117 u_long *copiousCalls;
121 Copious(c, buf, buflen)
127 struct rx_call *call;
129 long inlen = c->sendLen;
130 long outlen = c->recvLen;
136 for (i = 0; i < inlen; i++)
137 mysum += (d++ & 0xff);
139 call = rx_NewCall(c->conn);
140 code = StartRXKST_Copious(call, inlen, mysum, outlen);
146 while (xfer < inlen) {
150 for (i = 0; i < tlen; i++)
151 buf[i] = (d++ & 0xff);
152 n = rx_Write(call, buf, tlen);
157 code = RXKST_WRITESHORT;
165 while (xfer < outlen) {
166 tlen = outlen - xfer;
169 n = rx_Read(call, buf, tlen);
174 code = RXKST_READSHORT;
177 for (i = 0; i < tlen; i++)
184 code = EndRXKST_Copious(call, &outsum);
185 code = rx_EndCall(call, code);
188 if (outsum != mysum) {
189 return RXKST_BADOUTPUTSUM;
195 DoClient(index, rock)
199 struct client *c = (struct client *)rock;
205 for (i = 0; i < c->fastCalls[index]; i++) {
206 code = RXKST_Fast(c->conn, n, &inc_n);
210 return RXKST_INCFAILED;
214 for (i = 0; i < c->slowCalls[index]; i++) {
217 code = RXKST_Slow(c->conn, 1, &ntime);
220 now = FT_ApproxTime();
221 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
222 return RXKST_TIMESKEW;
225 if (c->copiousCalls[index] > 0) {
226 u_long buflen = 10000;
227 u_char *buf = (u_char *) osi_Alloc(buflen);
228 for (i = 0; i < c->copiousCalls[index]; i++) {
229 code = Copious(c, buf, buflen);
233 osi_Free(buf, buflen);
242 long exitCode; /* is PROCESSRUNNING until exit */
248 #ifdef AFS_PTHREAD_ENV
249 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
250 static pthread_mutex_t workerLock;
251 static pthread_cond_t workerCV;
255 pthread_mutex_init(&workerLock, NULL);
256 pthread_cond_init(&workerCV, NULL);
259 static struct worker *workers;
266 code = (*w->proc) (w->index, w->rock);
267 #ifdef AFS_PTHREAD_ENV
268 pthread_mutex_lock(&workerLock);
271 #ifdef AFS_PTHREAD_ENV
272 pthread_mutex_unlock(&workerLock);
274 #ifdef AFS_PTHREAD_ENV
275 pthread_cond_signal(&workerCV);
277 LWP_NoYieldSignal(&workers);
282 #define MAX_CTHREADS 25
285 CallSimultaneously(threads, rock, proc)
292 #ifdef AFS_PTHREAD_ENV
293 pthread_once(&workerOnce, WorkerInit);
297 for (i = 0; i < threads; i++) {
299 #ifdef AFS_PTHREAD_ENV
304 assert(i < MAX_CTHREADS);
305 w = (struct worker *)osi_Alloc(sizeof(struct worker));
306 memset(w, 0, sizeof(*w));
310 w->exitCode = RXKST_PROCESSRUNNING;
313 #ifdef AFS_PTHREAD_ENV
315 pthread_attr_t tattr;
317 code = pthread_attr_init(&tattr);
319 afs_com_err(whoami, code,
320 "can't pthread_attr_init worker process");
325 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
327 afs_com_err(whoami, code,
328 "can't pthread_attr_setdetachstate worker process");
332 code = pthread_create(&pid, &tattr, DoWorker, (void *)w);
336 LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
337 (opaque) w, "Worker Process", &pid);
340 afs_com_err(whoami, code, "can't create worker process");
344 code = 0; /* last non-zero code encountered */
345 #ifdef AFS_PTHREAD_ENV
346 pthread_mutex_lock(&workerLock);
349 struct worker *w, *prevW, *nextW;
351 for (w = workers; w; w = nextW) {
353 if (w->exitCode != RXKST_PROCESSRUNNING) {
359 prevW->next = w->next;
362 osi_Free(w, sizeof(*w));
363 continue; /* don't bump prevW */
367 #ifdef AFS_PTHREAD_ENV
369 pthread_cond_wait(&workerCV, &workerLock);
372 LWP_WaitProcess(&workers);
375 #ifdef AFS_PTHREAD_ENV
376 pthread_mutex_unlock(&workerLock);
382 DivideUpCalls(calls, threads, threadCalls)
385 IN u_long threadCalls[];
388 for (i = 0; i < threads; i++) {
389 threadCalls[i] = calls / (threads - i);
390 calls -= threadCalls[i];
398 gettimeofday(&tv, 0);
399 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
403 RunLoadTest(parms, conn)
404 IN struct clientParms *parms;
405 IN struct rx_connection *conn;
409 u_long fastCalls[MAX_CTHREADS];
410 u_long slowCalls[MAX_CTHREADS];
411 u_long copiousCalls[MAX_CTHREADS];
412 double start, interval;
414 DivideUpCalls(parms->fastCalls, parms->threads, fastCalls);
415 DivideUpCalls(parms->slowCalls, parms->threads, slowCalls);
416 DivideUpCalls(parms->copiousCalls, parms->threads, copiousCalls);
418 memset(&c, 0, sizeof(c));
420 c.sendLen = parms->sendLen;
421 c.recvLen = parms->recvLen;
422 c.fastCalls = fastCalls;
423 c.slowCalls = slowCalls;
424 c.copiousCalls = copiousCalls;
427 code = CallSimultaneously(parms->threads, &c, DoClient);
429 afs_com_err(whoami, code, "in DoClient");
432 interval = ftime() - start;
434 if (parms->printTiming) {
436 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
437 int t = (interval / totalCalls) * 1000.0 + 0.5;
438 if (totalCalls > 0) {
439 printf("For %d calls: %d msec/call\n", totalCalls, t);
441 if (parms->copiousCalls > 0) {
442 long n = parms->sendLen + parms->recvLen;
445 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
448 I just cannot get printing of floats to work on the pmax !
449 !!!printf("%g %d %d %d\n", (float)kbps, b);
450 printf("%g %d %d %d\n", kbps, b);
451 fprintf(stdout, "%g %d %d\n", kbps, b);
454 buf[sizeof(buf) - 1] = 0;
455 sprintf(buf, "%g %d %d\n", kbps, b);
456 assert(buf[sizeof(buf) - 1] == 0);
461 ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
462 parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
464 printf("%g\n", kbps);
472 RepeatLoadTest(parms, conn)
473 IN struct clientParms *parms;
474 IN struct rx_connection *conn;
479 if (parms->repeatInterval == 0) {
480 if (parms->repeatCount == 0)
481 parms->repeatCount = 1;
483 if (parms->repeatCount == 0)
484 parms->repeatCount = 0x7fffffff;
487 if (parms->printTiming) {
490 (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
491 (parms->copiousCalls ? 1 : 0);
494 "Combined timings of several types of calls may not be meaningful.\n");
496 /* do timings of copious calls by default */
497 parms->copiousCalls = 10;
500 for (count = 0; count < parms->repeatCount; count++) {
501 code = RunLoadTest(parms, conn);
504 if (parms->repeatInterval) {
505 u_long i = parms->repeatInterval;
506 u_long now = time(0);
507 u_long next = (now + i - 1) / i * i; /* round up to next interval */
509 #ifdef AFS_PTHREAD_ENV
512 IOMGR_Sleep(next - now);
521 /* For backward compatibility, don't try to use the CallNumber stuff unless
522 * we're compiling against the new Rx. */
524 #ifdef rx_GetPacketCksum
526 struct multiChannel {
527 struct rx_connection *conn;
530 int changes[RX_MAXCALLS];
531 long callNumbers[RX_MAXCALLS];
533 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
534 static u_long sequence = 0;
538 IN struct rx_connection *conn;
541 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
544 code = RXKST_Fast(conn, n, &inc_n);
548 return RXKST_INCFAILED;
553 UniChannelCall(index, rock)
557 struct multiChannel *mc = (struct multiChannel *)rock;
559 long callNumbers[RX_MAXCALLS];
564 while (!mc->done && unchanged) {
566 code = FastCall(mc->conn);
569 code = rxi_GetCallNumberVector(mc->conn, callNumbers);
573 for (i = 0; i < RX_MAXCALLS; i++) {
574 if (callNumbers[i] > mc->callNumbers[i]) {
575 mc->callNumbers[i] = callNumbers[i];
576 mc->changes[i]--; /* may go negative */
578 if (mc->changes[i] > 0)
582 mc->codes[index] = code;
588 MakeMultiChannelCall(conn, each, expectedCode, codes)
589 IN struct rx_connection *conn;
590 IN int each; /* calls to make on each channel */
591 IN long expectedCode;
596 struct multiChannel mc;
598 memset(&mc, 0, sizeof(mc));
600 for (i = 0; i < RX_MAXCALLS; i++) {
601 codes[i] = RXKST_PROCESSRUNNING;
602 mc.changes[i] = each;
605 code = rxi_GetCallNumberVector(conn, mc.callNumbers);
609 code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
610 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3))); /* strange cases */
611 else if (code != expectedCode) {
612 afs_com_err(whoami, code,
613 "problem making multichannel call, expected '%s'",
615 ? "no error" : (char *)afs_error_message(expectedCode)));
621 CheckCallFailure(conn, codes, code, msg)
622 IN struct rx_connection *conn;
628 fprintf(stderr, "Failed to detect %s\n", msg);
629 return RXKST_NODUPLICATECALL;
634 for (i = 0; i < RX_MAXCALLS; i++)
635 if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
640 fprintf(stderr, "%s produced these errors:\n", msg);
641 for (i = 0; i < RX_MAXCALLS; i++) {
642 assert(codes[i] != RXKST_PROCESSRUNNING);
645 fprintf(stderr, " %d no error\n", i);
647 fprintf(stderr, " %d %s\n", i, afs_error_message(codes[i]));
651 sprintf(buf, "connection dead following %s", msg);
652 code = FastCall(conn);
654 afs_com_err(whoami, code, buf);
661 #endif /* rx_GetPacketCksum */
664 RunCallTest(parms, host, sc, si)
665 IN struct clientParms *parms;
667 IN struct rx_securityClass *sc;
672 #ifndef rx_GetPacketCksum
674 code = RXKST_BADARGS;
675 afs_com_err(whoami, code,
676 "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
682 struct rx_connection *conn;
684 long callNumbers[RX_MAXCALLS];
685 long codes[RX_MAXCALLS];
686 long retCode = 0; /* ret. if nothing fatal goes wrong */
689 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
692 return RXKST_NEWCONNFAILED;
694 /* First check the basic behaviour of call number handling */
696 code = rxi_GetCallNumberVector(conn, callNumbers);
699 for (i = 0; i < RX_MAXCALLS; i++) {
700 if (callNumbers[i] != 0) {
702 "Connection's initial call numbers not zero. call[%d] = %d\n",
704 return RXKST_BADCALLNUMBERS;
707 code = FastCall(conn);
710 code = rxi_GetCallNumberVector(conn, callNumbers);
713 firstCall = callNumbers[0];
714 code = FastCall(conn);
717 code = rxi_GetCallNumberVector(conn, callNumbers);
720 if ((callNumbers[0] != firstCall + 1)
721 && ((firstCall == 1) || (firstCall == 2))) {
722 /* The call number after the first call should be one or, more likely,
723 * two (if the call is still DALLYing). Between first and second call,
724 * the call number should have incremented by one. */
726 "Connection's first channel call number not one. call[%d] = %d\n",
728 return RXKST_BADCALLNUMBERS;
730 for (i = 1; i < RX_MAXCALLS; i++) {
731 if (callNumbers[i] != 0) {
733 "Connection's other channel call numbers not zero. call[%d] = %d\n",
735 return RXKST_BADCALLNUMBERS;
738 code = MakeMultiChannelCall(conn, 1, 0, codes);
742 /* Now try to resend a call that's already been executed by finding a
743 * non-zero call number on a channel other than zero and decrementing it by
744 * one. This should appear to the server as a retransmitted call. Since
745 * this is behaving as a broken client different strange behaviors may be
746 * exhibited by different servers. If the response packet to the original
747 * call is discarded by the time the "retransmitted" call arrives (perhaps
748 * due to high server or client load) there is no way for the server to
749 * respond at all. Further, it seems, that under some cases the connection
750 * will be kept alive indefinitely even though the server has discarded the
751 * "retransmitted" call and is making no effort to reexecute the call. To
752 * handle these, accept either a timeout (-1) or and INCFAILED error here,
753 * also set the connenction HardDeadTime to punt after a reasonable
756 /* short dead time since may we expect some trouble */
757 rx_SetConnHardDeadTime(conn, 30);
758 code = rxi_GetCallNumberVector(conn, callNumbers);
761 for (ch = 1; ch < RX_MAXCALLS; ch++)
762 if (callNumbers[ch] > 1) {
764 code = rxi_SetCallNumberVector(conn, callNumbers);
769 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
770 return RXKST_BADCALLNUMBERS;
771 code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
772 code = CheckCallFailure(conn, codes, code, "retransmitted call");
773 if (code && !retCode)
776 /* Get a fresh connection, becasue if the above failed as it should the
777 * connection is dead. */
778 rx_DestroyConnection(conn);
780 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
783 return RXKST_NEWCONNFAILED;
785 /* Similarly, but decrement call number by two which should be completely
786 * unmistakeable as a broken or malicious client. */
788 /* short dead time since may we expect some trouble */
789 rx_SetConnHardDeadTime(conn, 30);
790 code = MakeMultiChannelCall(conn, 2, 0, codes);
793 code = rxi_GetCallNumberVector(conn, callNumbers);
796 for (ch = 1; ch < RX_MAXCALLS; ch++)
797 if (callNumbers[ch] > 2) {
798 callNumbers[ch] -= 2;
799 code = rxi_SetCallNumberVector(conn, callNumbers);
802 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
803 return RXKST_BADCALLNUMBERS;
804 code = MakeMultiChannelCall(conn, 1, -1, codes);
805 code = CheckCallFailure(conn, codes, code, "duplicate call");
806 if (code && !retCode)
809 rx_DestroyConnection(conn);
811 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
814 return RXKST_NEWCONNFAILED;
816 /* Next, without waiting for the server to discard its state, we will check
817 * to see if the Challenge/Response protocol correctly informs the server
818 * of the client's callNumber state. We do this by artificially increasing
819 * the call numbers of a new connection for all channels beyond zero,
820 * making a call on channel zero, then resetting the call number for the
821 * unused channels back to zero, then making calls on all channels. */
823 code = rxi_GetCallNumberVector(conn, callNumbers);
826 for (i = 0; i < RX_MAXCALLS; i++) {
827 if (callNumbers[i] != 0)
828 return RXKST_BADCALLNUMBERS;
829 callNumbers[i] = 51; /* an arbitrary value... */
831 code = rxi_SetCallNumberVector(conn, callNumbers);
834 code = FastCall(conn); /* use channel 0 */
837 code = rxi_GetCallNumberVector(conn, callNumbers);
840 if (callNumbers[0] != 52)
841 return RXKST_BADCALLNUMBERS;
842 for (i = 1; i < RX_MAXCALLS; i++) {
843 if (callNumbers[i] != 51)
844 return RXKST_BADCALLNUMBERS;
845 callNumbers[i] = 37; /* back up a ways */
847 code = rxi_SetCallNumberVector(conn, callNumbers);
850 /* now try calls on all channels... */
851 code = MakeMultiChannelCall(conn, 1, -1, codes);
853 CheckCallFailure(conn, codes, code, "alternate channel call replay");
854 if (code && !retCode)
857 rx_DestroyConnection(conn);
860 #endif /* rx_GetPacketCksum */
864 #ifdef rx_GetPacketCksum
868 u_long epoch; /* connection to attack */
870 int client; /* TRUE => client side */
871 u_long newEpoch; /* conn to direct challenges to */
873 u_long counts[RX_N_PACKET_TYPES];
877 #define IO_REDIRECTCHALLENGE 2
880 HandleIncoming(p, addr)
881 INOUT struct rx_packet *p;
882 INOUT struct sockaddr_in *addr;
884 int client; /* packet sent by client */
885 u_char type; /* packet type */
887 if (incomingOps.op == IO_NOOP)
890 client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
891 if ((p->header.epoch != incomingOps.epoch)
892 || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
893 || (client != incomingOps.client))
895 type = p->header.type;
896 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
898 incomingOps.counts[type]++;
900 switch (incomingOps.op) {
905 case IO_REDIRECTCHALLENGE:
906 if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
908 p->header.epoch = incomingOps.newEpoch;
909 p->header.cid = incomingOps.newCid;
910 /* Now set up to watch for the corresponding challenge. */
911 incomingOps.epoch = incomingOps.newEpoch;
912 incomingOps.cid = incomingOps.newCid;
913 incomingOps.op = IO_COUNT;
917 fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
925 u_long epoch; /* connection to attack */
927 int client; /* TRUE => client side */
928 u_long counts[RX_N_PACKET_TYPES];
932 #define OO_ZEROCKSUM 2
933 #define OO_MUNGCKSUM 3
936 HandleOutgoing(p, addr)
937 INOUT struct rx_packet *p;
938 INOUT struct sockaddr_in *addr;
940 int client; /* packet sent by client */
941 u_char type; /* packet type */
943 if (outgoingOps.op == OO_NOOP)
946 client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
947 if ((p->header.epoch != outgoingOps.epoch)
948 || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
949 || (client != outgoingOps.client))
951 type = p->header.type;
952 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
954 outgoingOps.counts[type]++;
956 switch (outgoingOps.op) {
959 /* counting always happens above if not noop */
963 if (p->header.type != RX_PACKET_TYPE_DATA)
965 if (rx_GetPacketCksum(p) == 0) {
966 /* probably, a retransmitted packet */
967 fprintf(stderr, "Packet cksum already zero\n");
970 rx_SetPacketCksum(p, 0);
975 if (p->header.type != RX_PACKET_TYPE_DATA)
977 cksum = rx_GetPacketCksum(p);
979 fprintf(stderr, "Packet cksum already zero\n");
982 rx_SetPacketCksum(p, cksum ^ 8);
986 fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
992 #ifdef AFS_PTHREAD_ENV
993 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
994 static pthread_mutex_t slowCallLock;
995 static pthread_cond_t slowCallCV;
999 pthread_mutex_init(&slowCallLock, NULL);
1000 pthread_cond_init(&slowCallCV, NULL);
1003 static long slowCallCode;
1012 #ifdef AFS_PTHREAD_ENV
1013 pthread_mutex_lock(&slowCallLock);
1015 slowCallCode = RXKST_PROCESSRUNNING;
1016 #ifdef AFS_PTHREAD_ENV
1017 pthread_cond_signal(&slowCallCV);
1019 LWP_NoYieldSignal(&slowCallCode);
1021 slowCallCode = RXKST_Slow(conn, 1, &ntime);
1022 if (!slowCallCode) {
1023 now = FT_ApproxTime();
1024 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
1025 slowCallCode = RXKST_TIMESKEW;
1027 temp_rc = slowCallCode;
1028 #ifdef AFS_PTHREAD_ENV
1029 pthread_cond_signal(&slowCallCV);
1030 pthread_mutex_unlock(&slowCallLock);
1032 LWP_NoYieldSignal(&slowCallCode);
1037 #endif /* rx_GetPacketCksum */
1040 RunHijackTest(parms, host, sc, si)
1041 IN struct clientParms *parms;
1043 IN struct rx_securityClass *sc;
1047 #ifndef rx_GetPacketCksum
1049 code = RXKST_BADARGS;
1050 afs_com_err(whoami, code,
1051 "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
1056 extern int (*rx_justReceived) ();
1057 extern int (*rx_almostSent) ();
1060 struct rx_connection *conn = 0;
1061 struct rx_connection *otherConn = 0;
1062 #ifdef AFS_PTHREAD_ENV
1067 int nResp; /* otherConn responses seen */
1070 #ifdef AFS_PTHREAD_ENV
1071 pthread_once(&slowCallOnce, SlowCallInit);
1073 rx_justReceived = HandleIncoming;
1074 rx_almostSent = HandleOutgoing;
1076 incomingOps.op = IO_NOOP;
1077 outgoingOps.op = OO_NOOP;
1079 #define HIJACK_CONN(conn) \
1080 { if (conn) rx_DestroyConnection (conn); \
1081 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1082 RXKST_SERVICEID, sc, si); \
1083 if (!(conn)) return RXKST_NEWCONNFAILED; \
1084 outgoingOps.client = 1; \
1085 outgoingOps.epoch = (conn)->epoch; \
1086 outgoingOps.cid = (conn)->cid; }
1090 /* First try switching from no packet cksum to sending packet cksum between
1091 * calls, and see if server complains. */
1093 outgoingOps.op = OO_ZEROCKSUM;
1094 code = FastCall(conn);
1096 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1099 /* The server thinks we're an old style client. Now start sending cksums.
1100 * Server shouldn't care. */
1101 outgoingOps.op = OO_NOOP;
1102 code = FastCall(conn);
1104 afs_com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1107 /* The server now thinks we're a new style client, we can't go back now. */
1108 outgoingOps.op = OO_ZEROCKSUM;
1109 code = FastCall(conn);
1111 code = RXKST_NOBADCKSUM;
1112 if (code != RXKADSEALEDINCON) {
1113 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1115 } else if (!conn->error) {
1116 code = RXKST_NOCONNERROR;
1117 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1124 /* Now try modifying packet cksum to see if server complains. */
1126 outgoingOps.op = OO_MUNGCKSUM;
1127 code = FastCall(conn);
1129 code = RXKST_NOBADCKSUM;
1130 if (code != RXKADSEALEDINCON) {
1131 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1133 } else if (!conn->error) {
1134 code = RXKST_NOCONNERROR;
1135 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1140 /* Now make two connection and direct the first challenge on one connection
1141 * to the other connection to see if it generates a response. The
1142 * retransmitted challenge should allow the call on the first connection to
1143 * complete correctly. Part one is to attack a new connection, then attack
1144 * it after it has made a call. Part three, just for comparison, attacks a
1145 * otherConn while it is making a slow call (and thus has an active call).
1146 * Against this attack we have no defense so we expect a challenge in this
1147 * case, which the server will discard. */
1149 #define RedirectChallenge(conn,otherConn) \
1150 (incomingOps.epoch = (conn)->epoch, \
1151 incomingOps.cid = (conn)->cid, \
1152 incomingOps.client = 1, \
1153 incomingOps.newEpoch = (otherConn)->epoch, \
1154 incomingOps.newCid = (otherConn)->cid, \
1155 incomingOps.op = IO_REDIRECTCHALLENGE, \
1156 outgoingOps.epoch = (otherConn)->epoch, \
1157 outgoingOps.cid = (otherConn)->cid, \
1158 outgoingOps.client = 1, \
1159 outgoingOps.op = OO_COUNT, \
1160 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1163 HIJACK_CONN(otherConn)
1164 RedirectChallenge(conn, otherConn);
1166 code = FastCall(conn);
1169 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1170 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1172 code = RXKST_CHALLENGEORACLE;
1173 afs_com_err(whoami, code, "misdirecting challenge");
1176 code = FastCall(otherConn); /* generate some activity here */
1179 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1181 code = FastCall(conn);
1184 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1188 RedirectChallenge(conn, otherConn);
1189 /* otherConn was authenticated during part one */
1190 code = FastCall(conn);
1193 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1194 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1198 RedirectChallenge(conn, otherConn);
1199 /* otherConn is still authenticated */
1200 slowCallCode = RXKST_PROCESSCREATED;
1201 #ifdef AFS_PTHREAD_ENV
1203 pthread_attr_t tattr;
1205 code = pthread_attr_init(&tattr);
1207 afs_com_err(whoami, code,
1208 "can't pthread_attr_init slow call process");
1212 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1214 afs_com_err(whoami, code,
1215 "can't pthread_attr_setdetachstate slow call process");
1219 code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1223 LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1224 (opaque) otherConn, "Slow Call Process", &pid);
1227 afs_com_err(whoami, code, "can't create slow call process");
1230 #ifdef AFS_PTHREAD_ENV
1231 pthread_mutex_lock(&slowCallLock);
1232 while (slowCallCode == RXKST_PROCESSCREATED)
1233 pthread_cond_wait(&slowCallCV, &slowCallLock);
1235 while (slowCallCode == RXKST_PROCESSCREATED)
1236 LWP_WaitProcess(&slowCallCode); /* wait for process start */
1238 if (slowCallCode != RXKST_PROCESSRUNNING) {
1239 tmp_rc = slowCallCode;
1240 #ifdef AFS_PTHREAD_ENV
1241 pthread_mutex_unlock(&slowCallLock);
1243 return tmp_rc; /* make sure didn't fail immediately */
1245 assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1246 code = FastCall(conn);
1249 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1250 #ifdef AFS_PTHREAD_ENV
1251 while (slowCallCode == RXKST_PROCESSRUNNING)
1252 pthread_cond_wait(&slowCallCV, &slowCallLock);
1253 pthread_mutex_unlock(&slowCallLock);
1255 while (slowCallCode == RXKST_PROCESSRUNNING)
1256 LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1258 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1261 rx_justReceived = 0;
1263 rx_DestroyConnection(otherConn);
1264 rx_DestroyConnection(conn);
1267 #endif /* rx_GetPacketCksum */
1272 rxkst_StartClient(parms)
1273 IN struct clientParms *parms;
1278 struct rx_securityClass *sc;
1280 whoami = parms->whoami; /* set this global variable */
1282 host = GetServer(parms->server);
1284 if (parms->authentication >= 0) {
1286 char ticket[MAXKTCTICKETLEN];
1288 struct ktc_encryptionKey Ksession;
1290 if (parms->useTokens)
1292 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1295 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1299 /* next, we have ticket, kvno and session key, authenticate the conn */
1300 sc = (struct rx_securityClass *)
1301 rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1302 kvno, ticketLen, ticket);
1304 scIndex = 2; /* kerberos security index */
1306 /* unauthenticated connection */
1307 sc = rxnull_NewClientSecurityObject();
1309 scIndex = 0; /* null security index */
1313 if (!code && parms->callTest) {
1314 code = RunCallTest(parms, host, sc, scIndex);
1316 if (!code && parms->hijackTest) {
1317 code = RunHijackTest(parms, host, sc, scIndex);
1320 && (parms->printTiming || parms->fastCalls || parms->slowCalls
1321 || parms->copiousCalls)) {
1322 struct rx_connection *conn;
1324 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1327 code = RepeatLoadTest(parms, conn);
1328 rx_DestroyConnection(conn);
1330 code = RXKST_NEWCONNFAILED;
1332 if (!code && parms->stopServer) {
1333 struct rx_connection *conn;
1335 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1338 code = RXKST_Kill(conn);
1340 afs_com_err(whoami, code, "trying to stop server");
1342 rx_DestroyConnection(conn);
1344 code = RXKST_NEWCONNFAILED;
1347 if (parms->printStats) {
1348 rx_PrintStats(stdout);
1350 /* use rxdebug style iteration here */
1351 rx_PrintPeerStats(stdout, rx_PeerOf(conn));
1358 afs_com_err(parms->whoami, code, "test fails");
1361 printf("Test Okay\n");