fix-indent-bug-with-lock-macros-part-three-20040818
[openafs.git] / src / rxkad / rxkad_common.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 /* The rxkad security object.  Routines used by both client and servers. */
11
12 #include <afsconfig.h>
13 #ifdef KERNEL
14 #include "afs/param.h"
15 #else
16 #include <afs/param.h>
17 #endif
18
19 #ifdef AFS_SUN59_ENV
20 #include <sys/time_impl.h>
21 #endif
22
23 #define INCLUDE_RXKAD_PRIVATE_DECLS
24
25 RCSID
26     ("$Header$");
27
28 #ifdef KERNEL
29 #ifndef UKERNEL
30 #include "afs/stds.h"
31 #include "afs/afs_osi.h"
32 #ifdef  AFS_AIX_ENV
33 #include "h/systm.h"
34 #endif
35 #ifdef AFS_DARWIN60_ENV
36 #include "h/kernel.h"
37 #endif
38 #include "h/types.h"
39 #include "h/time.h"
40 #ifndef AFS_LINUX22_ENV
41 #include "rpc/types.h"
42 #include "rx/xdr.h"
43 #endif /* AFS_LINUX22_ENV */
44 #else /* !UKERNEL */
45 #include "afs/sysincludes.h"
46 #include "afsincludes.h"
47 #endif /* !UKERNEL */
48 #include "rx/rx.h"
49
50 #else /* KERNEL */
51 #include <afs/stds.h>
52 #include <sys/types.h>
53 #include <time.h>
54 #ifdef AFS_NT40_ENV
55 #include <winsock2.h>
56 #ifdef AFS_PTHREAD_ENV
57 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
58 #endif
59 #else
60 #include <netinet/in.h>
61 #endif
62 #include <rx/rx.h>
63 #include <rx/xdr.h>
64 #ifdef HAVE_STRING_H
65 #include <string.h>
66 #else
67 #ifdef HAVE_STRINGS_H
68 #include <strings.h>
69 #endif
70 #endif
71
72 #endif /* KERNEL */
73
74 #include "private_data.h"
75 #define XPRT_RXKAD_COMMON
76
77 #ifndef afs_max
78 #define afs_max(a,b)    ((a) < (b)? (b) : (a))
79 #endif /* afs_max */
80
81 #ifndef KERNEL
82 #define osi_Time() time(0)
83 #endif
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 };
87
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);
92
93 /* this call sets up an endpoint structure, leaving it in *network* byte
94  * order so that it can be used quickly for encryption.
95  */
96 int
97 rxkad_SetupEndpoint(struct rx_connection *aconnp,
98                     struct rxkad_endpoint *aendpointp)
99 {
100     register afs_int32 i;
101
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);
107     return 0;
108 }
109
110 /* setup xor information based on session key */
111 int
112 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
113                     char *aivec, char *aresult)
114 {
115     struct rxkad_endpoint tendpoint;
116     afs_uint32 xor[2];
117
118     rxkad_SetupEndpoint(aconnp, &tendpoint);
119     memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
120     fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), aschedule, xor,
121                    ENCRYPT);
122     memcpy(aresult,
123            ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
124            ENCRYPTIONBLOCKSIZE);
125     return 0;
126 }
127
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. */
132
133 afs_uint32
134 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
135 {
136     int i;
137     afs_uint32 cksum;
138     u_char *cp = (u_char *) v2r;
139     afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
140
141     v2r->encrypted.endpoint.cksum = 0;
142
143     /* this function captured from budb/db_hash.c */
144     cksum = 1000003;
145     for (i = 0; i < sizeof(*v2r); i++)
146         cksum = (*cp++) + cksum * 0x10204081;
147
148     v2r->encrypted.endpoint.cksum = savedCksum;
149     return htonl(cksum);
150 }
151
152 void
153 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
154 {
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? */
162     }
163 }
164
165 /* returns a short integer in host byte order representing a good checksum of
166  * the packet header.
167  */
168 static afs_int32
169 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
170            afs_int32 * aivec)
171 {
172     afs_uint32 word[2];
173     register afs_uint32 t;
174
175     t = apacket->header.callNumber;
176     word[0] = htonl(t);
177     /* note that word [1] includes the channel # */
178     t = ((apacket->header.cid & 0x3) << 30)
179         | ((apacket->header.seq & 0x3fffffff));
180     word[1] = htonl(t);
181     /* XOR in the ivec from the per-endpoint encryption */
182     word[0] ^= aivec[0];
183     word[1] ^= aivec[1];
184     /* encrypts word as if it were a character string */
185     fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
186     t = ntohl(word[1]);
187     t = (t >> 16) & 0xffff;
188     if (t == 0)
189         t = 1;                  /* so that 0 means don't care */
190     return t;
191 }
192
193
194 static afs_int32
195 FreeObject(struct rx_securityClass *aobj)
196 {
197     struct rxkad_cprivate *tcp; /* both structs start w/ type field */
198
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));
207     } else {
208         return RXKADINCONSISTENCY;
209     }                           /* unknown type */
210     LOCK_RXKAD_STATS;
211     rxkad_stats.destroyObject++;
212     UNLOCK_RXKAD_STATS;
213     return 0;
214 }
215
216 /* rxkad_Close - called by rx with the security class object as a parameter
217  * when a security object is to be discarded */
218
219 int
220 rxkad_Close(struct rx_securityClass *aobj)
221 {
222     afs_int32 code;
223     aobj->refCount--;
224     code = FreeObject(aobj);
225     return code;
226 }
227
228 /* either: called to (re)create a new connection. */
229
230 int
231 rxkad_NewConnection(struct rx_securityClass *aobj,
232                     struct rx_connection *aconn)
233 {
234     if (aconn->securityData)
235         return RXKADINCONSISTENCY;      /* already allocated??? */
236
237     if (rx_IsServerConn(aconn)) {
238         int size = sizeof(struct rxkad_sconn);
239         aconn->securityData = (char *)rxi_Alloc(size);
240         memset(aconn->securityData, 0, size);   /* initialize it conveniently */
241     } else {                    /* client */
242         struct rxkad_cprivate *tcp;
243         struct rxkad_cconn *tccp;
244         int size = sizeof(struct rxkad_cconn);
245         tccp = (struct rxkad_cconn *)rxi_Alloc(size);
246         aconn->securityData = (char *)tccp;
247         memset(aconn->securityData, 0, size);   /* initialize it conveniently */
248         tcp = (struct rxkad_cprivate *)aobj->privateData;
249         if (!(tcp->type & rxkad_client))
250             return RXKADINCONSISTENCY;
251         rxkad_SetLevel(aconn, tcp->level);      /* set header and trailer sizes */
252         rxkad_AllocCID(aobj, aconn);    /* CHANGES cid AND epoch!!!! */
253         rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
254         LOCK_RXKAD_STATS;
255         rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
256         UNLOCK_RXKAD_STATS;
257     }
258
259     aobj->refCount++;           /* attached connection */
260     return 0;
261 }
262
263 /* either: called to destroy a connection. */
264
265 int
266 rxkad_DestroyConnection(struct rx_securityClass *aobj,
267                         struct rx_connection *aconn)
268 {
269     if (rx_IsServerConn(aconn)) {
270         struct rxkad_sconn *sconn;
271         struct rxkad_serverinfo *rock;
272         sconn = (struct rxkad_sconn *)aconn->securityData;
273         if (sconn) {
274             aconn->securityData = 0;
275             LOCK_RXKAD_STATS;
276             if (sconn->authenticated)
277                 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
278             else
279                 rxkad_stats.destroyUnauth++;
280             UNLOCK_RXKAD_STATS;
281             rock = sconn->rock;
282             if (rock)
283                 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
284             rxi_Free(sconn, sizeof(struct rxkad_sconn));
285         } else {
286             LOCK_RXKAD_STATS;
287             rxkad_stats.destroyUnused++;
288             UNLOCK_RXKAD_STATS;
289         }
290     } else {                    /* client */
291         struct rxkad_cconn *cconn;
292         struct rxkad_cprivate *tcp;
293         cconn = (struct rxkad_cconn *)aconn->securityData;
294         tcp = (struct rxkad_cprivate *)aobj->privateData;
295         if (!(tcp->type & rxkad_client))
296             return RXKADINCONSISTENCY;
297         if (cconn) {
298             aconn->securityData = 0;
299             rxi_Free(cconn, sizeof(struct rxkad_cconn));
300         }
301         LOCK_RXKAD_STATS;
302         rxkad_stats.destroyClient++;
303         UNLOCK_RXKAD_STATS;
304     }
305     aobj->refCount--;           /* decrement connection counter */
306     if (aobj->refCount <= 0) {
307         afs_int32 code;
308         code = FreeObject(aobj);
309         if (code)
310             return code;
311     }
312     return 0;
313 }
314
315 /* either: decode packet */
316
317 int
318 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
319                   struct rx_packet *apacket)
320 {
321     struct rx_connection *tconn;
322     rxkad_level level;
323     fc_KeySchedule *schedule;
324     fc_InitializationVector *ivec;
325     int len;
326     int nlen;
327     u_int word;                 /* so we get unsigned right-shift */
328     int checkCksum;
329     afs_int32 *preSeq;
330     afs_int32 code;
331
332     tconn = rx_ConnectionOf(acall);
333     len = rx_GetDataSize(apacket);
334     checkCksum = 0;             /* init */
335     if (rx_IsServerConn(tconn)) {
336         struct rxkad_sconn *sconn;
337         sconn = (struct rxkad_sconn *)tconn->securityData;
338         if (rx_GetPacketCksum(apacket) != 0)
339             sconn->cksumSeen = 1;
340         checkCksum = sconn->cksumSeen;
341         if (sconn && sconn->authenticated
342             && (osi_Time() < sconn->expirationTime)) {
343             level = sconn->level;
344             LOCK_RXKAD_STATS;
345             rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
346             UNLOCK_RXKAD_STATS;
347             sconn->stats.packetsReceived++;
348             sconn->stats.bytesReceived += len;
349             schedule = (fc_KeySchedule *) sconn->keysched;
350             ivec = (fc_InitializationVector *) sconn->ivec;
351         } else {
352             LOCK_RXKAD_STATS;
353             rxkad_stats.expired++;
354             UNLOCK_RXKAD_STATS;
355             return RXKADEXPIRED;
356         }
357         preSeq = sconn->preSeq;
358     } else {                    /* client connection */
359         struct rxkad_cconn *cconn;
360         struct rxkad_cprivate *tcp;
361         cconn = (struct rxkad_cconn *)tconn->securityData;
362         if (rx_GetPacketCksum(apacket) != 0)
363             cconn->cksumSeen = 1;
364         checkCksum = cconn->cksumSeen;
365         tcp = (struct rxkad_cprivate *)aobj->privateData;
366         if (!(tcp->type & rxkad_client))
367             return RXKADINCONSISTENCY;
368         level = tcp->level;
369         LOCK_RXKAD_STATS;
370         rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
371         UNLOCK_RXKAD_STATS;
372         cconn->stats.packetsReceived++;
373         cconn->stats.bytesReceived += len;
374         preSeq = cconn->preSeq;
375         schedule = (fc_KeySchedule *) tcp->keysched;
376         ivec = (fc_InitializationVector *) tcp->ivec;
377     }
378
379     if (checkCksum) {
380         code = ComputeSum(apacket, schedule, preSeq);
381         if (code != rx_GetPacketCksum(apacket))
382             return RXKADSEALEDINCON;
383     }
384
385     switch (level) {
386     case rxkad_clear:
387         return 0;               /* shouldn't happen */
388     case rxkad_auth:
389         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
390         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
391                        DECRYPT);
392         break;
393     case rxkad_crypt:
394         code = rxkad_DecryptPacket(tconn, schedule, ivec, len, apacket);
395         if (code)
396             return code;
397         break;
398     }
399     word = ntohl(rx_GetInt32(apacket, 0));      /* get first sealed word */
400     if ((word >> 16) !=
401         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
402         return RXKADSEALEDINCON;
403     nlen = word & 0xffff;       /* get real user data length */
404
405     /* The sealed length should be no larger than the initial length, since the  
406      * reverse (round-up) occurs in ...PreparePacket */
407     if (nlen > len)
408         return RXKADDATALEN;
409     rx_SetDataSize(apacket, nlen);
410     return 0;
411 }
412
413 /* either: encode packet */
414
415 int
416 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
417                     struct rx_packet *apacket)
418 {
419     struct rx_connection *tconn;
420     rxkad_level level;
421     fc_KeySchedule *schedule;
422     fc_InitializationVector *ivec;
423     int len;
424     int nlen;
425     int word;
426     afs_int32 code;
427     afs_int32 *preSeq;
428
429     tconn = rx_ConnectionOf(acall);
430     len = rx_GetDataSize(apacket);
431     if (rx_IsServerConn(tconn)) {
432         struct rxkad_sconn *sconn;
433         sconn = (struct rxkad_sconn *)tconn->securityData;
434         if (sconn && sconn->authenticated
435             && (osi_Time() < sconn->expirationTime)) {
436             level = sconn->level;
437             LOCK_RXKAD_STATS;
438             rxkad_stats.
439                 preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
440             UNLOCK_RXKAD_STATS;
441             sconn->stats.packetsSent++;
442             sconn->stats.bytesSent += len;
443             schedule = (fc_KeySchedule *) sconn->keysched;
444             ivec = (fc_InitializationVector *) sconn->ivec;
445         } else {
446             LOCK_RXKAD_STATS;
447             rxkad_stats.expired++;      /* this is a pretty unlikely path... */
448             UNLOCK_RXKAD_STATS;
449             return RXKADEXPIRED;
450         }
451         preSeq = sconn->preSeq;
452     } else {                    /* client connection */
453         struct rxkad_cconn *cconn;
454         struct rxkad_cprivate *tcp;
455         cconn = (struct rxkad_cconn *)tconn->securityData;
456         tcp = (struct rxkad_cprivate *)aobj->privateData;
457         if (!(tcp->type & rxkad_client))
458             return RXKADINCONSISTENCY;
459         level = tcp->level;
460         LOCK_RXKAD_STATS;
461         rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
462         UNLOCK_RXKAD_STATS;
463         cconn->stats.packetsSent++;
464         cconn->stats.bytesSent += len;
465         preSeq = cconn->preSeq;
466         schedule = (fc_KeySchedule *) tcp->keysched;
467         ivec = (fc_InitializationVector *) tcp->ivec;
468     }
469
470     /* compute upward compatible checksum */
471     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
472     if (level == rxkad_clear)
473         return 0;
474
475     len = rx_GetDataSize(apacket);
476     word = (((apacket->header.seq ^ apacket->header.callNumber)
477              & 0xffff) << 16) | (len & 0xffff);
478     rx_PutInt32(apacket, 0, htonl(word));
479
480     switch (level) {
481     case rxkad_clear:
482         return 0;               /* shouldn't happen */
483     case rxkad_auth:
484         nlen =
485             afs_max(ENCRYPTIONBLOCKSIZE,
486                     len + rx_GetSecurityHeaderSize(tconn));
487         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
488             rxi_RoundUpPacket(apacket,
489                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
490         }
491         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
492         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), schedule,
493                        ENCRYPT);
494         break;
495     case rxkad_crypt:
496         nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
497         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
498             rxi_RoundUpPacket(apacket,
499                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
500         }
501         code = rxkad_EncryptPacket(tconn, schedule, ivec, nlen, apacket);
502         if (code)
503             return code;
504         break;
505     }
506     rx_SetDataSize(apacket, nlen);
507     return 0;
508 }
509
510 /* either: return connection stats */
511
512 int
513 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
514                struct rx_securityObjectStats *astats)
515 {
516     astats->type = 3;
517     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
518     if (!aconn->securityData) {
519         astats->flags |= 1;
520         return 0;
521     }
522     if (rx_IsServerConn(aconn)) {
523         struct rxkad_sconn *sconn;
524         sconn = (struct rxkad_sconn *)aconn->securityData;
525         astats->level = sconn->level;
526         if (sconn->authenticated)
527             astats->flags |= 2;
528         if (sconn->cksumSeen)
529             astats->flags |= 8;
530         astats->expires = sconn->expirationTime;
531         astats->bytesReceived = sconn->stats.bytesReceived;
532         astats->packetsReceived = sconn->stats.packetsReceived;
533         astats->bytesSent = sconn->stats.bytesSent;
534         astats->packetsSent = sconn->stats.packetsSent;
535     } else {                    /* client connection */
536         struct rxkad_cconn *cconn;
537         cconn = (struct rxkad_cconn *)aconn->securityData;
538         if (cconn->cksumSeen)
539             astats->flags |= 8;
540         astats->bytesReceived = cconn->stats.bytesReceived;
541         astats->packetsReceived = cconn->stats.packetsReceived;
542         astats->bytesSent = cconn->stats.bytesSent;
543         astats->packetsSent = cconn->stats.packetsSent;
544     }
545     return 0;
546 }
547
548 rxkad_level
549 rxkad_StringToLevel(char *name)
550 {
551     if (strcmp(name, "clear") == 0)
552         return rxkad_clear;
553     if (strcmp(name, "auth") == 0)
554         return rxkad_auth;
555     if (strcmp(name, "crypt") == 0)
556         return rxkad_crypt;
557     return -1;
558 }
559
560 char *
561 rxkad_LevelToString(rxkad_level level)
562 {
563     if (level == rxkad_clear)
564         return "clear";
565     if (level == rxkad_auth)
566         return "auth";
567     if (level == rxkad_crypt)
568         return "crypt";
569     return "unknown";
570 }