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"
35 #ifdef AFS_DARWIN60_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>
74 #include "private_data.h"
75 #define XPRT_RXKAD_COMMON
78 #define afs_max(a,b) ((a) < (b)? (b) : (a))
82 #define osi_Time() time(0)
84 /* variable initialization for the benefit of darwin compiler; if it causes
85 problems elsewhere, conditionalize for darwin or fc_test compile breaks */
86 struct rxkad_stats rxkad_stats = { { 0 } };
88 /* static prototypes */
89 static afs_int32 ComputeSum(struct rx_packet *apacket,
90 fc_KeySchedule * aschedule, afs_int32 * aivec);
91 static afs_int32 FreeObject(struct rx_securityClass *aobj);
93 /* this call sets up an endpoint structure, leaving it in *network* byte
94 * order so that it can be used quickly for encryption.
97 rxkad_SetupEndpoint(struct rx_connection *aconnp,
98 struct rxkad_endpoint *aendpointp)
100 register afs_int32 i;
102 aendpointp->cuid[0] = htonl(aconnp->epoch);
103 i = aconnp->cid & RX_CIDMASK;
104 aendpointp->cuid[1] = htonl(i);
105 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
106 aendpointp->securityIndex = htonl(aconnp->securityIndex);
110 /* setup xor information based on session key */
112 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
113 char *aivec, char *aresult)
115 struct rxkad_endpoint tendpoint;
118 rxkad_SetupEndpoint(aconnp, &tendpoint);
119 memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
120 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), aschedule, xor,
123 ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
124 ENCRYPTIONBLOCKSIZE);
128 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
129 * challenge response packet (which must be unencrypted and in network order).
130 * The endpoint.cksum field is omitted and treated as zero. The cksum is
131 * returned in network order. */
134 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
138 u_char *cp = (u_char *) v2r;
139 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
141 v2r->encrypted.endpoint.cksum = 0;
143 /* this function captured from budb/db_hash.c */
145 for (i = 0; i < sizeof(*v2r); i++)
146 cksum = (*cp++) + cksum * 0x10204081;
148 v2r->encrypted.endpoint.cksum = savedCksum;
153 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
155 if (level == rxkad_auth) {
156 rx_SetSecurityHeaderSize(conn, 4);
157 rx_SetSecurityMaxTrailerSize(conn, 4);
158 } else if (level == rxkad_crypt) {
159 rx_SetSecurityHeaderSize(conn, 8);
160 rx_SetSecurityMaxTrailerSize(conn, 8); /* XXX was 7, but why screw with
161 * unaligned accesses? */
165 /* returns a short integer in host byte order representing a good checksum of
169 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
173 register afs_uint32 t;
175 t = apacket->header.callNumber;
177 /* note that word [1] includes the channel # */
178 t = ((apacket->header.cid & 0x3) << 30)
179 | ((apacket->header.seq & 0x3fffffff));
181 /* XOR in the ivec from the per-endpoint encryption */
184 /* encrypts word as if it were a character string */
185 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
187 t = (t >> 16) & 0xffff;
189 t = 1; /* so that 0 means don't care */
195 FreeObject(struct rx_securityClass *aobj)
197 struct rxkad_cprivate *tcp; /* both structs start w/ type field */
199 if (aobj->refCount > 0)
200 return 0; /* still in use */
201 tcp = (struct rxkad_cprivate *)aobj->privateData;
202 rxi_Free(aobj, sizeof(struct rx_securityClass));
203 if (tcp->type & rxkad_client) {
204 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
205 } else if (tcp->type & rxkad_server) {
206 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
208 return RXKADINCONSISTENCY;
211 rxkad_stats.destroyObject++;
216 /* rxkad_Close - called by rx with the security class object as a parameter
217 * when a security object is to be discarded */
220 rxkad_Close(struct rx_securityClass *aobj)
224 code = FreeObject(aobj);
228 /* either: called to (re)create a new connection. */
231 rxkad_NewConnection(struct rx_securityClass *aobj,
232 struct rx_connection *aconn)
234 if (aconn->securityData)
235 return RXKADINCONSISTENCY; /* already allocated??? */
237 if (rx_IsServerConn(aconn)) {
238 int size = sizeof(struct rxkad_sconn);
239 aconn->securityData = (char *)rxi_Alloc(size);
240 memset(aconn->securityData, 0, size); /* initialize it conveniently */
241 } else { /* client */
242 struct rxkad_cprivate *tcp;
243 struct rxkad_cconn *tccp;
244 int size = sizeof(struct rxkad_cconn);
245 tccp = (struct rxkad_cconn *)rxi_Alloc(size);
246 aconn->securityData = (char *)tccp;
247 memset(aconn->securityData, 0, size); /* initialize it conveniently */
248 tcp = (struct rxkad_cprivate *)aobj->privateData;
249 if (!(tcp->type & rxkad_client))
250 return RXKADINCONSISTENCY;
251 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
252 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
253 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
255 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
259 aobj->refCount++; /* attached connection */
263 /* either: called to destroy a connection. */
266 rxkad_DestroyConnection(struct rx_securityClass *aobj,
267 struct rx_connection *aconn)
269 if (rx_IsServerConn(aconn)) {
270 struct rxkad_sconn *sconn;
271 struct rxkad_serverinfo *rock;
272 sconn = (struct rxkad_sconn *)aconn->securityData;
274 aconn->securityData = 0;
276 if (sconn->authenticated)
277 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
279 rxkad_stats.destroyUnauth++;
283 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
284 rxi_Free(sconn, sizeof(struct rxkad_sconn));
287 rxkad_stats.destroyUnused++;
290 } else { /* client */
291 struct rxkad_cconn *cconn;
292 struct rxkad_cprivate *tcp;
293 cconn = (struct rxkad_cconn *)aconn->securityData;
294 tcp = (struct rxkad_cprivate *)aobj->privateData;
295 if (!(tcp->type & rxkad_client))
296 return RXKADINCONSISTENCY;
298 aconn->securityData = 0;
299 rxi_Free(cconn, sizeof(struct rxkad_cconn));
302 rxkad_stats.destroyClient++;
305 aobj->refCount--; /* decrement connection counter */
306 if (aobj->refCount <= 0) {
308 code = FreeObject(aobj);
315 /* either: decode packet */
318 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
319 struct rx_packet *apacket)
321 struct rx_connection *tconn;
323 fc_KeySchedule *schedule;
324 fc_InitializationVector *ivec;
327 u_int word; /* so we get unsigned right-shift */
332 tconn = rx_ConnectionOf(acall);
333 len = rx_GetDataSize(apacket);
334 checkCksum = 0; /* init */
335 if (rx_IsServerConn(tconn)) {
336 struct rxkad_sconn *sconn;
337 sconn = (struct rxkad_sconn *)tconn->securityData;
338 if (rx_GetPacketCksum(apacket) != 0)
339 sconn->cksumSeen = 1;
340 checkCksum = sconn->cksumSeen;
341 if (sconn && sconn->authenticated
342 && (osi_Time() < sconn->expirationTime)) {
343 level = sconn->level;
345 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
347 sconn->stats.packetsReceived++;
348 sconn->stats.bytesReceived += len;
349 schedule = (fc_KeySchedule *) sconn->keysched;
350 ivec = (fc_InitializationVector *) sconn->ivec;
353 rxkad_stats.expired++;
357 preSeq = sconn->preSeq;
358 } else { /* client connection */
359 struct rxkad_cconn *cconn;
360 struct rxkad_cprivate *tcp;
361 cconn = (struct rxkad_cconn *)tconn->securityData;
362 if (rx_GetPacketCksum(apacket) != 0)
363 cconn->cksumSeen = 1;
364 checkCksum = cconn->cksumSeen;
365 tcp = (struct rxkad_cprivate *)aobj->privateData;
366 if (!(tcp->type & rxkad_client))
367 return RXKADINCONSISTENCY;
370 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
372 cconn->stats.packetsReceived++;
373 cconn->stats.bytesReceived += len;
374 preSeq = cconn->preSeq;
375 schedule = (fc_KeySchedule *) tcp->keysched;
376 ivec = (fc_InitializationVector *) tcp->ivec;
380 code = ComputeSum(apacket, schedule, preSeq);
381 if (code != rx_GetPacketCksum(apacket))
382 return RXKADSEALEDINCON;
387 return 0; /* shouldn't happen */
389 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
390 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
394 code = rxkad_DecryptPacket(tconn, schedule, ivec, len, apacket);
399 word = ntohl(rx_GetInt32(apacket, 0)); /* get first sealed word */
401 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
402 return RXKADSEALEDINCON;
403 nlen = word & 0xffff; /* get real user data length */
405 /* The sealed length should be no larger than the initial length, since the
406 * reverse (round-up) occurs in ...PreparePacket */
409 rx_SetDataSize(apacket, nlen);
413 /* either: encode packet */
416 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
417 struct rx_packet *apacket)
419 struct rx_connection *tconn;
421 fc_KeySchedule *schedule;
422 fc_InitializationVector *ivec;
429 tconn = rx_ConnectionOf(acall);
430 len = rx_GetDataSize(apacket);
431 if (rx_IsServerConn(tconn)) {
432 struct rxkad_sconn *sconn;
433 sconn = (struct rxkad_sconn *)tconn->securityData;
434 if (sconn && sconn->authenticated
435 && (osi_Time() < sconn->expirationTime)) {
436 level = sconn->level;
439 preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
441 sconn->stats.packetsSent++;
442 sconn->stats.bytesSent += len;
443 schedule = (fc_KeySchedule *) sconn->keysched;
444 ivec = (fc_InitializationVector *) sconn->ivec;
447 rxkad_stats.expired++; /* this is a pretty unlikely path... */
451 preSeq = sconn->preSeq;
452 } else { /* client connection */
453 struct rxkad_cconn *cconn;
454 struct rxkad_cprivate *tcp;
455 cconn = (struct rxkad_cconn *)tconn->securityData;
456 tcp = (struct rxkad_cprivate *)aobj->privateData;
457 if (!(tcp->type & rxkad_client))
458 return RXKADINCONSISTENCY;
461 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
463 cconn->stats.packetsSent++;
464 cconn->stats.bytesSent += len;
465 preSeq = cconn->preSeq;
466 schedule = (fc_KeySchedule *) tcp->keysched;
467 ivec = (fc_InitializationVector *) tcp->ivec;
470 /* compute upward compatible checksum */
471 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
472 if (level == rxkad_clear)
475 len = rx_GetDataSize(apacket);
476 word = (((apacket->header.seq ^ apacket->header.callNumber)
477 & 0xffff) << 16) | (len & 0xffff);
478 rx_PutInt32(apacket, 0, htonl(word));
482 return 0; /* shouldn't happen */
485 afs_max(ENCRYPTIONBLOCKSIZE,
486 len + rx_GetSecurityHeaderSize(tconn));
487 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
488 rxi_RoundUpPacket(apacket,
489 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
491 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
492 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
496 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
497 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
498 rxi_RoundUpPacket(apacket,
499 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
501 code = rxkad_EncryptPacket(tconn, schedule, ivec, nlen, apacket);
506 rx_SetDataSize(apacket, nlen);
510 /* either: return connection stats */
513 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
514 struct rx_securityObjectStats *astats)
517 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
518 if (!aconn->securityData) {
522 if (rx_IsServerConn(aconn)) {
523 struct rxkad_sconn *sconn;
524 sconn = (struct rxkad_sconn *)aconn->securityData;
525 astats->level = sconn->level;
526 if (sconn->authenticated)
528 if (sconn->cksumSeen)
530 astats->expires = sconn->expirationTime;
531 astats->bytesReceived = sconn->stats.bytesReceived;
532 astats->packetsReceived = sconn->stats.packetsReceived;
533 astats->bytesSent = sconn->stats.bytesSent;
534 astats->packetsSent = sconn->stats.packetsSent;
535 } else { /* client connection */
536 struct rxkad_cconn *cconn;
537 cconn = (struct rxkad_cconn *)aconn->securityData;
538 if (cconn->cksumSeen)
540 astats->bytesReceived = cconn->stats.bytesReceived;
541 astats->packetsReceived = cconn->stats.packetsReceived;
542 astats->bytesSent = cconn->stats.bytesSent;
543 astats->packetsSent = cconn->stats.packetsSent;
549 rxkad_StringToLevel(char *name)
551 if (strcmp(name, "clear") == 0)
553 if (strcmp(name, "auth") == 0)
555 if (strcmp(name, "crypt") == 0)
561 rxkad_LevelToString(rxkad_level level)
563 if (level == rxkad_clear)
565 if (level == rxkad_auth)
567 if (level == rxkad_crypt)