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>
19 #include <sys/types.h>
25 #include <netinet/in.h>
27 #include <afs/com_err.h>
28 #include <afs/afsutil.h>
32 #include "stress_internal.h"
33 #ifdef AFS_PTHREAD_ENV
35 #define FT_ApproxTime() (int)time(0)
46 register struct hostent *th;
49 th = gethostbyname(aname);
51 fprintf(stderr, "host %s not found\n", aname);
54 memcpy(&addr, th->h_addr, sizeof(addr));
59 GetToken(versionP, session, ticketLenP, ticket, cell)
61 OUT struct ktc_encryptionKey *session;
65 struct ktc_principal sname;
66 struct ktc_token ttoken;
69 strcpy(sname.cell, cell);
70 sname.instance[0] = 0;
71 strcpy(sname.name, "afs");
72 code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
76 *versionP = ttoken.kvno;
77 *ticketLenP = ttoken.ticketLen;
78 memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
79 memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
84 GetTicket(versionP, session, ticketLenP, ticket, cell)
86 OUT struct ktc_encryptionKey *session;
92 /* create random session key, using key for seed to good random */
93 des_init_random_number_generator(&serviceKey);
94 code = des_random_key(session);
98 /* now create the actual ticket */
101 tkt_MakeTicket(ticket, ticketLenP, &serviceKey, RXKST_CLIENT_NAME,
102 RXKST_CLIENT_INST, cell,
103 /*start,end */ 0, 0xffffffff, session, /*host */ 0,
104 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
105 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
106 * instance and cell, start time, end time, session key to seal in ticket,
107 * inet host, server name and server instance */
110 *versionP = serviceKeyVersion;
114 struct rx_connection *conn;
115 u_long sendLen; /* parameters for call to Copious */
117 u_long *fastCalls; /* number of calls to perform */
119 u_long *copiousCalls;
123 Copious(c, buf, buflen)
129 struct rx_call *call;
131 long inlen = c->sendLen;
132 long outlen = c->recvLen;
138 for (i = 0; i < inlen; i++)
139 mysum += (d++ & 0xff);
141 call = rx_NewCall(c->conn);
142 code = StartRXKST_Copious(call, inlen, mysum, outlen);
148 while (xfer < inlen) {
152 for (i = 0; i < tlen; i++)
153 buf[i] = (d++ & 0xff);
154 n = rx_Write(call, buf, tlen);
159 code = RXKST_WRITESHORT;
167 while (xfer < outlen) {
168 tlen = outlen - xfer;
171 n = rx_Read(call, buf, tlen);
176 code = RXKST_READSHORT;
179 for (i = 0; i < tlen; i++)
186 code = EndRXKST_Copious(call, &outsum);
187 code = rx_EndCall(call, code);
190 if (outsum != mysum) {
191 return RXKST_BADOUTPUTSUM;
197 DoClient(index, rock)
201 struct client *c = (struct client *)rock;
207 for (i = 0; i < c->fastCalls[index]; i++) {
208 code = RXKST_Fast(c->conn, n, &inc_n);
212 return RXKST_INCFAILED;
216 for (i = 0; i < c->slowCalls[index]; i++) {
219 code = RXKST_Slow(c->conn, 1, &ntime);
222 now = FT_ApproxTime();
223 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
224 return RXKST_TIMESKEW;
227 if (c->copiousCalls[index] > 0) {
228 u_long buflen = 10000;
229 u_char *buf = (u_char *) osi_Alloc(buflen);
230 for (i = 0; i < c->copiousCalls[index]; i++) {
231 code = Copious(c, buf, buflen);
235 osi_Free(buf, buflen);
244 long exitCode; /* is PROCESSRUNNING until exit */
250 #ifdef AFS_PTHREAD_ENV
251 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
252 static pthread_mutex_t workerLock;
253 static pthread_cond_t workerCV;
257 pthread_mutex_init(&workerLock, NULL);
258 pthread_cond_init(&workerCV, NULL);
261 static struct worker *workers;
268 code = (*w->proc) (w->index, w->rock);
269 #ifdef AFS_PTHREAD_ENV
270 pthread_mutex_lock(&workerLock);
273 #ifdef AFS_PTHREAD_ENV
274 pthread_mutex_unlock(&workerLock);
276 #ifdef AFS_PTHREAD_ENV
277 pthread_cond_signal(&workerCV);
279 LWP_NoYieldSignal(&workers);
284 #define MAX_CTHREADS 25
287 CallSimultaneously(threads, rock, proc)
294 #ifdef AFS_PTHREAD_ENV
295 pthread_once(&workerOnce, WorkerInit);
299 for (i = 0; i < threads; i++) {
301 #ifdef AFS_PTHREAD_ENV
306 assert(i < MAX_CTHREADS);
307 w = (struct worker *)osi_Alloc(sizeof(struct worker));
308 memset(w, 0, sizeof(*w));
312 w->exitCode = RXKST_PROCESSRUNNING;
315 #ifdef AFS_PTHREAD_ENV
317 pthread_attr_t tattr;
319 code = pthread_attr_init(&tattr);
321 com_err(whoami, code,
322 "can't pthread_attr_init worker process");
327 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
329 com_err(whoami, code,
330 "can't pthread_attr_setdetachstate worker process");
334 code = pthread_create(&pid, &tattr, DoWorker, (void *)w);
338 LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
339 (opaque) w, "Worker Process", &pid);
342 com_err(whoami, code, "can't create worker process");
346 code = 0; /* last non-zero code encountered */
347 #ifdef AFS_PTHREAD_ENV
348 pthread_mutex_lock(&workerLock);
351 struct worker *w, *prevW, *nextW;
353 for (w = workers; w; w = nextW) {
355 if (w->exitCode != RXKST_PROCESSRUNNING) {
361 prevW->next = w->next;
364 osi_Free(w, sizeof(*w));
365 continue; /* don't bump prevW */
369 #ifdef AFS_PTHREAD_ENV
371 pthread_cond_wait(&workerCV, &workerLock);
374 LWP_WaitProcess(&workers);
377 #ifdef AFS_PTHREAD_ENV
378 pthread_mutex_unlock(&workerLock);
384 DivideUpCalls(calls, threads, threadCalls)
387 IN u_long threadCalls[];
390 for (i = 0; i < threads; i++) {
391 threadCalls[i] = calls / (threads - i);
392 calls -= threadCalls[i];
400 gettimeofday(&tv, 0);
401 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
405 RunLoadTest(parms, conn)
406 IN struct clientParms *parms;
407 IN struct rx_connection *conn;
411 u_long fastCalls[MAX_CTHREADS];
412 u_long slowCalls[MAX_CTHREADS];
413 u_long copiousCalls[MAX_CTHREADS];
414 double start, interval;
416 DivideUpCalls(parms->fastCalls, parms->threads, fastCalls);
417 DivideUpCalls(parms->slowCalls, parms->threads, slowCalls);
418 DivideUpCalls(parms->copiousCalls, parms->threads, copiousCalls);
420 memset(&c, 0, sizeof(c));
422 c.sendLen = parms->sendLen;
423 c.recvLen = parms->recvLen;
424 c.fastCalls = fastCalls;
425 c.slowCalls = slowCalls;
426 c.copiousCalls = copiousCalls;
429 code = CallSimultaneously(parms->threads, &c, DoClient);
431 com_err(whoami, code, "in DoClient");
434 interval = ftime() - start;
436 if (parms->printTiming) {
438 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
439 int t = (interval / totalCalls) * 1000.0 + 0.5;
440 if (totalCalls > 0) {
441 printf("For %d calls: %d msec/call\n", totalCalls, t);
443 if (parms->copiousCalls > 0) {
444 long n = parms->sendLen + parms->recvLen;
447 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
450 I just cannot get printing of floats to work on the pmax !
451 !!!printf("%g %d %d %d\n", (float)kbps, b);
452 printf("%g %d %d %d\n", kbps, b);
453 fprintf(stdout, "%g %d %d\n", kbps, b);
456 buf[sizeof(buf) - 1] = 0;
457 sprintf(buf, "%g %d %d\n", kbps, b);
458 assert(buf[sizeof(buf) - 1] == 0);
463 ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
464 parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
466 printf("%g\n", kbps);
474 RepeatLoadTest(parms, conn)
475 IN struct clientParms *parms;
476 IN struct rx_connection *conn;
481 if (parms->repeatInterval == 0) {
482 if (parms->repeatCount == 0)
483 parms->repeatCount = 1;
485 if (parms->repeatCount == 0)
486 parms->repeatCount = 0x7fffffff;
489 if (parms->printTiming) {
492 (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
493 (parms->copiousCalls ? 1 : 0);
496 "Combined timings of several types of calls may not be meaningful.\n");
498 /* do timings of copious calls by default */
499 parms->copiousCalls = 10;
502 for (count = 0; count < parms->repeatCount; count++) {
503 code = RunLoadTest(parms, conn);
506 if (parms->repeatInterval) {
507 u_long i = parms->repeatInterval;
508 u_long now = time(0);
509 u_long next = (now + i - 1) / i * i; /* round up to next interval */
511 #ifdef AFS_PTHREAD_ENV
514 IOMGR_Sleep(next - now);
523 /* For backward compatibility, don't try to use the CallNumber stuff unless
524 * we're compiling against the new Rx. */
526 #ifdef rx_GetPacketCksum
528 struct multiChannel {
529 struct rx_connection *conn;
532 int changes[RX_MAXCALLS];
533 long callNumbers[RX_MAXCALLS];
535 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
536 static u_long sequence = 0;
540 IN struct rx_connection *conn;
543 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
546 code = RXKST_Fast(conn, n, &inc_n);
550 return RXKST_INCFAILED;
555 UniChannelCall(index, rock)
559 struct multiChannel *mc = (struct multiChannel *)rock;
561 long callNumbers[RX_MAXCALLS];
566 while (!mc->done && unchanged) {
568 code = FastCall(mc->conn);
571 code = rxi_GetCallNumberVector(mc->conn, callNumbers);
575 for (i = 0; i < RX_MAXCALLS; i++) {
576 if (callNumbers[i] > mc->callNumbers[i]) {
577 mc->callNumbers[i] = callNumbers[i];
578 mc->changes[i]--; /* may go negative */
580 if (mc->changes[i] > 0)
584 mc->codes[index] = code;
590 MakeMultiChannelCall(conn, each, expectedCode, codes)
591 IN struct rx_connection *conn;
592 IN int each; /* calls to make on each channel */
593 IN long expectedCode;
598 struct multiChannel mc;
600 memset(&mc, 0, sizeof(mc));
602 for (i = 0; i < RX_MAXCALLS; i++) {
603 codes[i] = RXKST_PROCESSRUNNING;
604 mc.changes[i] = each;
607 code = rxi_GetCallNumberVector(conn, mc.callNumbers);
611 code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
612 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3))); /* strange cases */
613 else if (code != expectedCode) {
614 com_err(whoami, code,
615 "problem making multichannel call, expected '%s'",
617 ? "no error" : (char *)error_message(expectedCode)));
623 CheckCallFailure(conn, codes, code, msg)
624 IN struct rx_connection *conn;
630 fprintf(stderr, "Failed to detect %s\n", msg);
631 return RXKST_NODUPLICATECALL;
636 for (i = 0; i < RX_MAXCALLS; i++)
637 if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
642 fprintf(stderr, "%s produced these errors:\n", msg);
643 for (i = 0; i < RX_MAXCALLS; i++) {
644 assert(codes[i] != RXKST_PROCESSRUNNING);
647 fprintf(stderr, " %d no error\n", i);
649 fprintf(stderr, " %d %s\n", i, error_message(codes[i]));
653 sprintf(buf, "connection dead following %s", msg);
654 code = FastCall(conn);
656 com_err(whoami, code, buf);
663 #endif /* rx_GetPacketCksum */
666 RunCallTest(parms, host, sc, si)
667 IN struct clientParms *parms;
669 IN struct rx_securityClass *sc;
674 #ifndef rx_GetPacketCksum
676 code = RXKST_BADARGS;
677 com_err(whoami, code,
678 "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
684 struct rx_connection *conn;
686 long callNumbers[RX_MAXCALLS];
687 long codes[RX_MAXCALLS];
688 long retCode = 0; /* ret. if nothing fatal goes wrong */
691 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
694 return RXKST_NEWCONNFAILED;
696 /* First check the basic behaviour of call number handling */
698 code = rxi_GetCallNumberVector(conn, callNumbers);
701 for (i = 0; i < RX_MAXCALLS; i++) {
702 if (callNumbers[i] != 0) {
704 "Connection's initial call numbers not zero. call[%d] = %d\n",
706 return RXKST_BADCALLNUMBERS;
709 code = FastCall(conn);
712 code = rxi_GetCallNumberVector(conn, callNumbers);
715 firstCall = callNumbers[0];
716 code = FastCall(conn);
719 code = rxi_GetCallNumberVector(conn, callNumbers);
722 if ((callNumbers[0] != firstCall + 1)
723 && ((firstCall == 1) || (firstCall == 2))) {
724 /* The call number after the first call should be one or, more likely,
725 * two (if the call is still DALLYing). Between first and second call,
726 * the call number should have incremented by one. */
728 "Connection's first channel call number not one. call[%d] = %d\n",
730 return RXKST_BADCALLNUMBERS;
732 for (i = 1; i < RX_MAXCALLS; i++) {
733 if (callNumbers[i] != 0) {
735 "Connection's other channel call numbers not zero. call[%d] = %d\n",
737 return RXKST_BADCALLNUMBERS;
740 code = MakeMultiChannelCall(conn, 1, 0, codes);
744 /* Now try to resend a call that's already been executed by finding a
745 * non-zero call number on a channel other than zero and decrementing it by
746 * one. This should appear to the server as a retransmitted call. Since
747 * this is behaving as a broken client different strange behaviors may be
748 * exhibited by different servers. If the response packet to the original
749 * call is discarded by the time the "retransmitted" call arrives (perhaps
750 * due to high server or client load) there is no way for the server to
751 * respond at all. Further, it seems, that under some cases the connection
752 * will be kept alive indefinitely even though the server has discarded the
753 * "retransmitted" call and is making no effort to reexecute the call. To
754 * handle these, accept either a timeout (-1) or and INCFAILED error here,
755 * also set the connenction HardDeadTime to punt after a reasonable
758 /* short dead time since may we expect some trouble */
759 rx_SetConnHardDeadTime(conn, 30);
760 code = rxi_GetCallNumberVector(conn, callNumbers);
763 for (ch = 1; ch < RX_MAXCALLS; ch++)
764 if (callNumbers[ch] > 1) {
766 code = rxi_SetCallNumberVector(conn, callNumbers);
771 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
772 return RXKST_BADCALLNUMBERS;
773 code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
774 code = CheckCallFailure(conn, codes, code, "retransmitted call");
775 if (code && !retCode)
778 /* Get a fresh connection, becasue if the above failed as it should the
779 * connection is dead. */
780 rx_DestroyConnection(conn);
782 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
785 return RXKST_NEWCONNFAILED;
787 /* Similarly, but decrement call number by two which should be completely
788 * unmistakeable as a broken or malicious client. */
790 /* short dead time since may we expect some trouble */
791 rx_SetConnHardDeadTime(conn, 30);
792 code = MakeMultiChannelCall(conn, 2, 0, codes);
795 code = rxi_GetCallNumberVector(conn, callNumbers);
798 for (ch = 1; ch < RX_MAXCALLS; ch++)
799 if (callNumbers[ch] > 2) {
800 callNumbers[ch] -= 2;
801 code = rxi_SetCallNumberVector(conn, callNumbers);
804 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
805 return RXKST_BADCALLNUMBERS;
806 code = MakeMultiChannelCall(conn, 1, -1, codes);
807 code = CheckCallFailure(conn, codes, code, "duplicate call");
808 if (code && !retCode)
811 rx_DestroyConnection(conn);
813 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
816 return RXKST_NEWCONNFAILED;
818 /* Next, without waiting for the server to discard its state, we will check
819 * to see if the Challenge/Response protocol correctly informs the server
820 * of the client's callNumber state. We do this by artificially increasing
821 * the call numbers of a new connection for all channels beyond zero,
822 * making a call on channel zero, then resetting the call number for the
823 * unused channels back to zero, then making calls on all channels. */
825 code = rxi_GetCallNumberVector(conn, callNumbers);
828 for (i = 0; i < RX_MAXCALLS; i++) {
829 if (callNumbers[i] != 0)
830 return RXKST_BADCALLNUMBERS;
831 callNumbers[i] = 51; /* an arbitrary value... */
833 code = rxi_SetCallNumberVector(conn, callNumbers);
836 code = FastCall(conn); /* use channel 0 */
839 code = rxi_GetCallNumberVector(conn, callNumbers);
842 if (callNumbers[0] != 52)
843 return RXKST_BADCALLNUMBERS;
844 for (i = 1; i < RX_MAXCALLS; i++) {
845 if (callNumbers[i] != 51)
846 return RXKST_BADCALLNUMBERS;
847 callNumbers[i] = 37; /* back up a ways */
849 code = rxi_SetCallNumberVector(conn, callNumbers);
852 /* now try calls on all channels... */
853 code = MakeMultiChannelCall(conn, 1, -1, codes);
855 CheckCallFailure(conn, codes, code, "alternate channel call replay");
856 if (code && !retCode)
859 rx_DestroyConnection(conn);
862 #endif /* rx_GetPacketCksum */
866 #ifdef rx_GetPacketCksum
870 u_long epoch; /* connection to attack */
872 int client; /* TRUE => client side */
873 u_long newEpoch; /* conn to direct challenges to */
875 u_long counts[RX_N_PACKET_TYPES];
879 #define IO_REDIRECTCHALLENGE 2
882 HandleIncoming(p, addr)
883 INOUT struct rx_packet *p;
884 INOUT struct sockaddr_in *addr;
886 int client; /* packet sent by client */
887 u_char type; /* packet type */
889 if (incomingOps.op == IO_NOOP)
892 client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
893 if ((p->header.epoch != incomingOps.epoch)
894 || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
895 || (client != incomingOps.client))
897 type = p->header.type;
898 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
900 incomingOps.counts[type]++;
902 switch (incomingOps.op) {
907 case IO_REDIRECTCHALLENGE:
908 if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
910 p->header.epoch = incomingOps.newEpoch;
911 p->header.cid = incomingOps.newCid;
912 /* Now set up to watch for the corresponding challenge. */
913 incomingOps.epoch = incomingOps.newEpoch;
914 incomingOps.cid = incomingOps.newCid;
915 incomingOps.op = IO_COUNT;
919 fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
927 u_long epoch; /* connection to attack */
929 int client; /* TRUE => client side */
930 u_long counts[RX_N_PACKET_TYPES];
934 #define OO_ZEROCKSUM 2
935 #define OO_MUNGCKSUM 3
938 HandleOutgoing(p, addr)
939 INOUT struct rx_packet *p;
940 INOUT struct sockaddr_in *addr;
942 int client; /* packet sent by client */
943 u_char type; /* packet type */
945 if (outgoingOps.op == OO_NOOP)
948 client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
949 if ((p->header.epoch != outgoingOps.epoch)
950 || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
951 || (client != outgoingOps.client))
953 type = p->header.type;
954 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
956 outgoingOps.counts[type]++;
958 switch (outgoingOps.op) {
961 /* counting always happens above if not noop */
965 if (p->header.type != RX_PACKET_TYPE_DATA)
967 if (rx_GetPacketCksum(p) == 0) {
968 /* probably, a retransmitted packet */
969 fprintf(stderr, "Packet cksum already zero\n");
972 rx_SetPacketCksum(p, 0);
977 if (p->header.type != RX_PACKET_TYPE_DATA)
979 cksum = rx_GetPacketCksum(p);
981 fprintf(stderr, "Packet cksum already zero\n");
984 rx_SetPacketCksum(p, cksum ^ 8);
988 fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
994 #ifdef AFS_PTHREAD_ENV
995 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
996 static pthread_mutex_t slowCallLock;
997 static pthread_cond_t slowCallCV;
1001 pthread_mutex_init(&slowCallLock, NULL);
1002 pthread_cond_init(&slowCallCV, NULL);
1005 static long slowCallCode;
1014 #ifdef AFS_PTHREAD_ENV
1015 pthread_mutex_lock(&slowCallLock);
1017 slowCallCode = RXKST_PROCESSRUNNING;
1018 #ifdef AFS_PTHREAD_ENV
1019 pthread_cond_signal(&slowCallCV);
1021 LWP_NoYieldSignal(&slowCallCode);
1023 slowCallCode = RXKST_Slow(conn, 1, &ntime);
1024 if (!slowCallCode) {
1025 now = FT_ApproxTime();
1026 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
1027 slowCallCode = RXKST_TIMESKEW;
1029 temp_rc = slowCallCode;
1030 #ifdef AFS_PTHREAD_ENV
1031 pthread_cond_signal(&slowCallCV);
1032 pthread_mutex_unlock(&slowCallLock);
1034 LWP_NoYieldSignal(&slowCallCode);
1039 #endif /* rx_GetPacketCksum */
1042 RunHijackTest(parms, host, sc, si)
1043 IN struct clientParms *parms;
1045 IN struct rx_securityClass *sc;
1049 #ifndef rx_GetPacketCksum
1051 code = RXKST_BADARGS;
1052 com_err(whoami, code,
1053 "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
1058 extern int (*rx_justReceived) ();
1059 extern int (*rx_almostSent) ();
1062 struct rx_connection *conn = 0;
1063 struct rx_connection *otherConn = 0;
1064 #ifdef AFS_PTHREAD_ENV
1069 int nResp; /* otherConn responses seen */
1072 #ifdef AFS_PTHREAD_ENV
1073 pthread_once(&slowCallOnce, SlowCallInit);
1075 rx_justReceived = HandleIncoming;
1076 rx_almostSent = HandleOutgoing;
1078 incomingOps.op = IO_NOOP;
1079 outgoingOps.op = OO_NOOP;
1081 #define HIJACK_CONN(conn) \
1082 { if (conn) rx_DestroyConnection (conn); \
1083 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1084 RXKST_SERVICEID, sc, si); \
1085 if (!(conn)) return RXKST_NEWCONNFAILED; \
1086 outgoingOps.client = 1; \
1087 outgoingOps.epoch = (conn)->epoch; \
1088 outgoingOps.cid = (conn)->cid; }
1092 /* First try switching from no packet cksum to sending packet cksum between
1093 * calls, and see if server complains. */
1095 outgoingOps.op = OO_ZEROCKSUM;
1096 code = FastCall(conn);
1098 com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1101 /* The server thinks we're an old style client. Now start sending cksums.
1102 * Server shouldn't care. */
1103 outgoingOps.op = OO_NOOP;
1104 code = FastCall(conn);
1106 com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1109 /* The server now thinks we're a new style client, we can't go back now. */
1110 outgoingOps.op = OO_ZEROCKSUM;
1111 code = FastCall(conn);
1113 code = RXKST_NOBADCKSUM;
1114 if (code != RXKADSEALEDINCON) {
1115 com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1117 } else if (!conn->error) {
1118 code = RXKST_NOCONNERROR;
1119 com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1126 /* Now try modifying packet cksum to see if server complains. */
1128 outgoingOps.op = OO_MUNGCKSUM;
1129 code = FastCall(conn);
1131 code = RXKST_NOBADCKSUM;
1132 if (code != RXKADSEALEDINCON) {
1133 com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1135 } else if (!conn->error) {
1136 code = RXKST_NOCONNERROR;
1137 com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1142 /* Now make two connection and direct the first challenge on one connection
1143 * to the other connection to see if it generates a response. The
1144 * retransmitted challenge should allow the call on the first connection to
1145 * complete correctly. Part one is to attack a new connection, then attack
1146 * it after it has made a call. Part three, just for comparison, attacks a
1147 * otherConn while it is making a slow call (and thus has an active call).
1148 * Against this attack we have no defense so we expect a challenge in this
1149 * case, which the server will discard. */
1151 #define RedirectChallenge(conn,otherConn) \
1152 (incomingOps.epoch = (conn)->epoch, \
1153 incomingOps.cid = (conn)->cid, \
1154 incomingOps.client = 1, \
1155 incomingOps.newEpoch = (otherConn)->epoch, \
1156 incomingOps.newCid = (otherConn)->cid, \
1157 incomingOps.op = IO_REDIRECTCHALLENGE, \
1158 outgoingOps.epoch = (otherConn)->epoch, \
1159 outgoingOps.cid = (otherConn)->cid, \
1160 outgoingOps.client = 1, \
1161 outgoingOps.op = OO_COUNT, \
1162 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1165 HIJACK_CONN(otherConn)
1166 RedirectChallenge(conn, otherConn);
1168 code = FastCall(conn);
1171 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1172 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1174 code = RXKST_CHALLENGEORACLE;
1175 com_err(whoami, code, "misdirecting challenge");
1178 code = FastCall(otherConn); /* generate some activity here */
1181 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1183 code = FastCall(conn);
1186 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1190 RedirectChallenge(conn, otherConn);
1191 /* otherConn was authenticated during part one */
1192 code = FastCall(conn);
1195 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1196 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1200 RedirectChallenge(conn, otherConn);
1201 /* otherConn is still authenticated */
1202 slowCallCode = RXKST_PROCESSCREATED;
1203 #ifdef AFS_PTHREAD_ENV
1205 pthread_attr_t tattr;
1207 code = pthread_attr_init(&tattr);
1209 com_err(whoami, code,
1210 "can't pthread_attr_init slow call process");
1214 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1216 com_err(whoami, code,
1217 "can't pthread_attr_setdetachstate slow call process");
1221 code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1225 LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1226 (opaque) otherConn, "Slow Call Process", &pid);
1229 com_err(whoami, code, "can't create slow call process");
1232 #ifdef AFS_PTHREAD_ENV
1233 pthread_mutex_lock(&slowCallLock);
1234 while (slowCallCode == RXKST_PROCESSCREATED)
1235 pthread_cond_wait(&slowCallCV, &slowCallLock);
1237 while (slowCallCode == RXKST_PROCESSCREATED)
1238 LWP_WaitProcess(&slowCallCode); /* wait for process start */
1240 if (slowCallCode != RXKST_PROCESSRUNNING) {
1241 tmp_rc = slowCallCode;
1242 #ifdef AFS_PTHREAD_ENV
1243 pthread_mutex_unlock(&slowCallLock);
1245 return tmp_rc; /* make sure didn't fail immediately */
1247 assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1248 code = FastCall(conn);
1251 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1252 #ifdef AFS_PTHREAD_ENV
1253 while (slowCallCode == RXKST_PROCESSRUNNING)
1254 pthread_cond_wait(&slowCallCV, &slowCallLock);
1255 pthread_mutex_unlock(&slowCallLock);
1257 while (slowCallCode == RXKST_PROCESSRUNNING)
1258 LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1260 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1263 rx_justReceived = 0;
1265 rx_DestroyConnection(otherConn);
1266 rx_DestroyConnection(conn);
1269 #endif /* rx_GetPacketCksum */
1274 rxkst_StartClient(parms)
1275 IN struct clientParms *parms;
1280 struct rx_securityClass *sc;
1282 whoami = parms->whoami; /* set this global variable */
1284 host = GetServer(parms->server);
1286 if (parms->authentication >= 0) {
1288 char ticket[MAXKTCTICKETLEN];
1290 struct ktc_encryptionKey Ksession;
1292 if (parms->useTokens)
1294 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1297 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1301 /* next, we have ticket, kvno and session key, authenticate the conn */
1302 sc = (struct rx_securityClass *)
1303 rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1304 kvno, ticketLen, ticket);
1306 scIndex = 2; /* kerberos security index */
1308 /* unauthenticated connection */
1309 sc = rxnull_NewClientSecurityObject();
1311 scIndex = 0; /* null security index */
1315 if (!code && parms->callTest) {
1316 code = RunCallTest(parms, host, sc, scIndex);
1318 if (!code && parms->hijackTest) {
1319 code = RunHijackTest(parms, host, sc, scIndex);
1322 && (parms->printTiming || parms->fastCalls || parms->slowCalls
1323 || parms->copiousCalls)) {
1324 struct rx_connection *conn;
1326 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1329 code = RepeatLoadTest(parms, conn);
1330 rx_DestroyConnection(conn);
1332 code = RXKST_NEWCONNFAILED;
1334 if (!code && parms->stopServer) {
1335 struct rx_connection *conn;
1337 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1340 code = RXKST_Kill(conn);
1342 com_err(whoami, code, "trying to stop server");
1344 rx_DestroyConnection(conn);
1346 code = RXKST_NEWCONNFAILED;
1349 if (parms->printStats) {
1350 rx_PrintStats(stdout);
1352 /* use rxdebug style iteration here */
1353 rx_PrintPeerStats(stdout, rx_PeerOf(conn));
1360 com_err(parms->whoami, code, "test fails");
1363 printf("Test Okay\n");