]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kstuff.c
423d5bd02eca7204de322eaabbff7864ccc44be9
[1ts-debian.git] / zephyr / server / kstuff.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dealing with Kerberos functions in the server.
3  *
4  *      Created by:     John T Kohl
5  *
6  *      Copyright (c) 1988 by the Massachusetts Institute of Technology.
7  *      For copying and distribution information, see the file
8  *      "mit-copyright.h". 
9  */
10 /*
11  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/kstuff.c,v $
12  *      $Header: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/kstuff.c,v 1.26 2001/04/10 23:28:20 ghudson Exp $
13  */
14
15 #include "zserver.h"
16
17 #ifndef lint
18 #ifndef SABER
19 static const char rcsid_kstuff_c[] = "$Id: kstuff.c,v 1.26 2001/04/10 23:28:20 ghudson Exp $";
20 #endif
21 #endif
22
23 #ifdef HAVE_KRB4
24
25 /* Keep a hash table mapping tickets to session keys, so we can do a fast
26  * check of the cryptographic checksum without doing and DES decryptions.
27  * Also remember the expiry time of the ticket, so that we can sweep the
28  * table periodically. */
29
30 #define HASHTAB_SIZE 4091
31
32 typedef struct hash_entry Hash_entry;
33
34 /* The ticket comes at the end, in a variable-length array. */
35 struct hash_entry {
36     C_Block session_key;
37     time_t expires;
38     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
39     Hash_entry *next;
40     int ticket_len;
41     unsigned char ticket[1];
42 };
43
44 Hash_entry *hashtab[HASHTAB_SIZE];
45
46 static int hash_ticket __P((unsigned char *, int));
47 static void add_session_key __P((KTEXT, C_Block, char *, time_t));
48 static int find_session_key __P((KTEXT, C_Block, char *));
49 static ZChecksum_t compute_checksum __P((ZNotice_t *, C_Block));
50 static ZChecksum_t compute_rlm_checksum __P((ZNotice_t *, C_Block));
51
52 /*
53  * GetKerberosData
54  *
55  * get ticket from file descriptor and decode it.
56  * Return KFAILURE if we barf on reading the ticket, else return
57  * the value of rd_ap_req() applied to the ticket.
58  */
59 int
60 GetKerberosData(fd, haddr, kdata, service, srvtab)
61      int fd; /* file descr. to read from */
62      struct in_addr haddr; /* address of foreign host on fd */
63      AUTH_DAT *kdata;   /* kerberos data (returned) */
64      char *service; /* service principal desired */
65      char *srvtab; /* file to get keys from */
66 {
67     char p[20];
68     KTEXT_ST ticket;            /* will get Kerberos ticket from client */
69     int i;
70     char instance[INST_SZ];
71
72     /*
73      * Get the Kerberos ticket.  The first few characters, terminated
74      * by a blank, should give us a length; then get than many chars
75      * which will be the ticket proper.
76      */
77     for (i=0; i<20; i++) {
78         if (read(fd, &p[i], 1) != 1) {
79             syslog(LOG_WARNING,"bad read tkt len");
80             return(KFAILURE);
81         }
82         if (p[i] == ' ') {
83             p[i] = '\0';
84             break;
85         }
86     }
87     ticket.length = atoi(p);
88     if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) {
89         syslog(LOG_WARNING,"bad tkt len %d",ticket.length);
90         return(KFAILURE);
91     }
92     for (i=0; i<ticket.length; i++) {
93         if (read(fd, (caddr_t) &(ticket.dat[i]), 1) != 1) {
94             syslog(LOG_WARNING,"bad tkt read");
95             return(KFAILURE);
96         }
97     }
98     /*
99      * now have the ticket.  use it to get the authenticated
100      * data from Kerberos.
101      */
102     (void) strcpy(instance,"*");                /* let Kerberos fill it in */
103
104     return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
105                       kdata, srvtab ? srvtab : ""));
106 }
107
108 /*
109  * SendKerberosData
110  * 
111  * create and transmit a ticket over the file descriptor for service.host
112  * return failure codes if appropriate, or 0 if we
113  * get the ticket and write it to the file descriptor
114  */
115
116 Code_t
117 SendKerberosData(fd, ticket, service, host)
118      int fd;            /* file descriptor to write onto */
119      KTEXT ticket;      /* where to put ticket (return) */
120      char *service;     /* service name, foreign host */
121      char *host;
122 {
123     int rem;
124     char p[32];
125     int written;
126     int size_to_write;
127
128     rem = krb_mk_req(ticket, service, host, ZGetRealm(), (u_long) 0);
129     if (rem != KSUCCESS)
130         return rem + krb_err_base;
131
132     (void) sprintf(p,"%d ",ticket->length);
133     size_to_write = strlen (p);
134     if ((written = write(fd, p, size_to_write)) != size_to_write)
135         return (written < 0) ? errno : ZSRV_PKSHORT;
136     if ((written = write(fd, (caddr_t) (ticket->dat), ticket->length))
137         != ticket->length)
138         return (written < 0) ? errno : ZSRV_PKSHORT;
139
140     return 0;
141 }
142
143 #endif /* HAVE_KRB4 */
144
145 Code_t
146 ZCheckRealmAuthentication(notice, from, realm)
147     ZNotice_t *notice;
148     struct sockaddr_in *from;
149     char *realm;
150 {       
151 #ifdef HAVE_KRB5
152     char *authbuf;
153     char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4+1024];
154     krb5_principal princ;
155     krb5_data packet;
156     krb5_ticket *tkt;
157     char *name;
158     krb5_error_code result;
159     krb5_principal server;
160     krb5_keytab keytabid = 0;
161     krb5_auth_context authctx;
162     krb5_keyblock *keyblock; 
163     krb5_enctype enctype; 
164     krb5_cksumtype cksumtype; 
165     krb5_data cksumbuf; 
166 #if HAVE_KRB5_C_MAKE_CHECKSUM 
167     krb5_checksum checksum; 
168     krb5_boolean valid; 
169 #else 
170     krb5_crypto cryptctx; 
171     Checksum checksum; 
172     size_t xlen; 
173 #endif 
174     char *cksum0_base, *cksum1_base, *cksum2_base; 
175     char *svcinst, *x, *y; 
176     char *asn1_data, *key_data; 
177     int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len; 
178 #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
179     krb5_authenticator *authenticator;
180 #define KRB5AUTHENT authenticator
181 #else
182     krb5_authenticator authenticator;
183 #define KRB5AUTHENT &authenticator
184 #endif
185     int len;
186
187     if (!notice->z_auth)
188         return ZAUTH_NO;
189
190     /* Check for bogus authentication data length. */
191     if (notice->z_authent_len <= 0)
192         return ZAUTH_FAILED;
193
194     len = strlen(notice->z_ascii_authent)+1;
195     authbuf=malloc(len);
196
197     /* Read in the authentication data. */
198     if (ZReadZcode(notice->z_ascii_authent, 
199                    authbuf,
200                    len, &len) == ZERR_BADFIELD) {
201         return ZAUTH_FAILED;
202     }
203
204     (void) sprintf(rlmprincipal, "%s/%s@%s", SERVER_SERVICE,
205                    SERVER_INSTANCE, realm);
206
207     packet.length = len;
208     packet.data = authbuf;
209
210     result = krb5_kt_resolve(Z_krb5_ctx, 
211                         keytab_file, &keytabid);
212     if (result) {
213       free(authbuf);
214       return (result);
215     }
216
217     /* HOLDING: authbuf, keytabid */
218     /* Create the auth context */
219     result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
220     if (result) {
221         krb5_kt_close(Z_krb5_ctx, keytabid);
222         free(authbuf);
223         return (result);
224     }
225
226     /* HOLDING: authbuf, authctx */
227     result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), 
228                                   __Zephyr_realm, SERVER_SERVICE, 
229                                   SERVER_INSTANCE, NULL);
230     if (!result) {
231         result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, 
232                              keytabid, 0, &tkt);
233         krb5_free_principal(Z_krb5_ctx, server);
234     }
235     krb5_kt_close(Z_krb5_ctx, keytabid);
236
237     if (result) {
238       if (result == KRB5KRB_AP_ERR_REPEAT)
239         syslog(LOG_DEBUG, "k5 auth failed: %s", error_message(result));
240       else
241         syslog(LOG_WARNING,"k5 auth failed: %s", error_message(result));
242         free(authbuf);
243         krb5_auth_con_free(Z_krb5_ctx, authctx);
244         return ZAUTH_FAILED;
245     }
246
247     /* HOLDING: authbuf, authctx */
248 #ifndef HAVE_KRB5_TICKET_ENC_PART2
249     if (tkt == 0 || tkt->client == 0) {
250        if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
251        free(authbuf);
252        krb5_auth_con_free(Z_krb5_ctx, authctx);
253        return ZAUTH_FAILED;
254     }
255     princ = tkt->client;
256 #else
257     if (tkt == 0 || tkt->enc_part2 == 0) {
258         if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
259         free(authbuf);
260         krb5_auth_con_free(Z_krb5_ctx, authctx);
261         return ZAUTH_FAILED;
262     }
263     princ = tkt->enc_part2->client;
264 #endif
265     if (princ == 0) {
266         krb5_free_ticket(Z_krb5_ctx, tkt);
267         free(authbuf);
268         krb5_auth_con_free(Z_krb5_ctx, authctx);
269         return ZAUTH_FAILED;
270     }
271
272     /* HOLDING: authbuf, authctx, tkt */
273     result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
274     if (result) {
275         syslog(LOG_WARNING, "k5 unparse_name failed: %s",
276                error_message(result));
277         free(authbuf);
278         krb5_auth_con_free(Z_krb5_ctx, authctx);
279         krb5_free_ticket(Z_krb5_ctx, tkt);
280         return ZAUTH_FAILED;
281     }
282
283     krb5_free_ticket(Z_krb5_ctx, tkt);
284
285     /* HOLDING: authbuf, authctx, name */
286     if (strcmp(name, rlmprincipal)) {
287         syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
288                name, rlmprincipal);
289         krb5_auth_con_free(Z_krb5_ctx, authctx);
290         free(name);
291         free(authbuf);
292         return ZAUTH_FAILED;
293     }
294     free(name);
295     free(authbuf);
296
297     /* HOLDING: authctx */
298     /* Get an authenticator so we can get the keyblock */
299     result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
300                                              &authenticator);
301     if(result) {
302         krb5_auth_con_free(Z_krb5_ctx, authctx);
303         return result;
304     }
305
306     /* HOLDING: authctx, authenticator */
307     result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
308     if (result) {
309       krb5_auth_con_free(Z_krb5_ctx, authctx);
310       krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
311       return (ZAUTH_FAILED);
312     }
313     
314     /* HOLDING: authctx, authenticator, keyblock */
315     /* Figure out what checksum type to use */ 
316 #if HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
317     key_data = keyblock->contents; 
318     key_len  = keyblock->length; 
319     enctype  = keyblock->enctype; 
320     result = Z_krb5_lookup_cksumtype(enctype, &cksumtype); 
321     if (result) { 
322         krb5_free_keyblock(Z_krb5_ctx, keyblock);
323         krb5_auth_con_free(Z_krb5_ctx, authctx);
324         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
325         return (ZAUTH_FAILED); 
326     } 
327 #else 
328     key_data = keyblock->keyvalue.data; 
329     key_len  = keyblock->keyvalue.length; 
330     { 
331        unsigned int len; 
332        ENCTYPE *val; 
333        int i = 0; 
334  
335        result  = krb5_keytype_to_enctypes(Z_krb5_ctx, keyblock->keytype, 
336                                           &len, &val); 
337        if (result) { 
338            krb5_free_keyblock(Z_krb5_ctx, keyblock);
339            krb5_auth_con_free(Z_krb5_ctx, authctx); 
340            krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); 
341            return (ZAUTH_FAILED);  
342        } 
343  
344        do { 
345            if (i == len) break;
346            result = Z_krb5_lookup_cksumtype(val[i], &cksumtype); 
347            i++;
348        } while (result != 0); 
349
350        if (result) { 
351            krb5_free_keyblock(Z_krb5_ctx, keyblock);
352            krb5_auth_con_free(Z_krb5_ctx, authctx); 
353            krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); 
354            return (ZAUTH_FAILED);  
355        } 
356        enctype = val[i-1]; 
357     } 
358 #endif 
359     /* HOLDING: authctx, authenticator, keyblock */
360  
361     /* Assemble the things to be checksummed */ 
362     /* first part is from start of packet through z_default_format: 
363      * - z_version 
364      * - z_num_other_fields 
365      * - z_kind 
366      * - z_uid 
367      * - z_port 
368      * - z_auth 
369      * - z_authent_len 
370      * - z_ascii_authent 
371      * - z_class 
372      * - z_class_inst 
373      * - z_opcode 
374      * - z_sender 
375      * - z_recipient 
376      * - z_default_format 
377      */ 
378     cksum0_base = notice->z_packet; 
379     x           = notice->z_default_format; 
380     cksum0_len  = x + strlen(x) + 1 - cksum0_base; 
381     /* second part is from z_multinotice through other fields: 
382      * - z_multinotice 
383      * - z_multiuid 
384      * - z_other_fields[] 
385      */ 
386     cksum1_base = notice->z_multinotice; 
387     if (notice->z_num_other_fields) 
388         x = notice->z_other_fields[notice->z_num_other_fields]; 
389     else 
390         x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */ 
391     cksum1_len  = x + strlen(x) + 1 - cksum1_base; 
392  
393     /* last part is the message body */ 
394     cksum2_base = notice->z_message; 
395     cksum2_len  = notice->z_message_len; 
396  
397     if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && 
398         key_len == 8 && 
399         (enctype == ENCTYPE_DES_CBC_CRC || 
400          enctype == ENCTYPE_DES_CBC_MD4 || 
401          enctype == ENCTYPE_DES_CBC_MD5)) { 
402       /* try old-format checksum (covers cksum0 only) */ 
403  
404       ZChecksum_t our_checksum; 
405  
406       our_checksum = des_quad_cksum(cksum0_base, NULL, cksum0_len, 0, 
407                                     key_data); 
408       if (our_checksum == notice->z_checksum) { 
409           krb5_free_keyblock(Z_krb5_ctx, keyblock);
410           krb5_auth_con_free(Z_krb5_ctx, authctx);
411           krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
412           return ZAUTH_YES; 
413       } 
414     } 
415
416     /* HOLDING: authctx, authenticator */
417  
418     cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; 
419     cksumbuf.data = malloc(cksumbuf.length); 
420     if (!cksumbuf.data) { 
421         krb5_free_keyblock(Z_krb5_ctx, keyblock);
422         krb5_auth_con_free(Z_krb5_ctx, authctx);
423         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
424         return ZAUTH_NO; 
425     } 
426     /* HOLDING: authctx, authenticator, cksumbuf.data */ 
427  
428     memcpy(cksumbuf.data, cksum0_base, cksum0_len); 
429     memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len); 
430     memcpy(cksumbuf.data + cksum0_len + cksum1_len, 
431            cksum2_base, cksum2_len); 
432  
433     /* decode zcoded checksum */ 
434     /* The encoded form is always longer than the original */ 
435     asn1_len = strlen(notice->z_ascii_checksum) + 1; 
436     asn1_data = malloc(asn1_len); 
437     if (!asn1_data) { 
438         krb5_free_keyblock(Z_krb5_ctx, keyblock);
439         krb5_auth_con_free(Z_krb5_ctx, authctx);
440         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
441         free(cksumbuf.data); 
442         return ZAUTH_FAILED; 
443     } 
444     /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ 
445     result = ZReadZcode(notice->z_ascii_checksum, 
446                         asn1_data, asn1_len, &asn1_len); 
447     if (result != ZERR_NONE) { 
448         krb5_free_keyblock(Z_krb5_ctx, keyblock);
449         krb5_auth_con_free(Z_krb5_ctx, authctx);
450         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
451         free(asn1_data); 
452         free(cksumbuf.data); 
453         return ZAUTH_FAILED; 
454     } 
455     /* HOLDING: asn1_data, cksumbuf.data */ 
456  
457 #if HAVE_KRB5_C_MAKE_CHECKSUM 
458     /* Verify the checksum -- MIT crypto API */ 
459     memset(&checksum, 0, sizeof(checksum)); 
460     checksum.length = asn1_len; 
461     checksum.contents = asn1_data; 
462     checksum.checksum_type = cksumtype;
463     result = krb5_c_verify_checksum(Z_krb5_ctx, 
464                                     keyblock, Z_KEYUSAGE_SRV_CKSUM, 
465                                     &cksumbuf, &checksum, &valid); 
466     free(asn1_data); 
467     krb5_auth_con_free(Z_krb5_ctx, authctx);
468     krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
469     krb5_free_keyblock(Z_krb5_ctx, keyblock);
470     free(cksumbuf.data); 
471     if (!result && valid) 
472         return (ZAUTH_YES); 
473     else 
474         return (ZAUTH_FAILED); 
475 #else 
476     /* Verify the checksum -- heimdal crypto API */ 
477     checksum.checksum.length = asn1_len;
478     checksum.checksum.data = asn1_data;
479     checksum.cksumtype = cksumtype;
480
481     /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
482
483     result = krb5_crypto_init(Z_krb5_ctx, keyblock, enctype, &cryptctx); 
484     if (result) { 
485         krb5_auth_con_free(Z_krb5_ctx, authctx);
486         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
487         krb5_free_keyblock(Z_krb5_ctx, keyblock);
488         free(asn1_data);
489         free(cksumbuf.data); 
490         return result; 
491     } 
492     /* HOLDING: authctx, authenticator, cryptctx, cksumbuf.data, checksum */ 
493     result = krb5_verify_checksum(Z_krb5_ctx, cryptctx, 
494                                   Z_KEYUSAGE_SRV_CKSUM, 
495                                   cksumbuf.data, cksumbuf.length, 
496                                   &checksum); 
497     krb5_free_keyblock(Z_krb5_ctx, keyblock);
498     krb5_crypto_destroy(Z_krb5_ctx, cryptctx); 
499     krb5_auth_con_free(Z_krb5_ctx, authctx);
500     krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
501     free(asn1_data);
502     free(cksumbuf.data); 
503     if (result) 
504         return (ZAUTH_FAILED); 
505     else 
506         return (ZAUTH_YES); 
507 #endif
508 #else
509     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
510 #endif
511 }
512
513 #undef KRB5AUTHENT
514
515 Code_t
516 ZCheckAuthentication(notice, from)
517     ZNotice_t *notice;
518     struct sockaddr_in *from;
519 {       
520 #ifdef HAVE_KRB4
521     int result;
522     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
523     KTEXT_ST authent, ticket;
524     AUTH_DAT dat;
525     ZChecksum_t checksum;
526     C_Block session_key;
527     char instance[INST_SZ+1];
528
529     if (!notice->z_auth)
530         return ZAUTH_NO;
531
532     /* Check for bogus authentication data length. */
533     if (notice->z_authent_len <= 0)
534         return ZAUTH_FAILED;
535
536     /* Read in the authentication data. */
537     if (ZReadAscii(notice->z_ascii_authent, 
538                    strlen(notice->z_ascii_authent)+1, 
539                    (unsigned char *)authent.dat, 
540                    notice->z_authent_len) == ZERR_BADFIELD) {
541         return ZAUTH_FAILED;
542     }
543     authent.length = notice->z_authent_len;
544
545 #if 0
546     /* Copy the ticket out of the authentication data. */
547     if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
548         return ZAUTH_FAILED;
549
550     /* Try to do a fast check against the cryptographic checksum. */
551     if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
552         if (strcmp(srcprincipal, notice->z_sender) != 0)
553             return ZAUTH_FAILED;
554         if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
555             return ZAUTH_FAILED;
556         checksum = compute_checksum(notice, session_key);
557
558         /* If checksum matches, packet is authentic.  If not, we might
559          * have an outdated session key, so keep going the slow way.
560          */
561         if (checksum == notice->z_checksum) {
562             memcpy(__Zephyr_session, session_key, sizeof(C_Block));
563             return ZAUTH_YES;
564         }
565     }
566 #endif
567
568     strcpy(instance, SERVER_INSTANCE);
569
570     /* We don't have the session key cached; do it the long way. */
571     result = krb_rd_req(&authent, SERVER_SERVICE, instance,
572                         from->sin_addr.s_addr, &dat, srvtab_file);
573     if (result == RD_AP_OK) {
574         memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
575         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
576                 dat.pinst, dat.prealm);
577         if (strcmp(srcprincipal, notice->z_sender))
578             return ZAUTH_FAILED;
579     } else {
580         return ZAUTH_FAILED;    /* didn't decode correctly */
581     }
582
583     /* Check the cryptographic checksum. */
584 #ifdef NOENCRYPTION
585     checksum = 0;
586 #else
587     checksum = compute_checksum(notice, dat.session);
588 #endif
589     if (checksum != notice->z_checksum)
590         return ZAUTH_FAILED;
591
592 #if 0
593     /* Record the session key, expiry time, and source principal in the
594      * hash table, so we can do a fast check next time. */
595     add_session_key(&ticket, dat.session, srcprincipal,
596                     (time_t)(dat.time_sec + dat.life * 5 * 60));
597 #endif
598
599     return ZAUTH_YES;
600
601 #else /* !HAVE_KRB4 */
602     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
603 #endif
604 }
605
606
607 #ifdef HAVE_KRB4
608
609 static int hash_ticket(p, len)
610     unsigned char *p;
611     int len;
612 {
613     unsigned long hashval = 0, g;
614
615     for (; len > 0; p++, len--) {
616         hashval = (hashval << 4) + *p;
617         g = hashval & 0xf0000000;
618         if (g) {
619             hashval ^= g >> 24;
620             hashval ^= g;
621         }
622     }
623     return hashval % HASHTAB_SIZE;
624 }
625
626 static void add_session_key(ticket, session_key, srcprincipal, expires)
627     KTEXT ticket;
628     C_Block session_key;
629     char *srcprincipal;
630     time_t expires;
631 {
632     Hash_entry *entry;
633     int hashval;
634
635     /* If we can't allocate memory for the hash table entry, just forget
636      * about it. */
637     entry = (Hash_entry *) malloc(sizeof(Hash_entry) - 1 + ticket->length);
638     if (!entry)
639         return;
640
641     /* Initialize the new entry. */
642     memcpy(entry->session_key, session_key, sizeof(entry->session_key));
643     strcpy(entry->srcprincipal, srcprincipal);
644     entry->expires = expires;
645     entry->ticket_len = ticket->length;
646     memcpy(entry->ticket, ticket->dat, ticket->length * sizeof(unsigned char));
647
648     /* Insert the new entry in the hash table. */
649     hashval = hash_ticket(ticket->dat, ticket->length);
650     entry->next = hashtab[hashval];
651     hashtab[hashval] = entry;
652 }
653
654 static int find_session_key(ticket, key, srcprincipal)
655     KTEXT ticket;
656     C_Block key;
657     char *srcprincipal;
658 {
659     unsigned char *dat;
660     int hashval, len;
661     Hash_entry *entry;
662
663     dat = ticket->dat;
664     len = ticket->length;
665     hashval = hash_ticket(dat, len);
666
667     for (entry = hashtab[hashval]; entry; entry = entry->next) {
668         if (entry->ticket_len == len && memcmp(entry->ticket, dat, len) == 0) {
669             memcpy(key, entry->session_key, sizeof(entry->session_key));
670             strcpy(srcprincipal, entry->srcprincipal);
671             return 0;
672         }
673     }
674     return -1;
675 }
676
677 static ZChecksum_t compute_checksum(notice, session_key)
678     ZNotice_t *notice;
679     C_Block session_key;
680 {
681 #ifdef NOENCRYPTION
682     return 0;
683 #else
684     ZChecksum_t checksum;
685     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
686
687     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
688     cend = cstart + strlen(cstart) + 1;
689     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
690     checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
691     checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
692                                0, session_key);
693     return checksum;
694 #endif
695 }
696
697 static ZChecksum_t compute_rlm_checksum(notice, session_key)
698     ZNotice_t *notice;
699     C_Block session_key;
700 {
701 #ifdef NOENCRYPTION
702     return 0;
703 #else
704     ZChecksum_t checksum;
705     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
706
707     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
708     cend = cstart + strlen(cstart) + 1;
709     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
710     return checksum;
711 #endif
712 }
713
714 void sweep_ticket_hash_table(arg)
715     void *arg;
716 {
717     int i;
718     Hash_entry **ptr, *entry;
719
720     for (i = 0; i < HASHTAB_SIZE; i++) {
721         ptr = &hashtab[i];
722         while (*ptr) {
723             entry = *ptr;
724             if (entry->expires < NOW) {
725                 *ptr = entry->next;
726                 free(entry);
727             } else {
728                 ptr = &(*ptr)->next;
729             }
730         }
731     }
732     timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
733 }
734
735 #endif /* HAVE_KRB4 */
736