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>
57 #include "private_data.h"
58 #define XPRT_RXKAD_COMMON
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 memcpy((void *)xor, aivec, 2*sizeof(afs_int32));
100 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
101 aschedule, xor, ENCRYPT);
102 memcpy(aresult, ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE, ENCRYPTIONBLOCKSIZE);
106 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
107 * challenge response packet (which must be unencrypted and in network order).
108 * The endpoint.cksum field is omitted and treated as zero. The cksum is
109 * returned in network order. */
111 afs_uint32 rxkad_CksumChallengeResponse (v2r)
112 IN struct rxkad_v2ChallengeResponse *v2r;
116 u_char *cp = (u_char *)v2r;
117 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
119 v2r->encrypted.endpoint.cksum = 0;
121 /* this function captured from budb/db_hash.c */
123 for (i=0; i<sizeof(*v2r); i++)
124 cksum = (*cp++) + cksum * 0x10204081;
126 v2r->encrypted.endpoint.cksum = savedCksum;
130 void rxkad_SetLevel(conn, level)
131 struct rx_connection *conn;
134 if (level == rxkad_auth) {
135 rx_SetSecurityHeaderSize (conn, 4);
136 rx_SetSecurityMaxTrailerSize (conn, 4);
138 else if (level == rxkad_crypt) {
139 rx_SetSecurityHeaderSize (conn, 8);
140 rx_SetSecurityMaxTrailerSize (conn, 8); /* XXX was 7, but why screw with
141 unaligned accesses? */
145 /* returns a short integer in host byte order representing a good checksum of
148 static afs_int32 ComputeSum(apacket, aschedule, aivec)
149 struct rx_packet *apacket;
151 fc_KeySchedule *aschedule; {
153 register afs_uint32 t;
155 t = apacket->header.callNumber;
157 /* note that word [1] includes the channel # */
158 t = ((apacket->header.cid & 0x3) << 30)
159 | ((apacket->header.seq & 0x3fffffff));
161 /* XOR in the ivec from the per-endpoint encryption */
164 /* encrypts word as if it were a character string */
165 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
167 t = (t >> 16) & 0xffff;
168 if (t == 0) t = 1; /* so that 0 means don't care */
173 static afs_int32 FreeObject (aobj)
174 IN struct rx_securityClass *aobj;
175 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
177 if (aobj->refCount > 0) return 0; /* still in use */
178 tcp = (struct rxkad_cprivate *)aobj->privateData;
179 rxi_Free(aobj, sizeof(struct rx_securityClass));
180 if (tcp->type & rxkad_client) {
181 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
183 else if (tcp->type & rxkad_server) {
184 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
186 else { return RXKADINCONSISTENCY; } /* unknown type */
188 rxkad_stats.destroyObject++;
193 /* rxkad_Close - called by rx with the security class object as a parameter
194 * when a security object is to be discarded */
196 rxs_return_t rxkad_Close (aobj)
197 IN struct rx_securityClass *aobj;
201 code = FreeObject (aobj);
205 /* either: called to (re)create a new connection. */
207 rxs_return_t rxkad_NewConnection (aobj, aconn)
208 struct rx_securityClass *aobj;
209 struct rx_connection *aconn;
211 if (aconn->securityData)
212 return RXKADINCONSISTENCY; /* already allocated??? */
214 if (rx_IsServerConn(aconn)) {
215 int size = sizeof(struct rxkad_sconn);
216 aconn->securityData = (char *) rxi_Alloc (size);
217 memset(aconn->securityData, 0, size); /* initialize it conveniently */
220 struct rxkad_cprivate *tcp;
221 struct rxkad_cconn *tccp;
222 int size = sizeof(struct rxkad_cconn);
223 tccp = (struct rxkad_cconn *) rxi_Alloc (size);
224 aconn->securityData = (char *) tccp;
225 memset(aconn->securityData, 0, size); /* initialize it conveniently */
226 tcp = (struct rxkad_cprivate *) aobj->privateData;
227 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
228 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
229 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
230 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
232 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
236 aobj->refCount++; /* attached connection */
240 /* either: called to destroy a connection. */
242 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
243 struct rx_securityClass *aobj;
244 struct rx_connection *aconn;
246 if (rx_IsServerConn(aconn)) {
247 struct rxkad_sconn *sconn;
248 struct rxkad_serverinfo *rock;
249 sconn = (struct rxkad_sconn *)aconn->securityData;
251 aconn->securityData = 0;
253 if (sconn->authenticated)
254 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
255 else rxkad_stats.destroyUnauth++;
258 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
259 rxi_Free (sconn, sizeof(struct rxkad_sconn));
263 rxkad_stats.destroyUnused++;
268 struct rxkad_cconn *cconn;
269 struct rxkad_cprivate *tcp;
270 cconn = (struct rxkad_cconn *)aconn->securityData;
271 tcp = (struct rxkad_cprivate *) aobj->privateData;
272 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
274 aconn->securityData = 0;
275 rxi_Free (cconn, sizeof(struct rxkad_cconn));
278 rxkad_stats.destroyClient++;
281 aobj->refCount--; /* decrement connection counter */
282 if (aobj->refCount <= 0) {
284 code = FreeObject (aobj);
285 if (code) return code;
290 /* either: decode packet */
292 rxs_return_t rxkad_CheckPacket (aobj, acall, apacket)
293 struct rx_securityClass *aobj;
294 struct rx_call *acall;
295 struct rx_packet *apacket;
296 { struct rx_connection *tconn;
298 fc_KeySchedule *schedule;
299 fc_InitializationVector *ivec;
302 u_int word; /* so we get unsigned right-shift */
307 tconn = rx_ConnectionOf(acall);
308 len = rx_GetDataSize (apacket);
309 checkCksum = 0; /* init */
310 if (rx_IsServerConn(tconn)) {
311 struct rxkad_sconn *sconn;
312 sconn = (struct rxkad_sconn *) tconn->securityData;
313 if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
314 checkCksum = sconn->cksumSeen;
315 if (sconn && sconn->authenticated &&
316 (osi_Time() < sconn->expirationTime)) {
317 level = sconn->level;
319 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
321 sconn->stats.packetsReceived++;
322 sconn->stats.bytesReceived += len;
323 schedule = (fc_KeySchedule *)sconn->keysched;
324 ivec = (fc_InitializationVector *)sconn->ivec;
328 rxkad_stats.expired++;
332 preSeq = sconn->preSeq;
334 else { /* client connection */
335 struct rxkad_cconn *cconn;
336 struct rxkad_cprivate *tcp;
337 cconn = (struct rxkad_cconn *) tconn->securityData;
338 if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
339 checkCksum = cconn->cksumSeen;
340 tcp = (struct rxkad_cprivate *) aobj->privateData;
341 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
344 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
346 cconn->stats.packetsReceived++;
347 cconn->stats.bytesReceived += len;
348 preSeq = cconn->preSeq;
349 schedule = (fc_KeySchedule *)tcp->keysched;
350 ivec = (fc_InitializationVector *)tcp->ivec;
354 code = ComputeSum(apacket, schedule, preSeq);
355 if (code != rx_GetPacketCksum(apacket))
356 return RXKADSEALEDINCON;
360 case rxkad_clear: return 0; /* shouldn't happen */
362 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
363 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
367 code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
368 if (code) return code;
371 word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
373 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
374 return RXKADSEALEDINCON;
375 nlen = word & 0xffff; /* get real user data length */
377 /* The sealed length should be no larger than the initial length, since the
378 * reverse (round-up) occurs in ...PreparePacket */
381 rx_SetDataSize (apacket, nlen);
385 /* either: encode packet */
387 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
388 struct rx_securityClass *aobj;
389 struct rx_call *acall;
390 struct rx_packet *apacket;
392 struct rx_connection *tconn;
394 fc_KeySchedule *schedule;
395 fc_InitializationVector *ivec;
402 tconn = rx_ConnectionOf(acall);
403 len = rx_GetDataSize (apacket);
404 if (rx_IsServerConn(tconn)) {
405 struct rxkad_sconn *sconn;
406 sconn = (struct rxkad_sconn *) tconn->securityData;
407 if (sconn && sconn->authenticated &&
408 (osi_Time() < sconn->expirationTime)) {
409 level = sconn->level;
411 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
413 sconn->stats.packetsSent++;
414 sconn->stats.bytesSent += len;
415 schedule = (fc_KeySchedule *)sconn->keysched;
416 ivec = (fc_InitializationVector *)sconn->ivec;
420 rxkad_stats.expired++; /* this is a pretty unlikely path... */
424 preSeq = sconn->preSeq;
426 else { /* client connection */
427 struct rxkad_cconn *cconn;
428 struct rxkad_cprivate *tcp;
429 cconn = (struct rxkad_cconn *) tconn->securityData;
430 tcp = (struct rxkad_cprivate *) aobj->privateData;
431 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
434 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
436 cconn->stats.packetsSent++;
437 cconn->stats.bytesSent += len;
438 preSeq = cconn->preSeq;
439 schedule = (fc_KeySchedule *)tcp->keysched;
440 ivec = (fc_InitializationVector *)tcp->ivec;
443 /* compute upward compatible checksum */
444 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
445 if (level == rxkad_clear) return 0;
447 len = rx_GetDataSize (apacket);
448 word = (((apacket->header.seq ^ apacket->header.callNumber)
449 & 0xffff) << 16) | (len & 0xffff);
450 rx_PutInt32(apacket,0, htonl(word));
453 case rxkad_clear: return 0; /* shouldn't happen */
455 nlen = max (ENCRYPTIONBLOCKSIZE,
456 len + rx_GetSecurityHeaderSize(tconn));
457 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
458 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
460 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
461 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
465 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
466 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
467 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
469 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
470 if (code) return code;
473 rx_SetDataSize (apacket, nlen);
477 /* either: return connection stats */
479 rxs_return_t rxkad_GetStats (aobj, aconn, astats)
480 IN struct rx_securityClass *aobj;
481 IN struct rx_connection *aconn;
482 OUT 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;