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 /* The rxkad security object. Routines used by both client and servers. */
12 #include <afsconfig.h>
14 #include "afs/param.h"
16 #include <afs/param.h>
20 #include <sys/time_impl.h>
23 #define INCLUDE_RXKAD_PRIVATE_DECLS
31 #include "afs/afs_osi.h"
32 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
35 #if defined(AFS_DARWIN60_ENV) || defined(AFS_OBSD_ENV)
40 #ifndef AFS_LINUX22_ENV
41 #include "rpc/types.h"
43 #endif /* AFS_LINUX22_ENV */
45 #include "afs/sysincludes.h"
46 #include "afsincludes.h"
52 #include <sys/types.h>
56 #ifdef AFS_PTHREAD_ENV
57 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
60 #include <netinet/in.h>
71 #include <afs/afsutil.h>
74 #include <des/stats.h>
75 #include "private_data.h"
76 #define XPRT_RXKAD_COMMON
79 #define afs_max(a,b) ((a) < (b)? (b) : (a))
83 #define osi_Time() time(0)
85 /* variable initialization for the benefit of darwin compiler; if it causes
86 problems elsewhere, conditionalize for darwin or fc_test compile breaks */
87 #ifdef AFS_PTHREAD_ENV
88 struct rxkad_global_stats rxkad_global_stats = { 0 };
89 pthread_mutex_t rxkad_global_stats_lock;
90 pthread_key_t rxkad_stats_key;
91 #else /* AFS_PTHREAD_ENV */
92 #if defined(KERNEL) && !defined(UKERNEL)
93 struct rxkad_stats rxkad_stats = { { 0 } };
95 /* Move delaration of this to des/key_sched.c */
97 #endif /* AFS_PTHREAD_ENV */
99 #ifdef AFS_PTHREAD_ENV
100 /* rxkad_stats related stuff */
103 * Macro to insert an element at the tail of a doubly linked list
105 #define DLL_INSERT_TAIL(ptr,head,tail,next,prev) \
107 (ptr)->next = NULL; \
108 (ptr)->prev = (tail); \
111 (ptr)->prev->next = (ptr); \
114 assert((head) && ((head)->prev == NULL)); \
117 void rxkad_global_stats_init() {
118 assert(pthread_mutex_init(&rxkad_global_stats_lock, (const pthread_mutexattr_t *)0) == 0);
119 assert(pthread_key_create(&rxkad_stats_key, NULL) == 0);
120 memset(&rxkad_global_stats, 0, sizeof(rxkad_global_stats));
124 rxkad_thr_stats_init() {
125 rxkad_stats_t * rxkad_stats;
126 rxkad_stats = (rxkad_stats_t *)malloc(sizeof(rxkad_stats_t));
127 assert(rxkad_stats != NULL && pthread_setspecific(rxkad_stats_key,rxkad_stats) == 0);
128 memset(rxkad_stats,0,sizeof(rxkad_stats_t));
129 RXKAD_GLOBAL_STATS_LOCK;
130 DLL_INSERT_TAIL(rxkad_stats, rxkad_global_stats.first, rxkad_global_stats.last, next, prev);
131 RXKAD_GLOBAL_STATS_UNLOCK;
135 int rxkad_stats_agg(rxkad_stats_t * rxkad_stats) {
136 rxkad_stats_t * thr_stats;
137 assert(rxkad_stats != NULL);
138 memset(rxkad_stats, 0, sizeof(rxkad_stats_t));
139 RXKAD_GLOBAL_STATS_LOCK;
140 for (thr_stats = rxkad_global_stats.first; thr_stats != NULL; thr_stats = thr_stats->next) {
141 rxkad_stats->connections[0] += thr_stats->connections[0];
142 rxkad_stats->connections[1] += thr_stats->connections[1];
143 rxkad_stats->connections[2] += thr_stats->connections[2];
144 rxkad_stats->destroyObject += thr_stats->destroyObject;
145 rxkad_stats->destroyClient += thr_stats->destroyClient;
146 rxkad_stats->destroyUnused += thr_stats->destroyUnused;
147 rxkad_stats->destroyUnauth += thr_stats->destroyUnauth;
148 rxkad_stats->destroyConn[0] += thr_stats->destroyConn[0];
149 rxkad_stats->destroyConn[1] += thr_stats->destroyConn[1];
150 rxkad_stats->destroyConn[2] += thr_stats->destroyConn[2];
151 rxkad_stats->expired += thr_stats->expired;
152 rxkad_stats->challengesSent += thr_stats->challengesSent;
153 rxkad_stats->challenges[0] += thr_stats->challenges[0];
154 rxkad_stats->challenges[1] += thr_stats->challenges[1];
155 rxkad_stats->challenges[2] += thr_stats->challenges[2];
156 rxkad_stats->responses[0] += thr_stats->responses[0];
157 rxkad_stats->responses[1] += thr_stats->responses[1];
158 rxkad_stats->responses[2] += thr_stats->responses[2];
159 rxkad_stats->preparePackets[0] += thr_stats->preparePackets[0];
160 rxkad_stats->preparePackets[1] += thr_stats->preparePackets[1];
161 rxkad_stats->preparePackets[2] += thr_stats->preparePackets[2];
162 rxkad_stats->preparePackets[3] += thr_stats->preparePackets[3];
163 rxkad_stats->preparePackets[4] += thr_stats->preparePackets[4];
164 rxkad_stats->preparePackets[5] += thr_stats->preparePackets[5];
165 rxkad_stats->checkPackets[0] += thr_stats->checkPackets[0];
166 rxkad_stats->checkPackets[1] += thr_stats->checkPackets[1];
167 rxkad_stats->checkPackets[2] += thr_stats->checkPackets[2];
168 rxkad_stats->checkPackets[3] += thr_stats->checkPackets[3];
169 rxkad_stats->checkPackets[4] += thr_stats->checkPackets[4];
170 rxkad_stats->checkPackets[5] += thr_stats->checkPackets[5];
171 rxkad_stats->bytesEncrypted[0] += thr_stats->bytesEncrypted[0];
172 rxkad_stats->bytesEncrypted[1] += thr_stats->bytesEncrypted[1];
173 rxkad_stats->bytesDecrypted[0] += thr_stats->bytesDecrypted[0];
174 rxkad_stats->bytesDecrypted[1] += thr_stats->bytesDecrypted[1];
175 rxkad_stats->fc_encrypts[0] += thr_stats->fc_encrypts[0];
176 rxkad_stats->fc_encrypts[1] += thr_stats->fc_encrypts[1];
177 rxkad_stats->fc_key_scheds += thr_stats->fc_key_scheds;
178 rxkad_stats->des_encrypts[0] += thr_stats->des_encrypts[0];
179 rxkad_stats->des_encrypts[1] += thr_stats->des_encrypts[1];
180 rxkad_stats->des_key_scheds += thr_stats->des_key_scheds;
181 rxkad_stats->des_randoms += thr_stats->des_randoms;
182 rxkad_stats->spares[0] += thr_stats->spares[0];
183 rxkad_stats->spares[1] += thr_stats->spares[1];
184 rxkad_stats->spares[2] += thr_stats->spares[2];
185 rxkad_stats->spares[3] += thr_stats->spares[3];
186 rxkad_stats->spares[4] += thr_stats->spares[4];
187 rxkad_stats->spares[5] += thr_stats->spares[5];
188 rxkad_stats->spares[6] += thr_stats->spares[6];
189 rxkad_stats->spares[7] += thr_stats->spares[7];
190 rxkad_stats->spares[8] += thr_stats->spares[8];
191 rxkad_stats->spares[9] += thr_stats->spares[9];
193 RXKAD_GLOBAL_STATS_UNLOCK;
196 #endif /* AFS_PTHREAD_ENV */
198 /* static prototypes */
199 static afs_int32 ComputeSum(struct rx_packet *apacket,
200 fc_KeySchedule * aschedule, afs_int32 * aivec);
201 static afs_int32 FreeObject(struct rx_securityClass *aobj);
203 /* this call sets up an endpoint structure, leaving it in *network* byte
204 * order so that it can be used quickly for encryption.
207 rxkad_SetupEndpoint(struct rx_connection *aconnp,
208 struct rxkad_endpoint *aendpointp)
210 register afs_int32 i;
212 aendpointp->cuid[0] = htonl(aconnp->epoch);
213 i = aconnp->cid & RX_CIDMASK;
214 aendpointp->cuid[1] = htonl(i);
215 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
216 aendpointp->securityIndex = htonl(aconnp->securityIndex);
220 /* setup xor information based on session key */
222 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
223 char *aivec, char *aresult)
225 struct rxkad_endpoint tendpoint;
228 rxkad_SetupEndpoint(aconnp, &tendpoint);
229 memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
230 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), aschedule, xor,
233 ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
234 ENCRYPTIONBLOCKSIZE);
238 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
239 * challenge response packet (which must be unencrypted and in network order).
240 * The endpoint.cksum field is omitted and treated as zero. The cksum is
241 * returned in network order. */
244 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
248 u_char *cp = (u_char *) v2r;
249 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
251 v2r->encrypted.endpoint.cksum = 0;
253 /* this function captured from budb/db_hash.c */
255 for (i = 0; i < sizeof(*v2r); i++)
256 cksum = (*cp++) + cksum * 0x10204081;
258 v2r->encrypted.endpoint.cksum = savedCksum;
263 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
265 if (level == rxkad_auth) {
266 rx_SetSecurityHeaderSize(conn, 4);
267 rx_SetSecurityMaxTrailerSize(conn, 4);
268 } else if (level == rxkad_crypt) {
269 rx_SetSecurityHeaderSize(conn, 8);
270 rx_SetSecurityMaxTrailerSize(conn, 8); /* XXX was 7, but why screw with
271 * unaligned accesses? */
275 /* returns a short integer in host byte order representing a good checksum of
279 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
283 register afs_uint32 t;
285 t = apacket->header.callNumber;
287 /* note that word [1] includes the channel # */
288 t = ((apacket->header.cid & 0x3) << 30)
289 | ((apacket->header.seq & 0x3fffffff));
291 /* XOR in the ivec from the per-endpoint encryption */
294 /* encrypts word as if it were a character string */
295 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
297 t = (t >> 16) & 0xffff;
299 t = 1; /* so that 0 means don't care */
305 FreeObject(struct rx_securityClass *aobj)
307 struct rxkad_cprivate *tcp; /* both structs start w/ type field */
309 if (aobj->refCount > 0)
310 return 0; /* still in use */
311 tcp = (struct rxkad_cprivate *)aobj->privateData;
312 rxi_Free(aobj, sizeof(struct rx_securityClass));
313 if (tcp->type & rxkad_client) {
314 afs_int32 psize = PDATA_SIZE(tcp->ticketLen);
315 rxi_Free(tcp, psize);
316 } else if (tcp->type & rxkad_server) {
317 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
319 return RXKADINCONSISTENCY;
321 INC_RXKAD_STATS(destroyObject);
325 /* rxkad_Close - called by rx with the security class object as a parameter
326 * when a security object is to be discarded */
329 rxkad_Close(struct rx_securityClass *aobj)
333 code = FreeObject(aobj);
337 /* either: called to (re)create a new connection. */
340 rxkad_NewConnection(struct rx_securityClass *aobj,
341 struct rx_connection *aconn)
343 if (aconn->securityData)
344 return RXKADINCONSISTENCY; /* already allocated??? */
346 if (rx_IsServerConn(aconn)) {
347 int size = sizeof(struct rxkad_sconn);
348 aconn->securityData = (char *)rxi_Alloc(size);
349 memset(aconn->securityData, 0, size); /* initialize it conveniently */
350 } else { /* client */
351 struct rxkad_cprivate *tcp;
352 struct rxkad_cconn *tccp;
353 int size = sizeof(struct rxkad_cconn);
354 tccp = (struct rxkad_cconn *)rxi_Alloc(size);
355 aconn->securityData = (char *)tccp;
356 memset(aconn->securityData, 0, size); /* initialize it conveniently */
357 tcp = (struct rxkad_cprivate *)aobj->privateData;
358 if (!(tcp->type & rxkad_client))
359 return RXKADINCONSISTENCY;
360 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
361 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
362 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
363 INC_RXKAD_STATS(connections[rxkad_LevelIndex(tcp->level)]);
366 aobj->refCount++; /* attached connection */
370 /* either: called to destroy a connection. */
373 rxkad_DestroyConnection(struct rx_securityClass *aobj,
374 struct rx_connection *aconn)
376 if (rx_IsServerConn(aconn)) {
377 struct rxkad_sconn *sconn;
378 struct rxkad_serverinfo *rock;
379 sconn = (struct rxkad_sconn *)aconn->securityData;
381 aconn->securityData = 0;
382 if (sconn->authenticated)
383 INC_RXKAD_STATS(destroyConn[rxkad_LevelIndex(sconn->level)]);
385 INC_RXKAD_STATS(destroyUnauth);
388 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
389 rxi_Free(sconn, sizeof(struct rxkad_sconn));
391 INC_RXKAD_STATS(destroyUnused);
393 } else { /* client */
394 struct rxkad_cconn *cconn;
395 struct rxkad_cprivate *tcp;
396 cconn = (struct rxkad_cconn *)aconn->securityData;
397 tcp = (struct rxkad_cprivate *)aobj->privateData;
398 if (!(tcp->type & rxkad_client))
399 return RXKADINCONSISTENCY;
401 aconn->securityData = 0;
402 rxi_Free(cconn, sizeof(struct rxkad_cconn));
404 INC_RXKAD_STATS(destroyClient);
406 aobj->refCount--; /* decrement connection counter */
407 if (aobj->refCount <= 0) {
409 code = FreeObject(aobj);
416 /* either: decode packet */
419 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
420 struct rx_packet *apacket)
422 struct rx_connection *tconn;
424 fc_KeySchedule *schedule;
425 fc_InitializationVector *ivec;
428 u_int word; /* so we get unsigned right-shift */
433 tconn = rx_ConnectionOf(acall);
434 len = rx_GetDataSize(apacket);
435 checkCksum = 0; /* init */
436 if (rx_IsServerConn(tconn)) {
437 struct rxkad_sconn *sconn;
438 sconn = (struct rxkad_sconn *)tconn->securityData;
439 if (rx_GetPacketCksum(apacket) != 0)
440 sconn->cksumSeen = 1;
441 checkCksum = sconn->cksumSeen;
442 if (sconn && sconn->authenticated
443 && (osi_Time() < sconn->expirationTime)) {
444 level = sconn->level;
445 INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_server, level)]);
446 sconn->stats.packetsReceived++;
447 sconn->stats.bytesReceived += len;
448 schedule = (fc_KeySchedule *) sconn->keysched;
449 ivec = (fc_InitializationVector *) sconn->ivec;
451 INC_RXKAD_STATS(expired);
454 preSeq = sconn->preSeq;
455 } else { /* client connection */
456 struct rxkad_cconn *cconn;
457 struct rxkad_cprivate *tcp;
458 cconn = (struct rxkad_cconn *)tconn->securityData;
459 if (rx_GetPacketCksum(apacket) != 0)
460 cconn->cksumSeen = 1;
461 checkCksum = cconn->cksumSeen;
462 tcp = (struct rxkad_cprivate *)aobj->privateData;
463 if (!(tcp->type & rxkad_client))
464 return RXKADINCONSISTENCY;
466 INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_client, level)]);
467 cconn->stats.packetsReceived++;
468 cconn->stats.bytesReceived += len;
469 preSeq = cconn->preSeq;
470 schedule = (fc_KeySchedule *) tcp->keysched;
471 ivec = (fc_InitializationVector *) tcp->ivec;
475 code = ComputeSum(apacket, schedule, preSeq);
476 if (code != rx_GetPacketCksum(apacket))
477 return RXKADSEALEDINCON;
482 return 0; /* shouldn't happen */
484 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
485 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
489 code = rxkad_DecryptPacket(tconn, schedule, ivec, len, apacket);
494 word = ntohl(rx_GetInt32(apacket, 0)); /* get first sealed word */
496 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
497 return RXKADSEALEDINCON;
498 nlen = word & 0xffff; /* get real user data length */
500 /* The sealed length should be no larger than the initial length, since the
501 * reverse (round-up) occurs in ...PreparePacket */
504 rx_SetDataSize(apacket, nlen);
508 /* either: encode packet */
511 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
512 struct rx_packet *apacket)
514 struct rx_connection *tconn;
516 fc_KeySchedule *schedule;
517 fc_InitializationVector *ivec;
524 tconn = rx_ConnectionOf(acall);
525 len = rx_GetDataSize(apacket);
526 if (rx_IsServerConn(tconn)) {
527 struct rxkad_sconn *sconn;
528 sconn = (struct rxkad_sconn *)tconn->securityData;
529 if (sconn && sconn->authenticated
530 && (osi_Time() < sconn->expirationTime)) {
531 level = sconn->level;
532 INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_server, level)]);
533 sconn->stats.packetsSent++;
534 sconn->stats.bytesSent += len;
535 schedule = (fc_KeySchedule *) sconn->keysched;
536 ivec = (fc_InitializationVector *) sconn->ivec;
538 INC_RXKAD_STATS(expired); /* this is a pretty unlikely path... */
541 preSeq = sconn->preSeq;
542 } else { /* client connection */
543 struct rxkad_cconn *cconn;
544 struct rxkad_cprivate *tcp;
545 cconn = (struct rxkad_cconn *)tconn->securityData;
546 tcp = (struct rxkad_cprivate *)aobj->privateData;
547 if (!(tcp->type & rxkad_client))
548 return RXKADINCONSISTENCY;
550 INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_client, level)]);
551 cconn->stats.packetsSent++;
552 cconn->stats.bytesSent += len;
553 preSeq = cconn->preSeq;
554 schedule = (fc_KeySchedule *) tcp->keysched;
555 ivec = (fc_InitializationVector *) tcp->ivec;
558 /* compute upward compatible checksum */
559 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
560 if (level == rxkad_clear)
563 len = rx_GetDataSize(apacket);
564 word = (((apacket->header.seq ^ apacket->header.callNumber)
565 & 0xffff) << 16) | (len & 0xffff);
566 rx_PutInt32(apacket, 0, htonl(word));
570 return 0; /* shouldn't happen */
573 afs_max(ENCRYPTIONBLOCKSIZE,
574 len + rx_GetSecurityHeaderSize(tconn));
575 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
576 rxi_RoundUpPacket(apacket,
577 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
579 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
580 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
584 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
585 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
586 rxi_RoundUpPacket(apacket,
587 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
589 code = rxkad_EncryptPacket(tconn, schedule, ivec, nlen, apacket);
594 rx_SetDataSize(apacket, nlen);
598 /* either: return connection stats */
601 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
602 struct rx_securityObjectStats *astats)
605 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
606 if (!aconn->securityData) {
610 if (rx_IsServerConn(aconn)) {
611 struct rxkad_sconn *sconn;
612 sconn = (struct rxkad_sconn *)aconn->securityData;
613 astats->level = sconn->level;
614 if (sconn->authenticated)
616 if (sconn->cksumSeen)
618 astats->expires = sconn->expirationTime;
619 astats->bytesReceived = sconn->stats.bytesReceived;
620 astats->packetsReceived = sconn->stats.packetsReceived;
621 astats->bytesSent = sconn->stats.bytesSent;
622 astats->packetsSent = sconn->stats.packetsSent;
623 } else { /* client connection */
624 struct rxkad_cconn *cconn;
625 cconn = (struct rxkad_cconn *)aconn->securityData;
626 if (cconn->cksumSeen)
628 astats->bytesReceived = cconn->stats.bytesReceived;
629 astats->packetsReceived = cconn->stats.packetsReceived;
630 astats->bytesSent = cconn->stats.bytesSent;
631 astats->packetsSent = cconn->stats.packetsSent;
637 rxkad_StringToLevel(char *name)
639 if (strcmp(name, "clear") == 0)
641 if (strcmp(name, "auth") == 0)
643 if (strcmp(name, "crypt") == 0)
649 rxkad_LevelToString(rxkad_level level)
651 if (level == rxkad_clear)
653 if (level == rxkad_auth)
655 if (level == rxkad_crypt)