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. */
13 #include "../afs/param.h"
15 #include "../afs/stds.h"
16 #include "../afs/afs_osi.h"
18 #include "../h/systm.h"
20 #include "../h/types.h"
21 #include "../h/time.h"
22 #include "../netinet/in.h"
23 #ifndef AFS_LINUX22_ENV
24 #include "../rpc/types.h"
25 #include "../rpc/xdr.h"
26 #endif /* AFS_LINUX22_ENV */
28 #include "../afs/sysincludes.h"
29 #include "../afs/afsincludes.h"
34 #include <afs/param.h>
36 #include <sys/types.h>
40 #ifdef AFS_PTHREAD_ENV
41 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
44 #include <netinet/in.h>
51 #include "private_data.h"
52 #define XPRT_RXKAD_COMMON
54 #include "../afs/permit_xprt.h"
56 #include "../permit_xprt.h"
63 #define max(a,b) ((a) < (b)? (b) : (a))
67 #define osi_Time() time(0)
69 struct rxkad_stats rxkad_stats;
71 /* this call sets up an endpoint structure, leaving it in *network* byte
72 * order so that it can be used quickly for encryption.
74 rxkad_SetupEndpoint(aconnp, aendpointp)
75 IN struct rx_connection *aconnp;
76 OUT struct rxkad_endpoint *aendpointp;
80 aendpointp->cuid[0] = htonl(aconnp->epoch);
81 i = aconnp->cid & RX_CIDMASK;
82 aendpointp->cuid[1] = htonl(i);
83 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
84 aendpointp->securityIndex = htonl(aconnp->securityIndex);
88 /* setup xor information based on session key */
89 rxkad_DeriveXORInfo(aconnp, aschedule, aivec, aresult)
90 IN struct rx_connection *aconnp;
91 IN fc_KeySchedule *aschedule;
95 struct rxkad_endpoint tendpoint;
98 rxkad_SetupEndpoint(aconnp, &tendpoint);
99 bcopy(aivec, (void *)xor, 2*sizeof(afs_int32));
100 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
101 aschedule, xor, ENCRYPT);
102 bcopy(((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
103 aresult, ENCRYPTIONBLOCKSIZE);
107 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
108 * challenge response packet (which must be unencrypted and in network order).
109 * The endpoint.cksum field is omitted and treated as zero. The cksum is
110 * returned in network order. */
112 afs_uint32 rxkad_CksumChallengeResponse (v2r)
113 IN struct rxkad_v2ChallengeResponse *v2r;
117 u_char *cp = (u_char *)v2r;
118 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
120 v2r->encrypted.endpoint.cksum = 0;
122 /* this function captured from budb/db_hash.c */
124 for (i=0; i<sizeof(*v2r); i++)
125 cksum = (*cp++) + cksum * 0x10204081;
127 v2r->encrypted.endpoint.cksum = savedCksum;
131 void rxkad_SetLevel(conn, level)
132 struct rx_connection *conn;
135 if (level == rxkad_auth) {
136 rx_SetSecurityHeaderSize (conn, 4);
137 rx_SetSecurityMaxTrailerSize (conn, 4);
139 else if (level == rxkad_crypt) {
140 rx_SetSecurityHeaderSize (conn, 8);
141 rx_SetSecurityMaxTrailerSize (conn, 8); /* XXX was 7, but why screw with
142 unaligned accesses? */
146 /* returns a short integer in host byte order representing a good checksum of
149 static afs_int32 ComputeSum(apacket, aschedule, aivec)
150 struct rx_packet *apacket;
152 fc_KeySchedule *aschedule; {
154 register afs_uint32 t;
156 t = apacket->header.callNumber;
158 /* note that word [1] includes the channel # */
159 t = ((apacket->header.cid & 0x3) << 30)
160 | ((apacket->header.seq & 0x3fffffff));
162 /* XOR in the ivec from the per-endpoint encryption */
165 /* encrypts word as if it were a character string */
166 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
168 t = (t >> 16) & 0xffff;
169 if (t == 0) t = 1; /* so that 0 means don't care */
174 static afs_int32 FreeObject (aobj)
175 IN struct rx_securityClass *aobj;
176 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
178 if (aobj->refCount > 0) return 0; /* still in use */
179 tcp = (struct rxkad_cprivate *)aobj->privateData;
180 rxi_Free(aobj, sizeof(struct rx_securityClass));
181 if (tcp->type & rxkad_client) {
182 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
184 else if (tcp->type & rxkad_server) {
185 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
187 else { return RXKADINCONSISTENCY; } /* unknown type */
189 rxkad_stats.destroyObject++;
194 /* rxkad_Close - called by rx with the security class object as a parameter
195 * when a security object is to be discarded */
197 rxs_return_t rxkad_Close (aobj)
198 IN struct rx_securityClass *aobj;
202 code = FreeObject (aobj);
206 /* either: called to (re)create a new connection. */
208 rxs_return_t rxkad_NewConnection (aobj, aconn)
209 struct rx_securityClass *aobj;
210 struct rx_connection *aconn;
212 if (aconn->securityData)
213 return RXKADINCONSISTENCY; /* already allocated??? */
215 if (rx_IsServerConn(aconn)) {
216 int size = sizeof(struct rxkad_sconn);
217 aconn->securityData = (char *) rxi_Alloc (size);
218 bzero(aconn->securityData, size); /* initialize it conveniently */
221 struct rxkad_cprivate *tcp;
222 struct rxkad_cconn *tccp;
223 int size = sizeof(struct rxkad_cconn);
224 tccp = (struct rxkad_cconn *) rxi_Alloc (size);
225 aconn->securityData = (char *) tccp;
226 bzero(aconn->securityData, size); /* initialize it conveniently */
227 tcp = (struct rxkad_cprivate *) aobj->privateData;
228 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
229 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
230 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
231 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
233 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
237 aobj->refCount++; /* attached connection */
241 /* either: called to destroy a connection. */
243 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
244 struct rx_securityClass *aobj;
245 struct rx_connection *aconn;
247 if (rx_IsServerConn(aconn)) {
248 struct rxkad_sconn *sconn;
249 struct rxkad_serverinfo *rock;
250 sconn = (struct rxkad_sconn *)aconn->securityData;
252 aconn->securityData = 0;
254 if (sconn->authenticated)
255 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
256 else rxkad_stats.destroyUnauth++;
259 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
260 rxi_Free (sconn, sizeof(struct rxkad_sconn));
264 rxkad_stats.destroyUnused++;
269 struct rxkad_cconn *cconn;
270 struct rxkad_cprivate *tcp;
271 cconn = (struct rxkad_cconn *)aconn->securityData;
272 tcp = (struct rxkad_cprivate *) aobj->privateData;
273 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
275 aconn->securityData = 0;
276 rxi_Free (cconn, sizeof(struct rxkad_cconn));
279 rxkad_stats.destroyClient++;
282 aobj->refCount--; /* decrement connection counter */
283 if (aobj->refCount <= 0) {
285 code = FreeObject (aobj);
286 if (code) return code;
291 /* either: decode packet */
293 rxs_return_t rxkad_CheckPacket (aobj, acall, apacket)
294 struct rx_securityClass *aobj;
295 struct rx_call *acall;
296 struct rx_packet *apacket;
297 { struct rx_connection *tconn;
299 fc_KeySchedule *schedule;
300 fc_InitializationVector *ivec;
303 u_int word; /* so we get unsigned right-shift */
308 tconn = rx_ConnectionOf(acall);
309 len = rx_GetDataSize (apacket);
310 checkCksum = 0; /* init */
311 if (rx_IsServerConn(tconn)) {
312 struct rxkad_sconn *sconn;
313 sconn = (struct rxkad_sconn *) tconn->securityData;
314 if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
315 checkCksum = sconn->cksumSeen;
316 if (sconn && sconn->authenticated &&
317 (osi_Time() < sconn->expirationTime)) {
318 level = sconn->level;
320 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
322 sconn->stats.packetsReceived++;
323 sconn->stats.bytesReceived += len;
324 schedule = (fc_KeySchedule *)sconn->keysched;
325 ivec = (fc_InitializationVector *)sconn->ivec;
329 rxkad_stats.expired++;
333 preSeq = sconn->preSeq;
335 else { /* client connection */
336 struct rxkad_cconn *cconn;
337 struct rxkad_cprivate *tcp;
338 cconn = (struct rxkad_cconn *) tconn->securityData;
339 if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
340 checkCksum = cconn->cksumSeen;
341 tcp = (struct rxkad_cprivate *) aobj->privateData;
342 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
345 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
347 cconn->stats.packetsReceived++;
348 cconn->stats.bytesReceived += len;
349 preSeq = cconn->preSeq;
350 schedule = (fc_KeySchedule *)tcp->keysched;
351 ivec = (fc_InitializationVector *)tcp->ivec;
355 code = ComputeSum(apacket, schedule, preSeq);
356 if (code != rx_GetPacketCksum(apacket))
357 return RXKADSEALEDINCON;
361 case rxkad_clear: return 0; /* shouldn't happen */
363 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
364 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
368 code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
369 if (code) return code;
372 word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
374 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
375 return RXKADSEALEDINCON;
376 nlen = word & 0xffff; /* get real user data length */
378 /* The sealed length should be no larger than the initial length, since the
379 * reverse (round-up) occurs in ...PreparePacket */
382 rx_SetDataSize (apacket, nlen);
386 /* either: encode packet */
388 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
389 struct rx_securityClass *aobj;
390 struct rx_call *acall;
391 struct rx_packet *apacket;
393 struct rx_connection *tconn;
395 fc_KeySchedule *schedule;
396 fc_InitializationVector *ivec;
403 tconn = rx_ConnectionOf(acall);
404 len = rx_GetDataSize (apacket);
405 if (rx_IsServerConn(tconn)) {
406 struct rxkad_sconn *sconn;
407 sconn = (struct rxkad_sconn *) tconn->securityData;
408 if (sconn && sconn->authenticated &&
409 (osi_Time() < sconn->expirationTime)) {
410 level = sconn->level;
412 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
414 sconn->stats.packetsSent++;
415 sconn->stats.bytesSent += len;
416 schedule = (fc_KeySchedule *)sconn->keysched;
417 ivec = (fc_InitializationVector *)sconn->ivec;
421 rxkad_stats.expired++; /* this is a pretty unlikely path... */
425 preSeq = sconn->preSeq;
427 else { /* client connection */
428 struct rxkad_cconn *cconn;
429 struct rxkad_cprivate *tcp;
430 cconn = (struct rxkad_cconn *) tconn->securityData;
431 tcp = (struct rxkad_cprivate *) aobj->privateData;
432 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
435 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
437 cconn->stats.packetsSent++;
438 cconn->stats.bytesSent += len;
439 preSeq = cconn->preSeq;
440 schedule = (fc_KeySchedule *)tcp->keysched;
441 ivec = (fc_InitializationVector *)tcp->ivec;
444 /* compute upward compatible checksum */
445 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
446 if (level == rxkad_clear) return 0;
448 len = rx_GetDataSize (apacket);
449 word = (((apacket->header.seq ^ apacket->header.callNumber)
450 & 0xffff) << 16) | (len & 0xffff);
451 rx_PutInt32(apacket,0, htonl(word));
454 case rxkad_clear: return 0; /* shouldn't happen */
456 nlen = max (ENCRYPTIONBLOCKSIZE,
457 len + rx_GetSecurityHeaderSize(tconn));
458 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
459 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
461 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
462 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
466 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
467 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
468 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
470 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
471 if (code) return code;
474 rx_SetDataSize (apacket, nlen);
478 /* either: return connection stats */
480 rxs_return_t rxkad_GetStats (aobj, aconn, astats)
481 IN struct rx_securityClass *aobj;
482 IN struct rx_connection *aconn;
483 OUT struct rx_securityObjectStats *astats;
486 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
487 if (!aconn->securityData) {
491 if (rx_IsServerConn(aconn)) {
492 struct rxkad_sconn *sconn;
493 sconn = (struct rxkad_sconn *) aconn->securityData;
494 astats->level = sconn->level;
495 if (sconn->authenticated) astats->flags |= 2;
496 if (sconn->cksumSeen) astats->flags |= 8;
497 astats->expires = sconn->expirationTime;
498 astats->bytesReceived = sconn->stats.bytesReceived;
499 astats->packetsReceived = sconn->stats.packetsReceived;
500 astats->bytesSent = sconn->stats.bytesSent;
501 astats->packetsSent = sconn->stats.packetsSent;
503 else { /* client connection */
504 struct rxkad_cconn *cconn;
505 cconn = (struct rxkad_cconn *) aconn->securityData;
506 if (cconn->cksumSeen) astats->flags |= 8;
507 astats->bytesReceived = cconn->stats.bytesReceived;
508 astats->packetsReceived = cconn->stats.packetsReceived;
509 astats->bytesSent = cconn->stats.bytesSent;
510 astats->packetsSent = cconn->stats.packetsSent;