1 /* The rxkad security object. Routines used by both client and servers. */
3 /* Copyright (C) 1991, 1990 Transarc Corporation - All rights reserved */
5 ****************************************************************************
6 * Copyright IBM Corporation 1988, 1989 - All Rights Reserved *
8 * Permission to use, copy, modify, and distribute this software and its *
9 * documentation for any purpose and without fee is hereby granted, *
10 * provided that the above copyright notice appear in all copies and *
11 * that both that copyright notice and this permission notice appear in *
12 * supporting documentation, and that the name of IBM not be used in *
13 * advertising or publicity pertaining to distribution of the software *
14 * without specific, written prior permission. *
16 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY *
19 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER *
20 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING *
21 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
22 ****************************************************************************
26 #include "../afs/param.h"
28 #include "../afs/stds.h"
29 #include "../afs/afs_osi.h"
31 #include "../h/systm.h"
33 #include "../h/types.h"
34 #include "../h/time.h"
35 #include "../netinet/in.h"
36 #ifndef AFS_LINUX22_ENV
37 #include "../rpc/types.h"
38 #include "../rpc/xdr.h"
39 #endif /* AFS_LINUX22_ENV */
41 #include "../afs/sysincludes.h"
42 #include "../afs/afsincludes.h"
47 #include <afs/param.h>
49 #include <sys/types.h>
53 #ifdef AFS_PTHREAD_ENV
54 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
57 #include <netinet/in.h>
64 #include "private_data.h"
65 #define XPRT_RXKAD_COMMON
67 #include "../afs/permit_xprt.h"
69 #include "../permit_xprt.h"
76 #define max(a,b) ((a) < (b)? (b) : (a))
80 #define osi_Time() time(0)
82 struct rxkad_stats rxkad_stats;
84 /* this call sets up an endpoint structure, leaving it in *network* byte
85 * order so that it can be used quickly for encryption.
87 rxkad_SetupEndpoint(aconnp, aendpointp)
88 IN struct rx_connection *aconnp;
89 OUT struct rxkad_endpoint *aendpointp;
93 aendpointp->cuid[0] = htonl(aconnp->epoch);
94 i = aconnp->cid & RX_CIDMASK;
95 aendpointp->cuid[1] = htonl(i);
96 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
97 aendpointp->securityIndex = htonl(aconnp->securityIndex);
101 /* setup xor information based on session key */
102 rxkad_DeriveXORInfo(aconnp, aschedule, aivec, aresult)
103 IN struct rx_connection *aconnp;
104 IN fc_KeySchedule *aschedule;
108 struct rxkad_endpoint tendpoint;
111 rxkad_SetupEndpoint(aconnp, &tendpoint);
112 bcopy(aivec, (void *)xor, 2*sizeof(afs_int32));
113 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
114 aschedule, xor, ENCRYPT);
115 bcopy(((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
116 aresult, 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 (v2r)
126 IN struct rxkad_v2ChallengeResponse *v2r;
130 u_char *cp = (u_char *)v2r;
131 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
133 v2r->encrypted.endpoint.cksum = 0;
135 /* this function captured from budb/db_hash.c */
137 for (i=0; i<sizeof(*v2r); i++)
138 cksum = (*cp++) + cksum * 0x10204081;
140 v2r->encrypted.endpoint.cksum = savedCksum;
144 void rxkad_SetLevel(conn, level)
145 struct rx_connection *conn;
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(apacket, aschedule, aivec)
163 struct rx_packet *apacket;
165 fc_KeySchedule *aschedule; {
167 register afs_uint32 t;
169 t = apacket->header.callNumber;
171 /* note that word [1] includes the channel # */
172 t = ((apacket->header.cid & 0x3) << 30)
173 | ((apacket->header.seq & 0x3fffffff));
175 /* XOR in the ivec from the per-endpoint encryption */
178 /* encrypts word as if it were a character string */
179 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
181 t = (t >> 16) & 0xffff;
182 if (t == 0) t = 1; /* so that 0 means don't care */
187 static afs_int32 FreeObject (aobj)
188 IN struct rx_securityClass *aobj;
189 { struct rxkad_cprivate *tcp; /* both structs start w/ type field */
191 if (aobj->refCount > 0) return 0; /* still in use */
192 tcp = (struct rxkad_cprivate *)aobj->privateData;
193 rxi_Free(aobj, sizeof(struct rx_securityClass));
194 if (tcp->type & rxkad_client) {
195 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
197 else if (tcp->type & rxkad_server) {
198 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
200 else { return RXKADINCONSISTENCY; } /* unknown type */
202 rxkad_stats.destroyObject++;
207 /* rxkad_Close - called by rx with the security class object as a parameter
208 * when a security object is to be discarded */
210 rxs_return_t rxkad_Close (aobj)
211 IN struct rx_securityClass *aobj;
215 code = FreeObject (aobj);
219 /* either: called to (re)create a new connection. */
221 rxs_return_t rxkad_NewConnection (aobj, aconn)
222 struct rx_securityClass *aobj;
223 struct rx_connection *aconn;
225 if (aconn->securityData)
226 return RXKADINCONSISTENCY; /* already allocated??? */
228 if (rx_IsServerConn(aconn)) {
229 int size = sizeof(struct rxkad_sconn);
230 aconn->securityData = (char *) rxi_Alloc (size);
231 bzero(aconn->securityData, size); /* initialize it conveniently */
234 struct rxkad_cprivate *tcp;
235 struct rxkad_cconn *tccp;
236 int size = sizeof(struct rxkad_cconn);
237 tccp = (struct rxkad_cconn *) rxi_Alloc (size);
238 aconn->securityData = (char *) tccp;
239 bzero(aconn->securityData, size); /* initialize it conveniently */
240 tcp = (struct rxkad_cprivate *) aobj->privateData;
241 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
242 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
243 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
244 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
246 rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
250 aobj->refCount++; /* attached connection */
254 /* either: called to destroy a connection. */
256 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
257 struct rx_securityClass *aobj;
258 struct rx_connection *aconn;
260 if (rx_IsServerConn(aconn)) {
261 struct rxkad_sconn *sconn;
262 struct rxkad_serverinfo *rock;
263 sconn = (struct rxkad_sconn *)aconn->securityData;
265 aconn->securityData = 0;
267 if (sconn->authenticated)
268 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
269 else rxkad_stats.destroyUnauth++;
272 if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
273 rxi_Free (sconn, sizeof(struct rxkad_sconn));
277 rxkad_stats.destroyUnused++;
282 struct rxkad_cconn *cconn;
283 struct rxkad_cprivate *tcp;
284 cconn = (struct rxkad_cconn *)aconn->securityData;
285 tcp = (struct rxkad_cprivate *) aobj->privateData;
286 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
288 aconn->securityData = 0;
289 rxi_Free (cconn, sizeof(struct rxkad_cconn));
292 rxkad_stats.destroyClient++;
295 aobj->refCount--; /* decrement connection counter */
296 if (aobj->refCount <= 0) {
298 code = FreeObject (aobj);
299 if (code) return code;
304 /* either: decode packet */
306 rxs_return_t rxkad_CheckPacket (aobj, acall, apacket)
307 struct rx_securityClass *aobj;
308 struct rx_call *acall;
309 struct rx_packet *apacket;
310 { struct rx_connection *tconn;
312 fc_KeySchedule *schedule;
313 fc_InitializationVector *ivec;
316 u_int word; /* so we get unsigned right-shift */
321 tconn = rx_ConnectionOf(acall);
322 len = rx_GetDataSize (apacket);
323 checkCksum = 0; /* init */
324 if (rx_IsServerConn(tconn)) {
325 struct rxkad_sconn *sconn;
326 sconn = (struct rxkad_sconn *) tconn->securityData;
327 if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
328 checkCksum = sconn->cksumSeen;
329 if (sconn && sconn->authenticated &&
330 (osi_Time() < sconn->expirationTime)) {
331 level = sconn->level;
333 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
335 sconn->stats.packetsReceived++;
336 sconn->stats.bytesReceived += len;
337 schedule = (fc_KeySchedule *)sconn->keysched;
338 ivec = (fc_InitializationVector *)sconn->ivec;
342 rxkad_stats.expired++;
346 preSeq = sconn->preSeq;
348 else { /* client connection */
349 struct rxkad_cconn *cconn;
350 struct rxkad_cprivate *tcp;
351 cconn = (struct rxkad_cconn *) tconn->securityData;
352 if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
353 checkCksum = cconn->cksumSeen;
354 tcp = (struct rxkad_cprivate *) aobj->privateData;
355 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
358 rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
360 cconn->stats.packetsReceived++;
361 cconn->stats.bytesReceived += len;
362 preSeq = cconn->preSeq;
363 schedule = (fc_KeySchedule *)tcp->keysched;
364 ivec = (fc_InitializationVector *)tcp->ivec;
368 code = ComputeSum(apacket, schedule, preSeq);
369 if (code != rx_GetPacketCksum(apacket))
370 return RXKADSEALEDINCON;
374 case rxkad_clear: return 0; /* shouldn't happen */
376 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
377 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
381 code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
382 if (code) return code;
385 word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
387 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
388 return RXKADSEALEDINCON;
389 nlen = word & 0xffff; /* get real user data length */
391 /* The sealed length should be no larger than the initial length, since the
392 * reverse (round-up) occurs in ...PreparePacket */
395 rx_SetDataSize (apacket, nlen);
399 /* either: encode packet */
401 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
402 struct rx_securityClass *aobj;
403 struct rx_call *acall;
404 struct rx_packet *apacket;
406 struct rx_connection *tconn;
408 fc_KeySchedule *schedule;
409 fc_InitializationVector *ivec;
416 tconn = rx_ConnectionOf(acall);
417 len = rx_GetDataSize (apacket);
418 if (rx_IsServerConn(tconn)) {
419 struct rxkad_sconn *sconn;
420 sconn = (struct rxkad_sconn *) tconn->securityData;
421 if (sconn && sconn->authenticated &&
422 (osi_Time() < sconn->expirationTime)) {
423 level = sconn->level;
425 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
427 sconn->stats.packetsSent++;
428 sconn->stats.bytesSent += len;
429 schedule = (fc_KeySchedule *)sconn->keysched;
430 ivec = (fc_InitializationVector *)sconn->ivec;
434 rxkad_stats.expired++; /* this is a pretty unlikely path... */
438 preSeq = sconn->preSeq;
440 else { /* client connection */
441 struct rxkad_cconn *cconn;
442 struct rxkad_cprivate *tcp;
443 cconn = (struct rxkad_cconn *) tconn->securityData;
444 tcp = (struct rxkad_cprivate *) aobj->privateData;
445 if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
448 rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
450 cconn->stats.packetsSent++;
451 cconn->stats.bytesSent += len;
452 preSeq = cconn->preSeq;
453 schedule = (fc_KeySchedule *)tcp->keysched;
454 ivec = (fc_InitializationVector *)tcp->ivec;
457 /* compute upward compatible checksum */
458 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
459 if (level == rxkad_clear) return 0;
461 len = rx_GetDataSize (apacket);
462 word = (((apacket->header.seq ^ apacket->header.callNumber)
463 & 0xffff) << 16) | (len & 0xffff);
464 rx_PutInt32(apacket,0, htonl(word));
467 case rxkad_clear: return 0; /* shouldn't happen */
469 nlen = max (ENCRYPTIONBLOCKSIZE,
470 len + rx_GetSecurityHeaderSize(tconn));
471 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
472 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
474 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
475 fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
479 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
480 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
481 rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
483 code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
484 if (code) return code;
487 rx_SetDataSize (apacket, nlen);
491 /* either: return connection stats */
493 rxs_return_t rxkad_GetStats (aobj, aconn, astats)
494 IN struct rx_securityClass *aobj;
495 IN struct rx_connection *aconn;
496 OUT struct rx_securityObjectStats *astats;
499 astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
500 if (!aconn->securityData) {
504 if (rx_IsServerConn(aconn)) {
505 struct rxkad_sconn *sconn;
506 sconn = (struct rxkad_sconn *) aconn->securityData;
507 astats->level = sconn->level;
508 if (sconn->authenticated) astats->flags |= 2;
509 if (sconn->cksumSeen) astats->flags |= 8;
510 astats->expires = sconn->expirationTime;
511 astats->bytesReceived = sconn->stats.bytesReceived;
512 astats->packetsReceived = sconn->stats.packetsReceived;
513 astats->bytesSent = sconn->stats.bytesSent;
514 astats->packetsSent = sconn->stats.packetsSent;
516 else { /* client connection */
517 struct rxkad_cconn *cconn;
518 cconn = (struct rxkad_cconn *) aconn->securityData;
519 if (cconn->cksumSeen) astats->flags |= 8;
520 astats->bytesReceived = cconn->stats.bytesReceived;
521 astats->packetsReceived = cconn->stats.packetsReceived;
522 astats->bytesSent = cconn->stats.bytesSent;
523 astats->packetsSent = cconn->stats.packetsSent;