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