Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / rxkad / rxkad_common.c
1 /* The rxkad security object.  Routines used by both client and servers. */
2
3 /* Copyright (C) 1991, 1990 Transarc Corporation - All rights reserved */
4 /*
5 ****************************************************************************
6 *        Copyright IBM Corporation 1988, 1989 - All Rights Reserved        *
7 *                                                                          *
8 * Permission to use, copy, modify, and distribute this software and its    *
9 * documentation for any purpose and without fee is hereby granted,         *
10 * provided that the above copyright notice appear in all copies and        *
11 * that both that copyright notice and this permission notice appear in     *
12 * supporting documentation, and that the name of IBM not be used in        *
13 * advertising or publicity pertaining to distribution of the software      *
14 * without specific, written prior permission.                              *
15 *                                                                          *
16 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY      *
19 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER  *
20 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING   *
21 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.    *
22 ****************************************************************************
23 */
24
25 #ifdef KERNEL
26 #include "../afs/param.h"
27 #ifndef UKERNEL
28 #include "../afs/stds.h"
29 #include "../afs/afs_osi.h"
30 #ifdef  AFS_AIX_ENV
31 #include "../h/systm.h"
32 #endif
33 #include "../h/types.h"
34 #include "../h/time.h"
35 #include "../netinet/in.h"
36 #ifndef AFS_LINUX22_ENV
37 #include "../rpc/types.h"
38 #include "../rpc/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/param.h>
48 #include <afs/stds.h>
49 #include <sys/types.h>
50 #include <time.h>
51 #ifdef AFS_NT40_ENV
52 #include <winsock2.h>
53 #ifdef AFS_PTHREAD_ENV
54 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
55 #endif
56 #else
57 #include <netinet/in.h>
58 #endif
59 #include <rx/rx.h>
60 #include <rx/xdr.h>
61
62 #endif /* KERNEL */
63
64 #include "private_data.h"
65 #define XPRT_RXKAD_COMMON
66 #ifdef KERNEL
67 #include "../afs/permit_xprt.h"
68 #else
69 #include "../permit_xprt.h"
70 #endif
71
72
73 char *rxi_Alloc();
74
75 #ifndef max
76 #define max(a,b)    ((a) < (b)? (b) : (a))
77 #endif /* max */
78
79 #ifndef KERNEL
80 #define osi_Time() time(0)
81 #endif
82 struct rxkad_stats rxkad_stats;
83
84 /* this call sets up an endpoint structure, leaving it in *network* byte
85  * order so that it can be used quickly for encryption.
86  */
87 rxkad_SetupEndpoint(aconnp, aendpointp)
88   IN struct rx_connection *aconnp;
89   OUT struct rxkad_endpoint *aendpointp;
90 {
91     register afs_int32 i;
92
93     aendpointp->cuid[0] = htonl(aconnp->epoch);
94     i = aconnp->cid & RX_CIDMASK;
95     aendpointp->cuid[1] = htonl(i);
96     aendpointp->cksum = 0;              /* used as cksum only in chal resp. */
97     aendpointp->securityIndex = htonl(aconnp->securityIndex);
98     return 0;
99 }
100
101 /* setup xor information based on session key */
102 rxkad_DeriveXORInfo(aconnp, aschedule, aivec, aresult)
103   IN struct rx_connection *aconnp;
104   IN fc_KeySchedule *aschedule;
105   IN char *aivec;
106   OUT char *aresult;
107 {
108     struct rxkad_endpoint tendpoint;
109     afs_uint32 xor[2];
110
111     rxkad_SetupEndpoint(aconnp, &tendpoint);
112     bcopy(aivec, (void *)xor, 2*sizeof(afs_int32));
113     fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint),
114                    aschedule, xor, ENCRYPT);
115     bcopy(((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
116           aresult, 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 (v2r)
126   IN struct rxkad_v2ChallengeResponse *v2r;
127 {
128     int i;
129     afs_uint32 cksum;
130     u_char *cp = (u_char *)v2r;
131     afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
132
133     v2r->encrypted.endpoint.cksum = 0;
134
135     /* this function captured from budb/db_hash.c */
136     cksum = 1000003;
137     for (i=0; i<sizeof(*v2r); i++)
138         cksum = (*cp++) + cksum * 0x10204081;
139
140     v2r->encrypted.endpoint.cksum = savedCksum;
141     return htonl(cksum);
142 }
143
144 void rxkad_SetLevel(conn, level)
145   struct rx_connection *conn;
146   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(apacket, aschedule, aivec)
163 struct rx_packet *apacket;
164 afs_int32 *aivec;
165 fc_KeySchedule *aschedule; {
166     afs_uint32 word[2];
167     register afs_uint32 t;
168
169     t = apacket->header.callNumber;
170     word[0] = htonl(t);
171     /* note that word [1] includes the channel # */
172     t = ((apacket->header.cid & 0x3) << 30)
173             | ((apacket->header.seq & 0x3fffffff));
174     word[1] = htonl(t);
175     /* XOR in the ivec from the per-endpoint encryption */
176     word[0] ^= aivec[0];
177     word[1] ^= aivec[1];
178     /* encrypts word as if it were a character string */
179     fc_ecb_encrypt(word, word, aschedule, ENCRYPT);
180     t = ntohl(word[1]);
181     t = (t >> 16) & 0xffff;
182     if (t == 0) t = 1;  /* so that 0 means don't care */
183     return t;
184 }
185
186
187 static afs_int32 FreeObject (aobj)
188   IN struct rx_securityClass *aobj;
189 {   struct rxkad_cprivate *tcp;         /* both structs start w/ type field */
190
191     if (aobj->refCount > 0) return 0;   /* still in use */
192     tcp = (struct rxkad_cprivate *)aobj->privateData;
193     rxi_Free(aobj, sizeof(struct rx_securityClass));
194     if (tcp->type & rxkad_client) {
195         rxi_Free(tcp, sizeof(struct rxkad_cprivate));
196     }
197     else if (tcp->type & rxkad_server) {
198         rxi_Free(tcp, sizeof(struct rxkad_sprivate));
199     }
200     else { return RXKADINCONSISTENCY; } /* unknown type */
201     LOCK_RXKAD_STATS
202     rxkad_stats.destroyObject++;
203     UNLOCK_RXKAD_STATS
204     return 0;
205 }
206
207 /* rxkad_Close - called by rx with the security class object as a parameter
208  * when a security object is to be discarded */
209
210 rxs_return_t rxkad_Close (aobj)
211   IN struct rx_securityClass *aobj;
212 {
213     afs_int32 code;
214     aobj->refCount--;
215     code = FreeObject (aobj);
216     return code;
217 }
218
219 /* either: called to (re)create a new connection. */
220
221 rxs_return_t rxkad_NewConnection (aobj, aconn)
222   struct rx_securityClass *aobj;
223   struct rx_connection    *aconn;
224 {
225     if (aconn->securityData)
226         return RXKADINCONSISTENCY;      /* already allocated??? */
227
228     if (rx_IsServerConn(aconn)) {
229         int size = sizeof(struct rxkad_sconn);
230         aconn->securityData = (char *) rxi_Alloc (size);
231         bzero(aconn->securityData, size); /* initialize it conveniently */
232     }
233     else { /* client */
234         struct rxkad_cprivate *tcp;
235         struct rxkad_cconn *tccp;
236         int size = sizeof(struct rxkad_cconn);
237         tccp = (struct rxkad_cconn *) rxi_Alloc (size);
238         aconn->securityData = (char *) tccp;
239         bzero(aconn->securityData, size); /* initialize it conveniently */
240         tcp = (struct rxkad_cprivate *) aobj->privateData;
241         if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
242         rxkad_SetLevel(aconn, tcp->level); /* set header and trailer sizes */
243         rxkad_AllocCID(aobj, aconn);    /* CHANGES cid AND epoch!!!! */
244         rxkad_DeriveXORInfo(aconn, tcp->keysched, tcp->ivec, tccp->preSeq);
245         LOCK_RXKAD_STATS
246         rxkad_stats.connections[rxkad_LevelIndex(tcp->level)]++;
247         UNLOCK_RXKAD_STATS
248     }
249
250     aobj->refCount++;                   /* attached connection */
251     return 0;
252 }
253
254 /* either: called to destroy a connection. */
255
256 rxs_return_t rxkad_DestroyConnection (aobj, aconn)
257   struct rx_securityClass *aobj;
258   struct rx_connection    *aconn;
259 {
260     if (rx_IsServerConn(aconn)) {
261         struct rxkad_sconn *sconn;
262         struct rxkad_serverinfo *rock;
263         sconn = (struct rxkad_sconn *)aconn->securityData;
264         if (sconn) {
265             aconn->securityData = 0;
266             LOCK_RXKAD_STATS
267             if (sconn->authenticated)
268                 rxkad_stats.destroyConn[rxkad_LevelIndex(sconn->level)]++;
269             else rxkad_stats.destroyUnauth++;
270             UNLOCK_RXKAD_STATS
271             rock = sconn->rock;
272             if (rock) rxi_Free (rock, sizeof(struct rxkad_serverinfo));
273             rxi_Free (sconn, sizeof(struct rxkad_sconn));
274         }
275         else {
276             LOCK_RXKAD_STATS
277             rxkad_stats.destroyUnused++;
278             UNLOCK_RXKAD_STATS
279         }
280     }
281     else {                              /* client */
282         struct rxkad_cconn *cconn;
283         struct rxkad_cprivate *tcp;
284         cconn = (struct rxkad_cconn *)aconn->securityData;
285         tcp = (struct rxkad_cprivate *) aobj->privateData;
286         if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
287         if (cconn) {
288             aconn->securityData = 0;
289             rxi_Free (cconn, sizeof(struct rxkad_cconn));
290         }
291         LOCK_RXKAD_STATS
292         rxkad_stats.destroyClient++;
293         UNLOCK_RXKAD_STATS
294     }
295     aobj->refCount--;                   /* decrement connection counter */
296     if (aobj->refCount <= 0) {
297         afs_int32 code;
298         code = FreeObject (aobj);
299         if (code) return code;
300     }
301     return 0;
302 }
303
304 /* either: decode packet */
305
306 rxs_return_t rxkad_CheckPacket (aobj, acall, apacket)
307   struct rx_securityClass *aobj;
308   struct rx_call          *acall;
309   struct rx_packet        *apacket;
310 {   struct rx_connection  *tconn;
311     rxkad_level            level;
312     fc_KeySchedule *schedule;
313     fc_InitializationVector *ivec;
314     int len;
315     int nlen;
316     u_int word;                         /* so we get unsigned right-shift */
317     int checkCksum;
318     afs_int32 *preSeq;
319     afs_int32 code;
320
321     tconn = rx_ConnectionOf(acall);
322     len = rx_GetDataSize (apacket);
323     checkCksum = 0;                     /* init */
324     if (rx_IsServerConn(tconn)) {
325         struct rxkad_sconn *sconn;
326         sconn = (struct rxkad_sconn *) tconn->securityData;
327         if (rx_GetPacketCksum(apacket) != 0) sconn->cksumSeen = 1;
328         checkCksum = sconn->cksumSeen;
329         if (sconn && sconn->authenticated &&
330             (osi_Time() < sconn->expirationTime)) {
331             level = sconn->level;
332             LOCK_RXKAD_STATS
333             rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_server, level)]++;
334             UNLOCK_RXKAD_STATS
335             sconn->stats.packetsReceived++;
336             sconn->stats.bytesReceived += len;
337             schedule = (fc_KeySchedule *)sconn->keysched;
338             ivec = (fc_InitializationVector *)sconn->ivec;
339         }
340         else {
341             LOCK_RXKAD_STATS
342             rxkad_stats.expired++;
343             UNLOCK_RXKAD_STATS
344             return RXKADEXPIRED;
345         }
346         preSeq = sconn->preSeq;
347     }
348     else {                              /* client connection */
349         struct rxkad_cconn *cconn;
350         struct rxkad_cprivate *tcp;
351         cconn = (struct rxkad_cconn *) tconn->securityData;
352         if (rx_GetPacketCksum(apacket) != 0) cconn->cksumSeen = 1;
353         checkCksum = cconn->cksumSeen;
354         tcp = (struct rxkad_cprivate *) aobj->privateData;
355         if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
356         level = tcp->level;
357         LOCK_RXKAD_STATS
358         rxkad_stats.checkPackets[rxkad_StatIndex(rxkad_client, level)]++;
359         UNLOCK_RXKAD_STATS
360         cconn->stats.packetsReceived++;
361         cconn->stats.bytesReceived += len;
362         preSeq = cconn->preSeq;
363         schedule = (fc_KeySchedule *)tcp->keysched;
364         ivec = (fc_InitializationVector *)tcp->ivec;
365     }
366     
367     if (checkCksum) {
368         code = ComputeSum(apacket, schedule, preSeq);
369         if (code != rx_GetPacketCksum(apacket))
370             return RXKADSEALEDINCON;
371     }
372
373     switch (level) {
374       case rxkad_clear: return 0;       /* shouldn't happen */
375       case rxkad_auth:
376         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
377         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
378                         schedule, DECRYPT);
379         break;
380       case rxkad_crypt:
381         code = rxkad_DecryptPacket (tconn, schedule, ivec, len, apacket);
382         if (code) return code;
383         break;
384     }
385     word = ntohl(rx_GetInt32(apacket,0)); /* get first sealed word */
386     if ((word >> 16) !=
387         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
388         return RXKADSEALEDINCON;
389     nlen = word & 0xffff;               /* get real user data length */
390
391     /* The sealed length should be no larger than the initial length, since the  
392      * reverse (round-up) occurs in ...PreparePacket */
393     if (nlen > len)                     
394       return RXKADDATALEN;              
395     rx_SetDataSize (apacket, nlen);
396     return 0;
397 }
398
399 /* either: encode packet */
400
401 rxs_return_t rxkad_PreparePacket (aobj, acall, apacket)
402   struct rx_securityClass *aobj;
403   struct rx_call *acall;
404   struct rx_packet *apacket;
405 {
406     struct rx_connection *tconn;
407     rxkad_level         level;
408     fc_KeySchedule *schedule;
409     fc_InitializationVector *ivec;
410     int len;
411     int nlen;
412     int word;
413     afs_int32 code;
414     afs_int32 *preSeq;
415
416     tconn = rx_ConnectionOf(acall);
417     len = rx_GetDataSize (apacket);
418     if (rx_IsServerConn(tconn)) {
419         struct rxkad_sconn *sconn;
420         sconn = (struct rxkad_sconn *) tconn->securityData;
421         if (sconn && sconn->authenticated &&
422             (osi_Time() < sconn->expirationTime)) {
423             level = sconn->level;
424             LOCK_RXKAD_STATS
425             rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_server, level)]++;
426             UNLOCK_RXKAD_STATS
427             sconn->stats.packetsSent++;
428             sconn->stats.bytesSent += len;
429             schedule = (fc_KeySchedule *)sconn->keysched;
430             ivec = (fc_InitializationVector *)sconn->ivec;
431         }
432         else {
433             LOCK_RXKAD_STATS
434             rxkad_stats.expired++;      /* this is a pretty unlikely path... */
435             UNLOCK_RXKAD_STATS
436             return RXKADEXPIRED;
437         }
438         preSeq = sconn->preSeq;
439     }
440     else {                              /* client connection */
441         struct rxkad_cconn *cconn;
442         struct rxkad_cprivate *tcp;
443         cconn = (struct rxkad_cconn *) tconn->securityData;
444         tcp = (struct rxkad_cprivate *) aobj->privateData;
445         if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
446         level = tcp->level;
447         LOCK_RXKAD_STATS
448         rxkad_stats.preparePackets[rxkad_StatIndex(rxkad_client, level)]++;
449         UNLOCK_RXKAD_STATS
450         cconn->stats.packetsSent++;
451         cconn->stats.bytesSent += len;
452         preSeq = cconn->preSeq;
453         schedule = (fc_KeySchedule *)tcp->keysched;
454         ivec = (fc_InitializationVector *)tcp->ivec;
455     }
456
457     /* compute upward compatible checksum */
458     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
459     if (level == rxkad_clear) return 0;
460
461     len = rx_GetDataSize (apacket);
462     word = (((apacket->header.seq ^ apacket->header.callNumber)
463              & 0xffff) << 16) | (len & 0xffff);
464     rx_PutInt32(apacket,0, htonl(word));   
465
466     switch (level) {
467       case rxkad_clear: return 0;       /* shouldn't happen */
468       case rxkad_auth:
469         nlen = max (ENCRYPTIONBLOCKSIZE,
470                     len + rx_GetSecurityHeaderSize(tconn));
471         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
472           rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
473         }
474         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
475         fc_ecb_encrypt (rx_DataOf(apacket), rx_DataOf(apacket),
476                         schedule, ENCRYPT);
477         break;
478       case rxkad_crypt:
479         nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
480         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
481           rxi_RoundUpPacket(apacket, nlen - (len + rx_GetSecurityHeaderSize(tconn)));
482         }
483         code = rxkad_EncryptPacket (tconn, schedule, ivec, nlen, apacket);
484         if (code) return code;
485         break;
486     }
487     rx_SetDataSize (apacket, nlen);
488     return 0;
489 }
490
491 /* either: return connection stats */
492
493 rxs_return_t rxkad_GetStats (aobj, aconn, astats)
494   IN struct rx_securityClass *aobj;
495   IN struct rx_connection *aconn;
496   OUT struct rx_securityObjectStats *astats;
497 {
498     astats->type = 3;
499     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
500     if (!aconn->securityData) {
501         astats->flags |= 1;
502         return 0;
503     }
504     if (rx_IsServerConn(aconn)) {
505         struct rxkad_sconn *sconn;
506         sconn = (struct rxkad_sconn *) aconn->securityData;
507         astats->level = sconn->level;
508         if (sconn->authenticated) astats->flags |= 2;
509         if (sconn->cksumSeen) astats->flags |= 8;
510         astats->expires = sconn->expirationTime;
511         astats->bytesReceived = sconn->stats.bytesReceived;
512         astats->packetsReceived = sconn->stats.packetsReceived;
513         astats->bytesSent = sconn->stats.bytesSent;
514         astats->packetsSent = sconn->stats.packetsSent;
515     }
516     else { /* client connection */
517         struct rxkad_cconn *cconn;
518         cconn = (struct rxkad_cconn *) aconn->securityData;
519         if (cconn->cksumSeen) astats->flags |= 8;
520         astats->bytesReceived = cconn->stats.bytesReceived;
521         astats->packetsReceived = cconn->stats.packetsReceived;
522         astats->bytesSent = cconn->stats.bytesSent;
523         astats->packetsSent = cconn->stats.packetsSent;
524     }
525     return 0;
526 }