death to register
[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
26 #ifdef KERNEL
27 #ifndef UKERNEL
28 #include "afs/stds.h"
29 #include "afs/afs_osi.h"
30 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV) 
31 #include "h/systm.h"
32 #endif
33 #if defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV)
34 #include "h/kernel.h"
35 #endif
36 #include "h/types.h"
37 #include "h/time.h"
38 #ifndef AFS_LINUX22_ENV
39 #include "rpc/types.h"
40 #include "rx/xdr.h"
41 #endif /* AFS_LINUX22_ENV */
42 #else /* !UKERNEL */
43 #include "afs/sysincludes.h"
44 #include "afsincludes.h"
45 #endif /* !UKERNEL */
46 #include "rx/rx.h"
47
48 #else /* KERNEL */
49 #include <afs/stds.h>
50 #include <sys/types.h>
51 #include <string.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 #include <afs/afsutil.h>
64 #endif /* KERNEL */
65
66 #include <des/stats.h>
67 #include "private_data.h"
68 #define XPRT_RXKAD_COMMON
69
70 #ifndef afs_max
71 #define afs_max(a,b)    ((a) < (b)? (b) : (a))
72 #endif /* afs_max */
73
74 #ifndef KERNEL
75 #define osi_Time() time(0)
76 #endif
77 /* variable initialization for the benefit of darwin compiler; if it causes
78    problems elsewhere, conditionalize for darwin or fc_test compile breaks */
79 #ifdef AFS_PTHREAD_ENV
80 struct rxkad_global_stats rxkad_global_stats = { 0 };
81 pthread_mutex_t rxkad_global_stats_lock;
82 pthread_key_t rxkad_stats_key;
83 #else /* AFS_PTHREAD_ENV */
84 #if defined(KERNEL) && !defined(UKERNEL)
85 struct rxkad_stats rxkad_stats = { { 0 } }; 
86 #else
87 /* Move delaration of this to des/key_sched.c */
88 #endif
89 #endif /* AFS_PTHREAD_ENV */
90
91 #ifdef AFS_PTHREAD_ENV
92 /* rxkad_stats related stuff */
93
94 /*
95  * Macro to insert an element at the tail of a doubly linked list
96  */
97 #define DLL_INSERT_TAIL(ptr,head,tail,next,prev) \
98     do {                                         \
99         (ptr)->next = NULL;                      \
100         (ptr)->prev = (tail);                    \
101         (tail) = (ptr);                          \
102         if ((ptr)->prev)                         \
103             (ptr)->prev->next = (ptr);           \
104         else                                     \
105             (head) = (ptr);                      \
106         assert((head) && ((head)->prev == NULL)); \
107     } while(0)
108
109 void rxkad_global_stats_init(void) {
110     assert(pthread_mutex_init(&rxkad_global_stats_lock, (const pthread_mutexattr_t *)0) == 0);
111     assert(pthread_key_create(&rxkad_stats_key, NULL) == 0);
112     memset(&rxkad_global_stats, 0, sizeof(rxkad_global_stats));
113 }
114
115 rxkad_stats_t * 
116 rxkad_thr_stats_init(void) {
117     rxkad_stats_t * rxkad_stats;
118     rxkad_stats = (rxkad_stats_t *)malloc(sizeof(rxkad_stats_t));
119     assert(rxkad_stats != NULL && pthread_setspecific(rxkad_stats_key,rxkad_stats) == 0);
120     memset(rxkad_stats,0,sizeof(rxkad_stats_t));
121     RXKAD_GLOBAL_STATS_LOCK;
122     DLL_INSERT_TAIL(rxkad_stats, rxkad_global_stats.first, rxkad_global_stats.last, next, prev);
123     RXKAD_GLOBAL_STATS_UNLOCK;
124     return rxkad_stats;
125 }
126
127 int rxkad_stats_agg(rxkad_stats_t * rxkad_stats) {
128     rxkad_stats_t * thr_stats;
129     assert(rxkad_stats != NULL);
130     memset(rxkad_stats, 0, sizeof(rxkad_stats_t));
131     RXKAD_GLOBAL_STATS_LOCK;
132     for (thr_stats = rxkad_global_stats.first; thr_stats != NULL; thr_stats = thr_stats->next) {
133         rxkad_stats->connections[0] += thr_stats->connections[0];
134         rxkad_stats->connections[1] += thr_stats->connections[1];
135         rxkad_stats->connections[2] += thr_stats->connections[2];
136         rxkad_stats->destroyObject += thr_stats->destroyObject;
137         rxkad_stats->destroyClient += thr_stats->destroyClient;
138         rxkad_stats->destroyUnused += thr_stats->destroyUnused;
139         rxkad_stats->destroyUnauth += thr_stats->destroyUnauth;
140         rxkad_stats->destroyConn[0] += thr_stats->destroyConn[0];
141         rxkad_stats->destroyConn[1] += thr_stats->destroyConn[1];
142         rxkad_stats->destroyConn[2] += thr_stats->destroyConn[2];
143         rxkad_stats->expired += thr_stats->expired;
144         rxkad_stats->challengesSent += thr_stats->challengesSent;
145         rxkad_stats->challenges[0] += thr_stats->challenges[0];
146         rxkad_stats->challenges[1] += thr_stats->challenges[1];
147         rxkad_stats->challenges[2] += thr_stats->challenges[2];
148         rxkad_stats->responses[0] += thr_stats->responses[0];
149         rxkad_stats->responses[1] += thr_stats->responses[1];
150         rxkad_stats->responses[2] += thr_stats->responses[2];
151         rxkad_stats->preparePackets[0] += thr_stats->preparePackets[0];
152         rxkad_stats->preparePackets[1] += thr_stats->preparePackets[1];
153         rxkad_stats->preparePackets[2] += thr_stats->preparePackets[2];
154         rxkad_stats->preparePackets[3] += thr_stats->preparePackets[3];
155         rxkad_stats->preparePackets[4] += thr_stats->preparePackets[4];
156         rxkad_stats->preparePackets[5] += thr_stats->preparePackets[5];
157         rxkad_stats->checkPackets[0] += thr_stats->checkPackets[0];
158         rxkad_stats->checkPackets[1] += thr_stats->checkPackets[1];
159         rxkad_stats->checkPackets[2] += thr_stats->checkPackets[2];
160         rxkad_stats->checkPackets[3] += thr_stats->checkPackets[3];
161         rxkad_stats->checkPackets[4] += thr_stats->checkPackets[4];
162         rxkad_stats->checkPackets[5] += thr_stats->checkPackets[5];
163         rxkad_stats->bytesEncrypted[0] += thr_stats->bytesEncrypted[0];
164         rxkad_stats->bytesEncrypted[1] += thr_stats->bytesEncrypted[1];
165         rxkad_stats->bytesDecrypted[0] += thr_stats->bytesDecrypted[0];
166         rxkad_stats->bytesDecrypted[1] += thr_stats->bytesDecrypted[1];
167         rxkad_stats->fc_encrypts[0] += thr_stats->fc_encrypts[0];
168         rxkad_stats->fc_encrypts[1] += thr_stats->fc_encrypts[1];
169         rxkad_stats->fc_key_scheds += thr_stats->fc_key_scheds;
170         rxkad_stats->des_encrypts[0] += thr_stats->des_encrypts[0];
171         rxkad_stats->des_encrypts[1] += thr_stats->des_encrypts[1];
172         rxkad_stats->des_key_scheds += thr_stats->des_key_scheds;
173         rxkad_stats->des_randoms += thr_stats->des_randoms;
174         rxkad_stats->clientObjects += thr_stats->clientObjects;
175         rxkad_stats->serverObjects += thr_stats->serverObjects;
176         rxkad_stats->spares[0] += thr_stats->spares[0];
177         rxkad_stats->spares[1] += thr_stats->spares[1];
178         rxkad_stats->spares[2] += thr_stats->spares[2];
179         rxkad_stats->spares[3] += thr_stats->spares[3];
180         rxkad_stats->spares[4] += thr_stats->spares[4];
181         rxkad_stats->spares[5] += thr_stats->spares[5];
182         rxkad_stats->spares[6] += thr_stats->spares[6];
183         rxkad_stats->spares[7] += thr_stats->spares[7];
184     }
185     RXKAD_GLOBAL_STATS_UNLOCK;
186     return 0;
187 }
188 #endif /* AFS_PTHREAD_ENV */
189
190 /* static prototypes */
191 static afs_int32 ComputeSum(struct rx_packet *apacket,
192                             fc_KeySchedule * aschedule, afs_int32 * aivec);
193 static afs_int32 FreeObject(struct rx_securityClass *aobj);
194
195 /* this call sets up an endpoint structure, leaving it in *network* byte
196  * order so that it can be used quickly for encryption.
197  */
198 int
199 rxkad_SetupEndpoint(struct rx_connection *aconnp,
200                     struct rxkad_endpoint *aendpointp)
201 {
202     afs_int32 i;
203
204     aendpointp->cuid[0] = htonl(aconnp->epoch);
205     i = aconnp->cid & RX_CIDMASK;
206     aendpointp->cuid[1] = htonl(i);
207     aendpointp->cksum = 0;      /* used as cksum only in chal resp. */
208     aendpointp->securityIndex = htonl(aconnp->securityIndex);
209     return 0;
210 }
211
212 /* setup xor information based on session key */
213 int
214 rxkad_DeriveXORInfo(struct rx_connection *aconnp, fc_KeySchedule * aschedule,
215                     char *aivec, char *aresult)
216 {
217     struct rxkad_endpoint tendpoint;
218     afs_uint32 xor[2];
219
220     rxkad_SetupEndpoint(aconnp, &tendpoint);
221     memcpy((void *)xor, aivec, 2 * sizeof(afs_int32));
222     fc_cbc_encrypt(&tendpoint, &tendpoint, sizeof(tendpoint), *aschedule, xor,
223                    ENCRYPT);
224     memcpy(aresult,
225            ((char *)&tendpoint) + sizeof(tendpoint) - ENCRYPTIONBLOCKSIZE,
226            ENCRYPTIONBLOCKSIZE);
227     return 0;
228 }
229
230 /* rxkad_CksumChallengeResponse - computes a checksum of the components of a
231  * challenge response packet (which must be unencrypted and in network order).
232  * The endpoint.cksum field is omitted and treated as zero.  The cksum is
233  * returned in network order. */
234
235 afs_uint32
236 rxkad_CksumChallengeResponse(struct rxkad_v2ChallengeResponse * v2r)
237 {
238     int i;
239     afs_uint32 cksum;
240     u_char *cp = (u_char *) v2r;
241     afs_uint32 savedCksum = v2r->encrypted.endpoint.cksum;
242
243     v2r->encrypted.endpoint.cksum = 0;
244
245     /* this function captured from budb/db_hash.c */
246     cksum = 1000003;
247     for (i = 0; i < sizeof(*v2r); i++)
248         cksum = (*cp++) + cksum * 0x10204081;
249
250     v2r->encrypted.endpoint.cksum = savedCksum;
251     return htonl(cksum);
252 }
253
254 void
255 rxkad_SetLevel(struct rx_connection *conn, rxkad_level level)
256 {
257     if (level == rxkad_auth) {
258         rx_SetSecurityHeaderSize(conn, 4);
259         rx_SetSecurityMaxTrailerSize(conn, 4);
260     } else if (level == rxkad_crypt) {
261         rx_SetSecurityHeaderSize(conn, 8);
262         rx_SetSecurityMaxTrailerSize(conn, 8);  /* XXX was 7, but why screw with 
263                                                  * unaligned accesses? */
264     }
265 }
266
267 /* returns a short integer in host byte order representing a good checksum of
268  * the packet header.
269  */
270 static afs_int32
271 ComputeSum(struct rx_packet *apacket, fc_KeySchedule * aschedule,
272            afs_int32 * aivec)
273 {
274     afs_uint32 word[2];
275     afs_uint32 t;
276
277     t = apacket->header.callNumber;
278     word[0] = htonl(t);
279     /* note that word [1] includes the channel # */
280     t = ((apacket->header.cid & 0x3) << 30)
281         | ((apacket->header.seq & 0x3fffffff));
282     word[1] = htonl(t);
283     /* XOR in the ivec from the per-endpoint encryption */
284     word[0] ^= aivec[0];
285     word[1] ^= aivec[1];
286     /* encrypts word as if it were a character string */
287     fc_ecb_encrypt(word, word, *aschedule, ENCRYPT);
288     t = ntohl(word[1]);
289     t = (t >> 16) & 0xffff;
290     if (t == 0)
291         t = 1;                  /* so that 0 means don't care */
292     return t;
293 }
294
295
296 static afs_int32
297 FreeObject(struct rx_securityClass *aobj)
298 {
299     struct rxkad_cprivate *tcp; /* both structs start w/ type field */
300
301     if (aobj->refCount > 0)
302         return 0;               /* still in use */
303     tcp = (struct rxkad_cprivate *)aobj->privateData;
304     rxi_Free(aobj, sizeof(struct rx_securityClass));
305     if (tcp->type & rxkad_client) {
306         afs_int32 psize = PDATA_SIZE(tcp->ticketLen);
307         rxi_Free(tcp, psize);
308     } else if (tcp->type & rxkad_server) {
309         rxi_Free(tcp, sizeof(struct rxkad_sprivate));
310     } else {
311         return RXKADINCONSISTENCY;
312     }                           /* unknown type */
313     INC_RXKAD_STATS(destroyObject);
314     return 0;
315 }
316
317 /* rxkad_Close - called by rx with the security class object as a parameter
318  * when a security object is to be discarded */
319
320 int
321 rxkad_Close(struct rx_securityClass *aobj)
322 {
323     afs_int32 code;
324     aobj->refCount--;
325     code = FreeObject(aobj);
326     return code;
327 }
328
329 /* either: called to (re)create a new connection. */
330
331 int
332 rxkad_NewConnection(struct rx_securityClass *aobj,
333                     struct rx_connection *aconn)
334 {
335     if (aconn->securityData)
336         return RXKADINCONSISTENCY;      /* already allocated??? */
337
338     if (rx_IsServerConn(aconn)) {
339         int size = sizeof(struct rxkad_sconn);
340         aconn->securityData = (char *)rxi_Alloc(size);
341         memset(aconn->securityData, 0, size);   /* initialize it conveniently */
342     } else {                    /* client */
343         struct rxkad_cprivate *tcp;
344         struct rxkad_cconn *tccp;
345         int size = sizeof(struct rxkad_cconn);
346         tccp = (struct rxkad_cconn *)rxi_Alloc(size);
347         aconn->securityData = (char *)tccp;
348         memset(aconn->securityData, 0, size);   /* initialize it conveniently */
349         tcp = (struct rxkad_cprivate *)aobj->privateData;
350         if (!(tcp->type & rxkad_client))
351             return RXKADINCONSISTENCY;
352         rxkad_SetLevel(aconn, tcp->level);      /* set header and trailer sizes */
353         rxkad_AllocCID(aobj, aconn);    /* CHANGES cid AND epoch!!!! */
354         rxkad_DeriveXORInfo(aconn, (fc_KeySchedule *)tcp->keysched, (char *)tcp->ivec, (char *)tccp->preSeq);
355         INC_RXKAD_STATS(connections[rxkad_LevelIndex(tcp->level)]);
356     }
357
358     aobj->refCount++;           /* attached connection */
359     return 0;
360 }
361
362 /* either: called to destroy a connection. */
363
364 int
365 rxkad_DestroyConnection(struct rx_securityClass *aobj,
366                         struct rx_connection *aconn)
367 {
368     if (rx_IsServerConn(aconn)) {
369         struct rxkad_sconn *sconn;
370         struct rxkad_serverinfo *rock;
371         sconn = (struct rxkad_sconn *)aconn->securityData;
372         if (sconn) {
373             aconn->securityData = 0;
374             if (sconn->authenticated)
375                 INC_RXKAD_STATS(destroyConn[rxkad_LevelIndex(sconn->level)]);
376             else
377                 INC_RXKAD_STATS(destroyUnauth);
378             rock = sconn->rock;
379             if (rock)
380                 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
381             rxi_Free(sconn, sizeof(struct rxkad_sconn));
382         } else {
383             INC_RXKAD_STATS(destroyUnused);
384         }
385     } else {                    /* client */
386         struct rxkad_cconn *cconn;
387         struct rxkad_cprivate *tcp;
388         cconn = (struct rxkad_cconn *)aconn->securityData;
389         tcp = (struct rxkad_cprivate *)aobj->privateData;
390         if (!(tcp->type & rxkad_client))
391             return RXKADINCONSISTENCY;
392         if (cconn) {
393             aconn->securityData = 0;
394             rxi_Free(cconn, sizeof(struct rxkad_cconn));
395         }
396         INC_RXKAD_STATS(destroyClient);
397     }
398     aobj->refCount--;           /* decrement connection counter */
399     if (aobj->refCount <= 0) {
400         afs_int32 code;
401         code = FreeObject(aobj);
402         if (code)
403             return code;
404     }
405     return 0;
406 }
407
408 /* either: decode packet */
409
410 int
411 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
412                   struct rx_packet *apacket)
413 {
414     struct rx_connection *tconn;
415     rxkad_level level;
416     const fc_KeySchedule *schedule;
417     fc_InitializationVector *ivec;
418     int len;
419     int nlen = 0;
420     u_int word;                 /* so we get unsigned right-shift */
421     int checkCksum;
422     afs_int32 *preSeq;
423     afs_int32 code;
424
425     tconn = rx_ConnectionOf(acall);
426     len = rx_GetDataSize(apacket);
427     checkCksum = 0;             /* init */
428     if (rx_IsServerConn(tconn)) {
429         struct rxkad_sconn *sconn;
430         sconn = (struct rxkad_sconn *)tconn->securityData;
431         if (rx_GetPacketCksum(apacket) != 0)
432             sconn->cksumSeen = 1;
433         checkCksum = sconn->cksumSeen;
434         if (sconn && sconn->authenticated
435             && (osi_Time() < sconn->expirationTime)) {
436             level = sconn->level;
437             INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_server, level)]);
438             sconn->stats.packetsReceived++;
439             sconn->stats.bytesReceived += len;
440             schedule = (const fc_KeySchedule *) sconn->keysched;
441             ivec = (fc_InitializationVector *) sconn->ivec;
442         } else {
443             INC_RXKAD_STATS(expired);
444             return RXKADEXPIRED;
445         }
446         preSeq = sconn->preSeq;
447     } else {                    /* client connection */
448         struct rxkad_cconn *cconn;
449         struct rxkad_cprivate *tcp;
450         cconn = (struct rxkad_cconn *)tconn->securityData;
451         if (rx_GetPacketCksum(apacket) != 0)
452             cconn->cksumSeen = 1;
453         checkCksum = cconn->cksumSeen;
454         tcp = (struct rxkad_cprivate *)aobj->privateData;
455         if (!(tcp->type & rxkad_client))
456             return RXKADINCONSISTENCY;
457         level = tcp->level;
458         INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_client, level)]);
459         cconn->stats.packetsReceived++;
460         cconn->stats.bytesReceived += len;
461         preSeq = cconn->preSeq;
462         schedule = (const fc_KeySchedule *) tcp->keysched;
463         ivec = (fc_InitializationVector *) tcp->ivec;
464     }
465
466     if (checkCksum) {
467         code = ComputeSum(apacket, (fc_KeySchedule *)schedule, preSeq);
468         if (code != rx_GetPacketCksum(apacket))
469             return RXKADSEALEDINCON;
470     }
471
472     switch (level) {
473     case rxkad_clear:
474         return 0;               /* shouldn't happen */
475     case rxkad_auth:
476         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
477         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
478                        DECRYPT);
479         break;
480     case rxkad_crypt:
481         code = rxkad_DecryptPacket(tconn, schedule, (const fc_InitializationVector *)ivec, len, apacket);
482         if (code)
483             return code;
484         break;
485     }
486     word = ntohl(rx_GetInt32(apacket, 0));      /* get first sealed word */
487     if ((word >> 16) !=
488         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
489         return RXKADSEALEDINCON;
490     nlen = word & 0xffff;       /* get real user data length */
491
492     /* The sealed length should be no larger than the initial length, since the  
493      * reverse (round-up) occurs in ...PreparePacket */
494     if (nlen > len)
495         return RXKADDATALEN;
496     rx_SetDataSize(apacket, nlen);
497     return 0;
498 }
499
500 /* either: encode packet */
501
502 int
503 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
504                     struct rx_packet *apacket)
505 {
506     struct rx_connection *tconn;
507     rxkad_level level;
508     fc_KeySchedule *schedule;
509     fc_InitializationVector *ivec;
510     int len;
511     int nlen = 0;
512     int word;
513     afs_int32 code;
514     afs_int32 *preSeq;
515
516     tconn = rx_ConnectionOf(acall);
517     len = rx_GetDataSize(apacket);
518     if (rx_IsServerConn(tconn)) {
519         struct rxkad_sconn *sconn;
520         sconn = (struct rxkad_sconn *)tconn->securityData;
521         if (sconn && sconn->authenticated
522             && (osi_Time() < sconn->expirationTime)) {
523             level = sconn->level;
524             INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_server, level)]);
525             sconn->stats.packetsSent++;
526             sconn->stats.bytesSent += len;
527             schedule = (fc_KeySchedule *) sconn->keysched;
528             ivec = (fc_InitializationVector *) sconn->ivec;
529         } else {
530             INC_RXKAD_STATS(expired);   /* this is a pretty unlikely path... */
531             return RXKADEXPIRED;
532         }
533         preSeq = sconn->preSeq;
534     } else {                    /* client connection */
535         struct rxkad_cconn *cconn;
536         struct rxkad_cprivate *tcp;
537         cconn = (struct rxkad_cconn *)tconn->securityData;
538         tcp = (struct rxkad_cprivate *)aobj->privateData;
539         if (!(tcp->type & rxkad_client))
540             return RXKADINCONSISTENCY;
541         level = tcp->level;
542         INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_client, level)]);
543         cconn->stats.packetsSent++;
544         cconn->stats.bytesSent += len;
545         preSeq = cconn->preSeq;
546         schedule = (fc_KeySchedule *) tcp->keysched;
547         ivec = (fc_InitializationVector *) tcp->ivec;
548     }
549
550     /* compute upward compatible checksum */
551     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
552     if (level == rxkad_clear)
553         return 0;
554
555     len = rx_GetDataSize(apacket);
556     word = (((apacket->header.seq ^ apacket->header.callNumber)
557              & 0xffff) << 16) | (len & 0xffff);
558     rx_PutInt32(apacket, 0, htonl(word));
559
560     switch (level) {
561     case rxkad_clear:
562         return 0;               /* shouldn't happen */
563     case rxkad_auth:
564         nlen =
565             afs_max(ENCRYPTIONBLOCKSIZE,
566                     len + rx_GetSecurityHeaderSize(tconn));
567         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
568             rxi_RoundUpPacket(apacket,
569                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
570         }
571         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
572         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
573                        ENCRYPT);
574         break;
575     case rxkad_crypt:
576         nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
577         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
578             rxi_RoundUpPacket(apacket,
579                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
580         }
581         code = rxkad_EncryptPacket(tconn, (const fc_KeySchedule *)schedule,  (const fc_InitializationVector *)ivec, nlen, apacket);
582         if (code)
583             return code;
584         break;
585     }
586     rx_SetDataSize(apacket, nlen);
587     return 0;
588 }
589
590 /* either: return connection stats */
591
592 int
593 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
594                struct rx_securityObjectStats *astats)
595 {
596     astats->type = 3;
597     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
598     if (!aconn->securityData) {
599         astats->flags |= 1;
600         return 0;
601     }
602     if (rx_IsServerConn(aconn)) {
603         struct rxkad_sconn *sconn;
604         sconn = (struct rxkad_sconn *)aconn->securityData;
605         astats->level = sconn->level;
606         if (sconn->authenticated)
607             astats->flags |= 2;
608         if (sconn->cksumSeen)
609             astats->flags |= 8;
610         astats->expires = sconn->expirationTime;
611         astats->bytesReceived = sconn->stats.bytesReceived;
612         astats->packetsReceived = sconn->stats.packetsReceived;
613         astats->bytesSent = sconn->stats.bytesSent;
614         astats->packetsSent = sconn->stats.packetsSent;
615     } else {                    /* client connection */
616         struct rxkad_cconn *cconn;
617         cconn = (struct rxkad_cconn *)aconn->securityData;
618         if (cconn->cksumSeen)
619             astats->flags |= 8;
620         astats->bytesReceived = cconn->stats.bytesReceived;
621         astats->packetsReceived = cconn->stats.packetsReceived;
622         astats->bytesSent = cconn->stats.bytesSent;
623         astats->packetsSent = cconn->stats.packetsSent;
624     }
625     return 0;
626 }
627
628 rxkad_level
629 rxkad_StringToLevel(char *name)
630 {
631     if (strcmp(name, "clear") == 0)
632         return rxkad_clear;
633     if (strcmp(name, "auth") == 0)
634         return rxkad_auth;
635     if (strcmp(name, "crypt") == 0)
636         return rxkad_crypt;
637     return -1;
638 }
639
640 char *
641 rxkad_LevelToString(rxkad_level level)
642 {
643     if (level == rxkad_clear)
644         return "clear";
645     if (level == rxkad_auth)
646         return "auth";
647     if (level == rxkad_crypt)
648         return "crypt";
649     return "unknown";
650 }