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