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 <afs/param.h>
14 #include <sys/types.h>
20 #include <netinet/in.h>
22 #include <afs/com_err.h>
23 #include <afs/afsutil.h>
26 #include "stress_internal.h"
27 #ifdef AFS_PTHREAD_ENV
29 #define FT_ApproxTime() (int)time(0)
32 #ifdef rx_GetPacketCksum
33 #include "../../permit_xprt.h"
41 static long GetServer(aname)
44 register struct hostent *th;
47 th = gethostbyname(aname);
49 fprintf (stderr, "host %s not found\n", aname);
52 bcopy(th->h_addr, &addr, sizeof(addr));
56 static long GetTicket (versionP, session, ticketLenP, ticket)
58 OUT struct ktc_encryptionKey *session;
64 /* create random session key, using key for seed to good random */
65 des_init_random_number_generator (&serviceKey);
66 code = des_random_key (session);
67 if (code) return code;
69 /* now create the actual ticket */
71 code = tkt_MakeTicket(ticket, ticketLenP, &serviceKey,
72 RXKST_CLIENT_NAME, RXKST_CLIENT_INST, "",
73 /*start,end*/0, 0xffffffff, session, /*host*/0,
74 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
75 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
76 * instance and cell, start time, end time, session key to seal in ticket,
77 * inet host, server name and server instance */
78 if (code) return code;
79 *versionP = serviceKeyVersion;
83 struct rx_connection *conn;
84 u_long sendLen; /* parameters for call to Copious */
86 u_long *fastCalls; /* number of calls to perform */
91 static long Copious (c, buf, buflen)
99 long inlen = c->sendLen;
100 long outlen = c->recvLen;
106 for (i=0; i<inlen; i++) mysum += (d++ & 0xff);
108 call = rx_NewCall (c->conn);
109 code = StartRXKST_Copious (call, inlen, mysum, outlen);
115 while (xfer < inlen) {
117 if (tlen > buflen) tlen = buflen;
118 for (i=0; i<tlen; i++) buf[i] = (d++ & 0xff);
119 n = rx_Write (call, buf, tlen);
122 else code = RXKST_WRITESHORT;
130 while (xfer < outlen) {
131 tlen = outlen - xfer;
132 if (tlen > buflen) tlen = buflen;
133 n = rx_Read (call, buf, tlen);
136 else code = RXKST_READSHORT;
139 for (i=0; i<tlen; i++) mysum += buf[i];
145 code = EndRXKST_Copious (call, &outsum);
146 code = rx_EndCall(call, code);
147 if (code) return code;
148 if (outsum != mysum) {
149 return RXKST_BADOUTPUTSUM;
154 static long DoClient (index, rock)
158 struct client *c = (struct client *)rock;
164 for (i=0; i<c->fastCalls[index]; i++) {
165 code = RXKST_Fast (c->conn, n, &inc_n);
166 if (code) return (code);
167 if (n+1 != inc_n) return RXKST_INCFAILED;
171 for (i=0; i<c->slowCalls[index]; i++) {
174 code = RXKST_Slow (c->conn, 1, &ntime);
175 if (code) return (code);
176 now = FT_ApproxTime();
177 if ((ntime < now-maxSkew) || (ntime > now+maxSkew)) return RXKST_TIMESKEW;
180 if (c->copiousCalls[index] > 0) {
181 u_long buflen = 10000;
182 u_char *buf = (u_char *) osi_Alloc (buflen);
183 for (i=0; i<c->copiousCalls[index]; i++) {
184 code = Copious (c, buf, buflen);
187 osi_Free (buf, buflen);
188 if (code) return code;
195 long exitCode; /* is PROCESSRUNNING until exit */
201 #ifdef AFS_PTHREAD_ENV
202 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
203 static pthread_mutex_t workerLock;
204 static pthread_cond_t workerCV;
205 void WorkerInit(void)
207 pthread_mutex_init(&workerLock, NULL);
208 pthread_cond_init(&workerCV, NULL);
211 static struct worker *workers;
213 static long DoWorker (w)
217 code = (*w->proc) (w->index, w->rock);
218 #ifdef AFS_PTHREAD_ENV
219 pthread_mutex_lock(&workerLock);
222 #ifdef AFS_PTHREAD_ENV
223 pthread_mutex_unlock(&workerLock);
225 #ifdef AFS_PTHREAD_ENV
226 pthread_cond_signal(&workerCV);
228 LWP_NoYieldSignal(&workers);
233 #define MAX_CTHREADS 25
235 static long CallSimultaneously (threads, rock, proc)
242 #ifdef AFS_PTHREAD_ENV
243 pthread_once(&workerOnce, WorkerInit);
247 for (i=0; i<threads; i++) {
249 #ifdef AFS_PTHREAD_ENV
254 assert (i < MAX_CTHREADS);
255 w = (struct worker *) osi_Alloc (sizeof(struct worker));
256 bzero (w, sizeof(*w));
260 w->exitCode = RXKST_PROCESSRUNNING;
263 #ifdef AFS_PTHREAD_ENV
265 pthread_attr_t tattr;
267 code = pthread_attr_init(&tattr);
269 com_err (whoami, code, "can't pthread_attr_init worker process");
273 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
275 com_err (whoami, code, "can't pthread_attr_setdetachstate worker process");
279 code = pthread_create(&pid, &tattr, DoWorker, (void*)w);
282 code = LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
283 (opaque) w, "Worker Process", &pid);
286 com_err (whoami, code, "can't create worker process");
290 code = 0; /* last non-zero code encountered */
291 #ifdef AFS_PTHREAD_ENV
292 pthread_mutex_lock(&workerLock);
295 struct worker *w, *prevW, *nextW;
297 for (w=workers; w; w=nextW) {
299 if (w->exitCode != RXKST_PROCESSRUNNING) {
301 if (code == 0) code = w->exitCode;
303 if (prevW) prevW->next = w->next;
304 else workers = w->next;
305 osi_Free (w, sizeof(*w));
306 continue; /* don't bump prevW */
310 #ifdef AFS_PTHREAD_ENV
312 pthread_cond_wait(&workerCV, &workerLock);
314 if (workers) LWP_WaitProcess (&workers);
317 #ifdef AFS_PTHREAD_ENV
318 pthread_mutex_unlock(&workerLock);
323 static void DivideUpCalls (calls, threads, threadCalls)
326 IN u_long threadCalls[];
329 for (i=0; i<threads; i++) {
330 threadCalls[i] = calls / (threads-i);
331 calls -= threadCalls[i];
335 static double ftime ()
338 gettimeofday(&tv, 0);
339 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
342 static long RunLoadTest (parms, conn)
343 IN struct clientParms *parms;
344 IN struct rx_connection *conn;
348 u_long fastCalls[MAX_CTHREADS];
349 u_long slowCalls[MAX_CTHREADS];
350 u_long copiousCalls[MAX_CTHREADS];
351 double start, interval;
353 DivideUpCalls (parms->fastCalls, parms->threads, fastCalls);
354 DivideUpCalls (parms->slowCalls, parms->threads, slowCalls);
355 DivideUpCalls (parms->copiousCalls, parms->threads, copiousCalls);
357 bzero (&c, sizeof(c));
359 c.sendLen = parms->sendLen;
360 c.recvLen = parms->recvLen;
361 c.fastCalls = fastCalls;
362 c.slowCalls = slowCalls;
363 c.copiousCalls = copiousCalls;
366 code = CallSimultaneously (parms->threads, &c, DoClient);
368 com_err (whoami, code, "in DoClient");
371 interval = ftime() - start;
373 if (parms->printTiming) {
375 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
376 int t = (interval / totalCalls) * 1000.0 + 0.5;
377 if (totalCalls > 0) {
378 printf ("For %d calls: %d msec/call\n", totalCalls, t);
380 if (parms->copiousCalls > 0) {
381 long n = parms->sendLen + parms->recvLen;
384 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
387 I just cannot get printing of floats to work on the pmax!!!!
388 printf ("%g %d %d %d\n", (float)kbps, b);
389 printf ("%g %d %d %d\n", kbps, b);
390 fprintf (stdout, "%g %d %d\n", kbps, b);
393 buf[sizeof(buf)-1] = 0;
394 sprintf (buf, "%g %d %d\n", kbps, b);
395 assert (buf[sizeof(buf)-1] == 0);
399 printf ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
400 parms->copiousCalls, parms->sendLen, parms->recvLen,
403 printf ("%g\n", kbps);
410 static long RepeatLoadTest (parms, conn)
411 IN struct clientParms *parms;
412 IN struct rx_connection *conn;
417 if (parms->repeatInterval == 0) {
418 if (parms->repeatCount == 0) parms->repeatCount = 1;
420 if (parms->repeatCount == 0) parms->repeatCount = 0x7fffffff;
423 if (parms->printTiming) {
425 types = (parms->fastCalls ? 1 : 0) +
426 (parms->slowCalls ? 1 : 0) + (parms->copiousCalls ? 1 : 0);
428 fprintf (stderr, "Combined timings of several types of calls may not be meaningful.\n");
430 /* do timings of copious calls by default */
431 parms->copiousCalls = 10;
434 for (count=0; count<parms->repeatCount; count++) {
435 code = RunLoadTest (parms, conn);
436 if (code) return code;
437 if (parms->repeatInterval) {
438 u_long i = parms->repeatInterval;
439 u_long now = time(0);
440 u_long next = (now + i-1) / i * i; /* round up to next interval */
442 #ifdef AFS_PTHREAD_ENV
445 IOMGR_Sleep (next-now);
454 /* For backward compatibility, don't try to use the CallNumber stuff unless
455 * we're compiling against the new Rx. */
457 #ifdef rx_GetPacketCksum
459 struct multiChannel {
460 struct rx_connection *conn;
463 int changes[RX_MAXCALLS];
464 long callNumbers[RX_MAXCALLS];
466 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
467 static u_long sequence = 0;
469 static long FastCall (conn)
470 IN struct rx_connection *conn;
473 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
476 code = RXKST_Fast (conn, n, &inc_n);
477 if (code) return code;
478 if (inc_n != n+1) return RXKST_INCFAILED;
482 static long UniChannelCall (index, rock)
486 struct multiChannel *mc = (struct multiChannel *)rock;
488 long callNumbers[RX_MAXCALLS];
493 while (!mc->done && unchanged) {
495 code = FastCall (mc->conn);
497 code = rxi_GetCallNumberVector (mc->conn, callNumbers);
500 for (i=0; i<RX_MAXCALLS; i++) {
501 if (callNumbers[i] > mc->callNumbers[i]) {
502 mc->callNumbers[i] = callNumbers[i];
503 mc->changes[i]--; /* may go negative */
505 if (mc->changes[i] > 0) unchanged++;
508 mc->codes[index] = code;
513 static long MakeMultiChannelCall (conn, each, expectedCode, codes)
514 IN struct rx_connection *conn;
515 IN int each; /* calls to make on each channel */
516 IN long expectedCode;
521 struct multiChannel mc;
523 bzero (&mc, sizeof(mc));
525 for (i=0; i<RX_MAXCALLS; i++) {
526 codes[i] = RXKST_PROCESSRUNNING;
527 mc.changes[i] = each;
530 code = rxi_GetCallNumberVector (conn, mc.callNumbers);
531 if (code) return code;
533 code = CallSimultaneously (RX_MAXCALLS, &mc, UniChannelCall);
534 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) &&
535 ((code == expectedCode) || (code == -3))) ; /* strange cases */
536 else if (code != expectedCode) {
537 com_err (whoami, code,
538 "problem making multichannel call, expected '%s'",
540 ? "no error" : (char *)error_message (expectedCode)));
545 static long CheckCallFailure (conn, codes, code, msg)
546 IN struct rx_connection *conn;
552 fprintf (stderr, "Failed to detect %s\n", msg);
553 return RXKST_NODUPLICATECALL;
558 for (i=0; i<RX_MAXCALLS; i++)
559 if (!((codes[i] == 0) || (codes[i] == code) ||
560 (codes[i] == -3))) okay = 0;
561 if (conn->error) okay = 0;
563 fprintf (stderr, "%s produced these errors:\n", msg);
564 for (i=0; i<RX_MAXCALLS; i++) {
565 assert (codes[i] != RXKST_PROCESSRUNNING);
568 fprintf (stderr, " %d no error\n", i);
569 } else fprintf (stderr,
570 " %d %s\n", i, error_message(codes[i]));
574 sprintf (buf, "connection dead following %s", msg);
575 code = FastCall (conn);
576 if (code) com_err (whoami, code, buf);
583 #endif rx_GetPacketCksum
585 static long RunCallTest (parms, host, sc, si)
586 IN struct clientParms *parms;
588 IN struct rx_securityClass *sc;
593 #ifndef rx_GetPacketCksum
595 code = RXKST_BADARGS;
596 com_err (whoami, code, "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
602 struct rx_connection *conn;
604 long callNumbers[RX_MAXCALLS];
605 long codes[RX_MAXCALLS];
606 long retCode = 0; /* ret. if nothing fatal goes wrong */
608 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
610 if (!conn) return RXKST_NEWCONNFAILED;
612 /* First check the basic behaviour of call number handling */
614 code = rxi_GetCallNumberVector (conn, callNumbers);
615 if (code) return code;
616 for (i=0; i<RX_MAXCALLS; i++) {
617 if (callNumbers[i] != 0) {
618 fprintf (stderr, "Connection's initial call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
619 return RXKST_BADCALLNUMBERS;
622 code = FastCall (conn);
623 if (code) return code;
624 code = rxi_GetCallNumberVector (conn, callNumbers);
625 if (code) return code;
626 firstCall = callNumbers[0];
627 code = FastCall (conn);
628 if (code) return code;
629 code = rxi_GetCallNumberVector (conn, callNumbers);
630 if (code) return code;
631 if ((callNumbers[0] != firstCall+1) &&
632 ((firstCall == 1) || (firstCall == 2))) {
633 /* The call number after the first call should be one or, more likely,
634 * two (if the call is still DALLYing). Between first and second call,
635 * the call number should have incremented by one. */
636 fprintf (stderr, "Connection's first channel call number not one. call[%d] = %d\n", 0, callNumbers[0]);
637 return RXKST_BADCALLNUMBERS;
639 for (i=1; i<RX_MAXCALLS; i++) {
640 if (callNumbers[i] != 0) {
641 fprintf (stderr, "Connection's other channel call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
642 return RXKST_BADCALLNUMBERS;
645 code = MakeMultiChannelCall (conn, 1, 0, codes);
646 if (code) return code;
648 /* Now try to resend a call that's already been executed by finding a
649 * non-zero call number on a channel other than zero and decrementing it by
650 * one. This should appear to the server as a retransmitted call. Since
651 * this is behaving as a broken client different strange behaviors may be
652 * exhibited by different servers. If the response packet to the original
653 * call is discarded by the time the "retransmitted" call arrives (perhaps
654 * due to high server or client load) there is no way for the server to
655 * respond at all. Further, it seems, that under some cases the connection
656 * will be kept alive indefinitely even though the server has discarded the
657 * "retransmitted" call and is making no effort to reexecute the call. To
658 * handle these, accept either a timeout (-1) or and INCFAILED error here,
659 * also set the connenction HardDeadTime to punt after a reasonable
662 /* short dead time since may we expect some trouble */
663 rx_SetConnHardDeadTime (conn, 30);
664 code = rxi_GetCallNumberVector (conn, callNumbers);
665 if (code) return code;
666 for (ch=1; ch<RX_MAXCALLS; ch++)
667 if (callNumbers[ch] > 1) {
669 code = rxi_SetCallNumberVector (conn, callNumbers);
670 if (code) return code;
673 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
674 return RXKST_BADCALLNUMBERS;
675 code = MakeMultiChannelCall (conn, 1, RXKST_INCFAILED, codes);
676 code = CheckCallFailure (conn, codes, code, "retransmitted call");
677 if (code && !retCode) retCode = code;
679 /* Get a fresh connection, becasue if the above failed as it should the
680 * connection is dead. */
681 rx_DestroyConnection (conn);
682 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
684 if (!conn) return RXKST_NEWCONNFAILED;
686 /* Similarly, but decrement call number by two which should be completely
687 * unmistakeable as a broken or malicious client. */
689 /* short dead time since may we expect some trouble */
690 rx_SetConnHardDeadTime (conn, 30);
691 code = MakeMultiChannelCall (conn, 2, 0, codes);
692 if (code) return code;
693 code = rxi_GetCallNumberVector (conn, callNumbers);
694 if (code) return code;
695 for (ch=1; ch<RX_MAXCALLS; ch++)
696 if (callNumbers[ch] > 2) {
697 callNumbers[ch] -= 2;
698 code = rxi_SetCallNumberVector (conn, callNumbers);
701 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
702 return RXKST_BADCALLNUMBERS;
703 code = MakeMultiChannelCall (conn, 1, -1, codes);
704 code = CheckCallFailure (conn, codes, code, "duplicate call");
705 if (code && !retCode) retCode = code;
707 rx_DestroyConnection (conn);
708 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
710 if (!conn) return RXKST_NEWCONNFAILED;
712 /* Next, without waiting for the server to discard its state, we will check
713 * to see if the Challenge/Response protocol correctly informs the server
714 * of the client's callNumber state. We do this by artificially increasing
715 * the call numbers of a new connection for all channels beyond zero,
716 * making a call on channel zero, then resetting the call number for the
717 * unused channels back to zero, then making calls on all channels. */
719 code = rxi_GetCallNumberVector (conn, callNumbers);
720 if (code) return code;
721 for (i=0; i<RX_MAXCALLS; i++) {
722 if (callNumbers[i] != 0) return RXKST_BADCALLNUMBERS;
723 callNumbers[i] = 51; /* an arbitrary value... */
725 code = rxi_SetCallNumberVector (conn, callNumbers);
726 if (code) return code;
727 code = FastCall (conn); /* use channel 0 */
728 if (code) return code;
729 code = rxi_GetCallNumberVector (conn, callNumbers);
730 if (code) return code;
731 if (callNumbers[0] != 52) return RXKST_BADCALLNUMBERS;
732 for (i=1; i<RX_MAXCALLS; i++) {
733 if (callNumbers[i] != 51) return RXKST_BADCALLNUMBERS;
734 callNumbers[i] = 37; /* back up a ways */
736 code = rxi_SetCallNumberVector (conn, callNumbers);
737 if (code) return code;
738 /* now try calls on all channels... */
739 code = MakeMultiChannelCall (conn, 1, -1, codes);
740 code = CheckCallFailure (conn, codes, code,
741 "alternate channel call replay");
742 if (code && !retCode) retCode = code;
744 rx_DestroyConnection (conn);
747 #endif rx_GetPacketCksum
751 #ifdef rx_GetPacketCksum
755 u_long epoch; /* connection to attack */
757 int client; /* TRUE => client side */
758 u_long newEpoch; /* conn to direct challenges to */
760 u_long counts[RX_N_PACKET_TYPES];
764 #define IO_REDIRECTCHALLENGE 2
766 static int HandleIncoming (p, addr)
767 INOUT struct rx_packet *p;
768 INOUT struct sockaddr_in *addr;
770 int client; /* packet sent by client */
771 u_char type; /* packet type */
773 if (incomingOps.op == IO_NOOP) return 0;
775 client = ((p->header.flags&RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
776 if ((p->header.epoch != incomingOps.epoch) ||
777 ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK) ||
778 (client != incomingOps.client)) return 0;
779 type = p->header.type;
780 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
781 incomingOps.counts[type]++;
783 switch (incomingOps.op) {
788 case IO_REDIRECTCHALLENGE:
789 if (p->header.type != RX_PACKET_TYPE_CHALLENGE) break;
790 p->header.epoch = incomingOps.newEpoch;
791 p->header.cid = incomingOps.newCid;
792 /* Now set up to watch for the corresponding challenge. */
793 incomingOps.epoch = incomingOps.newEpoch;
794 incomingOps.cid = incomingOps.newCid;
795 incomingOps.op = IO_COUNT;
799 fprintf (stderr, "Unknown incoming op %d\n", incomingOps.op);
807 u_long epoch; /* connection to attack */
809 int client; /* TRUE => client side */
810 u_long counts[RX_N_PACKET_TYPES];
814 #define OO_ZEROCKSUM 2
815 #define OO_MUNGCKSUM 3
817 static int HandleOutgoing (p, addr)
818 INOUT struct rx_packet *p;
819 INOUT struct sockaddr_in *addr;
821 int client; /* packet sent by client */
822 u_char type; /* packet type */
824 if (outgoingOps.op == OO_NOOP) return 0;
826 client = ((p->header.flags&RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
827 if ((p->header.epoch != outgoingOps.epoch) ||
828 ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK) ||
829 (client != outgoingOps.client)) return 0;
830 type = p->header.type;
831 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
832 outgoingOps.counts[type]++;
834 switch (outgoingOps.op) {
837 /* counting always happens above if not noop */
841 if (p->header.type != RX_PACKET_TYPE_DATA) break;
842 if (rx_GetPacketCksum (p) == 0) {
843 /* probably, a retransmitted packet */
844 fprintf (stderr, "Packet cksum already zero\n");
847 rx_SetPacketCksum (p, 0);
852 if (p->header.type != RX_PACKET_TYPE_DATA) break;
853 cksum = rx_GetPacketCksum (p);
855 fprintf (stderr, "Packet cksum already zero\n");
858 rx_SetPacketCksum (p, cksum ^ 8);
862 fprintf (stderr, "Unknown outgoing op %d\n", outgoingOps.op);
868 #ifdef AFS_PTHREAD_ENV
869 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
870 static pthread_mutex_t slowCallLock;
871 static pthread_cond_t slowCallCV;
872 void SlowCallInit(void)
874 pthread_mutex_init(&slowCallLock, NULL);
875 pthread_cond_init(&slowCallCV, NULL);
878 static long slowCallCode;
879 static long SlowCall (conn)
886 #ifdef AFS_PTHREAD_ENV
887 pthread_mutex_lock(&slowCallLock);
889 slowCallCode = RXKST_PROCESSRUNNING;
890 #ifdef AFS_PTHREAD_ENV
891 pthread_cond_signal(&slowCallCV);
893 LWP_NoYieldSignal (&slowCallCode);
895 slowCallCode = RXKST_Slow (conn, 1, &ntime);
897 now = FT_ApproxTime();
898 if ((ntime < now-maxSkew) || (ntime > now+maxSkew))
899 slowCallCode = RXKST_TIMESKEW;
901 temp_rc = slowCallCode;
902 #ifdef AFS_PTHREAD_ENV
903 pthread_cond_signal(&slowCallCV);
904 pthread_mutex_unlock(&slowCallLock);
906 LWP_NoYieldSignal (&slowCallCode);
911 #endif rx_GetPacketCksum
913 static long RunHijackTest (parms, host, sc, si)
914 IN struct clientParms *parms;
916 IN struct rx_securityClass *sc;
920 #ifndef rx_GetPacketCksum
922 code = RXKST_BADARGS;
923 com_err (whoami, code, "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
928 extern int (*rx_justReceived)();
929 extern int (*rx_almostSent)();
932 struct rx_connection *conn = 0;
933 struct rx_connection *otherConn = 0;
934 #ifdef AFS_PTHREAD_ENV
939 int nResp; /* otherConn responses seen */
942 #ifdef AFS_PTHREAD_ENV
943 pthread_once(&slowCallOnce, SlowCallInit);
945 rx_justReceived = HandleIncoming;
946 rx_almostSent = HandleOutgoing;
948 incomingOps.op = IO_NOOP;
949 outgoingOps.op = OO_NOOP;
951 #define HIJACK_CONN(conn) \
952 { if (conn) rx_DestroyConnection (conn); \
953 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
954 RXKST_SERVICEID, sc, si); \
955 if (!(conn)) return RXKST_NEWCONNFAILED; \
956 outgoingOps.client = 1; \
957 outgoingOps.epoch = (conn)->epoch; \
958 outgoingOps.cid = (conn)->cid; }
962 /* First try switching from no packet cksum to sending packet cksum between
963 * calls, and see if server complains. */
965 outgoingOps.op = OO_ZEROCKSUM;
966 code = FastCall (conn);
968 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
971 /* The server thinks we're an old style client. Now start sending cksums.
972 * Server shouldn't care. */
973 outgoingOps.op = OO_NOOP;
974 code = FastCall (conn);
976 com_err (whoami, code, "doing FastCall with non-ZEROCKSUM");
979 /* The server now thinks we're a new style client, we can't go back now. */
980 outgoingOps.op = OO_ZEROCKSUM;
981 code = FastCall (conn);
982 if (code == 0) code = RXKST_NOBADCKSUM;
983 if (code != RXKADSEALEDINCON) {
984 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
986 } else if (!conn->error) {
987 code = RXKST_NOCONNERROR;
988 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
994 /* Now try modifying packet cksum to see if server complains. */
996 outgoingOps.op = OO_MUNGCKSUM;
997 code = FastCall (conn);
998 if (code == 0) code = RXKST_NOBADCKSUM;
999 if (code != RXKADSEALEDINCON) {
1000 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1002 } else if (!conn->error) {
1003 code = RXKST_NOCONNERROR;
1004 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1008 /* Now make two connection and direct the first challenge on one connection
1009 * to the other connection to see if it generates a response. The
1010 * retransmitted challenge should allow the call on the first connection to
1011 * complete correctly. Part one is to attack a new connection, then attack
1012 * it after it has made a call. Part three, just for comparison, attacks a
1013 * otherConn while it is making a slow call (and thus has an active call).
1014 * Against this attack we have no defense so we expect a challenge in this
1015 * case, which the server will discard. */
1017 #define RedirectChallenge(conn,otherConn) \
1018 (incomingOps.epoch = (conn)->epoch, \
1019 incomingOps.cid = (conn)->cid, \
1020 incomingOps.client = 1, \
1021 incomingOps.newEpoch = (otherConn)->epoch, \
1022 incomingOps.newCid = (otherConn)->cid, \
1023 incomingOps.op = IO_REDIRECTCHALLENGE, \
1024 outgoingOps.epoch = (otherConn)->epoch, \
1025 outgoingOps.cid = (otherConn)->cid, \
1026 outgoingOps.client = 1, \
1027 outgoingOps.op = OO_COUNT, \
1028 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1031 HIJACK_CONN(otherConn)
1032 RedirectChallenge (conn, otherConn);
1034 code = FastCall (conn);
1035 if (code) return code;
1036 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1037 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1039 code = RXKST_CHALLENGEORACLE;
1040 com_err (whoami, code, "misdirecting challenge");
1043 code = FastCall (otherConn); /* generate some activity here */
1044 if (code) return code;
1045 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1046 assert (nResp >= 1);
1047 code = FastCall (conn);
1048 if (code) return code;
1049 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp) goto oracle;
1052 RedirectChallenge (conn, otherConn);
1053 /* otherConn was authenticated during part one */
1054 code = FastCall (conn);
1055 if (code) return code;
1056 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1057 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0) goto oracle;
1060 RedirectChallenge (conn, otherConn);
1061 /* otherConn is still authenticated */
1062 slowCallCode = RXKST_PROCESSCREATED;
1063 #ifdef AFS_PTHREAD_ENV
1065 pthread_attr_t tattr;
1067 code = pthread_attr_init(&tattr);
1069 com_err (whoami, code, "can't pthread_attr_init slow call process");
1073 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1075 com_err (whoami, code, "can't pthread_attr_setdetachstate slow call process");
1079 code = pthread_create(&pid, &tattr, SlowCall, (void*) otherConn);
1082 code = LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1083 (opaque) otherConn, "Slow Call Process", &pid);
1086 com_err (whoami, code, "can't create slow call process");
1089 #ifdef AFS_PTHREAD_ENV
1090 pthread_mutex_lock(&slowCallLock);
1091 while (slowCallCode == RXKST_PROCESSCREATED)
1092 pthread_cond_wait(&slowCallCV, &slowCallLock);
1094 while (slowCallCode == RXKST_PROCESSCREATED)
1095 LWP_WaitProcess (&slowCallCode); /* wait for process start */
1097 if (slowCallCode != RXKST_PROCESSRUNNING) {
1098 tmp_rc = slowCallCode;
1099 #ifdef AFS_PTHREAD_ENV
1100 pthread_mutex_unlock(&slowCallLock);
1102 return tmp_rc; /* make sure didn't fail immediately */
1104 assert (incomingOps.op == IO_REDIRECTCHALLENGE);
1105 code = FastCall (conn);
1106 if (code) return code;
1107 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1108 #ifdef AFS_PTHREAD_ENV
1109 while (slowCallCode == RXKST_PROCESSRUNNING)
1110 pthread_cond_wait(&slowCallCV, &slowCallLock);
1111 pthread_mutex_unlock(&slowCallLock);
1113 while (slowCallCode == RXKST_PROCESSRUNNING)
1114 LWP_WaitProcess (&slowCallCode); /* wait for process finish */
1116 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1) goto oracle;
1118 rx_justReceived = 0;
1120 rx_DestroyConnection (otherConn);
1121 rx_DestroyConnection (conn);
1124 #endif rx_GetPacketCksum
1128 long rxkst_StartClient (parms)
1129 IN struct clientParms *parms;
1134 struct rx_securityClass *sc;
1136 whoami = parms->whoami; /* set this global variable */
1138 host = GetServer (parms->server);
1140 if (parms->authentication >= 0) {
1142 char ticket[MAXKTCTICKETLEN];
1144 struct ktc_encryptionKey Ksession;
1146 code = GetTicket (&kvno, &Ksession, &ticketLen, ticket);
1147 if (code) return code;
1149 /* next, we have ticket, kvno and session key, authenticate the conn */
1150 sc = (struct rx_securityClass *)
1151 rxkad_NewClientSecurityObject (parms->authentication,
1152 &Ksession, kvno, ticketLen, ticket);
1154 scIndex = 2; /* kerberos security index */
1156 /* unauthenticated connection */
1157 sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject ();
1159 scIndex = 0; /* null security index */
1163 if (!code && parms->callTest) {
1164 code = RunCallTest (parms, host, sc, scIndex);
1166 if (!code && parms->hijackTest) {
1167 code = RunHijackTest (parms, host, sc, scIndex);
1170 (parms->printTiming ||
1171 parms->fastCalls || parms->slowCalls || parms->copiousCalls)) {
1172 struct rx_connection *conn;
1173 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1174 RXKST_SERVICEID, sc, scIndex);
1176 code = RepeatLoadTest (parms, conn);
1177 rx_DestroyConnection (conn);
1178 } else code = RXKST_NEWCONNFAILED;
1180 if (!code && parms->stopServer) {
1181 struct rx_connection *conn;
1182 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1183 RXKST_SERVICEID, sc, scIndex);
1185 code = RXKST_Kill (conn);
1187 com_err (whoami, code, "trying to stop server");
1189 rx_DestroyConnection (conn);
1190 } else code = RXKST_NEWCONNFAILED;
1193 if (parms->printStats) {
1194 rx_PrintStats (stdout);
1196 /* use rxdebug style iteration here */
1197 rx_PrintPeerStats (stdout, rx_PeerOf(conn));
1204 com_err (parms->whoami, code, "test fails");
1207 printf ("Test Okay\n");
1208 if (!parms->noExit) exit (0);