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>
18 #include <sys/types.h>
24 #include <netinet/in.h>
26 #include <afs/com_err.h>
27 #include <afs/afsutil.h>
30 #include "stress_internal.h"
31 #ifdef AFS_PTHREAD_ENV
33 #define FT_ApproxTime() (int)time(0)
40 static long GetServer(aname)
43 register struct hostent *th;
46 th = gethostbyname(aname);
48 fprintf (stderr, "host %s not found\n", aname);
51 bcopy(th->h_addr, &addr, sizeof(addr));
55 static long GetTicket (versionP, session, ticketLenP, ticket)
57 OUT struct ktc_encryptionKey *session;
63 /* create random session key, using key for seed to good random */
64 des_init_random_number_generator (&serviceKey);
65 code = des_random_key (session);
66 if (code) return code;
68 /* now create the actual ticket */
70 code = tkt_MakeTicket(ticket, ticketLenP, &serviceKey,
71 RXKST_CLIENT_NAME, RXKST_CLIENT_INST, "",
72 /*start,end*/0, 0xffffffff, session, /*host*/0,
73 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
74 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
75 * instance and cell, start time, end time, session key to seal in ticket,
76 * inet host, server name and server instance */
77 if (code) return code;
78 *versionP = serviceKeyVersion;
82 struct rx_connection *conn;
83 u_long sendLen; /* parameters for call to Copious */
85 u_long *fastCalls; /* number of calls to perform */
90 static long Copious (c, buf, buflen)
98 long inlen = c->sendLen;
99 long outlen = c->recvLen;
105 for (i=0; i<inlen; i++) mysum += (d++ & 0xff);
107 call = rx_NewCall (c->conn);
108 code = StartRXKST_Copious (call, inlen, mysum, outlen);
114 while (xfer < inlen) {
116 if (tlen > buflen) tlen = buflen;
117 for (i=0; i<tlen; i++) buf[i] = (d++ & 0xff);
118 n = rx_Write (call, buf, tlen);
121 else code = RXKST_WRITESHORT;
129 while (xfer < outlen) {
130 tlen = outlen - xfer;
131 if (tlen > buflen) tlen = buflen;
132 n = rx_Read (call, buf, tlen);
135 else code = RXKST_READSHORT;
138 for (i=0; i<tlen; i++) mysum += buf[i];
144 code = EndRXKST_Copious (call, &outsum);
145 code = rx_EndCall(call, code);
146 if (code) return code;
147 if (outsum != mysum) {
148 return RXKST_BADOUTPUTSUM;
153 static long DoClient (index, rock)
157 struct client *c = (struct client *)rock;
163 for (i=0; i<c->fastCalls[index]; i++) {
164 code = RXKST_Fast (c->conn, n, &inc_n);
165 if (code) return (code);
166 if (n+1 != inc_n) return RXKST_INCFAILED;
170 for (i=0; i<c->slowCalls[index]; i++) {
173 code = RXKST_Slow (c->conn, 1, &ntime);
174 if (code) return (code);
175 now = FT_ApproxTime();
176 if ((ntime < now-maxSkew) || (ntime > now+maxSkew)) return RXKST_TIMESKEW;
179 if (c->copiousCalls[index] > 0) {
180 u_long buflen = 10000;
181 u_char *buf = (u_char *) osi_Alloc (buflen);
182 for (i=0; i<c->copiousCalls[index]; i++) {
183 code = Copious (c, buf, buflen);
186 osi_Free (buf, buflen);
187 if (code) return code;
194 long exitCode; /* is PROCESSRUNNING until exit */
200 #ifdef AFS_PTHREAD_ENV
201 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
202 static pthread_mutex_t workerLock;
203 static pthread_cond_t workerCV;
204 void WorkerInit(void)
206 pthread_mutex_init(&workerLock, NULL);
207 pthread_cond_init(&workerCV, NULL);
210 static struct worker *workers;
212 static long DoWorker (w)
216 code = (*w->proc) (w->index, w->rock);
217 #ifdef AFS_PTHREAD_ENV
218 pthread_mutex_lock(&workerLock);
221 #ifdef AFS_PTHREAD_ENV
222 pthread_mutex_unlock(&workerLock);
224 #ifdef AFS_PTHREAD_ENV
225 pthread_cond_signal(&workerCV);
227 LWP_NoYieldSignal(&workers);
232 #define MAX_CTHREADS 25
234 static long CallSimultaneously (threads, rock, proc)
241 #ifdef AFS_PTHREAD_ENV
242 pthread_once(&workerOnce, WorkerInit);
246 for (i=0; i<threads; i++) {
248 #ifdef AFS_PTHREAD_ENV
253 assert (i < MAX_CTHREADS);
254 w = (struct worker *) osi_Alloc (sizeof(struct worker));
255 bzero (w, sizeof(*w));
259 w->exitCode = RXKST_PROCESSRUNNING;
262 #ifdef AFS_PTHREAD_ENV
264 pthread_attr_t tattr;
266 code = pthread_attr_init(&tattr);
268 com_err (whoami, code, "can't pthread_attr_init worker process");
272 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
274 com_err (whoami, code, "can't pthread_attr_setdetachstate worker process");
278 code = pthread_create(&pid, &tattr, DoWorker, (void*)w);
281 code = LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
282 (opaque) w, "Worker Process", &pid);
285 com_err (whoami, code, "can't create worker process");
289 code = 0; /* last non-zero code encountered */
290 #ifdef AFS_PTHREAD_ENV
291 pthread_mutex_lock(&workerLock);
294 struct worker *w, *prevW, *nextW;
296 for (w=workers; w; w=nextW) {
298 if (w->exitCode != RXKST_PROCESSRUNNING) {
300 if (code == 0) code = w->exitCode;
302 if (prevW) prevW->next = w->next;
303 else workers = w->next;
304 osi_Free (w, sizeof(*w));
305 continue; /* don't bump prevW */
309 #ifdef AFS_PTHREAD_ENV
311 pthread_cond_wait(&workerCV, &workerLock);
313 if (workers) LWP_WaitProcess (&workers);
316 #ifdef AFS_PTHREAD_ENV
317 pthread_mutex_unlock(&workerLock);
322 static void DivideUpCalls (calls, threads, threadCalls)
325 IN u_long threadCalls[];
328 for (i=0; i<threads; i++) {
329 threadCalls[i] = calls / (threads-i);
330 calls -= threadCalls[i];
334 static double ftime ()
337 gettimeofday(&tv, 0);
338 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
341 static long RunLoadTest (parms, conn)
342 IN struct clientParms *parms;
343 IN struct rx_connection *conn;
347 u_long fastCalls[MAX_CTHREADS];
348 u_long slowCalls[MAX_CTHREADS];
349 u_long copiousCalls[MAX_CTHREADS];
350 double start, interval;
352 DivideUpCalls (parms->fastCalls, parms->threads, fastCalls);
353 DivideUpCalls (parms->slowCalls, parms->threads, slowCalls);
354 DivideUpCalls (parms->copiousCalls, parms->threads, copiousCalls);
356 bzero (&c, sizeof(c));
358 c.sendLen = parms->sendLen;
359 c.recvLen = parms->recvLen;
360 c.fastCalls = fastCalls;
361 c.slowCalls = slowCalls;
362 c.copiousCalls = copiousCalls;
365 code = CallSimultaneously (parms->threads, &c, DoClient);
367 com_err (whoami, code, "in DoClient");
370 interval = ftime() - start;
372 if (parms->printTiming) {
374 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
375 int t = (interval / totalCalls) * 1000.0 + 0.5;
376 if (totalCalls > 0) {
377 printf ("For %d calls: %d msec/call\n", totalCalls, t);
379 if (parms->copiousCalls > 0) {
380 long n = parms->sendLen + parms->recvLen;
383 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
386 I just cannot get printing of floats to work on the pmax!!!!
387 printf ("%g %d %d %d\n", (float)kbps, b);
388 printf ("%g %d %d %d\n", kbps, b);
389 fprintf (stdout, "%g %d %d\n", kbps, b);
392 buf[sizeof(buf)-1] = 0;
393 sprintf (buf, "%g %d %d\n", kbps, b);
394 assert (buf[sizeof(buf)-1] == 0);
398 printf ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
399 parms->copiousCalls, parms->sendLen, parms->recvLen,
402 printf ("%g\n", kbps);
409 static long RepeatLoadTest (parms, conn)
410 IN struct clientParms *parms;
411 IN struct rx_connection *conn;
416 if (parms->repeatInterval == 0) {
417 if (parms->repeatCount == 0) parms->repeatCount = 1;
419 if (parms->repeatCount == 0) parms->repeatCount = 0x7fffffff;
422 if (parms->printTiming) {
424 types = (parms->fastCalls ? 1 : 0) +
425 (parms->slowCalls ? 1 : 0) + (parms->copiousCalls ? 1 : 0);
427 fprintf (stderr, "Combined timings of several types of calls may not be meaningful.\n");
429 /* do timings of copious calls by default */
430 parms->copiousCalls = 10;
433 for (count=0; count<parms->repeatCount; count++) {
434 code = RunLoadTest (parms, conn);
435 if (code) return code;
436 if (parms->repeatInterval) {
437 u_long i = parms->repeatInterval;
438 u_long now = time(0);
439 u_long next = (now + i-1) / i * i; /* round up to next interval */
441 #ifdef AFS_PTHREAD_ENV
444 IOMGR_Sleep (next-now);
453 /* For backward compatibility, don't try to use the CallNumber stuff unless
454 * we're compiling against the new Rx. */
456 #ifdef rx_GetPacketCksum
458 struct multiChannel {
459 struct rx_connection *conn;
462 int changes[RX_MAXCALLS];
463 long callNumbers[RX_MAXCALLS];
465 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
466 static u_long sequence = 0;
468 static long FastCall (conn)
469 IN struct rx_connection *conn;
472 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
475 code = RXKST_Fast (conn, n, &inc_n);
476 if (code) return code;
477 if (inc_n != n+1) return RXKST_INCFAILED;
481 static long UniChannelCall (index, rock)
485 struct multiChannel *mc = (struct multiChannel *)rock;
487 long callNumbers[RX_MAXCALLS];
492 while (!mc->done && unchanged) {
494 code = FastCall (mc->conn);
496 code = rxi_GetCallNumberVector (mc->conn, callNumbers);
499 for (i=0; i<RX_MAXCALLS; i++) {
500 if (callNumbers[i] > mc->callNumbers[i]) {
501 mc->callNumbers[i] = callNumbers[i];
502 mc->changes[i]--; /* may go negative */
504 if (mc->changes[i] > 0) unchanged++;
507 mc->codes[index] = code;
512 static long MakeMultiChannelCall (conn, each, expectedCode, codes)
513 IN struct rx_connection *conn;
514 IN int each; /* calls to make on each channel */
515 IN long expectedCode;
520 struct multiChannel mc;
522 bzero (&mc, sizeof(mc));
524 for (i=0; i<RX_MAXCALLS; i++) {
525 codes[i] = RXKST_PROCESSRUNNING;
526 mc.changes[i] = each;
529 code = rxi_GetCallNumberVector (conn, mc.callNumbers);
530 if (code) return code;
532 code = CallSimultaneously (RX_MAXCALLS, &mc, UniChannelCall);
533 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) &&
534 ((code == expectedCode) || (code == -3))) ; /* strange cases */
535 else if (code != expectedCode) {
536 com_err (whoami, code,
537 "problem making multichannel call, expected '%s'",
539 ? "no error" : (char *)error_message (expectedCode)));
544 static long CheckCallFailure (conn, codes, code, msg)
545 IN struct rx_connection *conn;
551 fprintf (stderr, "Failed to detect %s\n", msg);
552 return RXKST_NODUPLICATECALL;
557 for (i=0; i<RX_MAXCALLS; i++)
558 if (!((codes[i] == 0) || (codes[i] == code) ||
559 (codes[i] == -3))) okay = 0;
560 if (conn->error) okay = 0;
562 fprintf (stderr, "%s produced these errors:\n", msg);
563 for (i=0; i<RX_MAXCALLS; i++) {
564 assert (codes[i] != RXKST_PROCESSRUNNING);
567 fprintf (stderr, " %d no error\n", i);
568 } else fprintf (stderr,
569 " %d %s\n", i, error_message(codes[i]));
573 sprintf (buf, "connection dead following %s", msg);
574 code = FastCall (conn);
575 if (code) com_err (whoami, code, buf);
582 #endif rx_GetPacketCksum
584 static long RunCallTest (parms, host, sc, si)
585 IN struct clientParms *parms;
587 IN struct rx_securityClass *sc;
592 #ifndef rx_GetPacketCksum
594 code = RXKST_BADARGS;
595 com_err (whoami, code, "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
601 struct rx_connection *conn;
603 long callNumbers[RX_MAXCALLS];
604 long codes[RX_MAXCALLS];
605 long retCode = 0; /* ret. if nothing fatal goes wrong */
607 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
609 if (!conn) return RXKST_NEWCONNFAILED;
611 /* First check the basic behaviour of call number handling */
613 code = rxi_GetCallNumberVector (conn, callNumbers);
614 if (code) return code;
615 for (i=0; i<RX_MAXCALLS; i++) {
616 if (callNumbers[i] != 0) {
617 fprintf (stderr, "Connection's initial call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
618 return RXKST_BADCALLNUMBERS;
621 code = FastCall (conn);
622 if (code) return code;
623 code = rxi_GetCallNumberVector (conn, callNumbers);
624 if (code) return code;
625 firstCall = callNumbers[0];
626 code = FastCall (conn);
627 if (code) return code;
628 code = rxi_GetCallNumberVector (conn, callNumbers);
629 if (code) return code;
630 if ((callNumbers[0] != firstCall+1) &&
631 ((firstCall == 1) || (firstCall == 2))) {
632 /* The call number after the first call should be one or, more likely,
633 * two (if the call is still DALLYing). Between first and second call,
634 * the call number should have incremented by one. */
635 fprintf (stderr, "Connection's first channel call number not one. call[%d] = %d\n", 0, callNumbers[0]);
636 return RXKST_BADCALLNUMBERS;
638 for (i=1; i<RX_MAXCALLS; i++) {
639 if (callNumbers[i] != 0) {
640 fprintf (stderr, "Connection's other channel call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
641 return RXKST_BADCALLNUMBERS;
644 code = MakeMultiChannelCall (conn, 1, 0, codes);
645 if (code) return code;
647 /* Now try to resend a call that's already been executed by finding a
648 * non-zero call number on a channel other than zero and decrementing it by
649 * one. This should appear to the server as a retransmitted call. Since
650 * this is behaving as a broken client different strange behaviors may be
651 * exhibited by different servers. If the response packet to the original
652 * call is discarded by the time the "retransmitted" call arrives (perhaps
653 * due to high server or client load) there is no way for the server to
654 * respond at all. Further, it seems, that under some cases the connection
655 * will be kept alive indefinitely even though the server has discarded the
656 * "retransmitted" call and is making no effort to reexecute the call. To
657 * handle these, accept either a timeout (-1) or and INCFAILED error here,
658 * also set the connenction HardDeadTime to punt after a reasonable
661 /* short dead time since may we expect some trouble */
662 rx_SetConnHardDeadTime (conn, 30);
663 code = rxi_GetCallNumberVector (conn, callNumbers);
664 if (code) return code;
665 for (ch=1; ch<RX_MAXCALLS; ch++)
666 if (callNumbers[ch] > 1) {
668 code = rxi_SetCallNumberVector (conn, callNumbers);
669 if (code) return code;
672 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
673 return RXKST_BADCALLNUMBERS;
674 code = MakeMultiChannelCall (conn, 1, RXKST_INCFAILED, codes);
675 code = CheckCallFailure (conn, codes, code, "retransmitted call");
676 if (code && !retCode) retCode = code;
678 /* Get a fresh connection, becasue if the above failed as it should the
679 * connection is dead. */
680 rx_DestroyConnection (conn);
681 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
683 if (!conn) return RXKST_NEWCONNFAILED;
685 /* Similarly, but decrement call number by two which should be completely
686 * unmistakeable as a broken or malicious client. */
688 /* short dead time since may we expect some trouble */
689 rx_SetConnHardDeadTime (conn, 30);
690 code = MakeMultiChannelCall (conn, 2, 0, codes);
691 if (code) return code;
692 code = rxi_GetCallNumberVector (conn, callNumbers);
693 if (code) return code;
694 for (ch=1; ch<RX_MAXCALLS; ch++)
695 if (callNumbers[ch] > 2) {
696 callNumbers[ch] -= 2;
697 code = rxi_SetCallNumberVector (conn, callNumbers);
700 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
701 return RXKST_BADCALLNUMBERS;
702 code = MakeMultiChannelCall (conn, 1, -1, codes);
703 code = CheckCallFailure (conn, codes, code, "duplicate call");
704 if (code && !retCode) retCode = code;
706 rx_DestroyConnection (conn);
707 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
709 if (!conn) return RXKST_NEWCONNFAILED;
711 /* Next, without waiting for the server to discard its state, we will check
712 * to see if the Challenge/Response protocol correctly informs the server
713 * of the client's callNumber state. We do this by artificially increasing
714 * the call numbers of a new connection for all channels beyond zero,
715 * making a call on channel zero, then resetting the call number for the
716 * unused channels back to zero, then making calls on all channels. */
718 code = rxi_GetCallNumberVector (conn, callNumbers);
719 if (code) return code;
720 for (i=0; i<RX_MAXCALLS; i++) {
721 if (callNumbers[i] != 0) return RXKST_BADCALLNUMBERS;
722 callNumbers[i] = 51; /* an arbitrary value... */
724 code = rxi_SetCallNumberVector (conn, callNumbers);
725 if (code) return code;
726 code = FastCall (conn); /* use channel 0 */
727 if (code) return code;
728 code = rxi_GetCallNumberVector (conn, callNumbers);
729 if (code) return code;
730 if (callNumbers[0] != 52) return RXKST_BADCALLNUMBERS;
731 for (i=1; i<RX_MAXCALLS; i++) {
732 if (callNumbers[i] != 51) return RXKST_BADCALLNUMBERS;
733 callNumbers[i] = 37; /* back up a ways */
735 code = rxi_SetCallNumberVector (conn, callNumbers);
736 if (code) return code;
737 /* now try calls on all channels... */
738 code = MakeMultiChannelCall (conn, 1, -1, codes);
739 code = CheckCallFailure (conn, codes, code,
740 "alternate channel call replay");
741 if (code && !retCode) retCode = code;
743 rx_DestroyConnection (conn);
746 #endif rx_GetPacketCksum
750 #ifdef rx_GetPacketCksum
754 u_long epoch; /* connection to attack */
756 int client; /* TRUE => client side */
757 u_long newEpoch; /* conn to direct challenges to */
759 u_long counts[RX_N_PACKET_TYPES];
763 #define IO_REDIRECTCHALLENGE 2
765 static int HandleIncoming (p, addr)
766 INOUT struct rx_packet *p;
767 INOUT struct sockaddr_in *addr;
769 int client; /* packet sent by client */
770 u_char type; /* packet type */
772 if (incomingOps.op == IO_NOOP) return 0;
774 client = ((p->header.flags&RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
775 if ((p->header.epoch != incomingOps.epoch) ||
776 ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK) ||
777 (client != incomingOps.client)) return 0;
778 type = p->header.type;
779 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
780 incomingOps.counts[type]++;
782 switch (incomingOps.op) {
787 case IO_REDIRECTCHALLENGE:
788 if (p->header.type != RX_PACKET_TYPE_CHALLENGE) break;
789 p->header.epoch = incomingOps.newEpoch;
790 p->header.cid = incomingOps.newCid;
791 /* Now set up to watch for the corresponding challenge. */
792 incomingOps.epoch = incomingOps.newEpoch;
793 incomingOps.cid = incomingOps.newCid;
794 incomingOps.op = IO_COUNT;
798 fprintf (stderr, "Unknown incoming op %d\n", incomingOps.op);
806 u_long epoch; /* connection to attack */
808 int client; /* TRUE => client side */
809 u_long counts[RX_N_PACKET_TYPES];
813 #define OO_ZEROCKSUM 2
814 #define OO_MUNGCKSUM 3
816 static int HandleOutgoing (p, addr)
817 INOUT struct rx_packet *p;
818 INOUT struct sockaddr_in *addr;
820 int client; /* packet sent by client */
821 u_char type; /* packet type */
823 if (outgoingOps.op == OO_NOOP) return 0;
825 client = ((p->header.flags&RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
826 if ((p->header.epoch != outgoingOps.epoch) ||
827 ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK) ||
828 (client != outgoingOps.client)) return 0;
829 type = p->header.type;
830 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
831 outgoingOps.counts[type]++;
833 switch (outgoingOps.op) {
836 /* counting always happens above if not noop */
840 if (p->header.type != RX_PACKET_TYPE_DATA) break;
841 if (rx_GetPacketCksum (p) == 0) {
842 /* probably, a retransmitted packet */
843 fprintf (stderr, "Packet cksum already zero\n");
846 rx_SetPacketCksum (p, 0);
851 if (p->header.type != RX_PACKET_TYPE_DATA) break;
852 cksum = rx_GetPacketCksum (p);
854 fprintf (stderr, "Packet cksum already zero\n");
857 rx_SetPacketCksum (p, cksum ^ 8);
861 fprintf (stderr, "Unknown outgoing op %d\n", outgoingOps.op);
867 #ifdef AFS_PTHREAD_ENV
868 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
869 static pthread_mutex_t slowCallLock;
870 static pthread_cond_t slowCallCV;
871 void SlowCallInit(void)
873 pthread_mutex_init(&slowCallLock, NULL);
874 pthread_cond_init(&slowCallCV, NULL);
877 static long slowCallCode;
878 static long SlowCall (conn)
885 #ifdef AFS_PTHREAD_ENV
886 pthread_mutex_lock(&slowCallLock);
888 slowCallCode = RXKST_PROCESSRUNNING;
889 #ifdef AFS_PTHREAD_ENV
890 pthread_cond_signal(&slowCallCV);
892 LWP_NoYieldSignal (&slowCallCode);
894 slowCallCode = RXKST_Slow (conn, 1, &ntime);
896 now = FT_ApproxTime();
897 if ((ntime < now-maxSkew) || (ntime > now+maxSkew))
898 slowCallCode = RXKST_TIMESKEW;
900 temp_rc = slowCallCode;
901 #ifdef AFS_PTHREAD_ENV
902 pthread_cond_signal(&slowCallCV);
903 pthread_mutex_unlock(&slowCallLock);
905 LWP_NoYieldSignal (&slowCallCode);
910 #endif rx_GetPacketCksum
912 static long RunHijackTest (parms, host, sc, si)
913 IN struct clientParms *parms;
915 IN struct rx_securityClass *sc;
919 #ifndef rx_GetPacketCksum
921 code = RXKST_BADARGS;
922 com_err (whoami, code, "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
927 extern int (*rx_justReceived)();
928 extern int (*rx_almostSent)();
931 struct rx_connection *conn = 0;
932 struct rx_connection *otherConn = 0;
933 #ifdef AFS_PTHREAD_ENV
938 int nResp; /* otherConn responses seen */
941 #ifdef AFS_PTHREAD_ENV
942 pthread_once(&slowCallOnce, SlowCallInit);
944 rx_justReceived = HandleIncoming;
945 rx_almostSent = HandleOutgoing;
947 incomingOps.op = IO_NOOP;
948 outgoingOps.op = OO_NOOP;
950 #define HIJACK_CONN(conn) \
951 { if (conn) rx_DestroyConnection (conn); \
952 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
953 RXKST_SERVICEID, sc, si); \
954 if (!(conn)) return RXKST_NEWCONNFAILED; \
955 outgoingOps.client = 1; \
956 outgoingOps.epoch = (conn)->epoch; \
957 outgoingOps.cid = (conn)->cid; }
961 /* First try switching from no packet cksum to sending packet cksum between
962 * calls, and see if server complains. */
964 outgoingOps.op = OO_ZEROCKSUM;
965 code = FastCall (conn);
967 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
970 /* The server thinks we're an old style client. Now start sending cksums.
971 * Server shouldn't care. */
972 outgoingOps.op = OO_NOOP;
973 code = FastCall (conn);
975 com_err (whoami, code, "doing FastCall with non-ZEROCKSUM");
978 /* The server now thinks we're a new style client, we can't go back now. */
979 outgoingOps.op = OO_ZEROCKSUM;
980 code = FastCall (conn);
981 if (code == 0) code = RXKST_NOBADCKSUM;
982 if (code != RXKADSEALEDINCON) {
983 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
985 } else if (!conn->error) {
986 code = RXKST_NOCONNERROR;
987 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
993 /* Now try modifying packet cksum to see if server complains. */
995 outgoingOps.op = OO_MUNGCKSUM;
996 code = FastCall (conn);
997 if (code == 0) code = RXKST_NOBADCKSUM;
998 if (code != RXKADSEALEDINCON) {
999 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1001 } else if (!conn->error) {
1002 code = RXKST_NOCONNERROR;
1003 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1007 /* Now make two connection and direct the first challenge on one connection
1008 * to the other connection to see if it generates a response. The
1009 * retransmitted challenge should allow the call on the first connection to
1010 * complete correctly. Part one is to attack a new connection, then attack
1011 * it after it has made a call. Part three, just for comparison, attacks a
1012 * otherConn while it is making a slow call (and thus has an active call).
1013 * Against this attack we have no defense so we expect a challenge in this
1014 * case, which the server will discard. */
1016 #define RedirectChallenge(conn,otherConn) \
1017 (incomingOps.epoch = (conn)->epoch, \
1018 incomingOps.cid = (conn)->cid, \
1019 incomingOps.client = 1, \
1020 incomingOps.newEpoch = (otherConn)->epoch, \
1021 incomingOps.newCid = (otherConn)->cid, \
1022 incomingOps.op = IO_REDIRECTCHALLENGE, \
1023 outgoingOps.epoch = (otherConn)->epoch, \
1024 outgoingOps.cid = (otherConn)->cid, \
1025 outgoingOps.client = 1, \
1026 outgoingOps.op = OO_COUNT, \
1027 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1030 HIJACK_CONN(otherConn)
1031 RedirectChallenge (conn, otherConn);
1033 code = FastCall (conn);
1034 if (code) return code;
1035 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1036 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1038 code = RXKST_CHALLENGEORACLE;
1039 com_err (whoami, code, "misdirecting challenge");
1042 code = FastCall (otherConn); /* generate some activity here */
1043 if (code) return code;
1044 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1045 assert (nResp >= 1);
1046 code = FastCall (conn);
1047 if (code) return code;
1048 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp) goto oracle;
1051 RedirectChallenge (conn, otherConn);
1052 /* otherConn was authenticated during part one */
1053 code = FastCall (conn);
1054 if (code) return code;
1055 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1056 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0) goto oracle;
1059 RedirectChallenge (conn, otherConn);
1060 /* otherConn is still authenticated */
1061 slowCallCode = RXKST_PROCESSCREATED;
1062 #ifdef AFS_PTHREAD_ENV
1064 pthread_attr_t tattr;
1066 code = pthread_attr_init(&tattr);
1068 com_err (whoami, code, "can't pthread_attr_init slow call process");
1072 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1074 com_err (whoami, code, "can't pthread_attr_setdetachstate slow call process");
1078 code = pthread_create(&pid, &tattr, SlowCall, (void*) otherConn);
1081 code = LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1082 (opaque) otherConn, "Slow Call Process", &pid);
1085 com_err (whoami, code, "can't create slow call process");
1088 #ifdef AFS_PTHREAD_ENV
1089 pthread_mutex_lock(&slowCallLock);
1090 while (slowCallCode == RXKST_PROCESSCREATED)
1091 pthread_cond_wait(&slowCallCV, &slowCallLock);
1093 while (slowCallCode == RXKST_PROCESSCREATED)
1094 LWP_WaitProcess (&slowCallCode); /* wait for process start */
1096 if (slowCallCode != RXKST_PROCESSRUNNING) {
1097 tmp_rc = slowCallCode;
1098 #ifdef AFS_PTHREAD_ENV
1099 pthread_mutex_unlock(&slowCallLock);
1101 return tmp_rc; /* make sure didn't fail immediately */
1103 assert (incomingOps.op == IO_REDIRECTCHALLENGE);
1104 code = FastCall (conn);
1105 if (code) return code;
1106 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1107 #ifdef AFS_PTHREAD_ENV
1108 while (slowCallCode == RXKST_PROCESSRUNNING)
1109 pthread_cond_wait(&slowCallCV, &slowCallLock);
1110 pthread_mutex_unlock(&slowCallLock);
1112 while (slowCallCode == RXKST_PROCESSRUNNING)
1113 LWP_WaitProcess (&slowCallCode); /* wait for process finish */
1115 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1) goto oracle;
1117 rx_justReceived = 0;
1119 rx_DestroyConnection (otherConn);
1120 rx_DestroyConnection (conn);
1123 #endif rx_GetPacketCksum
1127 long rxkst_StartClient (parms)
1128 IN struct clientParms *parms;
1133 struct rx_securityClass *sc;
1135 whoami = parms->whoami; /* set this global variable */
1137 host = GetServer (parms->server);
1139 if (parms->authentication >= 0) {
1141 char ticket[MAXKTCTICKETLEN];
1143 struct ktc_encryptionKey Ksession;
1145 code = GetTicket (&kvno, &Ksession, &ticketLen, ticket);
1146 if (code) return code;
1148 /* next, we have ticket, kvno and session key, authenticate the conn */
1149 sc = (struct rx_securityClass *)
1150 rxkad_NewClientSecurityObject (parms->authentication,
1151 &Ksession, kvno, ticketLen, ticket);
1153 scIndex = 2; /* kerberos security index */
1155 /* unauthenticated connection */
1156 sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject ();
1158 scIndex = 0; /* null security index */
1162 if (!code && parms->callTest) {
1163 code = RunCallTest (parms, host, sc, scIndex);
1165 if (!code && parms->hijackTest) {
1166 code = RunHijackTest (parms, host, sc, scIndex);
1169 (parms->printTiming ||
1170 parms->fastCalls || parms->slowCalls || parms->copiousCalls)) {
1171 struct rx_connection *conn;
1172 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1173 RXKST_SERVICEID, sc, scIndex);
1175 code = RepeatLoadTest (parms, conn);
1176 rx_DestroyConnection (conn);
1177 } else code = RXKST_NEWCONNFAILED;
1179 if (!code && parms->stopServer) {
1180 struct rx_connection *conn;
1181 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1182 RXKST_SERVICEID, sc, scIndex);
1184 code = RXKST_Kill (conn);
1186 com_err (whoami, code, "trying to stop server");
1188 rx_DestroyConnection (conn);
1189 } else code = RXKST_NEWCONNFAILED;
1192 if (parms->printStats) {
1193 rx_PrintStats (stdout);
1195 /* use rxdebug style iteration here */
1196 rx_PrintPeerStats (stdout, rx_PeerOf(conn));
1203 com_err (parms->whoami, code, "test fails");
1206 printf ("Test Okay\n");
1207 if (!parms->noExit) exit (0);