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>
23 #include "../afs/stds.h"
24 #include "../afs/afs_osi.h"
26 #include "../h/systm.h"
28 #include "../h/types.h"
29 #include "../h/time.h"
30 #ifndef AFS_LINUX22_ENV
31 #include "../rpc/types.h"
32 #include "../rpc/xdr.h"
33 #endif /* AFS_LINUX22_ENV */
35 #include "../afs/sysincludes.h"
36 #include "../afs/afsincludes.h"
42 #include <sys/types.h>
46 #ifdef AFS_PTHREAD_ENV
47 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
50 #include <netinet/in.h>
64 #include "private_data.h"
65 #define XPRT_RXKAD_COMMON
70 #define afs_max(a,b) ((a) < (b)? (b) : (a))
74 #define osi_Time() time(0)
76 struct rxkad_stats rxkad_stats;
78 /* this call sets up an endpoint structure, leaving it in *network* byte
79 * order so that it can be used quickly for encryption.
81 rxkad_SetupEndpoint(aconnp, aendpointp)
82 IN struct rx_connection *aconnp;
83 OUT struct rxkad_endpoint *aendpointp;
87 aendpointp->cuid[0] = htonl(aconnp->epoch);
88 i = aconnp->cid & RX_CIDMASK;
89 aendpointp->cuid[1] = htonl(i);
90 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
91 aendpointp->securityIndex = htonl(aconnp->securityIndex);
95 /* setup xor information based on session key */
96 rxkad_DeriveXORInfo(aconnp, aschedule, aivec, aresult)
97 IN struct rx_connection *aconnp;
98 IN fc_KeySchedule *aschedule;
102 struct rxkad_endpoint tendpoint;
105 rxkad_SetupEndpoint(aconnp, &tendpoint);
106 memcpy((void *)xor, aivec, 2*sizeof(afs_int32));
107 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
108 aschedule, xor, ENCRYPT);
109 memcpy(aresult, ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE, ENCRYPTIONBLOCKSIZE);
113 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
114 * challenge response packet (which must be unencrypted and in network order).
115 * The endpoint.cksum field is omitted and treated as zero. The cksum is
116 * returned in network order. */
118 afs_uint32 rxkad_CksumChallengeResponse (v2r)
119 IN struct rxkad_v2ChallengeResponse *v2r;
123 u_char *cp = (u_char *)v2r;
124 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
126 v2r->encrypted.endpoint.cksum = 0;
128 /* this function captured from budb/db_hash.c */
130 for (i=0; i<sizeof(*v2r); i++)
131 cksum = (*cp++) + cksum * 0x10204081;
133 v2r->encrypted.endpoint.cksum = savedCksum;
137 void rxkad_SetLevel(conn, level)
138 struct rx_connection *conn;
141 if (level == rxkad_auth) {
142 rx_SetSecurityHeaderSize (conn, 4);
143 rx_SetSecurityMaxTrailerSize (conn, 4);
145 else if (level == rxkad_crypt) {
146 rx_SetSecurityHeaderSize (conn, 8);
147 rx_SetSecurityMaxTrailerSize (conn, 8); /* XXX was 7, but why screw with
148 unaligned accesses? */
152 /* returns a short integer in host byte order representing a good checksum of
155 static afs_int32 ComputeSum(apacket, aschedule, aivec)
156 struct rx_packet *apacket;
158 fc_KeySchedule *aschedule; {
160 register afs_uint32 t;
162 t = apacket->header.callNumber;
164 /* note that word [1] includes the channel # */
165 t = ((apacket->header.cid & 0x3) << 30)
166 | ((apacket->header.seq & 0x3fffffff));
168 /* XOR in the ivec from the per-endpoint encryption */
171 /* encrypts word as if it were a character string */
172 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
174 t = (t >> 16) & 0xffff;
175 if (t == 0) t = 1; /* so that 0 means don't care */
180 static afs_int32 FreeObject (aobj)
181 IN struct rx_securityClass *aobj;
182 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
184 if (aobj->refCount > 0) return 0; /* still in use */
185 tcp = (struct rxkad_cprivate *)aobj->privateData;
186 rxi_Free(aobj, sizeof(struct rx_securityClass));
187 if (tcp->type & rxkad_client) {
188 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
190 else if (tcp->type & rxkad_server) {
191 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
193 else { return RXKADINCONSISTENCY; } /* unknown type */
195 rxkad_stats.destroyObject++;
200 /* rxkad_Close - called by rx with the security class object as a parameter
201 * when a security object is to be discarded */
203 rxs_return_t rxkad_Close (aobj)
204 IN struct rx_securityClass *aobj;
208 code = FreeObject (aobj);
212 /* either: called to (re)create a new connection. */
214 rxs_return_t rxkad_NewConnection (aobj, aconn)
215 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 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
250 struct rx_securityClass *aobj;
251 struct rx_connection *aconn;
253 if (rx_IsServerConn(aconn)) {
254 struct rxkad_sconn *sconn;
255 struct rxkad_serverinfo *rock;
256 sconn = (struct rxkad_sconn *)aconn->securityData;
258 aconn->securityData = 0;
260 if (sconn->authenticated)
261 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
262 else rxkad_stats.destroyUnauth++;
265 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
266 rxi_Free (sconn, sizeof(struct rxkad_sconn));
270 rxkad_stats.destroyUnused++;
275 struct rxkad_cconn *cconn;
276 struct rxkad_cprivate *tcp;
277 cconn = (struct rxkad_cconn *)aconn->securityData;
278 tcp = (struct rxkad_cprivate *) aobj->privateData;
279 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
281 aconn->securityData = 0;
282 rxi_Free (cconn, sizeof(struct rxkad_cconn));
285 rxkad_stats.destroyClient++;
288 aobj->refCount--; /* decrement connection counter */
289 if (aobj->refCount <= 0) {
291 code = FreeObject (aobj);
292 if (code) return code;
297 /* either: decode packet */
299 rxs_return_t rxkad_CheckPacket (aobj, acall, apacket)
300 struct rx_securityClass *aobj;
301 struct rx_call *acall;
302 struct rx_packet *apacket;
303 { struct rx_connection *tconn;
305 fc_KeySchedule *schedule;
306 fc_InitializationVector *ivec;
309 u_int word; /* so we get unsigned right-shift */
314 tconn = rx_ConnectionOf(acall);
315 len = rx_GetDataSize (apacket);
316 checkCksum = 0; /* init */
317 if (rx_IsServerConn(tconn)) {
318 struct rxkad_sconn *sconn;
319 sconn = (struct rxkad_sconn *) tconn->securityData;
320 if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
321 checkCksum = sconn->cksumSeen;
322 if (sconn && sconn->authenticated &&
323 (osi_Time() < sconn->expirationTime)) {
324 level = sconn->level;
326 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
328 sconn->stats.packetsReceived++;
329 sconn->stats.bytesReceived += len;
330 schedule = (fc_KeySchedule *)sconn->keysched;
331 ivec = (fc_InitializationVector *)sconn->ivec;
335 rxkad_stats.expired++;
339 preSeq = sconn->preSeq;
341 else { /* client connection */
342 struct rxkad_cconn *cconn;
343 struct rxkad_cprivate *tcp;
344 cconn = (struct rxkad_cconn *) tconn->securityData;
345 if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
346 checkCksum = cconn->cksumSeen;
347 tcp = (struct rxkad_cprivate *) aobj->privateData;
348 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
351 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
353 cconn->stats.packetsReceived++;
354 cconn->stats.bytesReceived += len;
355 preSeq = cconn->preSeq;
356 schedule = (fc_KeySchedule *)tcp->keysched;
357 ivec = (fc_InitializationVector *)tcp->ivec;
361 code = ComputeSum(apacket, schedule, preSeq);
362 if (code != rx_GetPacketCksum(apacket))
363 return RXKADSEALEDINCON;
367 case rxkad_clear: return 0; /* shouldn't happen */
369 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
370 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
374 code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
375 if (code) return code;
378 word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
380 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
381 return RXKADSEALEDINCON;
382 nlen = word & 0xffff; /* get real user data length */
384 /* The sealed length should be no larger than the initial length, since the
385 * reverse (round-up) occurs in ...PreparePacket */
388 rx_SetDataSize (apacket, nlen);
392 /* either: encode packet */
394 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
395 struct rx_securityClass *aobj;
396 struct rx_call *acall;
397 struct rx_packet *apacket;
399 struct rx_connection *tconn;
401 fc_KeySchedule *schedule;
402 fc_InitializationVector *ivec;
409 tconn = rx_ConnectionOf(acall);
410 len = rx_GetDataSize (apacket);
411 if (rx_IsServerConn(tconn)) {
412 struct rxkad_sconn *sconn;
413 sconn = (struct rxkad_sconn *) tconn->securityData;
414 if (sconn && sconn->authenticated &&
415 (osi_Time() < sconn->expirationTime)) {
416 level = sconn->level;
418 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
420 sconn->stats.packetsSent++;
421 sconn->stats.bytesSent += len;
422 schedule = (fc_KeySchedule *)sconn->keysched;
423 ivec = (fc_InitializationVector *)sconn->ivec;
427 rxkad_stats.expired++; /* this is a pretty unlikely path... */
431 preSeq = sconn->preSeq;
433 else { /* client connection */
434 struct rxkad_cconn *cconn;
435 struct rxkad_cprivate *tcp;
436 cconn = (struct rxkad_cconn *) tconn->securityData;
437 tcp = (struct rxkad_cprivate *) aobj->privateData;
438 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
441 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
443 cconn->stats.packetsSent++;
444 cconn->stats.bytesSent += len;
445 preSeq = cconn->preSeq;
446 schedule = (fc_KeySchedule *)tcp->keysched;
447 ivec = (fc_InitializationVector *)tcp->ivec;
450 /* compute upward compatible checksum */
451 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
452 if (level == rxkad_clear) return 0;
454 len = rx_GetDataSize (apacket);
455 word = (((apacket->header.seq ^ apacket->header.callNumber)
456 & 0xffff) << 16) | (len & 0xffff);
457 rx_PutInt32(apacket,0, htonl(word));
460 case rxkad_clear: return 0; /* shouldn't happen */
462 nlen = afs_max (ENCRYPTIONBLOCKSIZE,
463 len + rx_GetSecurityHeaderSize(tconn));
464 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
465 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
467 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
468 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
472 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
473 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
474 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
476 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
477 if (code) return code;
480 rx_SetDataSize (apacket, nlen);
484 /* either: return connection stats */
486 rxs_return_t rxkad_GetStats (aobj, aconn, astats)
487 IN struct rx_securityClass *aobj;
488 IN struct rx_connection *aconn;
489 OUT struct rx_securityObjectStats *astats;
492 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
493 if (!aconn->securityData) {
497 if (rx_IsServerConn(aconn)) {
498 struct rxkad_sconn *sconn;
499 sconn = (struct rxkad_sconn *) aconn->securityData;
500 astats->level = sconn->level;
501 if (sconn->authenticated) astats->flags |= 2;
502 if (sconn->cksumSeen) astats->flags |= 8;
503 astats->expires = sconn->expirationTime;
504 astats->bytesReceived = sconn->stats.bytesReceived;
505 astats->packetsReceived = sconn->stats.packetsReceived;
506 astats->bytesSent = sconn->stats.bytesSent;
507 astats->packetsSent = sconn->stats.packetsSent;
509 else { /* client connection */
510 struct rxkad_cconn *cconn;
511 cconn = (struct rxkad_cconn *) aconn->securityData;
512 if (cconn->cksumSeen) astats->flags |= 8;
513 astats->bytesReceived = cconn->stats.bytesReceived;
514 astats->packetsReceived = cconn->stats.packetsReceived;
515 astats->bytesSent = cconn->stats.bytesSent;
516 astats->packetsSent = cconn->stats.packetsSent;