1ce045d480cd926fcd6c605d4284ed87880006b7
[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 afs_max
63 #define afs_max(a,b)    ((a) < (b)? (b) : (a))
64 #endif /* afs_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     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);
103     return 0;
104 }
105
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. */
110
111 afs_uint32 rxkad_CksumChallengeResponse (v2r)
112   IN struct rxkad_v2ChallengeResponse *v2r;
113 {
114     int i;
115     afs_uint32 cksum;
116     u_char *cp = (u_char *)v2r;
117     afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
118
119     v2r->encrypted.endpoint.cksum = 0;
120
121     /* this function captured from budb/db_hash.c */
122     cksum = 1000003;
123     for (i=0; i<sizeof(*v2r); i++)
124         cksum = (*cp++) + cksum * 0x10204081;
125
126     v2r->encrypted.endpoint.cksum = savedCksum;
127     return htonl(cksum);
128 }
129
130 void rxkad_SetLevel(conn, level)
131   struct rx_connection *conn;
132   rxkad_level           level;
133 {
134     if (level == rxkad_auth) {
135         rx_SetSecurityHeaderSize (conn, 4);
136         rx_SetSecurityMaxTrailerSize (conn, 4);
137     }
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? */
142     }
143 }
144
145 /* returns a short integer in host byte order representing a good checksum of
146  * the packet header.
147  */
148 static afs_int32 ComputeSum(apacket, aschedule, aivec)
149 struct rx_packet *apacket;
150 afs_int32 *aivec;
151 fc_KeySchedule *aschedule; {
152     afs_uint32 word[2];
153     register afs_uint32 t;
154
155     t = apacket->header.callNumber;
156     word[0] = htonl(t);
157     /* note that word [1] includes the channel # */
158     t = ((apacket->header.cid & 0x3) << 30)
159             | ((apacket->header.seq & 0x3fffffff));
160     word[1] = htonl(t);
161     /* XOR in the ivec from the per-endpoint encryption */
162     word[0] ^= aivec[0];
163     word[1] ^= aivec[1];
164     /* encrypts word as if it were a character string */
165     fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
166     t = ntohl(word[1]);
167     t = (t >> 16) & 0xffff;
168     if (t == 0) t = 1;  /* so that 0 means don't care */
169     return t;
170 }
171
172
173 static afs_int32 FreeObject (aobj)
174   IN struct rx_securityClass *aobj;
175 {   struct rxkad_cprivate *tcp;         /* both structs start w/ type field */
176
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));
182     }
183     else if (tcp->type & rxkad_server) {
184         rxi_Free(tcp, sizeof(struct rxkad_sprivate));
185     }
186     else { return RXKADINCONSISTENCY; } /* unknown type */
187     LOCK_RXKAD_STATS
188     rxkad_stats.destroyObject++;
189     UNLOCK_RXKAD_STATS
190     return 0;
191 }
192
193 /* rxkad_Close - called by rx with the security class object as a parameter
194  * when a security object is to be discarded */
195
196 rxs_return_t rxkad_Close (aobj)
197   IN struct rx_securityClass *aobj;
198 {
199     afs_int32 code;
200     aobj->refCount--;
201     code = FreeObject (aobj);
202     return code;
203 }
204
205 /* either: called to (re)create a new connection. */
206
207 rxs_return_t rxkad_NewConnection (aobj, aconn)
208   struct rx_securityClass *aobj;
209   struct rx_connection    *aconn;
210 {
211     if (aconn->securityData)
212         return RXKADINCONSISTENCY;      /* already allocated??? */
213
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 */
218     }
219     else { /* client */
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);
231         LOCK_RXKAD_STATS
232         rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
233         UNLOCK_RXKAD_STATS
234     }
235
236     aobj->refCount++;                   /* attached connection */
237     return 0;
238 }
239
240 /* either: called to destroy a connection. */
241
242 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
243   struct rx_securityClass *aobj;
244   struct rx_connection    *aconn;
245 {
246     if (rx_IsServerConn(aconn)) {
247         struct rxkad_sconn *sconn;
248         struct rxkad_serverinfo *rock;
249         sconn = (struct rxkad_sconn *)aconn->securityData;
250         if (sconn) {
251             aconn->securityData = 0;
252             LOCK_RXKAD_STATS
253             if (sconn->authenticated)
254                 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
255             else rxkad_stats.destroyUnauth++;
256             UNLOCK_RXKAD_STATS
257             rock = sconn->rock;
258             if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
259             rxi_Free (sconn, sizeof(struct rxkad_sconn));
260         }
261         else {
262             LOCK_RXKAD_STATS
263             rxkad_stats.destroyUnused++;
264             UNLOCK_RXKAD_STATS
265         }
266     }
267     else {                              /* client */
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;
273         if (cconn) {
274             aconn->securityData = 0;
275             rxi_Free (cconn, sizeof(struct rxkad_cconn));
276         }
277         LOCK_RXKAD_STATS
278         rxkad_stats.destroyClient++;
279         UNLOCK_RXKAD_STATS
280     }
281     aobj->refCount--;                   /* decrement connection counter */
282     if (aobj->refCount <= 0) {
283         afs_int32 code;
284         code = FreeObject (aobj);
285         if (code) return code;
286     }
287     return 0;
288 }
289
290 /* either: decode packet */
291
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;
297     rxkad_level            level;
298     fc_KeySchedule *schedule;
299     fc_InitializationVector *ivec;
300     int len;
301     int nlen;
302     u_int word;                         /* so we get unsigned right-shift */
303     int checkCksum;
304     afs_int32 *preSeq;
305     afs_int32 code;
306
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;
318             LOCK_RXKAD_STATS
319             rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
320             UNLOCK_RXKAD_STATS
321             sconn->stats.packetsReceived++;
322             sconn->stats.bytesReceived += len;
323             schedule = (fc_KeySchedule *)sconn->keysched;
324             ivec = (fc_InitializationVector *)sconn->ivec;
325         }
326         else {
327             LOCK_RXKAD_STATS
328             rxkad_stats.expired++;
329             UNLOCK_RXKAD_STATS
330             return RXKADEXPIRED;
331         }
332         preSeq = sconn->preSeq;
333     }
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;
342         level = tcp->level;
343         LOCK_RXKAD_STATS
344         rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
345         UNLOCK_RXKAD_STATS
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;
351     }
352     
353     if (checkCksum) {
354         code = ComputeSum(apacket, schedule, preSeq);
355         if (code != rx_GetPacketCksum(apacket))
356             return RXKADSEALEDINCON;
357     }
358
359     switch (level) {
360       case rxkad_clear: return 0;       /* shouldn't happen */
361       case rxkad_auth:
362         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
363         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
364                         schedule, DECRYPT);
365         break;
366       case rxkad_crypt:
367         code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
368         if (code) return code;
369         break;
370     }
371     word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
372     if ((word >> 16) !=
373         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
374         return RXKADSEALEDINCON;
375     nlen = word & 0xffff;               /* get real user data length */
376
377     /* The sealed length should be no larger than the initial length, since the  
378      * reverse (round-up) occurs in ...PreparePacket */
379     if (nlen > len)                     
380       return RXKADDATALEN;              
381     rx_SetDataSize (apacket, nlen);
382     return 0;
383 }
384
385 /* either: encode packet */
386
387 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
388   struct rx_securityClass *aobj;
389   struct rx_call *acall;
390   struct rx_packet *apacket;
391 {
392     struct rx_connection *tconn;
393     rxkad_level         level;
394     fc_KeySchedule *schedule;
395     fc_InitializationVector *ivec;
396     int len;
397     int nlen;
398     int word;
399     afs_int32 code;
400     afs_int32 *preSeq;
401
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;
410             LOCK_RXKAD_STATS
411             rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
412             UNLOCK_RXKAD_STATS
413             sconn->stats.packetsSent++;
414             sconn->stats.bytesSent += len;
415             schedule = (fc_KeySchedule *)sconn->keysched;
416             ivec = (fc_InitializationVector *)sconn->ivec;
417         }
418         else {
419             LOCK_RXKAD_STATS
420             rxkad_stats.expired++;      /* this is a pretty unlikely path... */
421             UNLOCK_RXKAD_STATS
422             return RXKADEXPIRED;
423         }
424         preSeq = sconn->preSeq;
425     }
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;
432         level = tcp->level;
433         LOCK_RXKAD_STATS
434         rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
435         UNLOCK_RXKAD_STATS
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;
441     }
442
443     /* compute upward compatible checksum */
444     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
445     if (level == rxkad_clear) return 0;
446
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));   
451
452     switch (level) {
453       case rxkad_clear: return 0;       /* shouldn't happen */
454       case rxkad_auth:
455         nlen = afs_max (ENCRYPTIONBLOCKSIZE,
456                     len + rx_GetSecurityHeaderSize(tconn));
457         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
458           rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
459         }
460         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
461         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
462                         schedule, ENCRYPT);
463         break;
464       case rxkad_crypt:
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)));
468         }
469         code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
470         if (code) return code;
471         break;
472     }
473     rx_SetDataSize (apacket, nlen);
474     return 0;
475 }
476
477 /* either: return connection stats */
478
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;
483 {
484     astats->type = 3;
485     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
486     if (!aconn->securityData) {
487         astats->flags |= 1;
488         return 0;
489     }
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;
501     }
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;
510     }
511     return 0;
512 }