2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 /* RX Authentication Stress test: client side code. */
12 #include <afsconfig.h>
13 #include <afs/param.h>
19 #include <afs/com_err.h>
20 #include <afs/afsutil.h>
23 #include <des_prototypes.h>
25 #include "stress_internal.h"
26 #ifdef AFS_PTHREAD_ENV
28 #define FT_ApproxTime() (int)time(0)
36 GetServer(const char *aname)
41 th = gethostbyname(aname);
43 fprintf(stderr, "host %s not found\n", aname);
46 memcpy(&addr, th->h_addr, sizeof(addr));
51 GetToken(long *versionP, struct ktc_encryptionKey *session,
52 int *ticketLenP, char *ticket, char *cell)
54 struct ktc_principal sname;
55 struct ktc_token ttoken;
58 strcpy(sname.cell, cell);
59 sname.instance[0] = 0;
60 strcpy(sname.name, "afs");
61 code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
65 *versionP = ttoken.kvno;
66 *ticketLenP = ttoken.ticketLen;
67 memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
68 memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
73 GetTicket(long *versionP, struct ktc_encryptionKey *session,
74 int *ticketLenP, char *ticket, char *cell)
78 /* create random session key, using key for seed to good random */
79 des_init_random_number_generator(ktc_to_cblock(&serviceKey));
80 code = des_random_key(ktc_to_cblock(session));
84 /* now create the actual ticket */
87 tkt_MakeTicket(ticket, ticketLenP, &serviceKey, RXKST_CLIENT_NAME,
88 RXKST_CLIENT_INST, cell,
89 /*start,end */ 0, 0xffffffff, session, /*host */ 0,
90 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
91 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
92 * instance and cell, start time, end time, session key to seal in ticket,
93 * inet host, server name and server instance */
96 *versionP = serviceKeyVersion;
100 struct rx_connection *conn;
101 u_long sendLen; /* parameters for call to Copious */
103 u_long *fastCalls; /* number of calls to perform */
105 u_long *copiousCalls;
109 Copious(struct client *c, char *buf, u_long buflen)
112 struct rx_call *call;
114 long inlen = c->sendLen;
115 long outlen = c->recvLen;
121 for (i = 0; i < inlen; i++)
122 mysum += (d++ & 0xff);
124 call = rx_NewCall(c->conn);
125 code = StartRXKST_Copious(call, inlen, mysum, outlen);
131 while (xfer < inlen) {
135 for (i = 0; i < tlen; i++)
136 buf[i] = (d++ & 0xff);
137 n = rx_Write(call, buf, tlen);
142 code = RXKST_WRITESHORT;
150 while (xfer < outlen) {
151 tlen = outlen - xfer;
154 n = rx_Read(call, buf, tlen);
159 code = RXKST_READSHORT;
162 for (i = 0; i < tlen; i++)
169 code = EndRXKST_Copious(call, &outsum);
170 code = rx_EndCall(call, code);
173 if (outsum != mysum) {
174 return RXKST_BADOUTPUTSUM;
180 DoClient(int index, opaque rock)
182 struct client *c = (struct client *)rock;
188 for (i = 0; i < c->fastCalls[index]; i++) {
189 code = RXKST_Fast(c->conn, n, &inc_n);
193 return RXKST_INCFAILED;
197 for (i = 0; i < c->slowCalls[index]; i++) {
200 code = RXKST_Slow(c->conn, 1, &ntime);
203 now = FT_ApproxTime();
204 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
205 return RXKST_TIMESKEW;
208 if (c->copiousCalls[index] > 0) {
209 u_long buflen = 10000;
210 char *buf = osi_Alloc(buflen);
211 for (i = 0; i < c->copiousCalls[index]; i++) {
212 code = Copious(c, buf, buflen);
216 osi_Free(buf, buflen);
225 long exitCode; /* is PROCESSRUNNING until exit */
228 long (*proc) (int, opaque);
231 #ifdef AFS_PTHREAD_ENV
232 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
233 static pthread_mutex_t workerLock;
234 static pthread_cond_t workerCV;
238 pthread_mutex_init(&workerLock, NULL);
239 pthread_cond_init(&workerCV, NULL);
242 static struct worker *workers;
245 DoWorker(void * rock)
247 struct worker *w = rock;
249 code = (*w->proc) (w->index, w->rock);
250 #ifdef AFS_PTHREAD_ENV
251 pthread_mutex_lock(&workerLock);
254 #ifdef AFS_PTHREAD_ENV
255 pthread_mutex_unlock(&workerLock);
257 #ifdef AFS_PTHREAD_ENV
258 pthread_cond_signal(&workerCV);
260 LWP_NoYieldSignal(&workers);
262 return (void *)(intptr_t)code;
265 #define MAX_CTHREADS 25
268 CallSimultaneously(u_int threads, opaque rock, long (*proc)(int, opaque))
272 #ifdef AFS_PTHREAD_ENV
273 pthread_once(&workerOnce, WorkerInit);
277 for (i = 0; i < threads; i++) {
279 #ifdef AFS_PTHREAD_ENV
284 assert(i < MAX_CTHREADS);
285 w = osi_Alloc(sizeof(struct worker));
286 memset(w, 0, sizeof(*w));
290 w->exitCode = RXKST_PROCESSRUNNING;
293 #ifdef AFS_PTHREAD_ENV
295 pthread_attr_t tattr;
297 code = pthread_attr_init(&tattr);
299 afs_com_err(whoami, code,
300 "can't pthread_attr_init worker process");
305 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
307 afs_com_err(whoami, code,
308 "can't pthread_attr_setdetachstate worker process");
312 code = pthread_create(&pid, &tattr, DoWorker, (void *)w);
316 LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
317 (opaque) w, "Worker Process", &pid);
320 afs_com_err(whoami, code, "can't create worker process");
324 code = 0; /* last non-zero code encountered */
325 #ifdef AFS_PTHREAD_ENV
326 pthread_mutex_lock(&workerLock);
329 struct worker *w, *prevW, *nextW;
331 for (w = workers; w; w = nextW) {
333 if (w->exitCode != RXKST_PROCESSRUNNING) {
339 prevW->next = w->next;
342 osi_Free(w, sizeof(*w));
343 continue; /* don't bump prevW */
347 #ifdef AFS_PTHREAD_ENV
349 pthread_cond_wait(&workerCV, &workerLock);
352 LWP_WaitProcess(&workers);
355 #ifdef AFS_PTHREAD_ENV
356 pthread_mutex_unlock(&workerLock);
362 DivideUpCalls(u_long calls, u_int threads, u_long threadCalls[])
365 for (i = 0; i < threads; i++) {
366 threadCalls[i] = calls / (threads - i);
367 calls -= threadCalls[i];
375 gettimeofday(&tv, NULL);
376 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
380 RunLoadTest(struct clientParms *parms, struct rx_connection *conn)
384 u_long fastCalls[MAX_CTHREADS];
385 u_long slowCalls[MAX_CTHREADS];
386 u_long copiousCalls[MAX_CTHREADS];
387 double start, interval;
389 DivideUpCalls(parms->fastCalls, parms->threads, fastCalls);
390 DivideUpCalls(parms->slowCalls, parms->threads, slowCalls);
391 DivideUpCalls(parms->copiousCalls, parms->threads, copiousCalls);
393 memset(&c, 0, sizeof(c));
395 c.sendLen = parms->sendLen;
396 c.recvLen = parms->recvLen;
397 c.fastCalls = fastCalls;
398 c.slowCalls = slowCalls;
399 c.copiousCalls = copiousCalls;
402 code = CallSimultaneously(parms->threads, &c, DoClient);
404 afs_com_err(whoami, code, "in DoClient");
407 interval = ftime() - start;
409 if (parms->printTiming) {
411 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
412 int t = (interval / totalCalls) * 1000.0 + 0.5;
413 if (totalCalls > 0) {
414 printf("For %lu calls: %d msec/call\n", totalCalls, t);
416 if (parms->copiousCalls > 0) {
417 long n = parms->sendLen + parms->recvLen;
420 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
423 I just cannot get printing of floats to work on the pmax !
424 !!!printf("%g %d %d %d\n", (float)kbps, b);
425 printf("%g %d %d %d\n", kbps, b);
426 fprintf(stdout, "%g %d %d\n", kbps, b);
429 buf[sizeof(buf) - 1] = 0;
430 sprintf(buf, "%g %d %d\n", kbps, b);
431 assert(buf[sizeof(buf) - 1] == 0);
436 ("For %lu copious calls, %lu send + %lu recv = %lu bytes each: %d kbytes/sec\n",
437 parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
439 printf("%g\n", kbps);
447 RepeatLoadTest(struct clientParms *parms, struct rx_connection *conn)
452 if (parms->repeatInterval == 0) {
453 if (parms->repeatCount == 0)
454 parms->repeatCount = 1;
456 if (parms->repeatCount == 0)
457 parms->repeatCount = 0x7fffffff;
460 if (parms->printTiming) {
463 (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
464 (parms->copiousCalls ? 1 : 0);
467 "Combined timings of several types of calls may not be meaningful.\n");
469 /* do timings of copious calls by default */
470 parms->copiousCalls = 10;
473 for (count = 0; count < parms->repeatCount; count++) {
474 code = RunLoadTest(parms, conn);
477 if (parms->repeatInterval) {
478 u_long i = parms->repeatInterval;
479 u_long now = time(0);
480 u_long next = (now + i - 1) / i * i; /* round up to next interval */
482 #ifdef AFS_PTHREAD_ENV
485 IOMGR_Sleep(next - now);
494 /* For backward compatibility, don't try to use the CallNumber stuff unless
495 * we're compiling against the new Rx. */
497 #ifdef rx_GetPacketCksum
499 struct multiChannel {
500 struct rx_connection *conn;
503 int changes[RX_MAXCALLS];
504 afs_int32 callNumbers[RX_MAXCALLS];
506 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
507 static u_long sequence = 0;
510 FastCall(struct rx_connection *conn)
513 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
516 code = RXKST_Fast(conn, n, &inc_n);
520 return RXKST_INCFAILED;
525 UniChannelCall(int index, opaque rock)
527 struct multiChannel *mc = (struct multiChannel *)rock;
529 afs_int32 callNumbers[RX_MAXCALLS];
534 while (!mc->done && unchanged) {
536 code = FastCall(mc->conn);
539 code = rxi_GetCallNumberVector(mc->conn, callNumbers);
543 for (i = 0; i < RX_MAXCALLS; i++) {
544 if (callNumbers[i] > mc->callNumbers[i]) {
545 mc->callNumbers[i] = callNumbers[i];
546 mc->changes[i]--; /* may go negative */
548 if (mc->changes[i] > 0)
552 mc->codes[index] = code;
558 MakeMultiChannelCall(struct rx_connection *conn, int each,
559 long expectedCode, long codes[])
563 struct multiChannel mc;
565 memset(&mc, 0, sizeof(mc));
567 for (i = 0; i < RX_MAXCALLS; i++) {
568 codes[i] = RXKST_PROCESSRUNNING;
569 mc.changes[i] = each;
572 code = rxi_GetCallNumberVector(conn, mc.callNumbers);
576 code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
577 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3))); /* strange cases */
578 else if (code != expectedCode) {
579 afs_com_err(whoami, code,
580 "problem making multichannel call, expected '%s'",
582 ? "no error" : (char *)afs_error_message(expectedCode)));
588 CheckCallFailure(struct rx_connection *conn, long codes[], long code,
592 fprintf(stderr, "Failed to detect %s\n", msg);
593 return RXKST_NODUPLICATECALL;
598 for (i = 0; i < RX_MAXCALLS; i++)
599 if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
604 fprintf(stderr, "%s produced these errors:\n", msg);
605 for (i = 0; i < RX_MAXCALLS; i++) {
606 assert(codes[i] != RXKST_PROCESSRUNNING);
609 fprintf(stderr, " %d no error\n", i);
611 fprintf(stderr, " %d %s\n", i, afs_error_message(codes[i]));
615 sprintf(buf, "connection dead following %s", msg);
616 code = FastCall(conn);
618 afs_com_err(whoami, code, "%s", buf);
625 #endif /* rx_GetPacketCksum */
628 RunCallTest(struct clientParms *parms, long host,
629 struct rx_securityClass *sc, long si)
633 #ifndef rx_GetPacketCksum
635 code = RXKST_BADARGS;
636 afs_com_err(whoami, code,
637 "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
643 struct rx_connection *conn;
645 afs_int32 callNumbers[RX_MAXCALLS];
646 long codes[RX_MAXCALLS];
647 long retCode = 0; /* ret. if nothing fatal goes wrong */
650 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
653 return RXKST_NEWCONNFAILED;
655 /* First check the basic behaviour of call number handling */
657 code = rxi_GetCallNumberVector(conn, callNumbers);
660 for (i = 0; i < RX_MAXCALLS; i++) {
661 if (callNumbers[i] != 0) {
663 "Connection's initial call numbers not zero. call[%d] = %d\n",
665 return RXKST_BADCALLNUMBERS;
668 code = FastCall(conn);
671 code = rxi_GetCallNumberVector(conn, callNumbers);
674 firstCall = callNumbers[0];
675 code = FastCall(conn);
678 code = rxi_GetCallNumberVector(conn, callNumbers);
681 if ((callNumbers[0] != firstCall + 1)
682 && ((firstCall == 1) || (firstCall == 2))) {
683 /* The call number after the first call should be one or, more likely,
684 * two (if the call is still DALLYing). Between first and second call,
685 * the call number should have incremented by one. */
687 "Connection's first channel call number not one. call[%d] = %d\n",
689 return RXKST_BADCALLNUMBERS;
691 for (i = 1; i < RX_MAXCALLS; i++) {
692 if (callNumbers[i] != 0) {
694 "Connection's other channel call numbers not zero. call[%d] = %d\n",
696 return RXKST_BADCALLNUMBERS;
699 code = MakeMultiChannelCall(conn, 1, 0, codes);
703 /* Now try to resend a call that's already been executed by finding a
704 * non-zero call number on a channel other than zero and decrementing it by
705 * one. This should appear to the server as a retransmitted call. Since
706 * this is behaving as a broken client different strange behaviors may be
707 * exhibited by different servers. If the response packet to the original
708 * call is discarded by the time the "retransmitted" call arrives (perhaps
709 * due to high server or client load) there is no way for the server to
710 * respond at all. Further, it seems, that under some cases the connection
711 * will be kept alive indefinitely even though the server has discarded the
712 * "retransmitted" call and is making no effort to reexecute the call. To
713 * handle these, accept either a timeout (-1) or and INCFAILED error here,
714 * also set the connenction HardDeadTime to punt after a reasonable
717 /* short dead time since may we expect some trouble */
718 rx_SetConnHardDeadTime(conn, 30);
719 code = rxi_GetCallNumberVector(conn, callNumbers);
722 for (ch = 1; ch < RX_MAXCALLS; ch++)
723 if (callNumbers[ch] > 1) {
725 code = rxi_SetCallNumberVector(conn, callNumbers);
730 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
731 return RXKST_BADCALLNUMBERS;
732 code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
733 code = CheckCallFailure(conn, codes, code, "retransmitted call");
734 if (code && !retCode)
737 /* Get a fresh connection, becasue if the above failed as it should the
738 * connection is dead. */
739 rx_DestroyConnection(conn);
741 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
744 return RXKST_NEWCONNFAILED;
746 /* Similarly, but decrement call number by two which should be completely
747 * unmistakeable as a broken or malicious client. */
749 /* short dead time since may we expect some trouble */
750 rx_SetConnHardDeadTime(conn, 30);
751 code = MakeMultiChannelCall(conn, 2, 0, codes);
754 code = rxi_GetCallNumberVector(conn, callNumbers);
757 for (ch = 1; ch < RX_MAXCALLS; ch++)
758 if (callNumbers[ch] > 2) {
759 callNumbers[ch] -= 2;
760 code = rxi_SetCallNumberVector(conn, callNumbers);
763 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
764 return RXKST_BADCALLNUMBERS;
765 code = MakeMultiChannelCall(conn, 1, -1, codes);
766 code = CheckCallFailure(conn, codes, code, "duplicate call");
767 if (code && !retCode)
770 rx_DestroyConnection(conn);
772 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
775 return RXKST_NEWCONNFAILED;
777 /* Next, without waiting for the server to discard its state, we will check
778 * to see if the Challenge/Response protocol correctly informs the server
779 * of the client's callNumber state. We do this by artificially increasing
780 * the call numbers of a new connection for all channels beyond zero,
781 * making a call on channel zero, then resetting the call number for the
782 * unused channels back to zero, then making calls on all channels. */
784 code = rxi_GetCallNumberVector(conn, callNumbers);
787 for (i = 0; i < RX_MAXCALLS; i++) {
788 if (callNumbers[i] != 0)
789 return RXKST_BADCALLNUMBERS;
790 callNumbers[i] = 51; /* an arbitrary value... */
792 code = rxi_SetCallNumberVector(conn, callNumbers);
795 code = FastCall(conn); /* use channel 0 */
798 code = rxi_GetCallNumberVector(conn, callNumbers);
801 if (callNumbers[0] != 52)
802 return RXKST_BADCALLNUMBERS;
803 for (i = 1; i < RX_MAXCALLS; i++) {
804 if (callNumbers[i] != 51)
805 return RXKST_BADCALLNUMBERS;
806 callNumbers[i] = 37; /* back up a ways */
808 code = rxi_SetCallNumberVector(conn, callNumbers);
811 /* now try calls on all channels... */
812 code = MakeMultiChannelCall(conn, 1, -1, codes);
814 CheckCallFailure(conn, codes, code, "alternate channel call replay");
815 if (code && !retCode)
818 rx_DestroyConnection(conn);
821 #endif /* rx_GetPacketCksum */
825 #ifdef rx_GetPacketCksum
829 u_long epoch; /* connection to attack */
831 int client; /* TRUE => client side */
832 u_long newEpoch; /* conn to direct challenges to */
834 u_long counts[RX_N_PACKET_TYPES];
838 #define IO_REDIRECTCHALLENGE 2
841 HandleIncoming(struct rx_packet *p, struct sockaddr_in *addr)
843 int client; /* packet sent by client */
844 u_char type; /* packet type */
846 if (incomingOps.op == IO_NOOP)
849 client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
850 if ((p->header.epoch != incomingOps.epoch)
851 || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
852 || (client != incomingOps.client))
854 type = p->header.type;
855 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
857 incomingOps.counts[type]++;
859 switch (incomingOps.op) {
864 case IO_REDIRECTCHALLENGE:
865 if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
867 p->header.epoch = incomingOps.newEpoch;
868 p->header.cid = incomingOps.newCid;
869 /* Now set up to watch for the corresponding challenge. */
870 incomingOps.epoch = incomingOps.newEpoch;
871 incomingOps.cid = incomingOps.newCid;
872 incomingOps.op = IO_COUNT;
876 fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
884 u_long epoch; /* connection to attack */
886 int client; /* TRUE => client side */
887 u_long counts[RX_N_PACKET_TYPES];
891 #define OO_ZEROCKSUM 2
892 #define OO_MUNGCKSUM 3
895 HandleOutgoing(struct rx_packet *p, struct sockaddr_in *addr)
897 int client; /* packet sent by client */
898 u_char type; /* packet type */
900 if (outgoingOps.op == OO_NOOP)
903 client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
904 if ((p->header.epoch != outgoingOps.epoch)
905 || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
906 || (client != outgoingOps.client))
908 type = p->header.type;
909 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
911 outgoingOps.counts[type]++;
913 switch (outgoingOps.op) {
916 /* counting always happens above if not noop */
920 if (p->header.type != RX_PACKET_TYPE_DATA)
922 if (rx_GetPacketCksum(p) == 0) {
923 /* probably, a retransmitted packet */
924 fprintf(stderr, "Packet cksum already zero\n");
927 rx_SetPacketCksum(p, 0);
932 if (p->header.type != RX_PACKET_TYPE_DATA)
934 cksum = rx_GetPacketCksum(p);
936 fprintf(stderr, "Packet cksum already zero\n");
939 rx_SetPacketCksum(p, cksum ^ 8);
943 fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
949 #ifdef AFS_PTHREAD_ENV
950 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
951 static pthread_mutex_t slowCallLock;
952 static pthread_cond_t slowCallCV;
956 pthread_mutex_init(&slowCallLock, NULL);
957 pthread_cond_init(&slowCallCV, NULL);
960 static long slowCallCode;
962 SlowCall(void * rock)
964 struct rx_connection *conn = rock;
969 #ifdef AFS_PTHREAD_ENV
970 pthread_mutex_lock(&slowCallLock);
972 slowCallCode = RXKST_PROCESSRUNNING;
973 #ifdef AFS_PTHREAD_ENV
974 pthread_cond_signal(&slowCallCV);
976 LWP_NoYieldSignal(&slowCallCode);
978 slowCallCode = RXKST_Slow(conn, 1, &ntime);
980 now = FT_ApproxTime();
981 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
982 slowCallCode = RXKST_TIMESKEW;
984 temp_rc = slowCallCode;
985 #ifdef AFS_PTHREAD_ENV
986 pthread_cond_signal(&slowCallCV);
987 pthread_mutex_unlock(&slowCallLock);
989 LWP_NoYieldSignal(&slowCallCode);
991 return (void *)(intptr_t)temp_rc;
994 #endif /* rx_GetPacketCksum */
997 RunHijackTest(struct clientParms *parms, long host,
998 struct rx_securityClass *sc, long si)
1001 #ifndef rx_GetPacketCksum
1003 code = RXKST_BADARGS;
1004 afs_com_err(whoami, code,
1005 "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
1011 struct rx_connection *conn = 0;
1012 struct rx_connection *otherConn = 0;
1013 #ifdef AFS_PTHREAD_ENV
1018 int nResp; /* otherConn responses seen */
1021 #ifdef AFS_PTHREAD_ENV
1022 pthread_once(&slowCallOnce, SlowCallInit);
1024 rx_justReceived = HandleIncoming;
1025 rx_almostSent = HandleOutgoing;
1027 incomingOps.op = IO_NOOP;
1028 outgoingOps.op = OO_NOOP;
1030 #define HIJACK_CONN(conn) \
1031 { if (conn) rx_DestroyConnection (conn); \
1032 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1033 RXKST_SERVICEID, sc, si); \
1034 if (!(conn)) return RXKST_NEWCONNFAILED; \
1035 outgoingOps.client = 1; \
1036 outgoingOps.epoch = (conn)->epoch; \
1037 outgoingOps.cid = (conn)->cid; }
1041 /* First try switching from no packet cksum to sending packet cksum between
1042 * calls, and see if server complains. */
1044 outgoingOps.op = OO_ZEROCKSUM;
1045 code = FastCall(conn);
1047 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1050 /* The server thinks we're an old style client. Now start sending cksums.
1051 * Server shouldn't care. */
1052 outgoingOps.op = OO_NOOP;
1053 code = FastCall(conn);
1055 afs_com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1058 /* The server now thinks we're a new style client, we can't go back now. */
1059 outgoingOps.op = OO_ZEROCKSUM;
1060 code = FastCall(conn);
1062 code = RXKST_NOBADCKSUM;
1063 if (code != RXKADSEALEDINCON) {
1064 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1066 } else if (!conn->error) {
1067 code = RXKST_NOCONNERROR;
1068 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1075 /* Now try modifying packet cksum to see if server complains. */
1077 outgoingOps.op = OO_MUNGCKSUM;
1078 code = FastCall(conn);
1080 code = RXKST_NOBADCKSUM;
1081 if (code != RXKADSEALEDINCON) {
1082 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1084 } else if (!conn->error) {
1085 code = RXKST_NOCONNERROR;
1086 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1091 /* Now make two connection and direct the first challenge on one connection
1092 * to the other connection to see if it generates a response. The
1093 * retransmitted challenge should allow the call on the first connection to
1094 * complete correctly. Part one is to attack a new connection, then attack
1095 * it after it has made a call. Part three, just for comparison, attacks a
1096 * otherConn while it is making a slow call (and thus has an active call).
1097 * Against this attack we have no defense so we expect a challenge in this
1098 * case, which the server will discard. */
1100 #define RedirectChallenge(conn,otherConn) \
1101 (incomingOps.epoch = (conn)->epoch, \
1102 incomingOps.cid = (conn)->cid, \
1103 incomingOps.client = 1, \
1104 incomingOps.newEpoch = (otherConn)->epoch, \
1105 incomingOps.newCid = (otherConn)->cid, \
1106 incomingOps.op = IO_REDIRECTCHALLENGE, \
1107 outgoingOps.epoch = (otherConn)->epoch, \
1108 outgoingOps.cid = (otherConn)->cid, \
1109 outgoingOps.client = 1, \
1110 outgoingOps.op = OO_COUNT, \
1111 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1114 HIJACK_CONN(otherConn)
1115 RedirectChallenge(conn, otherConn);
1117 code = FastCall(conn);
1120 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1121 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1123 code = RXKST_CHALLENGEORACLE;
1124 afs_com_err(whoami, code, "misdirecting challenge");
1127 code = FastCall(otherConn); /* generate some activity here */
1130 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1132 code = FastCall(conn);
1135 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1139 RedirectChallenge(conn, otherConn);
1140 /* otherConn was authenticated during part one */
1141 code = FastCall(conn);
1144 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1145 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1149 RedirectChallenge(conn, otherConn);
1150 /* otherConn is still authenticated */
1151 slowCallCode = RXKST_PROCESSCREATED;
1152 #ifdef AFS_PTHREAD_ENV
1154 pthread_attr_t tattr;
1156 code = pthread_attr_init(&tattr);
1158 afs_com_err(whoami, code,
1159 "can't pthread_attr_init slow call process");
1163 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1165 afs_com_err(whoami, code,
1166 "can't pthread_attr_setdetachstate slow call process");
1170 code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1174 LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1175 (opaque) otherConn, "Slow Call Process", &pid);
1178 afs_com_err(whoami, code, "can't create slow call process");
1181 #ifdef AFS_PTHREAD_ENV
1182 pthread_mutex_lock(&slowCallLock);
1183 while (slowCallCode == RXKST_PROCESSCREATED)
1184 pthread_cond_wait(&slowCallCV, &slowCallLock);
1186 while (slowCallCode == RXKST_PROCESSCREATED)
1187 LWP_WaitProcess(&slowCallCode); /* wait for process start */
1189 if (slowCallCode != RXKST_PROCESSRUNNING) {
1190 tmp_rc = slowCallCode;
1191 #ifdef AFS_PTHREAD_ENV
1192 pthread_mutex_unlock(&slowCallLock);
1194 return tmp_rc; /* make sure didn't fail immediately */
1196 assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1197 code = FastCall(conn);
1200 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1201 #ifdef AFS_PTHREAD_ENV
1202 while (slowCallCode == RXKST_PROCESSRUNNING)
1203 pthread_cond_wait(&slowCallCV, &slowCallLock);
1204 pthread_mutex_unlock(&slowCallLock);
1206 while (slowCallCode == RXKST_PROCESSRUNNING)
1207 LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1209 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1212 rx_justReceived = 0;
1214 rx_DestroyConnection(otherConn);
1215 rx_DestroyConnection(conn);
1218 #endif /* rx_GetPacketCksum */
1223 rxkst_StartClient(struct clientParms *parms)
1228 struct rx_securityClass *sc;
1230 whoami = parms->whoami; /* set this global variable */
1232 host = GetServer(parms->server);
1234 if (parms->authentication >= 0) {
1236 char ticket[MAXKTCTICKETLEN];
1238 struct ktc_encryptionKey Ksession;
1240 if (parms->useTokens)
1242 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1245 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1249 /* next, we have ticket, kvno and session key, authenticate the conn */
1250 sc = (struct rx_securityClass *)
1251 rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1252 kvno, ticketLen, ticket);
1254 scIndex = RX_SECIDX_KAD;
1256 /* unauthenticated connection */
1257 sc = rxnull_NewClientSecurityObject();
1259 scIndex = RX_SECIDX_NULL;
1263 if (!code && parms->callTest) {
1264 code = RunCallTest(parms, host, sc, scIndex);
1266 if (!code && parms->hijackTest) {
1267 code = RunHijackTest(parms, host, sc, scIndex);
1270 && (parms->printTiming || parms->fastCalls || parms->slowCalls
1271 || parms->copiousCalls)) {
1272 struct rx_connection *conn;
1274 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1277 code = RepeatLoadTest(parms, conn);
1278 rx_DestroyConnection(conn);
1280 code = RXKST_NEWCONNFAILED;
1282 if (!code && parms->stopServer) {
1283 struct rx_connection *conn;
1285 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1288 code = RXKST_Kill(conn);
1290 afs_com_err(whoami, code, "trying to stop server");
1292 rx_DestroyConnection(conn);
1294 code = RXKST_NEWCONNFAILED;
1297 if (parms->printStats) {
1298 rx_PrintStats(stdout);
1300 /* use rxdebug style iteration here */
1301 rx_PrintPeerStats(stdout, rx_PeerOf(conn));
1308 afs_com_err(parms->whoami, code, "test fails");
1311 printf("Test Okay\n");