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