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