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