484909df36fc71e284b26039d50f522d0c378f2f
[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 #include <afs/param.h>
14 #include <afs/stds.h>
15
16 #ifdef AFS_SUN59_ENV
17 #include <sys/time_impl.h>
18 #endif
19
20 #define INCLUDE_RXKAD_PRIVATE_DECLS
21
22 #ifdef KERNEL
23 #ifndef UKERNEL
24 #include "afs/afs_osi.h"
25 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
26 #include "h/systm.h"
27 #endif
28 #if defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV)
29 #include "h/kernel.h"
30 #endif
31 #include "h/types.h"
32 #include "h/time.h"
33 #else /* !UKERNEL */
34 #include "afs/sysincludes.h"
35 #include "afsincludes.h"
36 #endif /* !UKERNEL */
37 #else /* KERNEL */
38 #include <roken.h>
39 #include <afs/opr.h>
40 #if defined(AFS_NT40_ENV) && defined(AFS_PTHREAD_ENV)
41 #define RXKAD_STATS_DECLSPEC __declspec(dllexport)
42 #endif
43 #endif /* KERNEL */
44
45 #include <rx/rx.h>
46 #include <rx/rx_packet.h>
47 #include <rx/xdr.h>
48
49 #include "stats.h"
50 #include "private_data.h"
51 #define XPRT_RXKAD_COMMON
52
53 #ifndef afs_max
54 #define afs_max(a,b)    ((a) < (b)? (b) : (a))
55 #endif /* afs_max */
56
57 #ifndef KERNEL
58 #define osi_Time() time(0)
59 #endif
60 /* variable initialization for the benefit of darwin compiler; if it causes
61    problems elsewhere, conditionalize for darwin or fc_test compile breaks */
62 #ifdef AFS_PTHREAD_ENV
63 struct rxkad_global_stats rxkad_global_stats = { 0 };
64 pthread_mutex_t rxkad_global_stats_lock;
65 pthread_key_t rxkad_stats_key;
66 #else /* AFS_PTHREAD_ENV */
67 struct rxkad_stats rxkad_stats = { { 0 } };
68 #endif /* AFS_PTHREAD_ENV */
69
70 #ifdef AFS_PTHREAD_ENV
71 /* Pthread initialisation */
72 static pthread_once_t rxkad_once_init = PTHREAD_ONCE_INIT;
73 extern pthread_mutex_t rxkad_client_uid_mutex;
74 extern pthread_mutex_t rxkad_random_mutex;
75
76 static void
77 rxkad_global_stats_init(void)
78 {
79     osi_Assert(pthread_mutex_init(&rxkad_global_stats_lock, (const pthread_mutexattr_t *)0) == 0);
80     osi_Assert(pthread_key_create(&rxkad_stats_key, NULL) == 0);
81     memset(&rxkad_global_stats, 0, sizeof(rxkad_global_stats));
82 }
83
84 static void
85 rxkad_InitPthread(void) {
86     MUTEX_INIT(&rxkad_client_uid_mutex, "uid", MUTEX_DEFAULT, 0);
87     MUTEX_INIT(&rxkad_random_mutex, "rxkad random", MUTEX_DEFAULT, 0);
88
89     rxkad_global_stats_init();
90 }
91
92 void
93 rxkad_Init(void) {
94     osi_Assert(pthread_once(&rxkad_once_init, rxkad_InitPthread) == 0);
95 }
96
97 /* rxkad_stats related stuff */
98
99 /*
100  * Macro to insert an element at the tail of a doubly linked list
101  */
102 #define DLL_INSERT_TAIL(ptr,head,tail,next,prev) \
103     do {                                         \
104         (ptr)->next = NULL;                      \
105         (ptr)->prev = (tail);                    \
106         (tail) = (ptr);                          \
107         if ((ptr)->prev)                         \
108             (ptr)->prev->next = (ptr);           \
109         else                                     \
110             (head) = (ptr);                      \
111         osi_Assert((head) && ((head)->prev == NULL)); \
112     } while(0)
113
114 rxkad_stats_t *
115 rxkad_thr_stats_init(void) {
116     rxkad_stats_t * rxkad_stats;
117     rxkad_stats = calloc(1, sizeof(rxkad_stats_t));
118     osi_Assert(rxkad_stats != NULL && pthread_setspecific(rxkad_stats_key,rxkad_stats) == 0);
119     RXKAD_GLOBAL_STATS_LOCK;
120     DLL_INSERT_TAIL(rxkad_stats, rxkad_global_stats.first, rxkad_global_stats.last, next, prev);
121     RXKAD_GLOBAL_STATS_UNLOCK;
122     return rxkad_stats;
123 }
124
125 int rxkad_stats_agg(rxkad_stats_t * rxkad_stats) {
126     rxkad_stats_t * thr_stats;
127     osi_Assert(rxkad_stats != NULL);
128     memset(rxkad_stats, 0, sizeof(rxkad_stats_t));
129     RXKAD_GLOBAL_STATS_LOCK;
130     for (thr_stats = rxkad_global_stats.first; thr_stats != NULL; thr_stats = thr_stats->next) {
131         rxkad_stats->connections[0] += thr_stats->connections[0];
132         rxkad_stats->connections[1] += thr_stats->connections[1];
133         rxkad_stats->connections[2] += thr_stats->connections[2];
134         rxkad_stats->destroyObject += thr_stats->destroyObject;
135         rxkad_stats->destroyClient += thr_stats->destroyClient;
136         rxkad_stats->destroyUnused += thr_stats->destroyUnused;
137         rxkad_stats->destroyUnauth += thr_stats->destroyUnauth;
138         rxkad_stats->destroyConn[0] += thr_stats->destroyConn[0];
139         rxkad_stats->destroyConn[1] += thr_stats->destroyConn[1];
140         rxkad_stats->destroyConn[2] += thr_stats->destroyConn[2];
141         rxkad_stats->expired += thr_stats->expired;
142         rxkad_stats->challengesSent += thr_stats->challengesSent;
143         rxkad_stats->challenges[0] += thr_stats->challenges[0];
144         rxkad_stats->challenges[1] += thr_stats->challenges[1];
145         rxkad_stats->challenges[2] += thr_stats->challenges[2];
146         rxkad_stats->responses[0] += thr_stats->responses[0];
147         rxkad_stats->responses[1] += thr_stats->responses[1];
148         rxkad_stats->responses[2] += thr_stats->responses[2];
149         rxkad_stats->preparePackets[0] += thr_stats->preparePackets[0];
150         rxkad_stats->preparePackets[1] += thr_stats->preparePackets[1];
151         rxkad_stats->preparePackets[2] += thr_stats->preparePackets[2];
152         rxkad_stats->preparePackets[3] += thr_stats->preparePackets[3];
153         rxkad_stats->preparePackets[4] += thr_stats->preparePackets[4];
154         rxkad_stats->preparePackets[5] += thr_stats->preparePackets[5];
155         rxkad_stats->checkPackets[0] += thr_stats->checkPackets[0];
156         rxkad_stats->checkPackets[1] += thr_stats->checkPackets[1];
157         rxkad_stats->checkPackets[2] += thr_stats->checkPackets[2];
158         rxkad_stats->checkPackets[3] += thr_stats->checkPackets[3];
159         rxkad_stats->checkPackets[4] += thr_stats->checkPackets[4];
160         rxkad_stats->checkPackets[5] += thr_stats->checkPackets[5];
161         rxkad_stats->bytesEncrypted[0] += thr_stats->bytesEncrypted[0];
162         rxkad_stats->bytesEncrypted[1] += thr_stats->bytesEncrypted[1];
163         rxkad_stats->bytesDecrypted[0] += thr_stats->bytesDecrypted[0];
164         rxkad_stats->bytesDecrypted[1] += thr_stats->bytesDecrypted[1];
165         rxkad_stats->fc_encrypts[0] += thr_stats->fc_encrypts[0];
166         rxkad_stats->fc_encrypts[1] += thr_stats->fc_encrypts[1];
167         rxkad_stats->fc_key_scheds += thr_stats->fc_key_scheds;
168         rxkad_stats->des_encrypts[0] += thr_stats->des_encrypts[0];
169         rxkad_stats->des_encrypts[1] += thr_stats->des_encrypts[1];
170         rxkad_stats->des_key_scheds += thr_stats->des_key_scheds;
171         rxkad_stats->des_randoms += thr_stats->des_randoms;
172         rxkad_stats->clientObjects += thr_stats->clientObjects;
173         rxkad_stats->serverObjects += thr_stats->serverObjects;
174         rxkad_stats->spares[0] += thr_stats->spares[0];
175         rxkad_stats->spares[1] += thr_stats->spares[1];
176         rxkad_stats->spares[2] += thr_stats->spares[2];
177         rxkad_stats->spares[3] += thr_stats->spares[3];
178         rxkad_stats->spares[4] += thr_stats->spares[4];
179         rxkad_stats->spares[5] += thr_stats->spares[5];
180         rxkad_stats->spares[6] += thr_stats->spares[6];
181         rxkad_stats->spares[7] += thr_stats->spares[7];
182     }
183     RXKAD_GLOBAL_STATS_UNLOCK;
184     return 0;
185 }
186 #else
187 void
188 rxkad_Init(void)
189 {
190     return;
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     afs_int32 i;
207
208     aendpointp->cuid[0] = htonl(rx_GetConnectionEpoch(aconnp));
209     i = rx_GetConnectionId(aconnp) & RX_CIDMASK;
210     aendpointp->cuid[1] = htonl(i);
211     aendpointp->cksum = 0;      /* used as cksum only in chal resp. */
212     aendpointp->securityIndex = htonl(rx_SecurityClassOf(aconnp));
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     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         afs_int32 psize = PDATA_SIZE(tcp->ticketLen);
311         rxi_Free(tcp, psize);
312     } else if (tcp->type & rxkad_server) {
313         rxi_Free(tcp, sizeof(struct rxkad_sprivate));
314     } else {
315         return RXKADINCONSISTENCY;
316     }                           /* unknown type */
317     INC_RXKAD_STATS(destroyObject);
318     return 0;
319 }
320
321 /* rxkad_Close - called by rx with the security class object as a parameter
322  * when a security object is to be discarded */
323
324 int
325 rxkad_Close(struct rx_securityClass *aobj)
326 {
327     afs_int32 code;
328     aobj->refCount--;
329     code = FreeObject(aobj);
330     return code;
331 }
332
333 /* either: called to (re)create a new connection. */
334
335 int
336 rxkad_NewConnection(struct rx_securityClass *aobj,
337                     struct rx_connection *aconn)
338 {
339     if (rx_GetSecurityData(aconn) != NULL)
340         return RXKADINCONSISTENCY;      /* already allocated??? */
341
342     if (rx_IsServerConn(aconn)) {
343         struct rxkad_sconn *data;
344         data = rxi_Alloc(sizeof(struct rxkad_sconn));
345         memset(data, 0, sizeof(struct rxkad_sconn));
346         rx_SetSecurityData(aconn, data);
347     } else {                    /* client */
348         struct rxkad_cprivate *tcp;
349         struct rxkad_cconn *data;
350
351         data = rxi_Alloc(sizeof(struct rxkad_cconn));
352         memset(data, 0, sizeof(struct rxkad_cconn));
353         rx_SetSecurityData(aconn, data);
354
355         tcp = (struct rxkad_cprivate *)aobj->privateData;
356         if (!(tcp->type & rxkad_client))
357             return RXKADINCONSISTENCY;
358         rxkad_SetLevel(aconn, tcp->level);      /* set header and trailer sizes */
359         rxkad_AllocCID(aobj, aconn);    /* CHANGES cid AND epoch!!!! */
360         rxkad_DeriveXORInfo(aconn, (fc_KeySchedule *)tcp->keysched, (char *)tcp->ivec, (char *)data->preSeq);
361         INC_RXKAD_STATS(connections[rxkad_LevelIndex(tcp->level)]);
362     }
363
364     aobj->refCount++;           /* attached connection */
365     return 0;
366 }
367
368 /* either: called to destroy a connection. */
369
370 int
371 rxkad_DestroyConnection(struct rx_securityClass *aobj,
372                         struct rx_connection *aconn)
373 {
374     if (rx_IsServerConn(aconn)) {
375         struct rxkad_sconn *sconn;
376         struct rxkad_serverinfo *rock;
377         sconn = rx_GetSecurityData(aconn);
378         if (sconn) {
379             rx_SetSecurityData(aconn, NULL);
380             if (sconn->authenticated)
381                 INC_RXKAD_STATS(destroyConn[rxkad_LevelIndex(sconn->level)]);
382             else
383                 INC_RXKAD_STATS(destroyUnauth);
384             rock = sconn->rock;
385             if (rock)
386                 rxi_Free(rock, sizeof(struct rxkad_serverinfo));
387             rxi_Free(sconn, sizeof(struct rxkad_sconn));
388         } else {
389             INC_RXKAD_STATS(destroyUnused);
390         }
391     } else {                    /* client */
392         struct rxkad_cconn *cconn;
393         struct rxkad_cprivate *tcp;
394         cconn = rx_GetSecurityData(aconn);
395         tcp = (struct rxkad_cprivate *)aobj->privateData;
396         if (!(tcp->type & rxkad_client))
397             return RXKADINCONSISTENCY;
398         if (cconn) {
399             rx_SetSecurityData(aconn, NULL);
400             rxi_Free(cconn, sizeof(struct rxkad_cconn));
401         }
402         INC_RXKAD_STATS(destroyClient);
403     }
404     aobj->refCount--;           /* decrement connection counter */
405     if (aobj->refCount <= 0) {
406         afs_int32 code;
407         code = FreeObject(aobj);
408         if (code)
409             return code;
410     }
411     return 0;
412 }
413
414 /* either: decode packet */
415
416 int
417 rxkad_CheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
418                   struct rx_packet *apacket)
419 {
420     struct rx_connection *tconn;
421     rxkad_level level;
422     const fc_KeySchedule *schedule;
423     fc_InitializationVector *ivec;
424     int len;
425     int nlen = 0;
426     u_int word;                 /* so we get unsigned right-shift */
427     int checkCksum;
428     afs_int32 *preSeq;
429     afs_int32 code;
430
431     tconn = rx_ConnectionOf(acall);
432     len = rx_GetDataSize(apacket);
433     checkCksum = 0;             /* init */
434     if (rx_IsServerConn(tconn)) {
435         struct rxkad_sconn *sconn;
436         sconn = rx_GetSecurityData(tconn);
437         if (rx_GetPacketCksum(apacket) != 0)
438             sconn->cksumSeen = 1;
439         checkCksum = sconn->cksumSeen;
440         if (sconn && sconn->authenticated
441             && (osi_Time() < sconn->expirationTime)) {
442             level = sconn->level;
443             INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_server, level)]);
444             sconn->stats.packetsReceived++;
445             sconn->stats.bytesReceived += len;
446             schedule = (const fc_KeySchedule *) sconn->keysched;
447             ivec = (fc_InitializationVector *) sconn->ivec;
448         } else {
449             INC_RXKAD_STATS(expired);
450             return RXKADEXPIRED;
451         }
452         preSeq = sconn->preSeq;
453     } else {                    /* client connection */
454         struct rxkad_cconn *cconn;
455         struct rxkad_cprivate *tcp;
456         cconn = rx_GetSecurityData(tconn);
457         if (rx_GetPacketCksum(apacket) != 0)
458             cconn->cksumSeen = 1;
459         checkCksum = cconn->cksumSeen;
460         tcp = (struct rxkad_cprivate *)aobj->privateData;
461         if (!(tcp->type & rxkad_client))
462             return RXKADINCONSISTENCY;
463         level = tcp->level;
464         INC_RXKAD_STATS(checkPackets[rxkad_StatIndex(rxkad_client, level)]);
465         cconn->stats.packetsReceived++;
466         cconn->stats.bytesReceived += len;
467         preSeq = cconn->preSeq;
468         schedule = (const fc_KeySchedule *) tcp->keysched;
469         ivec = (fc_InitializationVector *) tcp->ivec;
470     }
471
472     if (checkCksum) {
473         code = ComputeSum(apacket, (fc_KeySchedule *)schedule, preSeq);
474         if (code != rx_GetPacketCksum(apacket))
475             return RXKADSEALEDINCON;
476     }
477
478     switch (level) {
479     case rxkad_clear:
480         return 0;               /* shouldn't happen */
481     case rxkad_auth:
482         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
483         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
484                        DECRYPT);
485         break;
486     case rxkad_crypt:
487         code = rxkad_DecryptPacket(tconn, schedule, (const fc_InitializationVector *)ivec, len, apacket);
488         if (code)
489             return code;
490         break;
491     }
492     word = ntohl(rx_GetInt32(apacket, 0));      /* get first sealed word */
493     if ((word >> 16) !=
494         ((apacket->header.seq ^ apacket->header.callNumber) & 0xffff))
495         return RXKADSEALEDINCON;
496     nlen = word & 0xffff;       /* get real user data length */
497
498     /* The sealed length should be no larger than the initial length, since the
499      * reverse (round-up) occurs in ...PreparePacket */
500     if (nlen > len)
501         return RXKADDATALEN;
502     rx_SetDataSize(apacket, nlen);
503     return 0;
504 }
505
506 /* either: encode packet */
507
508 int
509 rxkad_PreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
510                     struct rx_packet *apacket)
511 {
512     struct rx_connection *tconn;
513     rxkad_level level;
514     fc_KeySchedule *schedule;
515     fc_InitializationVector *ivec;
516     int len;
517     int nlen = 0;
518     int word;
519     afs_int32 code;
520     afs_int32 *preSeq;
521
522     tconn = rx_ConnectionOf(acall);
523     len = rx_GetDataSize(apacket);
524     if (rx_IsServerConn(tconn)) {
525         struct rxkad_sconn *sconn;
526         sconn = rx_GetSecurityData(tconn);
527         if (sconn && sconn->authenticated
528             && (osi_Time() < sconn->expirationTime)) {
529             level = sconn->level;
530             INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_server, level)]);
531             sconn->stats.packetsSent++;
532             sconn->stats.bytesSent += len;
533             schedule = (fc_KeySchedule *) sconn->keysched;
534             ivec = (fc_InitializationVector *) sconn->ivec;
535         } else {
536             INC_RXKAD_STATS(expired);   /* this is a pretty unlikely path... */
537             return RXKADEXPIRED;
538         }
539         preSeq = sconn->preSeq;
540     } else {                    /* client connection */
541         struct rxkad_cconn *cconn;
542         struct rxkad_cprivate *tcp;
543         cconn = rx_GetSecurityData(tconn);
544         tcp = (struct rxkad_cprivate *)aobj->privateData;
545         if (!(tcp->type & rxkad_client))
546             return RXKADINCONSISTENCY;
547         level = tcp->level;
548         INC_RXKAD_STATS(preparePackets[rxkad_StatIndex(rxkad_client, level)]);
549         cconn->stats.packetsSent++;
550         cconn->stats.bytesSent += len;
551         preSeq = cconn->preSeq;
552         schedule = (fc_KeySchedule *) tcp->keysched;
553         ivec = (fc_InitializationVector *) tcp->ivec;
554     }
555
556     /* compute upward compatible checksum */
557     rx_SetPacketCksum(apacket, ComputeSum(apacket, schedule, preSeq));
558     if (level == rxkad_clear)
559         return 0;
560
561     len = rx_GetDataSize(apacket);
562     word = (((apacket->header.seq ^ apacket->header.callNumber)
563              & 0xffff) << 16) | (len & 0xffff);
564     rx_PutInt32(apacket, 0, htonl(word));
565
566     switch (level) {
567     case rxkad_clear:
568         return 0;               /* shouldn't happen */
569     case rxkad_auth:
570         nlen =
571             afs_max(ENCRYPTIONBLOCKSIZE,
572                     len + rx_GetSecurityHeaderSize(tconn));
573         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
574             rxi_RoundUpPacket(apacket,
575                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
576         }
577         rx_Pullup(apacket, 8);  /* the following encrypts 8 bytes only */
578         fc_ecb_encrypt(rx_DataOf(apacket), rx_DataOf(apacket), *schedule,
579                        ENCRYPT);
580         break;
581     case rxkad_crypt:
582         nlen = round_up_to_ebs(len + rx_GetSecurityHeaderSize(tconn));
583         if (nlen > (len + rx_GetSecurityHeaderSize(tconn))) {
584             rxi_RoundUpPacket(apacket,
585                               nlen - (len + rx_GetSecurityHeaderSize(tconn)));
586         }
587         code = rxkad_EncryptPacket(tconn, (const fc_KeySchedule *)schedule,  (const fc_InitializationVector *)ivec, nlen, apacket);
588         if (code)
589             return code;
590         break;
591     }
592     rx_SetDataSize(apacket, nlen);
593     return 0;
594 }
595
596 /* either: return connection stats */
597
598 int
599 rxkad_GetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
600                struct rx_securityObjectStats *astats)
601 {
602     void *securityData;
603
604     astats->type = 3;
605     astats->level = ((struct rxkad_cprivate *)aobj->privateData)->level;
606
607     securityData = rx_GetSecurityData(aconn);
608
609     if (!securityData) {
610         astats->flags |= 1;
611         return 0;
612     }
613     if (rx_IsServerConn(aconn)) {
614         struct rxkad_sconn *sconn = securityData;
615
616         astats->level = sconn->level;
617         if (sconn->authenticated)
618             astats->flags |= 2;
619         if (sconn->cksumSeen)
620             astats->flags |= 8;
621         astats->expires = sconn->expirationTime;
622         astats->bytesReceived = sconn->stats.bytesReceived;
623         astats->packetsReceived = sconn->stats.packetsReceived;
624         astats->bytesSent = sconn->stats.bytesSent;
625         astats->packetsSent = sconn->stats.packetsSent;
626     } else {                    /* client connection */
627         struct rxkad_cconn *cconn = securityData;
628
629         if (cconn->cksumSeen)
630             astats->flags |= 8;
631         astats->bytesReceived = cconn->stats.bytesReceived;
632         astats->packetsReceived = cconn->stats.packetsReceived;
633         astats->bytesSent = cconn->stats.bytesSent;
634         astats->packetsSent = cconn->stats.packetsSent;
635     }
636     return 0;
637 }
638
639 rxkad_level
640 rxkad_StringToLevel(char *name)
641 {
642     if (strcmp(name, "clear") == 0)
643         return rxkad_clear;
644     if (strcmp(name, "auth") == 0)
645         return rxkad_auth;
646     if (strcmp(name, "crypt") == 0)
647         return rxkad_crypt;
648     return -1;
649 }
650
651 char *
652 rxkad_LevelToString(rxkad_level level)
653 {
654     if (level == rxkad_clear)
655         return "clear";
656     if (level == rxkad_auth)
657         return "auth";
658     if (level == rxkad_crypt)
659         return "crypt";
660     return "unknown";
661 }