include-afsconfig-before-param-h-20010712
[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 RCSID("$Header$");
20
21 #ifdef KERNEL
22 #ifndef UKERNEL
23 #include "../afs/stds.h"
24 #include "../afs/afs_osi.h"
25 #ifdef  AFS_AIX_ENV
26 #include "../h/systm.h"
27 #endif
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 */
34 #else /* !UKERNEL */
35 #include "../afs/sysincludes.h"
36 #include "../afs/afsincludes.h"
37 #endif /* !UKERNEL */
38 #include "../rx/rx.h"
39
40 #else /* KERNEL */
41 #include <afs/stds.h>
42 #include <sys/types.h>
43 #include <time.h>
44 #ifdef AFS_NT40_ENV
45 #include <winsock2.h>
46 #ifdef AFS_PTHREAD_ENV
47 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
48 #endif
49 #else
50 #include <netinet/in.h>
51 #endif
52 #include <rx/rx.h>
53 #include <rx/xdr.h>
54
55 #endif /* KERNEL */
56
57 #include "private_data.h"
58 #define XPRT_RXKAD_COMMON
59
60 char *rxi_Alloc();
61
62 #ifndef max
63 #define max(a,b)    ((a) < (b)? (b) : (a))
64 #endif /* max */
65
66 #ifndef KERNEL
67 #define osi_Time() time(0)
68 #endif
69 struct rxkad_stats rxkad_stats;
70
71 /* this call sets up an endpoint structure, leaving it in *network* byte
72  * order so that it can be used quickly for encryption.
73  */
74 rxkad_SetupEndpoint(aconnp, aendpointp)
75   IN struct rx_connection *aconnp;
76   OUT struct rxkad_endpoint *aendpointp;
77 {
78     register afs_int32 i;
79
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);
85     return 0;
86 }
87
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;
92   IN char *aivec;
93   OUT char *aresult;
94 {
95     struct rxkad_endpoint tendpoint;
96     afs_uint32 xor[2];
97
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);
104     return 0;
105 }
106
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. */
111
112 afs_uint32 rxkad_CksumChallengeResponse (v2r)
113   IN struct rxkad_v2ChallengeResponse *v2r;
114 {
115     int i;
116     afs_uint32 cksum;
117     u_char *cp = (u_char *)v2r;
118     afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
119
120     v2r->encrypted.endpoint.cksum = 0;
121
122     /* this function captured from budb/db_hash.c */
123     cksum = 1000003;
124     for (i=0; i<sizeof(*v2r); i++)
125         cksum = (*cp++) + cksum * 0x10204081;
126
127     v2r->encrypted.endpoint.cksum = savedCksum;
128     return htonl(cksum);
129 }
130
131 void rxkad_SetLevel(conn, level)
132   struct rx_connection *conn;
133   rxkad_level           level;
134 {
135     if (level == rxkad_auth) {
136         rx_SetSecurityHeaderSize (conn, 4);
137         rx_SetSecurityMaxTrailerSize (conn, 4);
138     }
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? */
143     }
144 }
145
146 /* returns a short integer in host byte order representing a good checksum of
147  * the packet header.
148  */
149 static afs_int32 ComputeSum(apacket, aschedule, aivec)
150 struct rx_packet *apacket;
151 afs_int32 *aivec;
152 fc_KeySchedule *aschedule; {
153     afs_uint32 word[2];
154     register afs_uint32 t;
155
156     t = apacket->header.callNumber;
157     word[0] = htonl(t);
158     /* note that word [1] includes the channel # */
159     t = ((apacket->header.cid & 0x3) << 30)
160             | ((apacket->header.seq & 0x3fffffff));
161     word[1] = htonl(t);
162     /* XOR in the ivec from the per-endpoint encryption */
163     word[0] ^= aivec[0];
164     word[1] ^= aivec[1];
165     /* encrypts word as if it were a character string */
166     fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
167     t = ntohl(word[1]);
168     t = (t >> 16) & 0xffff;
169     if (t == 0) t = 1;  /* so that 0 means don't care */
170     return t;
171 }
172
173
174 static afs_int32 FreeObject (aobj)
175   IN struct rx_securityClass *aobj;
176 {   struct rxkad_cprivate *tcp;         /* both structs start w/ type field */
177
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));
183     }
184     else if (tcp->type & rxkad_server) {
185         rxi_Free(tcp, sizeof(struct rxkad_sprivate));
186     }
187     else { return RXKADINCONSISTENCY; } /* unknown type */
188     LOCK_RXKAD_STATS
189     rxkad_stats.destroyObject++;
190     UNLOCK_RXKAD_STATS
191     return 0;
192 }
193
194 /* rxkad_Close - called by rx with the security class object as a parameter
195  * when a security object is to be discarded */
196
197 rxs_return_t rxkad_Close (aobj)
198   IN struct rx_securityClass *aobj;
199 {
200     afs_int32 code;
201     aobj->refCount--;
202     code = FreeObject (aobj);
203     return code;
204 }
205
206 /* either: called to (re)create a new connection. */
207
208 rxs_return_t rxkad_NewConnection (aobj, aconn)
209   struct rx_securityClass *aobj;
210   struct rx_connection    *aconn;
211 {
212     if (aconn->securityData)
213         return RXKADINCONSISTENCY;      /* already allocated??? */
214
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 */
219     }
220     else { /* client */
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);
232         LOCK_RXKAD_STATS
233         rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
234         UNLOCK_RXKAD_STATS
235     }
236
237     aobj->refCount++;                   /* attached connection */
238     return 0;
239 }
240
241 /* either: called to destroy a connection. */
242
243 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
244   struct rx_securityClass *aobj;
245   struct rx_connection    *aconn;
246 {
247     if (rx_IsServerConn(aconn)) {
248         struct rxkad_sconn *sconn;
249         struct rxkad_serverinfo *rock;
250         sconn = (struct rxkad_sconn *)aconn->securityData;
251         if (sconn) {
252             aconn->securityData = 0;
253             LOCK_RXKAD_STATS
254             if (sconn->authenticated)
255                 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
256             else rxkad_stats.destroyUnauth++;
257             UNLOCK_RXKAD_STATS
258             rock = sconn->rock;
259             if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
260             rxi_Free (sconn, sizeof(struct rxkad_sconn));
261         }
262         else {
263             LOCK_RXKAD_STATS
264             rxkad_stats.destroyUnused++;
265             UNLOCK_RXKAD_STATS
266         }
267     }
268     else {                              /* client */
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;
274         if (cconn) {
275             aconn->securityData = 0;
276             rxi_Free (cconn, sizeof(struct rxkad_cconn));
277         }
278         LOCK_RXKAD_STATS
279         rxkad_stats.destroyClient++;
280         UNLOCK_RXKAD_STATS
281     }
282     aobj->refCount--;                   /* decrement connection counter */
283     if (aobj->refCount <= 0) {
284         afs_int32 code;
285         code = FreeObject (aobj);
286         if (code) return code;
287     }
288     return 0;
289 }
290
291 /* either: decode packet */
292
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;
298     rxkad_level            level;
299     fc_KeySchedule *schedule;
300     fc_InitializationVector *ivec;
301     int len;
302     int nlen;
303     u_int word;                         /* so we get unsigned right-shift */
304     int checkCksum;
305     afs_int32 *preSeq;
306     afs_int32 code;
307
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;
319             LOCK_RXKAD_STATS
320             rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
321             UNLOCK_RXKAD_STATS
322             sconn->stats.packetsReceived++;
323             sconn->stats.bytesReceived += len;
324             schedule = (fc_KeySchedule *)sconn->keysched;
325             ivec = (fc_InitializationVector *)sconn->ivec;
326         }
327         else {
328             LOCK_RXKAD_STATS
329             rxkad_stats.expired++;
330             UNLOCK_RXKAD_STATS
331             return RXKADEXPIRED;
332         }
333         preSeq = sconn->preSeq;
334     }
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;
343         level = tcp->level;
344         LOCK_RXKAD_STATS
345         rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
346         UNLOCK_RXKAD_STATS
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;
352     }
353     
354     if (checkCksum) {
355         code = ComputeSum(apacket, schedule, preSeq);
356         if (code != rx_GetPacketCksum(apacket))
357             return RXKADSEALEDINCON;
358     }
359
360     switch (level) {
361       case rxkad_clear: return 0;       /* shouldn't happen */
362       case rxkad_auth:
363         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
364         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
365                         schedule, DECRYPT);
366         break;
367       case rxkad_crypt:
368         code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
369         if (code) return code;
370         break;
371     }
372     word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
373     if ((word >> 16) !=
374         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
375         return RXKADSEALEDINCON;
376     nlen = word & 0xffff;               /* get real user data length */
377
378     /* The sealed length should be no larger than the initial length, since the  
379      * reverse (round-up) occurs in ...PreparePacket */
380     if (nlen > len)                     
381       return RXKADDATALEN;              
382     rx_SetDataSize (apacket, nlen);
383     return 0;
384 }
385
386 /* either: encode packet */
387
388 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
389   struct rx_securityClass *aobj;
390   struct rx_call *acall;
391   struct rx_packet *apacket;
392 {
393     struct rx_connection *tconn;
394     rxkad_level         level;
395     fc_KeySchedule *schedule;
396     fc_InitializationVector *ivec;
397     int len;
398     int nlen;
399     int word;
400     afs_int32 code;
401     afs_int32 *preSeq;
402
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;
411             LOCK_RXKAD_STATS
412             rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
413             UNLOCK_RXKAD_STATS
414             sconn->stats.packetsSent++;
415             sconn->stats.bytesSent += len;
416             schedule = (fc_KeySchedule *)sconn->keysched;
417             ivec = (fc_InitializationVector *)sconn->ivec;
418         }
419         else {
420             LOCK_RXKAD_STATS
421             rxkad_stats.expired++;      /* this is a pretty unlikely path... */
422             UNLOCK_RXKAD_STATS
423             return RXKADEXPIRED;
424         }
425         preSeq = sconn->preSeq;
426     }
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;
433         level = tcp->level;
434         LOCK_RXKAD_STATS
435         rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
436         UNLOCK_RXKAD_STATS
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;
442     }
443
444     /* compute upward compatible checksum */
445     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
446     if (level == rxkad_clear) return 0;
447
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));   
452
453     switch (level) {
454       case rxkad_clear: return 0;       /* shouldn't happen */
455       case rxkad_auth:
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)));
460         }
461         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
462         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
463                         schedule, ENCRYPT);
464         break;
465       case rxkad_crypt:
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)));
469         }
470         code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
471         if (code) return code;
472         break;
473     }
474     rx_SetDataSize (apacket, nlen);
475     return 0;
476 }
477
478 /* either: return connection stats */
479
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;
484 {
485     astats->type = 3;
486     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
487     if (!aconn->securityData) {
488         astats->flags |= 1;
489         return 0;
490     }
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;
502     }
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;
511     }
512     return 0;
513 }