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
29 #include "../afs/stds.h"
30 #include "../afs/afs_osi.h"
32 #include "../h/systm.h"
34 #include "../h/types.h"
35 #include "../h/time.h"
36 #ifndef AFS_LINUX22_ENV
37 #include "../rpc/types.h"
38 #include "../rx/xdr.h"
39 #endif /* AFS_LINUX22_ENV */
41 #include "../afs/sysincludes.h"
42 #include "../afs/afsincludes.h"
48 #include <sys/types.h>
52 #ifdef AFS_PTHREAD_ENV
53 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
56 #include <netinet/in.h>
70 #include "private_data.h"
71 #define XPRT_RXKAD_COMMON
76 #define afs_max(a,b) ((a) < (b)? (b) : (a))
80 #define osi_Time() time(0)
82 struct rxkad_stats rxkad_stats = {0};
84 /* static prototypes */
85 static afs_int32 ComputeSum(struct rx_packet *apacket,
86 fc_KeySchedule *aschedule, afs_int32 *aivec);
87 static afs_int32 FreeObject(struct rx_securityClass *aobj);
89 /* this call sets up an endpoint structure, leaving it in *network* byte
90 * order so that it can be used quickly for encryption.
92 int rxkad_SetupEndpoint(struct rx_connection *aconnp,
93 struct rxkad_endpoint *aendpointp)
97 aendpointp->cuid[0] = htonl(aconnp->epoch);
98 i = aconnp->cid & RX_CIDMASK;
99 aendpointp->cuid[1] = htonl(i);
100 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
101 aendpointp->securityIndex = htonl(aconnp->securityIndex);
105 /* setup xor information based on session key */
106 int rxkad_DeriveXORInfo(struct rx_connection *aconnp,
107 fc_KeySchedule *aschedule, char *aivec, char *aresult)
109 struct rxkad_endpoint tendpoint;
112 rxkad_SetupEndpoint(aconnp, &tendpoint);
113 memcpy((void *)xor, aivec, 2*sizeof(afs_int32));
114 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
115 aschedule, xor, ENCRYPT);
116 memcpy(aresult, ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE, ENCRYPTIONBLOCKSIZE);
120 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
121 * challenge response packet (which must be unencrypted and in network order).
122 * The endpoint.cksum field is omitted and treated as zero. The cksum is
123 * returned in network order. */
125 afs_uint32 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse *v2r)
129 u_char *cp = (u_char *)v2r;
130 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
132 v2r->encrypted.endpoint.cksum = 0;
134 /* this function captured from budb/db_hash.c */
136 for (i=0; i<sizeof(*v2r); i++)
137 cksum = (*cp++) + cksum * 0x10204081;
139 v2r->encrypted.endpoint.cksum = savedCksum;
143 void rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
145 if (level == rxkad_auth) {
146 rx_SetSecurityHeaderSize (conn, 4);
147 rx_SetSecurityMaxTrailerSize (conn, 4);
149 else if (level == rxkad_crypt) {
150 rx_SetSecurityHeaderSize (conn, 8);
151 rx_SetSecurityMaxTrailerSize (conn, 8); /* XXX was 7, but why screw with
152 unaligned accesses? */
156 /* returns a short integer in host byte order representing a good checksum of
159 static afs_int32 ComputeSum(struct rx_packet *apacket,
160 fc_KeySchedule *aschedule, afs_int32 *aivec)
163 register afs_uint32 t;
165 t = apacket->header.callNumber;
167 /* note that word [1] includes the channel # */
168 t = ((apacket->header.cid & 0x3) << 30)
169 | ((apacket->header.seq & 0x3fffffff));
171 /* XOR in the ivec from the per-endpoint encryption */
174 /* encrypts word as if it were a character string */
175 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
177 t = (t >> 16) & 0xffff;
178 if (t == 0) t = 1; /* so that 0 means don't care */
183 static afs_int32 FreeObject(struct rx_securityClass *aobj)
184 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
186 if (aobj->refCount > 0) return 0; /* still in use */
187 tcp = (struct rxkad_cprivate *)aobj->privateData;
188 rxi_Free(aobj, sizeof(struct rx_securityClass));
189 if (tcp->type & rxkad_client) {
190 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
192 else if (tcp->type & rxkad_server) {
193 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
195 else { return RXKADINCONSISTENCY; } /* unknown type */
197 rxkad_stats.destroyObject++;
202 /* rxkad_Close - called by rx with the security class object as a parameter
203 * when a security object is to be discarded */
205 int rxkad_Close(struct rx_securityClass *aobj)
209 code = FreeObject (aobj);
213 /* either: called to (re)create a new connection. */
215 int rxkad_NewConnection(struct rx_securityClass *aobj,
216 struct rx_connection *aconn)
218 if (aconn->securityData)
219 return RXKADINCONSISTENCY; /* already allocated??? */
221 if (rx_IsServerConn(aconn)) {
222 int size = sizeof(struct rxkad_sconn);
223 aconn->securityData = (char *) rxi_Alloc (size);
224 memset(aconn->securityData, 0, size); /* initialize it conveniently */
227 struct rxkad_cprivate *tcp;
228 struct rxkad_cconn *tccp;
229 int size = sizeof(struct rxkad_cconn);
230 tccp = (struct rxkad_cconn *) rxi_Alloc (size);
231 aconn->securityData = (char *) tccp;
232 memset(aconn->securityData, 0, size); /* initialize it conveniently */
233 tcp = (struct rxkad_cprivate *) aobj->privateData;
234 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
235 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
236 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
237 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
239 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
243 aobj->refCount++; /* attached connection */
247 /* either: called to destroy a connection. */
249 int rxkad_DestroyConnection(struct rx_securityClass *aobj,
250 struct rx_connection *aconn)
252 if (rx_IsServerConn(aconn)) {
253 struct rxkad_sconn *sconn;
254 struct rxkad_serverinfo *rock;
255 sconn = (struct rxkad_sconn *)aconn->securityData;
257 aconn->securityData = 0;
259 if (sconn->authenticated)
260 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
261 else rxkad_stats.destroyUnauth++;
264 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
265 rxi_Free (sconn, sizeof(struct rxkad_sconn));
269 rxkad_stats.destroyUnused++;
274 struct rxkad_cconn *cconn;
275 struct rxkad_cprivate *tcp;
276 cconn = (struct rxkad_cconn *)aconn->securityData;
277 tcp = (struct rxkad_cprivate *) aobj->privateData;
278 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
280 aconn->securityData = 0;
281 rxi_Free (cconn, sizeof(struct rxkad_cconn));
284 rxkad_stats.destroyClient++;
287 aobj->refCount--; /* decrement connection counter */
288 if (aobj->refCount <= 0) {
290 code = FreeObject (aobj);
291 if (code) return code;
296 /* either: decode packet */
298 int rxkad_CheckPacket(struct rx_securityClass *aobj,
299 struct rx_call *acall, struct rx_packet *apacket)
300 { struct rx_connection *tconn;
302 fc_KeySchedule *schedule;
303 fc_InitializationVector *ivec;
306 u_int word; /* so we get unsigned right-shift */
311 tconn = rx_ConnectionOf(acall);
312 len = rx_GetDataSize (apacket);
313 checkCksum = 0; /* init */
314 if (rx_IsServerConn(tconn)) {
315 struct rxkad_sconn *sconn;
316 sconn = (struct rxkad_sconn *) tconn->securityData;
317 if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
318 checkCksum = sconn->cksumSeen;
319 if (sconn && sconn->authenticated &&
320 (osi_Time() < sconn->expirationTime)) {
321 level = sconn->level;
323 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
325 sconn->stats.packetsReceived++;
326 sconn->stats.bytesReceived += len;
327 schedule = (fc_KeySchedule *)sconn->keysched;
328 ivec = (fc_InitializationVector *)sconn->ivec;
332 rxkad_stats.expired++;
336 preSeq = sconn->preSeq;
338 else { /* client connection */
339 struct rxkad_cconn *cconn;
340 struct rxkad_cprivate *tcp;
341 cconn = (struct rxkad_cconn *) tconn->securityData;
342 if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
343 checkCksum = cconn->cksumSeen;
344 tcp = (struct rxkad_cprivate *) aobj->privateData;
345 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
348 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
350 cconn->stats.packetsReceived++;
351 cconn->stats.bytesReceived += len;
352 preSeq = cconn->preSeq;
353 schedule = (fc_KeySchedule *)tcp->keysched;
354 ivec = (fc_InitializationVector *)tcp->ivec;
358 code = ComputeSum(apacket, schedule, preSeq);
359 if (code != rx_GetPacketCksum(apacket))
360 return RXKADSEALEDINCON;
364 case rxkad_clear: return 0; /* shouldn't happen */
366 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
367 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
371 code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
372 if (code) return code;
375 word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
377 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
378 return RXKADSEALEDINCON;
379 nlen = word & 0xffff; /* get real user data length */
381 /* The sealed length should be no larger than the initial length, since the
382 * reverse (round-up) occurs in ...PreparePacket */
385 rx_SetDataSize (apacket, nlen);
389 /* either: encode packet */
391 int rxkad_PreparePacket(struct rx_securityClass *aobj,
392 struct rx_call *acall, struct rx_packet *apacket)
394 struct rx_connection *tconn;
396 fc_KeySchedule *schedule;
397 fc_InitializationVector *ivec;
404 tconn = rx_ConnectionOf(acall);
405 len = rx_GetDataSize (apacket);
406 if (rx_IsServerConn(tconn)) {
407 struct rxkad_sconn *sconn;
408 sconn = (struct rxkad_sconn *) tconn->securityData;
409 if (sconn && sconn->authenticated &&
410 (osi_Time() < sconn->expirationTime)) {
411 level = sconn->level;
413 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
415 sconn->stats.packetsSent++;
416 sconn->stats.bytesSent += len;
417 schedule = (fc_KeySchedule *)sconn->keysched;
418 ivec = (fc_InitializationVector *)sconn->ivec;
422 rxkad_stats.expired++; /* this is a pretty unlikely path... */
426 preSeq = sconn->preSeq;
428 else { /* client connection */
429 struct rxkad_cconn *cconn;
430 struct rxkad_cprivate *tcp;
431 cconn = (struct rxkad_cconn *) tconn->securityData;
432 tcp = (struct rxkad_cprivate *) aobj->privateData;
433 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
436 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
438 cconn->stats.packetsSent++;
439 cconn->stats.bytesSent += len;
440 preSeq = cconn->preSeq;
441 schedule = (fc_KeySchedule *)tcp->keysched;
442 ivec = (fc_InitializationVector *)tcp->ivec;
445 /* compute upward compatible checksum */
446 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
447 if (level == rxkad_clear) return 0;
449 len = rx_GetDataSize (apacket);
450 word = (((apacket->header.seq ^ apacket->header.callNumber)
451 & 0xffff) << 16) | (len & 0xffff);
452 rx_PutInt32(apacket,0, htonl(word));
455 case rxkad_clear: return 0; /* shouldn't happen */
457 nlen = afs_max (ENCRYPTIONBLOCKSIZE,
458 len + rx_GetSecurityHeaderSize(tconn));
459 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
460 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
462 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
463 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
467 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
468 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
469 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
471 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
472 if (code) return code;
475 rx_SetDataSize (apacket, nlen);
479 /* either: return connection stats */
481 int rxkad_GetStats(struct rx_securityClass *aobj,
482 struct rx_connection *aconn, struct rx_securityObjectStats *astats)
485 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
486 if (!aconn->securityData) {
490 if (rx_IsServerConn(aconn)) {
491 struct rxkad_sconn *sconn;
492 sconn = (struct rxkad_sconn *) aconn->securityData;
493 astats->level = sconn->level;
494 if (sconn->authenticated) astats->flags |= 2;
495 if (sconn->cksumSeen) astats->flags |= 8;
496 astats->expires = sconn->expirationTime;
497 astats->bytesReceived = sconn->stats.bytesReceived;
498 astats->packetsReceived = sconn->stats.packetsReceived;
499 astats->bytesSent = sconn->stats.bytesSent;
500 astats->packetsSent = sconn->stats.packetsSent;
502 else { /* client connection */
503 struct rxkad_cconn *cconn;
504 cconn = (struct rxkad_cconn *) aconn->securityData;
505 if (cconn->cksumSeen) astats->flags |= 8;
506 astats->bytesReceived = cconn->stats.bytesReceived;
507 astats->packetsReceived = cconn->stats.packetsReceived;
508 astats->bytesSent = cconn->stats.bytesSent;
509 astats->packetsSent = cconn->stats.packetsSent;