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 #ifdef AFS_DARWIN60_ENV
35 #include "../h/kernel.h"
37 #include "../h/types.h"
38 #include "../h/time.h"
39 #ifndef AFS_LINUX22_ENV
40 #include "../rpc/types.h"
41 #include "../rx/xdr.h"
42 #endif /* AFS_LINUX22_ENV */
44 #include "../afs/sysincludes.h"
45 #include "../afs/afsincludes.h"
51 #include <sys/types.h>
55 #ifdef AFS_PTHREAD_ENV
56 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
59 #include <netinet/in.h>
73 #include "private_data.h"
74 #define XPRT_RXKAD_COMMON
79 #define afs_max(a,b) ((a) < (b)? (b) : (a))
83 #define osi_Time() time(0)
85 struct rxkad_stats rxkad_stats = {0};
87 /* static prototypes */
88 static afs_int32 ComputeSum(struct rx_packet *apacket,
89 fc_KeySchedule *aschedule, afs_int32 *aivec);
90 static afs_int32 FreeObject(struct rx_securityClass *aobj);
92 /* this call sets up an endpoint structure, leaving it in *network* byte
93 * order so that it can be used quickly for encryption.
95 int rxkad_SetupEndpoint(struct rx_connection *aconnp,
96 struct rxkad_endpoint *aendpointp)
100 aendpointp->cuid[0] = htonl(aconnp->epoch);
101 i = aconnp->cid & RX_CIDMASK;
102 aendpointp->cuid[1] = htonl(i);
103 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
104 aendpointp->securityIndex = htonl(aconnp->securityIndex);
108 /* setup xor information based on session key */
109 int rxkad_DeriveXORInfo(struct rx_connection *aconnp,
110 fc_KeySchedule *aschedule, char *aivec, char *aresult)
112 struct rxkad_endpoint tendpoint;
115 rxkad_SetupEndpoint(aconnp, &tendpoint);
116 memcpy((void *)xor, aivec, 2*sizeof(afs_int32));
117 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
118 aschedule, xor, ENCRYPT);
119 memcpy(aresult, ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE, ENCRYPTIONBLOCKSIZE);
123 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
124 * challenge response packet (which must be unencrypted and in network order).
125 * The endpoint.cksum field is omitted and treated as zero. The cksum is
126 * returned in network order. */
128 afs_uint32 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse *v2r)
132 u_char *cp = (u_char *)v2r;
133 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
135 v2r->encrypted.endpoint.cksum = 0;
137 /* this function captured from budb/db_hash.c */
139 for (i=0; i<sizeof(*v2r); i++)
140 cksum = (*cp++) + cksum * 0x10204081;
142 v2r->encrypted.endpoint.cksum = savedCksum;
146 void rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
148 if (level == rxkad_auth) {
149 rx_SetSecurityHeaderSize (conn, 4);
150 rx_SetSecurityMaxTrailerSize (conn, 4);
152 else if (level == rxkad_crypt) {
153 rx_SetSecurityHeaderSize (conn, 8);
154 rx_SetSecurityMaxTrailerSize (conn, 8); /* XXX was 7, but why screw with
155 unaligned accesses? */
159 /* returns a short integer in host byte order representing a good checksum of
162 static afs_int32 ComputeSum(struct rx_packet *apacket,
163 fc_KeySchedule *aschedule, afs_int32 *aivec)
166 register afs_uint32 t;
168 t = apacket->header.callNumber;
170 /* note that word [1] includes the channel # */
171 t = ((apacket->header.cid & 0x3) << 30)
172 | ((apacket->header.seq & 0x3fffffff));
174 /* XOR in the ivec from the per-endpoint encryption */
177 /* encrypts word as if it were a character string */
178 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
180 t = (t >> 16) & 0xffff;
181 if (t == 0) t = 1; /* so that 0 means don't care */
186 static afs_int32 FreeObject(struct rx_securityClass *aobj)
187 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
189 if (aobj->refCount > 0) return 0; /* still in use */
190 tcp = (struct rxkad_cprivate *)aobj->privateData;
191 rxi_Free(aobj, sizeof(struct rx_securityClass));
192 if (tcp->type & rxkad_client) {
193 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
195 else if (tcp->type & rxkad_server) {
196 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
198 else { return RXKADINCONSISTENCY; } /* unknown type */
200 rxkad_stats.destroyObject++;
205 /* rxkad_Close - called by rx with the security class object as a parameter
206 * when a security object is to be discarded */
208 int rxkad_Close(struct rx_securityClass *aobj)
212 code = FreeObject (aobj);
216 /* either: called to (re)create a new connection. */
218 int rxkad_NewConnection(struct rx_securityClass *aobj,
219 struct rx_connection *aconn)
221 if (aconn->securityData)
222 return RXKADINCONSISTENCY; /* already allocated??? */
224 if (rx_IsServerConn(aconn)) {
225 int size = sizeof(struct rxkad_sconn);
226 aconn->securityData = (char *) rxi_Alloc (size);
227 memset(aconn->securityData, 0, size); /* initialize it conveniently */
230 struct rxkad_cprivate *tcp;
231 struct rxkad_cconn *tccp;
232 int size = sizeof(struct rxkad_cconn);
233 tccp = (struct rxkad_cconn *) rxi_Alloc (size);
234 aconn->securityData = (char *) tccp;
235 memset(aconn->securityData, 0, size); /* initialize it conveniently */
236 tcp = (struct rxkad_cprivate *) aobj->privateData;
237 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
238 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
239 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
240 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
242 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
246 aobj->refCount++; /* attached connection */
250 /* either: called to destroy a connection. */
252 int rxkad_DestroyConnection(struct rx_securityClass *aobj,
253 struct rx_connection *aconn)
255 if (rx_IsServerConn(aconn)) {
256 struct rxkad_sconn *sconn;
257 struct rxkad_serverinfo *rock;
258 sconn = (struct rxkad_sconn *)aconn->securityData;
260 aconn->securityData = 0;
262 if (sconn->authenticated)
263 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
264 else rxkad_stats.destroyUnauth++;
267 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
268 rxi_Free (sconn, sizeof(struct rxkad_sconn));
272 rxkad_stats.destroyUnused++;
277 struct rxkad_cconn *cconn;
278 struct rxkad_cprivate *tcp;
279 cconn = (struct rxkad_cconn *)aconn->securityData;
280 tcp = (struct rxkad_cprivate *) aobj->privateData;
281 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
283 aconn->securityData = 0;
284 rxi_Free (cconn, sizeof(struct rxkad_cconn));
287 rxkad_stats.destroyClient++;
290 aobj->refCount--; /* decrement connection counter */
291 if (aobj->refCount <= 0) {
293 code = FreeObject (aobj);
294 if (code) return code;
299 /* either: decode packet */
301 int rxkad_CheckPacket(struct rx_securityClass *aobj,
302 struct rx_call *acall, 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 int rxkad_PreparePacket(struct rx_securityClass *aobj,
395 struct rx_call *acall, struct rx_packet *apacket)
397 struct rx_connection *tconn;
399 fc_KeySchedule *schedule;
400 fc_InitializationVector *ivec;
407 tconn = rx_ConnectionOf(acall);
408 len = rx_GetDataSize (apacket);
409 if (rx_IsServerConn(tconn)) {
410 struct rxkad_sconn *sconn;
411 sconn = (struct rxkad_sconn *) tconn->securityData;
412 if (sconn && sconn->authenticated &&
413 (osi_Time() < sconn->expirationTime)) {
414 level = sconn->level;
416 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
418 sconn->stats.packetsSent++;
419 sconn->stats.bytesSent += len;
420 schedule = (fc_KeySchedule *)sconn->keysched;
421 ivec = (fc_InitializationVector *)sconn->ivec;
425 rxkad_stats.expired++; /* this is a pretty unlikely path... */
429 preSeq = sconn->preSeq;
431 else { /* client connection */
432 struct rxkad_cconn *cconn;
433 struct rxkad_cprivate *tcp;
434 cconn = (struct rxkad_cconn *) tconn->securityData;
435 tcp = (struct rxkad_cprivate *) aobj->privateData;
436 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
439 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
441 cconn->stats.packetsSent++;
442 cconn->stats.bytesSent += len;
443 preSeq = cconn->preSeq;
444 schedule = (fc_KeySchedule *)tcp->keysched;
445 ivec = (fc_InitializationVector *)tcp->ivec;
448 /* compute upward compatible checksum */
449 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
450 if (level == rxkad_clear) return 0;
452 len = rx_GetDataSize (apacket);
453 word = (((apacket->header.seq ^ apacket->header.callNumber)
454 & 0xffff) << 16) | (len & 0xffff);
455 rx_PutInt32(apacket,0, htonl(word));
458 case rxkad_clear: return 0; /* shouldn't happen */
460 nlen = afs_max (ENCRYPTIONBLOCKSIZE,
461 len + rx_GetSecurityHeaderSize(tconn));
462 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
463 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
465 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
466 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
470 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
471 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
472 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
474 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
475 if (code) return code;
478 rx_SetDataSize (apacket, nlen);
482 /* either: return connection stats */
484 int rxkad_GetStats(struct rx_securityClass *aobj,
485 struct rx_connection *aconn, struct rx_securityObjectStats *astats)
488 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
489 if (!aconn->securityData) {
493 if (rx_IsServerConn(aconn)) {
494 struct rxkad_sconn *sconn;
495 sconn = (struct rxkad_sconn *) aconn->securityData;
496 astats->level = sconn->level;
497 if (sconn->authenticated) astats->flags |= 2;
498 if (sconn->cksumSeen) astats->flags |= 8;
499 astats->expires = sconn->expirationTime;
500 astats->bytesReceived = sconn->stats.bytesReceived;
501 astats->packetsReceived = sconn->stats.packetsReceived;
502 astats->bytesSent = sconn->stats.bytesSent;
503 astats->packetsSent = sconn->stats.packetsSent;
505 else { /* client connection */
506 struct rxkad_cconn *cconn;
507 cconn = (struct rxkad_cconn *) aconn->securityData;
508 if (cconn->cksumSeen) astats->flags |= 8;
509 astats->bytesReceived = cconn->stats.bytesReceived;
510 astats->packetsReceived = cconn->stats.packetsReceived;
511 astats->bytesSent = cconn->stats.bytesSent;
512 astats->packetsSent = cconn->stats.packetsSent;