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>
31 #include "stress_internal.h"
32 #ifdef AFS_PTHREAD_ENV
34 #define FT_ApproxTime() (int)time(0)
41 static long GetServer(aname)
44 register struct hostent *th;
47 th = gethostbyname(aname);
49 fprintf (stderr, "host %s not found\n", aname);
52 memcpy(&addr, th->h_addr, sizeof(addr));
56 static long GetToken (versionP, session, ticketLenP, ticket, cell)
58 OUT struct ktc_encryptionKey *session;
62 struct ktc_principal sname;
63 struct ktc_token ttoken;
66 strcpy(sname.cell, cell);
67 sname.instance[0] = 0;
68 strcpy(sname.name, "afs");
69 code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
70 if (code) return code;
72 *versionP = ttoken.kvno;
73 *ticketLenP = ttoken.ticketLen;
74 memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
75 memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
79 static long GetTicket (versionP, session, ticketLenP, ticket, cell)
81 OUT struct ktc_encryptionKey *session;
87 /* create random session key, using key for seed to good random */
88 des_init_random_number_generator (&serviceKey);
89 code = des_random_key (session);
90 if (code) return code;
92 /* now create the actual ticket */
94 code = tkt_MakeTicket(ticket, ticketLenP, &serviceKey,
95 RXKST_CLIENT_NAME, RXKST_CLIENT_INST, cell,
96 /*start,end*/0, 0xffffffff, session, /*host*/0,
97 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
98 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
99 * instance and cell, start time, end time, session key to seal in ticket,
100 * inet host, server name and server instance */
101 if (code) return code;
102 *versionP = serviceKeyVersion;
106 struct rx_connection *conn;
107 u_long sendLen; /* parameters for call to Copious */
109 u_long *fastCalls; /* number of calls to perform */
111 u_long *copiousCalls;
114 static long Copious (c, buf, buflen)
120 struct rx_call *call;
122 long inlen = c->sendLen;
123 long outlen = c->recvLen;
129 for (i=0; i<inlen; i++) mysum += (d++ & 0xff);
131 call = rx_NewCall (c->conn);
132 code = StartRXKST_Copious (call, inlen, mysum, outlen);
138 while (xfer < inlen) {
140 if (tlen > buflen) tlen = buflen;
141 for (i=0; i<tlen; i++) buf[i] = (d++ & 0xff);
142 n = rx_Write (call, buf, tlen);
145 else code = RXKST_WRITESHORT;
153 while (xfer < outlen) {
154 tlen = outlen - xfer;
155 if (tlen > buflen) tlen = buflen;
156 n = rx_Read (call, buf, tlen);
159 else code = RXKST_READSHORT;
162 for (i=0; i<tlen; i++) mysum += buf[i];
168 code = EndRXKST_Copious (call, &outsum);
169 code = rx_EndCall(call, code);
170 if (code) return code;
171 if (outsum != mysum) {
172 return RXKST_BADOUTPUTSUM;
177 static long DoClient (index, rock)
181 struct client *c = (struct client *)rock;
187 for (i=0; i<c->fastCalls[index]; i++) {
188 code = RXKST_Fast (c->conn, n, &inc_n);
189 if (code) return (code);
190 if (n+1 != inc_n) return RXKST_INCFAILED;
194 for (i=0; i<c->slowCalls[index]; i++) {
197 code = RXKST_Slow (c->conn, 1, &ntime);
198 if (code) return (code);
199 now = FT_ApproxTime();
200 if ((ntime < now-maxSkew) || (ntime > now+maxSkew)) return RXKST_TIMESKEW;
203 if (c->copiousCalls[index] > 0) {
204 u_long buflen = 10000;
205 u_char *buf = (u_char *) osi_Alloc (buflen);
206 for (i=0; i<c->copiousCalls[index]; i++) {
207 code = Copious (c, buf, buflen);
210 osi_Free (buf, buflen);
211 if (code) return code;
218 long exitCode; /* is PROCESSRUNNING until exit */
224 #ifdef AFS_PTHREAD_ENV
225 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
226 static pthread_mutex_t workerLock;
227 static pthread_cond_t workerCV;
228 void WorkerInit(void)
230 pthread_mutex_init(&workerLock, NULL);
231 pthread_cond_init(&workerCV, NULL);
234 static struct worker *workers;
236 static long DoWorker (w)
240 code = (*w->proc) (w->index, w->rock);
241 #ifdef AFS_PTHREAD_ENV
242 pthread_mutex_lock(&workerLock);
245 #ifdef AFS_PTHREAD_ENV
246 pthread_mutex_unlock(&workerLock);
248 #ifdef AFS_PTHREAD_ENV
249 pthread_cond_signal(&workerCV);
251 LWP_NoYieldSignal(&workers);
256 #define MAX_CTHREADS 25
258 static long CallSimultaneously (threads, rock, proc)
265 #ifdef AFS_PTHREAD_ENV
266 pthread_once(&workerOnce, WorkerInit);
270 for (i=0; i<threads; i++) {
272 #ifdef AFS_PTHREAD_ENV
277 assert (i < MAX_CTHREADS);
278 w = (struct worker *) osi_Alloc (sizeof(struct worker));
279 memset(w, 0, sizeof(*w));
283 w->exitCode = RXKST_PROCESSRUNNING;
286 #ifdef AFS_PTHREAD_ENV
288 pthread_attr_t tattr;
290 code = pthread_attr_init(&tattr);
292 com_err (whoami, code, "can't pthread_attr_init worker process");
296 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
298 com_err (whoami, code, "can't pthread_attr_setdetachstate worker process");
302 code = pthread_create(&pid, &tattr, DoWorker, (void*)w);
305 code = LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
306 (opaque) w, "Worker Process", &pid);
309 com_err (whoami, code, "can't create worker process");
313 code = 0; /* last non-zero code encountered */
314 #ifdef AFS_PTHREAD_ENV
315 pthread_mutex_lock(&workerLock);
318 struct worker *w, *prevW, *nextW;
320 for (w=workers; w; w=nextW) {
322 if (w->exitCode != RXKST_PROCESSRUNNING) {
324 if (code == 0) code = w->exitCode;
326 if (prevW) prevW->next = w->next;
327 else workers = w->next;
328 osi_Free (w, sizeof(*w));
329 continue; /* don't bump prevW */
333 #ifdef AFS_PTHREAD_ENV
335 pthread_cond_wait(&workerCV, &workerLock);
337 if (workers) LWP_WaitProcess (&workers);
340 #ifdef AFS_PTHREAD_ENV
341 pthread_mutex_unlock(&workerLock);
346 static void DivideUpCalls (calls, threads, threadCalls)
349 IN u_long threadCalls[];
352 for (i=0; i<threads; i++) {
353 threadCalls[i] = calls / (threads-i);
354 calls -= threadCalls[i];
358 static double ftime ()
361 gettimeofday(&tv, 0);
362 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
365 static long RunLoadTest (parms, conn)
366 IN struct clientParms *parms;
367 IN struct rx_connection *conn;
371 u_long fastCalls[MAX_CTHREADS];
372 u_long slowCalls[MAX_CTHREADS];
373 u_long copiousCalls[MAX_CTHREADS];
374 double start, interval;
376 DivideUpCalls (parms->fastCalls, parms->threads, fastCalls);
377 DivideUpCalls (parms->slowCalls, parms->threads, slowCalls);
378 DivideUpCalls (parms->copiousCalls, parms->threads, copiousCalls);
380 memset(&c, 0, sizeof(c));
382 c.sendLen = parms->sendLen;
383 c.recvLen = parms->recvLen;
384 c.fastCalls = fastCalls;
385 c.slowCalls = slowCalls;
386 c.copiousCalls = copiousCalls;
389 code = CallSimultaneously (parms->threads, &c, DoClient);
391 com_err (whoami, code, "in DoClient");
394 interval = ftime() - start;
396 if (parms->printTiming) {
398 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
399 int t = (interval / totalCalls) * 1000.0 + 0.5;
400 if (totalCalls > 0) {
401 printf ("For %d calls: %d msec/call\n", totalCalls, t);
403 if (parms->copiousCalls > 0) {
404 long n = parms->sendLen + parms->recvLen;
407 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
410 I just cannot get printing of floats to work on the pmax!!!!
411 printf ("%g %d %d %d\n", (float)kbps, b);
412 printf ("%g %d %d %d\n", kbps, b);
413 fprintf (stdout, "%g %d %d\n", kbps, b);
416 buf[sizeof(buf)-1] = 0;
417 sprintf (buf, "%g %d %d\n", kbps, b);
418 assert (buf[sizeof(buf)-1] == 0);
422 printf ("For %d copious calls, %d send + %d recv = %d bytes each: %d kbytes/sec\n",
423 parms->copiousCalls, parms->sendLen, parms->recvLen,
426 printf ("%g\n", kbps);
433 static long RepeatLoadTest (parms, conn)
434 IN struct clientParms *parms;
435 IN struct rx_connection *conn;
440 if (parms->repeatInterval == 0) {
441 if (parms->repeatCount == 0) parms->repeatCount = 1;
443 if (parms->repeatCount == 0) parms->repeatCount = 0x7fffffff;
446 if (parms->printTiming) {
448 types = (parms->fastCalls ? 1 : 0) +
449 (parms->slowCalls ? 1 : 0) + (parms->copiousCalls ? 1 : 0);
451 fprintf (stderr, "Combined timings of several types of calls may not be meaningful.\n");
453 /* do timings of copious calls by default */
454 parms->copiousCalls = 10;
457 for (count=0; count<parms->repeatCount; count++) {
458 code = RunLoadTest (parms, conn);
459 if (code) return code;
460 if (parms->repeatInterval) {
461 u_long i = parms->repeatInterval;
462 u_long now = time(0);
463 u_long next = (now + i-1) / i * i; /* round up to next interval */
465 #ifdef AFS_PTHREAD_ENV
468 IOMGR_Sleep (next-now);
477 /* For backward compatibility, don't try to use the CallNumber stuff unless
478 * we're compiling against the new Rx. */
480 #ifdef rx_GetPacketCksum
482 struct multiChannel {
483 struct rx_connection *conn;
486 int changes[RX_MAXCALLS];
487 long callNumbers[RX_MAXCALLS];
489 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
490 static u_long sequence = 0;
492 static long FastCall (conn)
493 IN struct rx_connection *conn;
496 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
499 code = RXKST_Fast (conn, n, &inc_n);
500 if (code) return code;
501 if (inc_n != n+1) return RXKST_INCFAILED;
505 static long UniChannelCall (index, rock)
509 struct multiChannel *mc = (struct multiChannel *)rock;
511 long callNumbers[RX_MAXCALLS];
516 while (!mc->done && unchanged) {
518 code = FastCall (mc->conn);
520 code = rxi_GetCallNumberVector (mc->conn, callNumbers);
523 for (i=0; i<RX_MAXCALLS; i++) {
524 if (callNumbers[i] > mc->callNumbers[i]) {
525 mc->callNumbers[i] = callNumbers[i];
526 mc->changes[i]--; /* may go negative */
528 if (mc->changes[i] > 0) unchanged++;
531 mc->codes[index] = code;
536 static long MakeMultiChannelCall (conn, each, expectedCode, codes)
537 IN struct rx_connection *conn;
538 IN int each; /* calls to make on each channel */
539 IN long expectedCode;
544 struct multiChannel mc;
546 memset(&mc, 0, sizeof(mc));
548 for (i=0; i<RX_MAXCALLS; i++) {
549 codes[i] = RXKST_PROCESSRUNNING;
550 mc.changes[i] = each;
553 code = rxi_GetCallNumberVector (conn, mc.callNumbers);
554 if (code) return code;
556 code = CallSimultaneously (RX_MAXCALLS, &mc, UniChannelCall);
557 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) &&
558 ((code == expectedCode) || (code == -3))) ; /* strange cases */
559 else if (code != expectedCode) {
560 com_err (whoami, code,
561 "problem making multichannel call, expected '%s'",
563 ? "no error" : (char *)error_message (expectedCode)));
568 static long CheckCallFailure (conn, codes, code, msg)
569 IN struct rx_connection *conn;
575 fprintf (stderr, "Failed to detect %s\n", msg);
576 return RXKST_NODUPLICATECALL;
581 for (i=0; i<RX_MAXCALLS; i++)
582 if (!((codes[i] == 0) || (codes[i] == code) ||
583 (codes[i] == -3))) okay = 0;
584 if (conn->error) okay = 0;
586 fprintf (stderr, "%s produced these errors:\n", msg);
587 for (i=0; i<RX_MAXCALLS; i++) {
588 assert (codes[i] != RXKST_PROCESSRUNNING);
591 fprintf (stderr, " %d no error\n", i);
592 } else fprintf (stderr,
593 " %d %s\n", i, error_message(codes[i]));
597 sprintf (buf, "connection dead following %s", msg);
598 code = FastCall (conn);
599 if (code) com_err (whoami, code, buf);
606 #endif rx_GetPacketCksum
608 static long RunCallTest (parms, host, sc, si)
609 IN struct clientParms *parms;
611 IN struct rx_securityClass *sc;
616 #ifndef rx_GetPacketCksum
618 code = RXKST_BADARGS;
619 com_err (whoami, code, "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
625 struct rx_connection *conn;
627 long callNumbers[RX_MAXCALLS];
628 long codes[RX_MAXCALLS];
629 long retCode = 0; /* ret. if nothing fatal goes wrong */
631 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
633 if (!conn) return RXKST_NEWCONNFAILED;
635 /* First check the basic behaviour of call number handling */
637 code = rxi_GetCallNumberVector (conn, callNumbers);
638 if (code) return code;
639 for (i=0; i<RX_MAXCALLS; i++) {
640 if (callNumbers[i] != 0) {
641 fprintf (stderr, "Connection's initial call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
642 return RXKST_BADCALLNUMBERS;
645 code = FastCall (conn);
646 if (code) return code;
647 code = rxi_GetCallNumberVector (conn, callNumbers);
648 if (code) return code;
649 firstCall = callNumbers[0];
650 code = FastCall (conn);
651 if (code) return code;
652 code = rxi_GetCallNumberVector (conn, callNumbers);
653 if (code) return code;
654 if ((callNumbers[0] != firstCall+1) &&
655 ((firstCall == 1) || (firstCall == 2))) {
656 /* The call number after the first call should be one or, more likely,
657 * two (if the call is still DALLYing). Between first and second call,
658 * the call number should have incremented by one. */
659 fprintf (stderr, "Connection's first channel call number not one. call[%d] = %d\n", 0, callNumbers[0]);
660 return RXKST_BADCALLNUMBERS;
662 for (i=1; i<RX_MAXCALLS; i++) {
663 if (callNumbers[i] != 0) {
664 fprintf (stderr, "Connection's other channel call numbers not zero. call[%d] = %d\n", i, callNumbers[i]);
665 return RXKST_BADCALLNUMBERS;
668 code = MakeMultiChannelCall (conn, 1, 0, codes);
669 if (code) return code;
671 /* Now try to resend a call that's already been executed by finding a
672 * non-zero call number on a channel other than zero and decrementing it by
673 * one. This should appear to the server as a retransmitted call. Since
674 * this is behaving as a broken client different strange behaviors may be
675 * exhibited by different servers. If the response packet to the original
676 * call is discarded by the time the "retransmitted" call arrives (perhaps
677 * due to high server or client load) there is no way for the server to
678 * respond at all. Further, it seems, that under some cases the connection
679 * will be kept alive indefinitely even though the server has discarded the
680 * "retransmitted" call and is making no effort to reexecute the call. To
681 * handle these, accept either a timeout (-1) or and INCFAILED error here,
682 * also set the connenction HardDeadTime to punt after a reasonable
685 /* short dead time since may we expect some trouble */
686 rx_SetConnHardDeadTime (conn, 30);
687 code = rxi_GetCallNumberVector (conn, callNumbers);
688 if (code) return code;
689 for (ch=1; ch<RX_MAXCALLS; ch++)
690 if (callNumbers[ch] > 1) {
692 code = rxi_SetCallNumberVector (conn, callNumbers);
693 if (code) return code;
696 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
697 return RXKST_BADCALLNUMBERS;
698 code = MakeMultiChannelCall (conn, 1, RXKST_INCFAILED, codes);
699 code = CheckCallFailure (conn, codes, code, "retransmitted call");
700 if (code && !retCode) retCode = code;
702 /* Get a fresh connection, becasue if the above failed as it should the
703 * connection is dead. */
704 rx_DestroyConnection (conn);
705 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
707 if (!conn) return RXKST_NEWCONNFAILED;
709 /* Similarly, but decrement call number by two which should be completely
710 * unmistakeable as a broken or malicious client. */
712 /* short dead time since may we expect some trouble */
713 rx_SetConnHardDeadTime (conn, 30);
714 code = MakeMultiChannelCall (conn, 2, 0, codes);
715 if (code) return code;
716 code = rxi_GetCallNumberVector (conn, callNumbers);
717 if (code) return code;
718 for (ch=1; ch<RX_MAXCALLS; ch++)
719 if (callNumbers[ch] > 2) {
720 callNumbers[ch] -= 2;
721 code = rxi_SetCallNumberVector (conn, callNumbers);
724 if (ch>= RX_MAXCALLS) /* didn't find any? all DALLYing? */
725 return RXKST_BADCALLNUMBERS;
726 code = MakeMultiChannelCall (conn, 1, -1, codes);
727 code = CheckCallFailure (conn, codes, code, "duplicate call");
728 if (code && !retCode) retCode = code;
730 rx_DestroyConnection (conn);
731 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
733 if (!conn) return RXKST_NEWCONNFAILED;
735 /* Next, without waiting for the server to discard its state, we will check
736 * to see if the Challenge/Response protocol correctly informs the server
737 * of the client's callNumber state. We do this by artificially increasing
738 * the call numbers of a new connection for all channels beyond zero,
739 * making a call on channel zero, then resetting the call number for the
740 * unused channels back to zero, then making calls on all channels. */
742 code = rxi_GetCallNumberVector (conn, callNumbers);
743 if (code) return code;
744 for (i=0; i<RX_MAXCALLS; i++) {
745 if (callNumbers[i] != 0) return RXKST_BADCALLNUMBERS;
746 callNumbers[i] = 51; /* an arbitrary value... */
748 code = rxi_SetCallNumberVector (conn, callNumbers);
749 if (code) return code;
750 code = FastCall (conn); /* use channel 0 */
751 if (code) return code;
752 code = rxi_GetCallNumberVector (conn, callNumbers);
753 if (code) return code;
754 if (callNumbers[0] != 52) return RXKST_BADCALLNUMBERS;
755 for (i=1; i<RX_MAXCALLS; i++) {
756 if (callNumbers[i] != 51) return RXKST_BADCALLNUMBERS;
757 callNumbers[i] = 37; /* back up a ways */
759 code = rxi_SetCallNumberVector (conn, callNumbers);
760 if (code) return code;
761 /* now try calls on all channels... */
762 code = MakeMultiChannelCall (conn, 1, -1, codes);
763 code = CheckCallFailure (conn, codes, code,
764 "alternate channel call replay");
765 if (code && !retCode) retCode = code;
767 rx_DestroyConnection (conn);
770 #endif rx_GetPacketCksum
774 #ifdef rx_GetPacketCksum
778 u_long epoch; /* connection to attack */
780 int client; /* TRUE => client side */
781 u_long newEpoch; /* conn to direct challenges to */
783 u_long counts[RX_N_PACKET_TYPES];
787 #define IO_REDIRECTCHALLENGE 2
789 static int HandleIncoming (p, addr)
790 INOUT struct rx_packet *p;
791 INOUT struct sockaddr_in *addr;
793 int client; /* packet sent by client */
794 u_char type; /* packet type */
796 if (incomingOps.op == IO_NOOP) return 0;
798 client = ((p->header.flags&RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
799 if ((p->header.epoch != incomingOps.epoch) ||
800 ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK) ||
801 (client != incomingOps.client)) return 0;
802 type = p->header.type;
803 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
804 incomingOps.counts[type]++;
806 switch (incomingOps.op) {
811 case IO_REDIRECTCHALLENGE:
812 if (p->header.type != RX_PACKET_TYPE_CHALLENGE) break;
813 p->header.epoch = incomingOps.newEpoch;
814 p->header.cid = incomingOps.newCid;
815 /* Now set up to watch for the corresponding challenge. */
816 incomingOps.epoch = incomingOps.newEpoch;
817 incomingOps.cid = incomingOps.newCid;
818 incomingOps.op = IO_COUNT;
822 fprintf (stderr, "Unknown incoming op %d\n", incomingOps.op);
830 u_long epoch; /* connection to attack */
832 int client; /* TRUE => client side */
833 u_long counts[RX_N_PACKET_TYPES];
837 #define OO_ZEROCKSUM 2
838 #define OO_MUNGCKSUM 3
840 static int HandleOutgoing (p, addr)
841 INOUT struct rx_packet *p;
842 INOUT struct sockaddr_in *addr;
844 int client; /* packet sent by client */
845 u_char type; /* packet type */
847 if (outgoingOps.op == OO_NOOP) return 0;
849 client = ((p->header.flags&RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
850 if ((p->header.epoch != outgoingOps.epoch) ||
851 ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK) ||
852 (client != outgoingOps.client)) return 0;
853 type = p->header.type;
854 if ((type <= 0) || (type >= RX_N_PACKET_TYPES)) type = 0;
855 outgoingOps.counts[type]++;
857 switch (outgoingOps.op) {
860 /* counting always happens above if not noop */
864 if (p->header.type != RX_PACKET_TYPE_DATA) break;
865 if (rx_GetPacketCksum (p) == 0) {
866 /* probably, a retransmitted packet */
867 fprintf (stderr, "Packet cksum already zero\n");
870 rx_SetPacketCksum (p, 0);
875 if (p->header.type != RX_PACKET_TYPE_DATA) break;
876 cksum = rx_GetPacketCksum (p);
878 fprintf (stderr, "Packet cksum already zero\n");
881 rx_SetPacketCksum (p, cksum ^ 8);
885 fprintf (stderr, "Unknown outgoing op %d\n", outgoingOps.op);
891 #ifdef AFS_PTHREAD_ENV
892 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
893 static pthread_mutex_t slowCallLock;
894 static pthread_cond_t slowCallCV;
895 void SlowCallInit(void)
897 pthread_mutex_init(&slowCallLock, NULL);
898 pthread_cond_init(&slowCallCV, NULL);
901 static long slowCallCode;
902 static long SlowCall (conn)
909 #ifdef AFS_PTHREAD_ENV
910 pthread_mutex_lock(&slowCallLock);
912 slowCallCode = RXKST_PROCESSRUNNING;
913 #ifdef AFS_PTHREAD_ENV
914 pthread_cond_signal(&slowCallCV);
916 LWP_NoYieldSignal (&slowCallCode);
918 slowCallCode = RXKST_Slow (conn, 1, &ntime);
920 now = FT_ApproxTime();
921 if ((ntime < now-maxSkew) || (ntime > now+maxSkew))
922 slowCallCode = RXKST_TIMESKEW;
924 temp_rc = slowCallCode;
925 #ifdef AFS_PTHREAD_ENV
926 pthread_cond_signal(&slowCallCV);
927 pthread_mutex_unlock(&slowCallLock);
929 LWP_NoYieldSignal (&slowCallCode);
934 #endif rx_GetPacketCksum
936 static long RunHijackTest (parms, host, sc, si)
937 IN struct clientParms *parms;
939 IN struct rx_securityClass *sc;
943 #ifndef rx_GetPacketCksum
945 code = RXKST_BADARGS;
946 com_err (whoami, code, "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
951 extern int (*rx_justReceived)();
952 extern int (*rx_almostSent)();
955 struct rx_connection *conn = 0;
956 struct rx_connection *otherConn = 0;
957 #ifdef AFS_PTHREAD_ENV
962 int nResp; /* otherConn responses seen */
965 #ifdef AFS_PTHREAD_ENV
966 pthread_once(&slowCallOnce, SlowCallInit);
968 rx_justReceived = HandleIncoming;
969 rx_almostSent = HandleOutgoing;
971 incomingOps.op = IO_NOOP;
972 outgoingOps.op = OO_NOOP;
974 #define HIJACK_CONN(conn) \
975 { if (conn) rx_DestroyConnection (conn); \
976 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
977 RXKST_SERVICEID, sc, si); \
978 if (!(conn)) return RXKST_NEWCONNFAILED; \
979 outgoingOps.client = 1; \
980 outgoingOps.epoch = (conn)->epoch; \
981 outgoingOps.cid = (conn)->cid; }
985 /* First try switching from no packet cksum to sending packet cksum between
986 * calls, and see if server complains. */
988 outgoingOps.op = OO_ZEROCKSUM;
989 code = FastCall (conn);
991 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
994 /* The server thinks we're an old style client. Now start sending cksums.
995 * Server shouldn't care. */
996 outgoingOps.op = OO_NOOP;
997 code = FastCall (conn);
999 com_err (whoami, code, "doing FastCall with non-ZEROCKSUM");
1002 /* The server now thinks we're a new style client, we can't go back now. */
1003 outgoingOps.op = OO_ZEROCKSUM;
1004 code = FastCall (conn);
1005 if (code == 0) code = RXKST_NOBADCKSUM;
1006 if (code != RXKADSEALEDINCON) {
1007 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1009 } else if (!conn->error) {
1010 code = RXKST_NOCONNERROR;
1011 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1017 /* Now try modifying packet cksum to see if server complains. */
1019 outgoingOps.op = OO_MUNGCKSUM;
1020 code = FastCall (conn);
1021 if (code == 0) code = RXKST_NOBADCKSUM;
1022 if (code != RXKADSEALEDINCON) {
1023 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1025 } else if (!conn->error) {
1026 code = RXKST_NOCONNERROR;
1027 com_err (whoami, code, "doing FastCall with ZEROCKSUM");
1031 /* Now make two connection and direct the first challenge on one connection
1032 * to the other connection to see if it generates a response. The
1033 * retransmitted challenge should allow the call on the first connection to
1034 * complete correctly. Part one is to attack a new connection, then attack
1035 * it after it has made a call. Part three, just for comparison, attacks a
1036 * otherConn while it is making a slow call (and thus has an active call).
1037 * Against this attack we have no defense so we expect a challenge in this
1038 * case, which the server will discard. */
1040 #define RedirectChallenge(conn,otherConn) \
1041 (incomingOps.epoch = (conn)->epoch, \
1042 incomingOps.cid = (conn)->cid, \
1043 incomingOps.client = 1, \
1044 incomingOps.newEpoch = (otherConn)->epoch, \
1045 incomingOps.newCid = (otherConn)->cid, \
1046 incomingOps.op = IO_REDIRECTCHALLENGE, \
1047 outgoingOps.epoch = (otherConn)->epoch, \
1048 outgoingOps.cid = (otherConn)->cid, \
1049 outgoingOps.client = 1, \
1050 outgoingOps.op = OO_COUNT, \
1051 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1054 HIJACK_CONN(otherConn)
1055 RedirectChallenge (conn, otherConn);
1057 code = FastCall (conn);
1058 if (code) return code;
1059 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1060 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1062 code = RXKST_CHALLENGEORACLE;
1063 com_err (whoami, code, "misdirecting challenge");
1066 code = FastCall (otherConn); /* generate some activity here */
1067 if (code) return code;
1068 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1069 assert (nResp >= 1);
1070 code = FastCall (conn);
1071 if (code) return code;
1072 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp) goto oracle;
1075 RedirectChallenge (conn, otherConn);
1076 /* otherConn was authenticated during part one */
1077 code = FastCall (conn);
1078 if (code) return code;
1079 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1080 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0) goto oracle;
1083 RedirectChallenge (conn, otherConn);
1084 /* otherConn is still authenticated */
1085 slowCallCode = RXKST_PROCESSCREATED;
1086 #ifdef AFS_PTHREAD_ENV
1088 pthread_attr_t tattr;
1090 code = pthread_attr_init(&tattr);
1092 com_err (whoami, code, "can't pthread_attr_init slow call process");
1096 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1098 com_err (whoami, code, "can't pthread_attr_setdetachstate slow call process");
1102 code = pthread_create(&pid, &tattr, SlowCall, (void*) otherConn);
1105 code = LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1106 (opaque) otherConn, "Slow Call Process", &pid);
1109 com_err (whoami, code, "can't create slow call process");
1112 #ifdef AFS_PTHREAD_ENV
1113 pthread_mutex_lock(&slowCallLock);
1114 while (slowCallCode == RXKST_PROCESSCREATED)
1115 pthread_cond_wait(&slowCallCV, &slowCallLock);
1117 while (slowCallCode == RXKST_PROCESSCREATED)
1118 LWP_WaitProcess (&slowCallCode); /* wait for process start */
1120 if (slowCallCode != RXKST_PROCESSRUNNING) {
1121 tmp_rc = slowCallCode;
1122 #ifdef AFS_PTHREAD_ENV
1123 pthread_mutex_unlock(&slowCallLock);
1125 return tmp_rc; /* make sure didn't fail immediately */
1127 assert (incomingOps.op == IO_REDIRECTCHALLENGE);
1128 code = FastCall (conn);
1129 if (code) return code;
1130 assert (incomingOps.op == IO_COUNT); /* redirect code was triggered */
1131 #ifdef AFS_PTHREAD_ENV
1132 while (slowCallCode == RXKST_PROCESSRUNNING)
1133 pthread_cond_wait(&slowCallCV, &slowCallLock);
1134 pthread_mutex_unlock(&slowCallLock);
1136 while (slowCallCode == RXKST_PROCESSRUNNING)
1137 LWP_WaitProcess (&slowCallCode); /* wait for process finish */
1139 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1) goto oracle;
1141 rx_justReceived = 0;
1143 rx_DestroyConnection (otherConn);
1144 rx_DestroyConnection (conn);
1147 #endif rx_GetPacketCksum
1151 long rxkst_StartClient (parms)
1152 IN struct clientParms *parms;
1157 struct rx_securityClass *sc;
1159 whoami = parms->whoami; /* set this global variable */
1161 host = GetServer (parms->server);
1163 if (parms->authentication >= 0) {
1165 char ticket[MAXKTCTICKETLEN];
1167 struct ktc_encryptionKey Ksession;
1169 if (parms->useTokens)
1170 code = GetToken (&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1172 code = GetTicket (&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1173 if (code) return code;
1175 /* next, we have ticket, kvno and session key, authenticate the conn */
1176 sc = (struct rx_securityClass *)
1177 rxkad_NewClientSecurityObject (parms->authentication,
1178 &Ksession, kvno, ticketLen, ticket);
1180 scIndex = 2; /* kerberos security index */
1182 /* unauthenticated connection */
1183 sc = rxnull_NewClientSecurityObject();
1185 scIndex = 0; /* null security index */
1189 if (!code && parms->callTest) {
1190 code = RunCallTest (parms, host, sc, scIndex);
1192 if (!code && parms->hijackTest) {
1193 code = RunHijackTest (parms, host, sc, scIndex);
1196 (parms->printTiming ||
1197 parms->fastCalls || parms->slowCalls || parms->copiousCalls)) {
1198 struct rx_connection *conn;
1199 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1200 RXKST_SERVICEID, sc, scIndex);
1202 code = RepeatLoadTest (parms, conn);
1203 rx_DestroyConnection (conn);
1204 } else code = RXKST_NEWCONNFAILED;
1206 if (!code && parms->stopServer) {
1207 struct rx_connection *conn;
1208 conn = rx_NewConnection(host, htons(RXKST_SERVICEPORT),
1209 RXKST_SERVICEID, sc, scIndex);
1211 code = RXKST_Kill (conn);
1213 com_err (whoami, code, "trying to stop server");
1215 rx_DestroyConnection (conn);
1216 } else code = RXKST_NEWCONNFAILED;
1219 if (parms->printStats) {
1220 rx_PrintStats (stdout);
1222 /* use rxdebug style iteration here */
1223 rx_PrintPeerStats (stdout, rx_PeerOf(conn));
1230 com_err (parms->whoami, code, "test fails");
1233 printf ("Test Okay\n");
1234 if (!parms->noExit) exit (0);