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
31 #include "afs/afs_osi.h"
35 #ifdef AFS_DARWIN60_ENV
40 #ifndef AFS_LINUX22_ENV
41 #include "rpc/types.h"
43 #endif /* AFS_LINUX22_ENV */
45 #include "afs/sysincludes.h"
46 #include "afsincludes.h"
52 #include <sys/types.h>
56 #ifdef AFS_PTHREAD_ENV
57 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
60 #include <netinet/in.h>
74 #include "private_data.h"
75 #define XPRT_RXKAD_COMMON
78 #define afs_max(a,b) ((a) < (b)? (b) : (a))
82 #define osi_Time() time(0)
84 /* variable initialization for the benefit of darwin compiler; if it causes
85 problems elsewhere, conditionalize for darwin or fc_test compile breaks */
86 struct rxkad_stats rxkad_stats = {0};
88 /* static prototypes */
89 static afs_int32 ComputeSum(struct rx_packet *apacket,
90 fc_KeySchedule * aschedule, afs_int32 * aivec);
91 static afs_int32 FreeObject(struct rx_securityClass *aobj);
93 /* this call sets up an endpoint structure, leaving it in *network* byte
94 * order so that it can be used quickly for encryption.
97 rxkad_SetupEndpoint(struct rx_connection *aconnp,
98 struct rxkad_endpoint *aendpointp)
100 register afs_int32 i;
102 aendpointp->cuid[0] = htonl(aconnp->epoch);
103 i = aconnp->cid & RX_CIDMASK;
104 aendpointp->cuid[1] = htonl(i);
105 aendpointp->cksum = 0; /* used as cksum only in chal resp. */
106 aendpointp->securityIndex = htonl(aconnp->securityIndex);
110 /* setup xor information based on session key */
112 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
113 char *aivec, char *aresult)
115 struct rxkad_endpoint tendpoint;
118 rxkad_SetupEndpoint(aconnp, &tendpoint);
119 memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
120 fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), aschedule, xor,
123 ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
124 ENCRYPTIONBLOCKSIZE);
128 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
129 * challenge response packet (which must be unencrypted and in network order).
130 * The endpoint.cksum field is omitted and treated as zero. The cksum is
131 * returned in network order. */
134 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
138 u_char *cp = (u_char *) v2r;
139 afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
141 v2r->encrypted.endpoint.cksum = 0;
143 /* this function captured from budb/db_hash.c */
145 for (i = 0; i < sizeof(*v2r); i++)
146 cksum = (*cp++) + cksum * 0x10204081;
148 v2r->encrypted.endpoint.cksum = savedCksum;
153 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
155 if (level == rxkad_auth) {
156 rx_SetSecurityHeaderSize(conn, 4);
157 rx_SetSecurityMaxTrailerSize(conn, 4);
158 } else if (level == rxkad_crypt) {
159 rx_SetSecurityHeaderSize(conn, 8);
160 rx_SetSecurityMaxTrailerSize(conn, 8); /* XXX was 7, but why screw with
161 * unaligned accesses? */
165 /* returns a short integer in host byte order representing a good checksum of
169 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
173 register afs_uint32 t;
175 t = apacket->header.callNumber;
177 /* note that word [1] includes the channel # */
178 t = ((apacket->header.cid & 0x3) << 30)
179 | ((apacket->header.seq & 0x3fffffff));
181 /* XOR in the ivec from the per-endpoint encryption */
184 /* encrypts word as if it were a character string */
185 fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
187 t = (t >> 16) & 0xffff;
189 t = 1; /* so that 0 means don't care */
195 FreeObject(struct rx_securityClass *aobj)
197 struct rxkad_cprivate *tcp; /* both structs start w/ type field */
199 if (aobj->refCount > 0)
200 return 0; /* still in use */
201 tcp = (struct rxkad_cprivate *)aobj->privateData;
202 rxi_Free(aobj, sizeof(struct rx_securityClass));
203 if (tcp->type & rxkad_client) {
204 rxi_Free(tcp, sizeof(struct rxkad_cprivate));
205 } else if (tcp->type & rxkad_server) {
206 rxi_Free(tcp, sizeof(struct rxkad_sprivate));
208 return RXKADINCONSISTENCY;
210 LOCK_RXKAD_STATS rxkad_stats.destroyObject++;
211 UNLOCK_RXKAD_STATS return 0;
214 /* rxkad_Close - called by rx with the security class object as a parameter
215 * when a security object is to be discarded */
218 rxkad_Close(struct rx_securityClass *aobj)
222 code = FreeObject(aobj);
226 /* either: called to (re)create a new connection. */
229 rxkad_NewConnection(struct rx_securityClass *aobj,
230 struct rx_connection *aconn)
232 if (aconn->securityData)
233 return RXKADINCONSISTENCY; /* already allocated??? */
235 if (rx_IsServerConn(aconn)) {
236 int size = sizeof(struct rxkad_sconn);
237 aconn->securityData = (char *)rxi_Alloc(size);
238 memset(aconn->securityData, 0, size); /* initialize it conveniently */
239 } else { /* client */
240 struct rxkad_cprivate *tcp;
241 struct rxkad_cconn *tccp;
242 int size = sizeof(struct rxkad_cconn);
243 tccp = (struct rxkad_cconn *)rxi_Alloc(size);
244 aconn->securityData = (char *)tccp;
245 memset(aconn->securityData, 0, size); /* initialize it conveniently */
246 tcp = (struct rxkad_cprivate *)aobj->privateData;
247 if (!(tcp->type & rxkad_client))
248 return RXKADINCONSISTENCY;
249 rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
250 rxkad_AllocCID(aobj, aconn); /* CHANGES cid AND epoch!!!! */
251 rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
252 LOCK_RXKAD_STATS rxkad_stats.
253 connections[rxkad_LevelIndex(tcp->level)]++;
256 aobj->refCount++; /* attached connection */
260 /* either: called to destroy a connection. */
263 rxkad_DestroyConnection(struct rx_securityClass *aobj,
264 struct rx_connection *aconn)
266 if (rx_IsServerConn(aconn)) {
267 struct rxkad_sconn *sconn;
268 struct rxkad_serverinfo *rock;
269 sconn = (struct rxkad_sconn *)aconn->securityData;
271 aconn->securityData = 0;
272 LOCK_RXKAD_STATS if (sconn->authenticated)
273 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
275 rxkad_stats.destroyUnauth++;
276 UNLOCK_RXKAD_STATS rock = sconn->rock;
278 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
279 rxi_Free(sconn, sizeof(struct rxkad_sconn));
281 LOCK_RXKAD_STATS rxkad_stats.destroyUnused++;
283 } else { /* client */
284 struct rxkad_cconn *cconn;
285 struct rxkad_cprivate *tcp;
286 cconn = (struct rxkad_cconn *)aconn->securityData;
287 tcp = (struct rxkad_cprivate *)aobj->privateData;
288 if (!(tcp->type & rxkad_client))
289 return RXKADINCONSISTENCY;
291 aconn->securityData = 0;
292 rxi_Free(cconn, sizeof(struct rxkad_cconn));
294 LOCK_RXKAD_STATS rxkad_stats.destroyClient++;
296 aobj->refCount--; /* decrement connection counter */
297 if (aobj->refCount <= 0) {
299 code = FreeObject(aobj);
306 /* either: decode packet */
309 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
310 struct rx_packet *apacket)
312 struct rx_connection *tconn;
314 fc_KeySchedule *schedule;
315 fc_InitializationVector *ivec;
318 u_int word; /* so we get unsigned right-shift */
323 tconn = rx_ConnectionOf(acall);
324 len = rx_GetDataSize(apacket);
325 checkCksum = 0; /* init */
326 if (rx_IsServerConn(tconn)) {
327 struct rxkad_sconn *sconn;
328 sconn = (struct rxkad_sconn *)tconn->securityData;
329 if (rx_GetPacketCksum(apacket) != 0)
330 sconn->cksumSeen = 1;
331 checkCksum = sconn->cksumSeen;
332 if (sconn && sconn->authenticated
333 && (osi_Time() < sconn->expirationTime)) {
334 level = sconn->level;
335 LOCK_RXKAD_STATS rxkad_stats.
336 checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
337 UNLOCK_RXKAD_STATS sconn->stats.packetsReceived++;
338 sconn->stats.bytesReceived += len;
339 schedule = (fc_KeySchedule *) sconn->keysched;
340 ivec = (fc_InitializationVector *) sconn->ivec;
342 LOCK_RXKAD_STATS rxkad_stats.expired++;
343 UNLOCK_RXKAD_STATS return RXKADEXPIRED;
345 preSeq = sconn->preSeq;
346 } else { /* client connection */
347 struct rxkad_cconn *cconn;
348 struct rxkad_cprivate *tcp;
349 cconn = (struct rxkad_cconn *)tconn->securityData;
350 if (rx_GetPacketCksum(apacket) != 0)
351 cconn->cksumSeen = 1;
352 checkCksum = cconn->cksumSeen;
353 tcp = (struct rxkad_cprivate *)aobj->privateData;
354 if (!(tcp->type & rxkad_client))
355 return RXKADINCONSISTENCY;
357 LOCK_RXKAD_STATS rxkad_stats.
358 checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
359 UNLOCK_RXKAD_STATS cconn->stats.packetsReceived++;
360 cconn->stats.bytesReceived += len;
361 preSeq = cconn->preSeq;
362 schedule = (fc_KeySchedule *) tcp->keysched;
363 ivec = (fc_InitializationVector *) tcp->ivec;
367 code = ComputeSum(apacket, schedule, preSeq);
368 if (code != rx_GetPacketCksum(apacket))
369 return RXKADSEALEDINCON;
374 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), schedule,
381 code = rxkad_DecryptPacket(tconn, schedule, ivec, len, apacket);
386 word = ntohl(rx_GetInt32(apacket, 0)); /* get first sealed word */
388 ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
389 return RXKADSEALEDINCON;
390 nlen = word & 0xffff; /* get real user data length */
392 /* The sealed length should be no larger than the initial length, since the
393 * reverse (round-up) occurs in ...PreparePacket */
396 rx_SetDataSize(apacket, nlen);
400 /* either: encode packet */
403 rxkad_PreparePacket(struct rx_securityClass *aobj, 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;
424 LOCK_RXKAD_STATS rxkad_stats.
425 preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
426 UNLOCK_RXKAD_STATS sconn->stats.packetsSent++;
427 sconn->stats.bytesSent += len;
428 schedule = (fc_KeySchedule *) sconn->keysched;
429 ivec = (fc_InitializationVector *) sconn->ivec;
431 LOCK_RXKAD_STATS rxkad_stats.expired++; /* this is a pretty unlikely path... */
432 UNLOCK_RXKAD_STATS return RXKADEXPIRED;
434 preSeq = sconn->preSeq;
435 } else { /* client connection */
436 struct rxkad_cconn *cconn;
437 struct rxkad_cprivate *tcp;
438 cconn = (struct rxkad_cconn *)tconn->securityData;
439 tcp = (struct rxkad_cprivate *)aobj->privateData;
440 if (!(tcp->type & rxkad_client))
441 return RXKADINCONSISTENCY;
443 LOCK_RXKAD_STATS rxkad_stats.
444 preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
445 UNLOCK_RXKAD_STATS cconn->stats.packetsSent++;
446 cconn->stats.bytesSent += len;
447 preSeq = cconn->preSeq;
448 schedule = (fc_KeySchedule *) tcp->keysched;
449 ivec = (fc_InitializationVector *) tcp->ivec;
452 /* compute upward compatible checksum */
453 rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
454 if (level == rxkad_clear)
457 len = rx_GetDataSize(apacket);
458 word = (((apacket->header.seq ^ apacket->header.callNumber)
459 & 0xffff) << 16) | (len & 0xffff);
460 rx_PutInt32(apacket, 0, htonl(word));
464 return 0; /* shouldn't happen */
467 afs_max(ENCRYPTIONBLOCKSIZE,
468 len + rx_GetSecurityHeaderSize(tconn));
469 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
470 rxi_RoundUpPacket(apacket,
471 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
473 rx_Pullup(apacket, 8); /* the following encrypts 8 bytes only */
474 fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
478 nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
479 if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
480 rxi_RoundUpPacket(apacket,
481 nlen - (len + rx_GetSecurityHeaderSize(tconn)));
483 code = rxkad_EncryptPacket(tconn, schedule, ivec, nlen, apacket);
488 rx_SetDataSize(apacket, nlen);
492 /* either: return connection stats */
495 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
496 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)
510 if (sconn->cksumSeen)
512 astats->expires = sconn->expirationTime;
513 astats->bytesReceived = sconn->stats.bytesReceived;
514 astats->packetsReceived = sconn->stats.packetsReceived;
515 astats->bytesSent = sconn->stats.bytesSent;
516 astats->packetsSent = sconn->stats.packetsSent;
517 } else { /* client connection */
518 struct rxkad_cconn *cconn;
519 cconn = (struct rxkad_cconn *)aconn->securityData;
520 if (cconn->cksumSeen)
522 astats->bytesReceived = cconn->stats.bytesReceived;
523 astats->packetsReceived = cconn->stats.packetsReceived;
524 astats->bytesSent = cconn->stats.bytesSent;
525 astats->packetsSent = cconn->stats.packetsSent;