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