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)
36 static long GetServer(aname)
39 register struct hostent *th;
42 th = gethostbyname(aname);
44 fprintf (stderr, "host %s not found\n", aname);
47 bcopy(th->h_addr, &addr, sizeof(addr));
51 static long GetTicket (versionP, session, ticketLenP, ticket)
53 OUT struct ktc_encryptionKey *session;
59 /* create random session key, using key for seed to good random */
60 des_init_random_number_generator (&serviceKey);
61 code = des_random_key (session);
62 if (code) return code;
64 /* now create the actual ticket */
66 code = tkt_MakeTicket(ticket, ticketLenP, &serviceKey,
67 RXKST_CLIENT_NAME, RXKST_CLIENT_INST, "",
68 /*start,end*/0, 0xffffffff, session, /*host*/0,
69 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
70 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
71 * instance and cell, start time, end time, session key to seal in ticket,
72 * inet host, server name and server instance */
73 if (code) return code;
74 *versionP = serviceKeyVersion;
78 struct rx_connection *conn;
79 u_long sendLen; /* parameters for call to Copious */
81 u_long *fastCalls; /* number of calls to perform */
86 static long Copious (c, buf, buflen)
94 long inlen = c->sendLen;
95 long outlen = c->recvLen;
101 for (i=0; i<inlen; i++) mysum += (d++ & 0xff);
103 call = rx_NewCall (c->conn);
104 code = StartRXKST_Copious (call, inlen, mysum, outlen);
110 while (xfer < inlen) {
112 if (tlen > buflen) tlen = buflen;
113 for (i=0; i<tlen; i++) buf[i] = (d++ & 0xff);
114 n = rx_Write (call, buf, tlen);
117 else code = RXKST_WRITESHORT;
125 while (xfer < outlen) {
126 tlen = outlen - xfer;
127 if (tlen > buflen) tlen = buflen;
128 n = rx_Read (call, buf, tlen);
131 else code = RXKST_READSHORT;
134 for (i=0; i<tlen; i++) mysum += buf[i];
140 code = EndRXKST_Copious (call, &outsum);
141 code = rx_EndCall(call, code);
142 if (code) return code;
143 if (outsum != mysum) {
144 return RXKST_BADOUTPUTSUM;
149 static long DoClient (index, rock)
153 struct client *c = (struct client *)rock;
159 for (i=0; i<c->fastCalls[index]; i++) {
160 code = RXKST_Fast (c->conn, n, &inc_n);
161 if (code) return (code);
162 if (n+1 != inc_n) return RXKST_INCFAILED;
166 for (i=0; i<c->slowCalls[index]; i++) {
169 code = RXKST_Slow (c->conn, 1, &ntime);
170 if (code) return (code);
171 now = FT_ApproxTime();
172 if ((ntime < now-maxSkew) || (ntime > now+maxSkew)) return RXKST_TIMESKEW;
175 if (c->copiousCalls[index] > 0) {
176 u_long buflen = 10000;
177 u_char *buf = (u_char *) osi_Alloc (buflen);
178 for (i=0; i<c->copiousCalls[index]; i++) {
179 code = Copious (c, buf, buflen);
182 osi_Free (buf, buflen);
183 if (code) return code;
190 long exitCode; /* is PROCESSRUNNING until exit */
196 #ifdef AFS_PTHREAD_ENV
197 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
198 static pthread_mutex_t workerLock;
199 static pthread_cond_t workerCV;
200 void WorkerInit(void)
202 pthread_mutex_init(&workerLock, NULL);
203 pthread_cond_init(&workerCV, NULL);
206 static struct worker *workers;
208 static long DoWorker (w)
212 code = (*w->proc) (w->index, w->rock);
213 #ifdef AFS_PTHREAD_ENV
214 pthread_mutex_lock(&workerLock);
217 #ifdef AFS_PTHREAD_ENV
218 pthread_mutex_unlock(&workerLock);
220 #ifdef AFS_PTHREAD_ENV
221 pthread_cond_signal(&workerCV);
223 LWP_NoYieldSignal(&workers);
228 #define MAX_CTHREADS 25
230 static long CallSimultaneously (threads, rock, proc)
237 #ifdef AFS_PTHREAD_ENV
238 pthread_once(&workerOnce, WorkerInit);
242 for (i=0; i<threads; i++) {
244 #ifdef AFS_PTHREAD_ENV
249 assert (i < MAX_CTHREADS);
250 w = (struct worker *) osi_Alloc (sizeof(struct worker));
251 bzero (w, sizeof(*w));
255 w->exitCode = RXKST_PROCESSRUNNING;
258 #ifdef AFS_PTHREAD_ENV
260 pthread_attr_t tattr;
262 code = pthread_attr_init(&tattr);
264 com_err (whoami, code, "can't pthread_attr_init worker process");
268 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
270 com_err (whoami, code, "can't pthread_attr_setdetachstate worker process");
274 code = pthread_create(&pid, &tattr, DoWorker, (void*)w);
277 code = LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
278 (opaque) w, "Worker Process", &pid);
281 com_err (whoami, code, "can't create worker process");
285 code = 0; /* last non-zero code encountered */
286 #ifdef AFS_PTHREAD_ENV
287 pthread_mutex_lock(&workerLock);
290 struct worker *w, *prevW, *nextW;
292 for (w=workers; w; w=nextW) {
294 if (w->exitCode != RXKST_PROCESSRUNNING) {
296 if (code == 0) code = w->exitCode;
298 if (prevW) prevW->next = w->next;
299 else workers = w->next;
300 osi_Free (w, sizeof(*w));
301 continue; /* don't bump prevW */
305 #ifdef AFS_PTHREAD_ENV
307 pthread_cond_wait(&workerCV, &workerLock);
309 if (workers) LWP_WaitProcess (&workers);
312 #ifdef AFS_PTHREAD_ENV
313 pthread_mutex_unlock(&workerLock);
318 static void DivideUpCalls (calls, threads, threadCalls)
321 IN u_long threadCalls[];
324 for (i=0; i<threads; i++) {
325 threadCalls[i] = calls / (threads-i);
326 calls -= threadCalls[i];
330 static double ftime ()
333 gettimeofday(&tv, 0);
334 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
337 static long RunLoadTest (parms, conn)
338 IN struct clientParms *parms;
339 IN struct rx_connection *conn;
343 u_long fastCalls[MAX_CTHREADS];
344 u_long slowCalls[MAX_CTHREADS];
345 u_long copiousCalls[MAX_CTHREADS];
346 double start, interval;
348 DivideUpCalls (parms->fastCalls, parms->threads, fastCalls);
349 DivideUpCalls (parms->slowCalls, parms->threads, slowCalls);
350 DivideUpCalls (parms->copiousCalls, parms->threads, copiousCalls);
352 bzero (&c, sizeof(c));
354 c.sendLen = parms->sendLen;
355 c.recvLen = parms->recvLen;
356 c.fastCalls = fastCalls;
357 c.slowCalls = slowCalls;
358 c.copiousCalls = copiousCalls;
361 code = CallSimultaneously (parms->threads, &c, DoClient);
363 com_err (whoami, code, "in DoClient");
366 interval = ftime() - start;
368 if (parms->printTiming) {
370 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
371 int t = (interval / totalCalls) * 1000.0 + 0.5;
372 if (totalCalls > 0) {
373 printf ("For %d calls: %d msec/call\n", totalCalls, t);
375 if (parms->copiousCalls > 0) {
376 long n = parms->sendLen + parms->recvLen;
379 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
382 I just cannot get printing of floats to work on the pmax!!!!
383 printf ("%g %d %d %d\n", (float)kbps, b);
384 printf ("%g %d %d %d\n", kbps, b);
385 fprintf (stdout, "%g %d %d\n", kbps, b);
388 buf[sizeof(buf)-1] = 0;
389 sprintf (buf, "%g %d %d\n", kbps, b);
390 assert (buf[sizeof(buf)-1] == 0);
394 printf ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
395 parms->copiousCalls, parms->sendLen, parms->recvLen,
398 printf ("%g\n", kbps);
405 static long RepeatLoadTest (parms, conn)
406 IN struct clientParms *parms;
407 IN struct rx_connection *conn;
412 if (parms->repeatInterval == 0) {
413 if (parms->repeatCount == 0) parms->repeatCount = 1;
415 if (parms->repeatCount == 0) parms->repeatCount = 0x7fffffff;
418 if (parms->printTiming) {
420 types = (parms->fastCalls ? 1 : 0) +
421 (parms->slowCalls ? 1 : 0) + (parms->copiousCalls ? 1 : 0);
423 fprintf (stderr, "Combined timings of several types of calls may not be meaningful.\n");
425 /* do timings of copious calls by default */
426 parms->copiousCalls = 10;
429 for (count=0; count<parms->repeatCount; count++) {
430 code = RunLoadTest (parms, conn);
431 if (code) return code;
432 if (parms->repeatInterval) {
433 u_long i = parms->repeatInterval;
434 u_long now = time(0);
435 u_long next = (now + i-1) / i * i; /* round up to next interval */
437 #ifdef AFS_PTHREAD_ENV
440 IOMGR_Sleep (next-now);
449 /* For backward compatibility, don't try to use the CallNumber stuff unless
450 * we're compiling against the new Rx. */
452 #ifdef rx_GetPacketCksum
454 struct multiChannel {
455 struct rx_connection *conn;
458 int changes[RX_MAXCALLS];
459 long callNumbers[RX_MAXCALLS];
461 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
462 static u_long sequence = 0;
464 static long FastCall (conn)
465 IN struct rx_connection *conn;
468 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
471 code = RXKST_Fast (conn, n, &inc_n);
472 if (code) return code;
473 if (inc_n != n+1) return RXKST_INCFAILED;
477 static long UniChannelCall (index, rock)
481 struct multiChannel *mc = (struct multiChannel *)rock;
483 long callNumbers[RX_MAXCALLS];
488 while (!mc->done && unchanged) {
490 code = FastCall (mc->conn);
492 code = rxi_GetCallNumberVector (mc->conn, callNumbers);
495 for (i=0; i<RX_MAXCALLS; i++) {
496 if (callNumbers[i] > mc->callNumbers[i]) {
497 mc->callNumbers[i] = callNumbers[i];
498 mc->changes[i]--; /* may go negative */
500 if (mc->changes[i] > 0) unchanged++;
503 mc->codes[index] = code;
508 static long MakeMultiChannelCall (conn, each, expectedCode, codes)
509 IN struct rx_connection *conn;
510 IN int each; /* calls to make on each channel */
511 IN long expectedCode;
516 struct multiChannel mc;
518 bzero (&mc, sizeof(mc));
520 for (i=0; i<RX_MAXCALLS; i++) {
521 codes[i] = RXKST_PROCESSRUNNING;
522 mc.changes[i] = each;
525 code = rxi_GetCallNumberVector (conn, mc.callNumbers);
526 if (code) return code;
528 code = CallSimultaneously (RX_MAXCALLS, &mc, UniChannelCall);
529 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) &&
530 ((code == expectedCode) || (code == -3))) ; /* strange cases */
531 else if (code != expectedCode) {
532 com_err (whoami, code,
533 "problem making multichannel call, expected '%s'",
535 ? "no error" : (char *)error_message (expectedCode)));
540 static long CheckCallFailure (conn, codes, code, msg)
541 IN struct rx_connection *conn;
547 fprintf (stderr, "Failed to detect %s\n", msg);
548 return RXKST_NODUPLICATECALL;
553 for (i=0; i<RX_MAXCALLS; i++)
554 if (!((codes[i] == 0) || (codes[i] == code) ||
555 (codes[i] == -3))) okay = 0;
556 if (conn->error) okay = 0;
558 fprintf (stderr, "%s produced these errors:\n", msg);
559 for (i=0; i<RX_MAXCALLS; i++) {
560 assert (codes[i] != RXKST_PROCESSRUNNING);
563 fprintf (stderr, " %d no error\n", i);
564 } else fprintf (stderr,
565 " %d %s\n", i, error_message(codes[i]));
569 sprintf (buf, "connection dead following %s", msg);
570 code = FastCall (conn);
571 if (code) com_err (whoami, code, buf);
578 #endif rx_GetPacketCksum
580 static long RunCallTest (parms, host, sc, si)
581 IN struct clientParms *parms;
583 IN struct rx_securityClass *sc;
588 #ifndef rx_GetPacketCksum
590 code = RXKST_BADARGS;
591 com_err (whoami, code, "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
597 struct rx_connection *conn;
599 long callNumbers[RX_MAXCALLS];
600 long codes[RX_MAXCALLS];
601 long retCode = 0; /* ret. if nothing fatal goes wrong */
603 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
605 if (!conn) return RXKST_NEWCONNFAILED;
607 /* First check the basic behaviour of call number handling */
609 code = rxi_GetCallNumberVector (conn, callNumbers);
610 if (code) return code;
611 for (i=0; i<RX_MAXCALLS; i++) {
612 if (callNumbers[i] != 0) {
613 fprintf (stderr, "Connection's initial call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
614 return RXKST_BADCALLNUMBERS;
617 code = FastCall (conn);
618 if (code) return code;
619 code = rxi_GetCallNumberVector (conn, callNumbers);
620 if (code) return code;
621 firstCall = callNumbers[0];
622 code = FastCall (conn);
623 if (code) return code;
624 code = rxi_GetCallNumberVector (conn, callNumbers);
625 if (code) return code;
626 if ((callNumbers[0] != firstCall+1) &&
627 ((firstCall == 1) || (firstCall == 2))) {
628 /* The call number after the first call should be one or, more likely,
629 * two (if the call is still DALLYing). Between first and second call,
630 * the call number should have incremented by one. */
631 fprintf (stderr, "Connection's first channel call number not one. call[%d] = %d\n", 0, callNumbers[0]);
632 return RXKST_BADCALLNUMBERS;
634 for (i=1; i<RX_MAXCALLS; i++) {
635 if (callNumbers[i] != 0) {
636 fprintf (stderr, "Connection's other channel call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
637 return RXKST_BADCALLNUMBERS;
640 code = MakeMultiChannelCall (conn, 1, 0, codes);
641 if (code) return code;
643 /* Now try to resend a call that's already been executed by finding a
644 * non-zero call number on a channel other than zero and decrementing it by
645 * one. This should appear to the server as a retransmitted call. Since
646 * this is behaving as a broken client different strange behaviors may be
647 * exhibited by different servers. If the response packet to the original
648 * call is discarded by the time the "retransmitted" call arrives (perhaps
649 * due to high server or client load) there is no way for the server to
650 * respond at all. Further, it seems, that under some cases the connection
651 * will be kept alive indefinitely even though the server has discarded the
652 * "retransmitted" call and is making no effort to reexecute the call. To
653 * handle these, accept either a timeout (-1) or and INCFAILED error here,
654 * also set the connenction HardDeadTime to punt after a reasonable
657 /* short dead time since may we expect some trouble */
658 rx_SetConnHardDeadTime (conn, 30);
659 code = rxi_GetCallNumberVector (conn, callNumbers);
660 if (code) return code;
661 for (ch=1; ch<RX_MAXCALLS; ch++)
662 if (callNumbers[ch] > 1) {
664 code = rxi_SetCallNumberVector (conn, callNumbers);
665 if (code) return code;
668 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
669 return RXKST_BADCALLNUMBERS;
670 code = MakeMultiChannelCall (conn, 1, RXKST_INCFAILED, codes);
671 code = CheckCallFailure (conn, codes, code, "retransmitted call");
672 if (code && !retCode) retCode = code;
674 /* Get a fresh connection, becasue if the above failed as it should the
675 * connection is dead. */
676 rx_DestroyConnection (conn);
677 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
679 if (!conn) return RXKST_NEWCONNFAILED;
681 /* Similarly, but decrement call number by two which should be completely
682 * unmistakeable as a broken or malicious client. */
684 /* short dead time since may we expect some trouble */
685 rx_SetConnHardDeadTime (conn, 30);
686 code = MakeMultiChannelCall (conn, 2, 0, codes);
687 if (code) return code;
688 code = rxi_GetCallNumberVector (conn, callNumbers);
689 if (code) return code;
690 for (ch=1; ch<RX_MAXCALLS; ch++)
691 if (callNumbers[ch] > 2) {
692 callNumbers[ch] -= 2;
693 code = rxi_SetCallNumberVector (conn, callNumbers);
696 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
697 return RXKST_BADCALLNUMBERS;
698 code = MakeMultiChannelCall (conn, 1, -1, codes);
699 code = CheckCallFailure (conn, codes, code, "duplicate call");
700 if (code && !retCode) retCode = code;
702 rx_DestroyConnection (conn);
703 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
705 if (!conn) return RXKST_NEWCONNFAILED;
707 /* Next, without waiting for the server to discard its state, we will check
708 * to see if the Challenge/Response protocol correctly informs the server
709 * of the client's callNumber state. We do this by artificially increasing
710 * the call numbers of a new connection for all channels beyond zero,
711 * making a call on channel zero, then resetting the call number for the
712 * unused channels back to zero, then making calls on all channels. */
714 code = rxi_GetCallNumberVector (conn, callNumbers);
715 if (code) return code;
716 for (i=0; i<RX_MAXCALLS; i++) {
717 if (callNumbers[i] != 0) return RXKST_BADCALLNUMBERS;
718 callNumbers[i] = 51; /* an arbitrary value... */
720 code = rxi_SetCallNumberVector (conn, callNumbers);
721 if (code) return code;
722 code = FastCall (conn); /* use channel 0 */
723 if (code) return code;
724 code = rxi_GetCallNumberVector (conn, callNumbers);
725 if (code) return code;
726 if (callNumbers[0] != 52) return RXKST_BADCALLNUMBERS;
727 for (i=1; i<RX_MAXCALLS; i++) {
728 if (callNumbers[i] != 51) return RXKST_BADCALLNUMBERS;
729 callNumbers[i] = 37; /* back up a ways */
731 code = rxi_SetCallNumberVector (conn, callNumbers);
732 if (code) return code;
733 /* now try calls on all channels... */
734 code = MakeMultiChannelCall (conn, 1, -1, codes);
735 code = CheckCallFailure (conn, codes, code,
736 "alternate channel call replay");
737 if (code && !retCode) retCode = code;
739 rx_DestroyConnection (conn);
742 #endif rx_GetPacketCksum
746 #ifdef rx_GetPacketCksum
750 u_long epoch; /* connection to attack */
752 int client; /* TRUE => client side */
753 u_long newEpoch; /* conn to direct challenges to */
755 u_long counts[RX_N_PACKET_TYPES];
759 #define IO_REDIRECTCHALLENGE 2
761 static int HandleIncoming (p, addr)
762 INOUT struct rx_packet *p;
763 INOUT struct sockaddr_in *addr;
765 int client; /* packet sent by client */
766 u_char type; /* packet type */
768 if (incomingOps.op == IO_NOOP) return 0;
770 client = ((p->header.flags&RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
771 if ((p->header.epoch != incomingOps.epoch) ||
772 ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK) ||
773 (client != incomingOps.client)) return 0;
774 type = p->header.type;
775 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
776 incomingOps.counts[type]++;
778 switch (incomingOps.op) {
783 case IO_REDIRECTCHALLENGE:
784 if (p->header.type != RX_PACKET_TYPE_CHALLENGE) break;
785 p->header.epoch = incomingOps.newEpoch;
786 p->header.cid = incomingOps.newCid;
787 /* Now set up to watch for the corresponding challenge. */
788 incomingOps.epoch = incomingOps.newEpoch;
789 incomingOps.cid = incomingOps.newCid;
790 incomingOps.op = IO_COUNT;
794 fprintf (stderr, "Unknown incoming op %d\n", incomingOps.op);
802 u_long epoch; /* connection to attack */
804 int client; /* TRUE => client side */
805 u_long counts[RX_N_PACKET_TYPES];
809 #define OO_ZEROCKSUM 2
810 #define OO_MUNGCKSUM 3
812 static int HandleOutgoing (p, addr)
813 INOUT struct rx_packet *p;
814 INOUT struct sockaddr_in *addr;
816 int client; /* packet sent by client */
817 u_char type; /* packet type */
819 if (outgoingOps.op == OO_NOOP) return 0;
821 client = ((p->header.flags&RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
822 if ((p->header.epoch != outgoingOps.epoch) ||
823 ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK) ||
824 (client != outgoingOps.client)) return 0;
825 type = p->header.type;
826 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
827 outgoingOps.counts[type]++;
829 switch (outgoingOps.op) {
832 /* counting always happens above if not noop */
836 if (p->header.type != RX_PACKET_TYPE_DATA) break;
837 if (rx_GetPacketCksum (p) == 0) {
838 /* probably, a retransmitted packet */
839 fprintf (stderr, "Packet cksum already zero\n");
842 rx_SetPacketCksum (p, 0);
847 if (p->header.type != RX_PACKET_TYPE_DATA) break;
848 cksum = rx_GetPacketCksum (p);
850 fprintf (stderr, "Packet cksum already zero\n");
853 rx_SetPacketCksum (p, cksum ^ 8);
857 fprintf (stderr, "Unknown outgoing op %d\n", outgoingOps.op);
863 #ifdef AFS_PTHREAD_ENV
864 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
865 static pthread_mutex_t slowCallLock;
866 static pthread_cond_t slowCallCV;
867 void SlowCallInit(void)
869 pthread_mutex_init(&slowCallLock, NULL);
870 pthread_cond_init(&slowCallCV, NULL);
873 static long slowCallCode;
874 static long SlowCall (conn)
881 #ifdef AFS_PTHREAD_ENV
882 pthread_mutex_lock(&slowCallLock);
884 slowCallCode = RXKST_PROCESSRUNNING;
885 #ifdef AFS_PTHREAD_ENV
886 pthread_cond_signal(&slowCallCV);
888 LWP_NoYieldSignal (&slowCallCode);
890 slowCallCode = RXKST_Slow (conn, 1, &ntime);
892 now = FT_ApproxTime();
893 if ((ntime < now-maxSkew) || (ntime > now+maxSkew))
894 slowCallCode = RXKST_TIMESKEW;
896 temp_rc = slowCallCode;
897 #ifdef AFS_PTHREAD_ENV
898 pthread_cond_signal(&slowCallCV);
899 pthread_mutex_unlock(&slowCallLock);
901 LWP_NoYieldSignal (&slowCallCode);
906 #endif rx_GetPacketCksum
908 static long RunHijackTest (parms, host, sc, si)
909 IN struct clientParms *parms;
911 IN struct rx_securityClass *sc;
915 #ifndef rx_GetPacketCksum
917 code = RXKST_BADARGS;
918 com_err (whoami, code, "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
923 extern int (*rx_justReceived)();
924 extern int (*rx_almostSent)();
927 struct rx_connection *conn = 0;
928 struct rx_connection *otherConn = 0;
929 #ifdef AFS_PTHREAD_ENV
934 int nResp; /* otherConn responses seen */
937 #ifdef AFS_PTHREAD_ENV
938 pthread_once(&slowCallOnce, SlowCallInit);
940 rx_justReceived = HandleIncoming;
941 rx_almostSent = HandleOutgoing;
943 incomingOps.op = IO_NOOP;
944 outgoingOps.op = OO_NOOP;
946 #define HIJACK_CONN(conn) \
947 { if (conn) rx_DestroyConnection (conn); \
948 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
949 RXKST_SERVICEID, sc, si); \
950 if (!(conn)) return RXKST_NEWCONNFAILED; \
951 outgoingOps.client = 1; \
952 outgoingOps.epoch = (conn)->epoch; \
953 outgoingOps.cid = (conn)->cid; }
957 /* First try switching from no packet cksum to sending packet cksum between
958 * calls, and see if server complains. */
960 outgoingOps.op = OO_ZEROCKSUM;
961 code = FastCall (conn);
963 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
966 /* The server thinks we're an old style client. Now start sending cksums.
967 * Server shouldn't care. */
968 outgoingOps.op = OO_NOOP;
969 code = FastCall (conn);
971 com_err (whoami, code, "doing FastCall with non-ZEROCKSUM");
974 /* The server now thinks we're a new style client, we can't go back now. */
975 outgoingOps.op = OO_ZEROCKSUM;
976 code = FastCall (conn);
977 if (code == 0) code = RXKST_NOBADCKSUM;
978 if (code != RXKADSEALEDINCON) {
979 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
981 } else if (!conn->error) {
982 code = RXKST_NOCONNERROR;
983 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
989 /* Now try modifying packet cksum to see if server complains. */
991 outgoingOps.op = OO_MUNGCKSUM;
992 code = FastCall (conn);
993 if (code == 0) code = RXKST_NOBADCKSUM;
994 if (code != RXKADSEALEDINCON) {
995 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
997 } else if (!conn->error) {
998 code = RXKST_NOCONNERROR;
999 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1003 /* Now make two connection and direct the first challenge on one connection
1004 * to the other connection to see if it generates a response. The
1005 * retransmitted challenge should allow the call on the first connection to
1006 * complete correctly. Part one is to attack a new connection, then attack
1007 * it after it has made a call. Part three, just for comparison, attacks a
1008 * otherConn while it is making a slow call (and thus has an active call).
1009 * Against this attack we have no defense so we expect a challenge in this
1010 * case, which the server will discard. */
1012 #define RedirectChallenge(conn,otherConn) \
1013 (incomingOps.epoch = (conn)->epoch, \
1014 incomingOps.cid = (conn)->cid, \
1015 incomingOps.client = 1, \
1016 incomingOps.newEpoch = (otherConn)->epoch, \
1017 incomingOps.newCid = (otherConn)->cid, \
1018 incomingOps.op = IO_REDIRECTCHALLENGE, \
1019 outgoingOps.epoch = (otherConn)->epoch, \
1020 outgoingOps.cid = (otherConn)->cid, \
1021 outgoingOps.client = 1, \
1022 outgoingOps.op = OO_COUNT, \
1023 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1026 HIJACK_CONN(otherConn)
1027 RedirectChallenge (conn, otherConn);
1029 code = FastCall (conn);
1030 if (code) return code;
1031 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1032 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1034 code = RXKST_CHALLENGEORACLE;
1035 com_err (whoami, code, "misdirecting challenge");
1038 code = FastCall (otherConn); /* generate some activity here */
1039 if (code) return code;
1040 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1041 assert (nResp >= 1);
1042 code = FastCall (conn);
1043 if (code) return code;
1044 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp) goto oracle;
1047 RedirectChallenge (conn, otherConn);
1048 /* otherConn was authenticated during part one */
1049 code = FastCall (conn);
1050 if (code) return code;
1051 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1052 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0) goto oracle;
1055 RedirectChallenge (conn, otherConn);
1056 /* otherConn is still authenticated */
1057 slowCallCode = RXKST_PROCESSCREATED;
1058 #ifdef AFS_PTHREAD_ENV
1060 pthread_attr_t tattr;
1062 code = pthread_attr_init(&tattr);
1064 com_err (whoami, code, "can't pthread_attr_init slow call process");
1068 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1070 com_err (whoami, code, "can't pthread_attr_setdetachstate slow call process");
1074 code = pthread_create(&pid, &tattr, SlowCall, (void*) otherConn);
1077 code = LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1078 (opaque) otherConn, "Slow Call Process", &pid);
1081 com_err (whoami, code, "can't create slow call process");
1084 #ifdef AFS_PTHREAD_ENV
1085 pthread_mutex_lock(&slowCallLock);
1086 while (slowCallCode == RXKST_PROCESSCREATED)
1087 pthread_cond_wait(&slowCallCV, &slowCallLock);
1089 while (slowCallCode == RXKST_PROCESSCREATED)
1090 LWP_WaitProcess (&slowCallCode); /* wait for process start */
1092 if (slowCallCode != RXKST_PROCESSRUNNING) {
1093 tmp_rc = slowCallCode;
1094 #ifdef AFS_PTHREAD_ENV
1095 pthread_mutex_unlock(&slowCallLock);
1097 return tmp_rc; /* make sure didn't fail immediately */
1099 assert (incomingOps.op == IO_REDIRECTCHALLENGE);
1100 code = FastCall (conn);
1101 if (code) return code;
1102 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1103 #ifdef AFS_PTHREAD_ENV
1104 while (slowCallCode == RXKST_PROCESSRUNNING)
1105 pthread_cond_wait(&slowCallCV, &slowCallLock);
1106 pthread_mutex_unlock(&slowCallLock);
1108 while (slowCallCode == RXKST_PROCESSRUNNING)
1109 LWP_WaitProcess (&slowCallCode); /* wait for process finish */
1111 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1) goto oracle;
1113 rx_justReceived = 0;
1115 rx_DestroyConnection (otherConn);
1116 rx_DestroyConnection (conn);
1119 #endif rx_GetPacketCksum
1123 long rxkst_StartClient (parms)
1124 IN struct clientParms *parms;
1129 struct rx_securityClass *sc;
1131 whoami = parms->whoami; /* set this global variable */
1133 host = GetServer (parms->server);
1135 if (parms->authentication >= 0) {
1137 char ticket[MAXKTCTICKETLEN];
1139 struct ktc_encryptionKey Ksession;
1141 code = GetTicket (&kvno, &Ksession, &ticketLen, ticket);
1142 if (code) return code;
1144 /* next, we have ticket, kvno and session key, authenticate the conn */
1145 sc = (struct rx_securityClass *)
1146 rxkad_NewClientSecurityObject (parms->authentication,
1147 &Ksession, kvno, ticketLen, ticket);
1149 scIndex = 2; /* kerberos security index */
1151 /* unauthenticated connection */
1152 sc = (struct rx_securityClass *) rxnull_NewClientSecurityObject ();
1154 scIndex = 0; /* null security index */
1158 if (!code && parms->callTest) {
1159 code = RunCallTest (parms, host, sc, scIndex);
1161 if (!code && parms->hijackTest) {
1162 code = RunHijackTest (parms, host, sc, scIndex);
1165 (parms->printTiming ||
1166 parms->fastCalls || parms->slowCalls || parms->copiousCalls)) {
1167 struct rx_connection *conn;
1168 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1169 RXKST_SERVICEID, sc, scIndex);
1171 code = RepeatLoadTest (parms, conn);
1172 rx_DestroyConnection (conn);
1173 } else code = RXKST_NEWCONNFAILED;
1175 if (!code && parms->stopServer) {
1176 struct rx_connection *conn;
1177 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1178 RXKST_SERVICEID, sc, scIndex);
1180 code = RXKST_Kill (conn);
1182 com_err (whoami, code, "trying to stop server");
1184 rx_DestroyConnection (conn);
1185 } else code = RXKST_NEWCONNFAILED;
1188 if (parms->printStats) {
1189 rx_PrintStats (stdout);
1191 /* use rxdebug style iteration here */
1192 rx_PrintPeerStats (stdout, rx_PeerOf(conn));
1199 com_err (parms->whoami, code, "test fails");
1202 printf ("Test Okay\n");
1203 if (!parms->noExit) exit (0);