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>
13 #include <afs/param.h>
17 #include <sys/time_impl.h>
20 #define INCLUDE_RXKAD_PRIVATE_DECLS
24 #include "afs/afs_osi.h"
25 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
28 #if defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV)
34 #include "afs/sysincludes.h"
35 #include "afsincludes.h"
39 #if defined(AFS_NT40_ENV) && defined(AFS_PTHREAD_ENV)
40 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
42 #include <afs/afsutil.h>
46 #include <rx/rx_packet.h>
50 #include "private_data.h"
51 #define XPRT_RXKAD_COMMON
54 #define afs_max(a,b) ((a) < (b)? (b) : (a))
58 #define osi_Time() time(0)
60 /* variable initialization for the benefit of darwin compiler; if it causes
61 problems elsewhere, conditionalize for darwin or fc_test compile breaks */
62 #ifdef AFS_PTHREAD_ENV
63 struct rxkad_global_stats rxkad_global_stats = { 0 };
64 pthread_mutex_t rxkad_global_stats_lock;
65 pthread_key_t rxkad_stats_key;
66 #else /* AFS_PTHREAD_ENV */
67 struct rxkad_stats rxkad_stats = { { 0 } };
68 #endif /* AFS_PTHREAD_ENV */
70 #ifdef AFS_PTHREAD_ENV
71 /* Pthread initialisation */
72 static pthread_once_t rxkad_once_init = PTHREAD_ONCE_INIT;
73 extern pthread_mutex_t rxkad_client_uid_mutex;
74 extern pthread_mutex_t rxkad_random_mutex;
77 rxkad_global_stats_init(void)
79 osi_Assert(pthread_mutex_init(&rxkad_global_stats_lock, (const pthread_mutexattr_t *)0) == 0);
80 osi_Assert(pthread_key_create(&rxkad_stats_key, NULL) == 0);
81 memset(&rxkad_global_stats, 0, sizeof(rxkad_global_stats));
85 rxkad_InitPthread(void) {
86 MUTEX_INIT(&rxkad_client_uid_mutex, "uid", MUTEX_DEFAULT, 0);
87 MUTEX_INIT(&rxkad_random_mutex, "rxkad random", MUTEX_DEFAULT, 0);
89 rxkad_global_stats_init();
94 osi_Assert(pthread_once(&rxkad_once_init, rxkad_InitPthread) == 0);
97 /* rxkad_stats related stuff */
100 * Macro to insert an element at the tail of a doubly linked list
102 #define DLL_INSERT_TAIL(ptr,head,tail,next,prev) \
104 (ptr)->next = NULL; \
105 (ptr)->prev = (tail); \
108 (ptr)->prev->next = (ptr); \
111 osi_Assert((head) && ((head)->prev == NULL)); \
115 rxkad_thr_stats_init(void) {
116 rxkad_stats_t * rxkad_stats;
117 rxkad_stats = calloc(1, sizeof(rxkad_stats_t));
118 osi_Assert(rxkad_stats != NULL && pthread_setspecific(rxkad_stats_key,rxkad_stats) == 0);
119 RXKAD_GLOBAL_STATS_LOCK;
120 DLL_INSERT_TAIL(rxkad_stats, rxkad_global_stats.first, rxkad_global_stats.last, next, prev);
121 RXKAD_GLOBAL_STATS_UNLOCK;
125 int rxkad_stats_agg(rxkad_stats_t * rxkad_stats) {
126 rxkad_stats_t * thr_stats;
127 osi_Assert(rxkad_stats != NULL);
128 memset(rxkad_stats, 0, sizeof(rxkad_stats_t));
129 RXKAD_GLOBAL_STATS_LOCK;
130 for (thr_stats = rxkad_global_stats.first; thr_stats != NULL; thr_stats = thr_stats->next) {
131 rxkad_stats->connections[0] += thr_stats->connections[0];
132 rxkad_stats->connections[1] += thr_stats->connections[1];
133 rxkad_stats->connections[2] += thr_stats->connections[2];
134 rxkad_stats->destroyObject += thr_stats->destroyObject;
135 rxkad_stats->destroyClient += thr_stats->destroyClient;
136 rxkad_stats->destroyUnused += thr_stats->destroyUnused;
137 rxkad_stats->destroyUnauth += thr_stats->destroyUnauth;
138 rxkad_stats->destroyConn[0] += thr_stats->destroyConn[0];
139 rxkad_stats->destroyConn[1] += thr_stats->destroyConn[1];
140 rxkad_stats->destroyConn[2] += thr_stats->destroyConn[2];
141 rxkad_stats->expired += thr_stats->expired;
142 rxkad_stats->challengesSent += thr_stats->challengesSent;
143 rxkad_stats->challenges[0] += thr_stats->challenges[0];
144 rxkad_stats->challenges[1] += thr_stats->challenges[1];
145 rxkad_stats->challenges[2] += thr_stats->challenges[2];
146 rxkad_stats->responses[0] += thr_stats->responses[0];
147 rxkad_stats->responses[1] += thr_stats->responses[1];
148 rxkad_stats->responses[2] += thr_stats->responses[2];
149 rxkad_stats->preparePackets[0] += thr_stats->preparePackets[0];
150 rxkad_stats->preparePackets[1] += thr_stats->preparePackets[1];
151 rxkad_stats->preparePackets[2] += thr_stats->preparePackets[2];
152 rxkad_stats->preparePackets[3] += thr_stats->preparePackets[3];
153 rxkad_stats->preparePackets[4] += thr_stats->preparePackets[4];
154 rxkad_stats->preparePackets[5] += thr_stats->preparePackets[5];
155 rxkad_stats->checkPackets[0] += thr_stats->checkPackets[0];
156 rxkad_stats->checkPackets[1] += thr_stats->checkPackets[1];
157 rxkad_stats->checkPackets[2] += thr_stats->checkPackets[2];
158 rxkad_stats->checkPackets[3] += thr_stats->checkPackets[3];
159 rxkad_stats->checkPackets[4] += thr_stats->checkPackets[4];
160 rxkad_stats->checkPackets[5] += thr_stats->checkPackets[5];
161 rxkad_stats->bytesEncrypted[0] += thr_stats->bytesEncrypted[0];
162 rxkad_stats->bytesEncrypted[1] += thr_stats->bytesEncrypted[1];
163 rxkad_stats->bytesDecrypted[0] += thr_stats->bytesDecrypted[0];
164 rxkad_stats->bytesDecrypted[1] += thr_stats->bytesDecrypted[1];
165 rxkad_stats->fc_encrypts[0] += thr_stats->fc_encrypts[0];
166 rxkad_stats->fc_encrypts[1] += thr_stats->fc_encrypts[1];
167 rxkad_stats->fc_key_scheds += thr_stats->fc_key_scheds;
168 rxkad_stats->des_encrypts[0] += thr_stats->des_encrypts[0];
169 rxkad_stats->des_encrypts[1] += thr_stats->des_encrypts[1];
170 rxkad_stats->des_key_scheds += thr_stats->des_key_scheds;
171 rxkad_stats->des_randoms += thr_stats->des_randoms;
172 rxkad_stats->clientObjects += thr_stats->clientObjects;
173 rxkad_stats->serverObjects += thr_stats->serverObjects;
174 rxkad_stats->spares[0] += thr_stats->spares[0];
175 rxkad_stats->spares[1] += thr_stats->spares[1];
176 rxkad_stats->spares[2] += thr_stats->spares[2];
177 rxkad_stats->spares[3] += thr_stats->spares[3];
178 rxkad_stats->spares[4] += thr_stats->spares[4];
179 rxkad_stats->spares[5] += thr_stats->spares[5];
180 rxkad_stats->spares[6] += thr_stats->spares[6];
181 rxkad_stats->spares[7] += thr_stats->spares[7];
183 RXKAD_GLOBAL_STATS_UNLOCK;
192 #endif /* AFS_PTHREAD_ENV */
194 /* static prototypes */
195 static afs_int32 ComputeSum(struct rx_packet *apacket,
196 fc_KeySchedule * aschedule, afs_int32 * aivec);
197 static afs_int32 FreeObject(struct rx_securityClass *aobj);
199 /* this call sets up an endpoint structure, leaving it in *network* byte
200 * order so that it can be used quickly for encryption.
203 rxkad_SetupEndpoint(struct rx_connection *aconnp,
204 struct rxkad_endpoint *aendpointp)
208 aendpointp->cuid[0] = htonl(rx_GetConnectionEpoch(aconnp));
209 i = rx_GetConnectionId(aconnp) & RX_CIDMASK;
210 aendpointp->cuid[1] = htonl(i);
211 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
212 aendpointp->securityIndex = htonl(rx_SecurityClassOf(aconnp));
216 /* setup xor information based on session key */
218 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
219 char *aivec, char *aresult)
221 struct rxkad_endpoint tendpoint;
224 rxkad_SetupEndpoint(aconnp, &tendpoint);
225 memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
226 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), *aschedule, xor,
229 ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
230 ENCRYPTIONBLOCKSIZE);
234 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
235 * challenge response packet (which must be unencrypted and in network order).
236 * The endpoint.cksum field is omitted and treated as zero. The cksum is
237 * returned in network order. */
240 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
244 u_char *cp = (u_char *) v2r;
245 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
247 v2r->encrypted.endpoint.cksum = 0;
249 /* this function captured from budb/db_hash.c */
251 for (i = 0; i < sizeof(*v2r); i++)
252 cksum = (*cp++) + cksum * 0x10204081;
254 v2r->encrypted.endpoint.cksum = savedCksum;
259 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
261 if (level == rxkad_auth) {
262 rx_SetSecurityHeaderSize(conn, 4);
263 rx_SetSecurityMaxTrailerSize(conn, 4);
264 } else if (level == rxkad_crypt) {
265 rx_SetSecurityHeaderSize(conn, 8);
266 rx_SetSecurityMaxTrailerSize(conn, 8); /* XXX was 7, but why screw with
267 * unaligned accesses? */
271 /* returns a short integer in host byte order representing a good checksum of
275 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
281 t = apacket->header.callNumber;
283 /* note that word [1] includes the channel # */
284 t = ((apacket->header.cid & 0x3) << 30)
285 | ((apacket->header.seq & 0x3fffffff));
287 /* XOR in the ivec from the per-endpoint encryption */
290 /* encrypts word as if it were a character string */
291 fc_ecb_encrypt(word, word, *aschedule, ENCRYPT);
293 t = (t >> 16) & 0xffff;
295 t = 1; /* so that 0 means don't care */
301 FreeObject(struct rx_securityClass *aobj)
303 struct rxkad_cprivate *tcp; /* both structs start w/ type field */
305 if (aobj->refCount > 0)
306 return 0; /* still in use */
307 tcp = (struct rxkad_cprivate *)aobj->privateData;
308 rxi_Free(aobj, sizeof(struct rx_securityClass));
309 if (tcp->type & rxkad_client) {
310 afs_int32 psize = PDATA_SIZE(tcp->ticketLen);
311 rxi_Free(tcp, psize);
312 } else if (tcp->type & rxkad_server) {
313 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
315 return RXKADINCONSISTENCY;
317 INC_RXKAD_STATS(destroyObject);
321 /* rxkad_Close - called by rx with the security class object as a parameter
322 * when a security object is to be discarded */
325 rxkad_Close(struct rx_securityClass *aobj)
329 code = FreeObject(aobj);
333 /* either: called to (re)create a new connection. */
336 rxkad_NewConnection(struct rx_securityClass *aobj,
337 struct rx_connection *aconn)
339 if (rx_GetSecurityData(aconn) != NULL)
340 return RXKADINCONSISTENCY; /* already allocated??? */
342 if (rx_IsServerConn(aconn)) {
343 struct rxkad_sconn *data;
344 data = rxi_Alloc(sizeof(struct rxkad_sconn));
345 memset(data, 0, sizeof(struct rxkad_sconn));
346 rx_SetSecurityData(aconn, data);
347 } else { /* client */
348 struct rxkad_cprivate *tcp;
349 struct rxkad_cconn *data;
351 data = rxi_Alloc(sizeof(struct rxkad_cconn));
352 memset(data, 0, sizeof(struct rxkad_cconn));
353 rx_SetSecurityData(aconn, data);
355 tcp = (struct rxkad_cprivate *)aobj->privateData;
356 if (!(tcp->type & rxkad_client))
357 return RXKADINCONSISTENCY;
358 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
359 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
360 rxkad_DeriveXORInfo(aconn, (fc_KeySchedule *)tcp->keysched, (char *)tcp->ivec, (char *)data->preSeq);
361 INC_RXKAD_STATS(connections[rxkad_LevelIndex(tcp->level)]);
364 aobj->refCount++; /* attached connection */
368 /* either: called to destroy a connection. */
371 rxkad_DestroyConnection(struct rx_securityClass *aobj,
372 struct rx_connection *aconn)
374 if (rx_IsServerConn(aconn)) {
375 struct rxkad_sconn *sconn;
376 struct rxkad_serverinfo *rock;
377 sconn = rx_GetSecurityData(aconn);
379 rx_SetSecurityData(aconn, NULL);
380 if (sconn->authenticated)
381 INC_RXKAD_STATS(destroyConn[rxkad_LevelIndex(sconn->level)]);
383 INC_RXKAD_STATS(destroyUnauth);
386 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
387 rxi_Free(sconn, sizeof(struct rxkad_sconn));
389 INC_RXKAD_STATS(destroyUnused);
391 } else { /* client */
392 struct rxkad_cconn *cconn;
393 struct rxkad_cprivate *tcp;
394 cconn = rx_GetSecurityData(aconn);
395 tcp = (struct rxkad_cprivate *)aobj->privateData;
396 if (!(tcp->type & rxkad_client))
397 return RXKADINCONSISTENCY;
399 rx_SetSecurityData(aconn, NULL);
400 rxi_Free(cconn, sizeof(struct rxkad_cconn));
402 INC_RXKAD_STATS(destroyClient);
404 aobj->refCount--; /* decrement connection counter */
405 if (aobj->refCount <= 0) {
407 code = FreeObject(aobj);
414 /* either: decode packet */
417 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
418 struct rx_packet *apacket)
420 struct rx_connection *tconn;
422 const fc_KeySchedule *schedule;
423 fc_InitializationVector *ivec;
426 u_int word; /* so we get unsigned right-shift */
431 tconn = rx_ConnectionOf(acall);
432 len = rx_GetDataSize(apacket);
433 checkCksum = 0; /* init */
434 if (rx_IsServerConn(tconn)) {
435 struct rxkad_sconn *sconn;
436 sconn = rx_GetSecurityData(tconn);
437 if (rx_GetPacketCksum(apacket) != 0)
438 sconn->cksumSeen = 1;
439 checkCksum = sconn->cksumSeen;
440 if (sconn && sconn->authenticated
441 && (osi_Time() < sconn->expirationTime)) {
442 level = sconn->level;
443 INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_server, level)]);
444 sconn->stats.packetsReceived++;
445 sconn->stats.bytesReceived += len;
446 schedule = (const fc_KeySchedule *) sconn->keysched;
447 ivec = (fc_InitializationVector *) sconn->ivec;
449 INC_RXKAD_STATS(expired);
452 preSeq = sconn->preSeq;
453 } else { /* client connection */
454 struct rxkad_cconn *cconn;
455 struct rxkad_cprivate *tcp;
456 cconn = rx_GetSecurityData(tconn);
457 if (rx_GetPacketCksum(apacket) != 0)
458 cconn->cksumSeen = 1;
459 checkCksum = cconn->cksumSeen;
460 tcp = (struct rxkad_cprivate *)aobj->privateData;
461 if (!(tcp->type & rxkad_client))
462 return RXKADINCONSISTENCY;
464 INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_client, level)]);
465 cconn->stats.packetsReceived++;
466 cconn->stats.bytesReceived += len;
467 preSeq = cconn->preSeq;
468 schedule = (const fc_KeySchedule *) tcp->keysched;
469 ivec = (fc_InitializationVector *) tcp->ivec;
473 code = ComputeSum(apacket, (fc_KeySchedule *)schedule, preSeq);
474 if (code != rx_GetPacketCksum(apacket))
475 return RXKADSEALEDINCON;
480 return 0; /* shouldn't happen */
482 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
483 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
487 code = rxkad_DecryptPacket(tconn, schedule, (const fc_InitializationVector *)ivec, len, apacket);
492 word = ntohl(rx_GetInt32(apacket, 0)); /* get first sealed word */
494 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
495 return RXKADSEALEDINCON;
496 nlen = word & 0xffff; /* get real user data length */
498 /* The sealed length should be no larger than the initial length, since the
499 * reverse (round-up) occurs in ...PreparePacket */
502 rx_SetDataSize(apacket, nlen);
506 /* either: encode packet */
509 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
510 struct rx_packet *apacket)
512 struct rx_connection *tconn;
514 fc_KeySchedule *schedule;
515 fc_InitializationVector *ivec;
522 tconn = rx_ConnectionOf(acall);
523 len = rx_GetDataSize(apacket);
524 if (rx_IsServerConn(tconn)) {
525 struct rxkad_sconn *sconn;
526 sconn = rx_GetSecurityData(tconn);
527 if (sconn && sconn->authenticated
528 && (osi_Time() < sconn->expirationTime)) {
529 level = sconn->level;
530 INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_server, level)]);
531 sconn->stats.packetsSent++;
532 sconn->stats.bytesSent += len;
533 schedule = (fc_KeySchedule *) sconn->keysched;
534 ivec = (fc_InitializationVector *) sconn->ivec;
536 INC_RXKAD_STATS(expired); /* this is a pretty unlikely path... */
539 preSeq = sconn->preSeq;
540 } else { /* client connection */
541 struct rxkad_cconn *cconn;
542 struct rxkad_cprivate *tcp;
543 cconn = rx_GetSecurityData(tconn);
544 tcp = (struct rxkad_cprivate *)aobj->privateData;
545 if (!(tcp->type & rxkad_client))
546 return RXKADINCONSISTENCY;
548 INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_client, level)]);
549 cconn->stats.packetsSent++;
550 cconn->stats.bytesSent += len;
551 preSeq = cconn->preSeq;
552 schedule = (fc_KeySchedule *) tcp->keysched;
553 ivec = (fc_InitializationVector *) tcp->ivec;
556 /* compute upward compatible checksum */
557 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
558 if (level == rxkad_clear)
561 len = rx_GetDataSize(apacket);
562 word = (((apacket->header.seq ^ apacket->header.callNumber)
563 & 0xffff) << 16) | (len & 0xffff);
564 rx_PutInt32(apacket, 0, htonl(word));
568 return 0; /* shouldn't happen */
571 afs_max(ENCRYPTIONBLOCKSIZE,
572 len + rx_GetSecurityHeaderSize(tconn));
573 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
574 rxi_RoundUpPacket(apacket,
575 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
577 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
578 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
582 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
583 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
584 rxi_RoundUpPacket(apacket,
585 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
587 code = rxkad_EncryptPacket(tconn, (const fc_KeySchedule *)schedule, (const fc_InitializationVector *)ivec, nlen, apacket);
592 rx_SetDataSize(apacket, nlen);
596 /* either: return connection stats */
599 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
600 struct rx_securityObjectStats *astats)
605 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
607 securityData = rx_GetSecurityData(aconn);
613 if (rx_IsServerConn(aconn)) {
614 struct rxkad_sconn *sconn = securityData;
616 astats->level = sconn->level;
617 if (sconn->authenticated)
619 if (sconn->cksumSeen)
621 astats->expires = sconn->expirationTime;
622 astats->bytesReceived = sconn->stats.bytesReceived;
623 astats->packetsReceived = sconn->stats.packetsReceived;
624 astats->bytesSent = sconn->stats.bytesSent;
625 astats->packetsSent = sconn->stats.packetsSent;
626 } else { /* client connection */
627 struct rxkad_cconn *cconn = securityData;
629 if (cconn->cksumSeen)
631 astats->bytesReceived = cconn->stats.bytesReceived;
632 astats->packetsReceived = cconn->stats.packetsReceived;
633 astats->bytesSent = cconn->stats.bytesSent;
634 astats->packetsSent = cconn->stats.packetsSent;
640 rxkad_StringToLevel(char *name)
642 if (strcmp(name, "clear") == 0)
644 if (strcmp(name, "auth") == 0)
646 if (strcmp(name, "crypt") == 0)
652 rxkad_LevelToString(rxkad_level level)
654 if (level == rxkad_clear)
656 if (level == rxkad_auth)
658 if (level == rxkad_crypt)