]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kstuff.c
done milestone 3; working on refactoring
[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 ZChecksum_t compute_checksum __P((ZNotice_t *, C_Block));
47 static ZChecksum_t compute_rlm_checksum __P((ZNotice_t *, C_Block));
48
49 /*
50  * GetKerberosData
51  *
52  * get ticket from file descriptor and decode it.
53  * Return KFAILURE if we barf on reading the ticket, else return
54  * the value of rd_ap_req() applied to the ticket.
55  */
56 int
57 GetKerberosData(fd, haddr, kdata, service, srvtab)
58      int fd; /* file descr. to read from */
59      struct in_addr haddr; /* address of foreign host on fd */
60      AUTH_DAT *kdata;   /* kerberos data (returned) */
61      char *service; /* service principal desired */
62      char *srvtab; /* file to get keys from */
63 {
64     char p[20];
65     KTEXT_ST ticket;            /* will get Kerberos ticket from client */
66     int i;
67     char instance[INST_SZ];
68
69     /*
70      * Get the Kerberos ticket.  The first few characters, terminated
71      * by a blank, should give us a length; then get than many chars
72      * which will be the ticket proper.
73      */
74     for (i=0; i<20; i++) {
75         if (read(fd, &p[i], 1) != 1) {
76             syslog(LOG_WARNING,"bad read tkt len");
77             return(KFAILURE);
78         }
79         if (p[i] == ' ') {
80             p[i] = '\0';
81             break;
82         }
83     }
84     ticket.length = atoi(p);
85     if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) {
86         syslog(LOG_WARNING,"bad tkt len %d",ticket.length);
87         return(KFAILURE);
88     }
89     for (i=0; i<ticket.length; i++) {
90         if (read(fd, (caddr_t) &(ticket.dat[i]), 1) != 1) {
91             syslog(LOG_WARNING,"bad tkt read");
92             return(KFAILURE);
93         }
94     }
95     /*
96      * now have the ticket.  use it to get the authenticated
97      * data from Kerberos.
98      */
99     (void) strcpy(instance,"*");                /* let Kerberos fill it in */
100
101     return(krb_rd_req(&ticket, service, instance, haddr.s_addr,
102                       kdata, srvtab ? srvtab : ""));
103 }
104
105 /*
106  * SendKerberosData
107  * 
108  * create and transmit a ticket over the file descriptor for service.host
109  * return failure codes if appropriate, or 0 if we
110  * get the ticket and write it to the file descriptor
111  */
112
113 Code_t
114 SendKerberosData(fd, ticket, service, host)
115      int fd;            /* file descriptor to write onto */
116      KTEXT ticket;      /* where to put ticket (return) */
117      char *service;     /* service name, foreign host */
118      char *host;
119 {
120     int rem;
121     char p[32];
122     int written;
123     int size_to_write;
124
125     rem = krb_mk_req(ticket, service, host, ZGetRealm(), (u_long) 0);
126     if (rem != KSUCCESS)
127         return rem + krb_err_base;
128
129     (void) sprintf(p,"%d ",ticket->length);
130     size_to_write = strlen (p);
131     if ((written = write(fd, p, size_to_write)) != size_to_write)
132         return (written < 0) ? errno : ZSRV_PKSHORT;
133     if ((written = write(fd, (caddr_t) (ticket->dat), ticket->length))
134         != ticket->length)
135         return (written < 0) ? errno : ZSRV_PKSHORT;
136
137     return 0;
138 }
139
140 #endif /* HAVE_KRB4 */
141
142 Code_t
143 ZCheckRealmAuthentication(notice, from, realm)
144     ZNotice_t *notice;
145     struct sockaddr_in *from;
146     char *realm;
147 {       
148 #ifdef HAVE_KRB5
149     char *authbuf;
150     char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4+1024];
151     krb5_principal princ;
152     krb5_data packet;
153     krb5_ticket *tkt;
154     char *name;
155     krb5_error_code result;
156     krb5_principal server;
157     krb5_keytab keytabid = 0;
158     krb5_auth_context authctx;
159     krb5_keyblock *keyblock; 
160     krb5_enctype enctype; 
161     krb5_cksumtype cksumtype; 
162     krb5_data cksumbuf; 
163 #if HAVE_KRB5_C_MAKE_CHECKSUM 
164     krb5_checksum checksum; 
165     krb5_boolean valid; 
166 #else 
167     krb5_crypto cryptctx; 
168     Checksum checksum; 
169     size_t xlen; 
170 #endif 
171     char *cksum0_base, *cksum1_base, *cksum2_base; 
172     char *svcinst, *x, *y; 
173     char *asn1_data, *key_data; 
174     int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len; 
175 #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
176     krb5_authenticator *authenticator;
177 #define KRB5AUTHENT authenticator
178 #else
179     krb5_authenticator authenticator;
180 #define KRB5AUTHENT &authenticator
181 #endif
182     int len;
183
184     if (!notice->z_auth)
185         return ZAUTH_NO;
186
187     /* Check for bogus authentication data length. */
188     if (notice->z_authent_len <= 0)
189         return ZAUTH_FAILED;
190
191     len = strlen(notice->z_ascii_authent)+1;
192     authbuf=malloc(len);
193
194     /* Read in the authentication data. */
195     if (ZReadZcode(notice->z_ascii_authent, 
196                    authbuf,
197                    len, &len) == ZERR_BADFIELD) {
198         return ZAUTH_FAILED;
199     }
200
201     (void) sprintf(rlmprincipal, "%s/%s@%s", SERVER_SERVICE,
202                    SERVER_INSTANCE, realm);
203
204     packet.length = len;
205     packet.data = authbuf;
206
207     result = krb5_kt_resolve(Z_krb5_ctx, 
208                         keytab_file, &keytabid);
209     if (result) {
210       free(authbuf);
211       return (result);
212     }
213
214     /* HOLDING: authbuf, keytabid */
215     /* Create the auth context */
216     result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
217     if (result) {
218         krb5_kt_close(Z_krb5_ctx, keytabid);
219         free(authbuf);
220         return (result);
221     }
222
223     /* HOLDING: authbuf, authctx */
224     result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), 
225                                   __Zephyr_realm, SERVER_SERVICE, 
226                                   SERVER_INSTANCE, NULL);
227     if (!result) {
228         result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, 
229                              keytabid, 0, &tkt);
230         krb5_free_principal(Z_krb5_ctx, server);
231     }
232     krb5_kt_close(Z_krb5_ctx, keytabid);
233
234     if (result) {
235       if (result == KRB5KRB_AP_ERR_REPEAT)
236         syslog(LOG_DEBUG, "k5 auth failed: %s", error_message(result));
237       else
238         syslog(LOG_WARNING,"k5 auth failed: %s", error_message(result));
239         free(authbuf);
240         krb5_auth_con_free(Z_krb5_ctx, authctx);
241         return ZAUTH_FAILED;
242     }
243
244     /* HOLDING: authbuf, authctx */
245 #ifndef HAVE_KRB5_TICKET_ENC_PART2
246     if (tkt == 0 || tkt->client == 0) {
247        if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
248        free(authbuf);
249        krb5_auth_con_free(Z_krb5_ctx, authctx);
250        return ZAUTH_FAILED;
251     }
252     princ = tkt->client;
253 #else
254     if (tkt == 0 || tkt->enc_part2 == 0) {
255         if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
256         free(authbuf);
257         krb5_auth_con_free(Z_krb5_ctx, authctx);
258         return ZAUTH_FAILED;
259     }
260     princ = tkt->enc_part2->client;
261 #endif
262     if (princ == 0) {
263         krb5_free_ticket(Z_krb5_ctx, tkt);
264         free(authbuf);
265         krb5_auth_con_free(Z_krb5_ctx, authctx);
266         return ZAUTH_FAILED;
267     }
268
269     /* HOLDING: authbuf, authctx, tkt */
270     result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
271     if (result) {
272         syslog(LOG_WARNING, "k5 unparse_name failed: %s",
273                error_message(result));
274         free(authbuf);
275         krb5_auth_con_free(Z_krb5_ctx, authctx);
276         krb5_free_ticket(Z_krb5_ctx, tkt);
277         return ZAUTH_FAILED;
278     }
279
280     krb5_free_ticket(Z_krb5_ctx, tkt);
281
282     /* HOLDING: authbuf, authctx, name */
283     if (strcmp(name, rlmprincipal)) {
284         syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
285                name, rlmprincipal);
286         krb5_auth_con_free(Z_krb5_ctx, authctx);
287         free(name);
288         free(authbuf);
289         return ZAUTH_FAILED;
290     }
291     free(name);
292     free(authbuf);
293
294     /* HOLDING: authctx */
295     /* Get an authenticator so we can get the keyblock */
296     result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
297                                              &authenticator);
298     if(result) {
299         krb5_auth_con_free(Z_krb5_ctx, authctx);
300         return result;
301     }
302
303     /* HOLDING: authctx, authenticator */
304     result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
305     if (result) {
306       krb5_auth_con_free(Z_krb5_ctx, authctx);
307       krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
308       return (ZAUTH_FAILED);
309     }
310     
311     /* HOLDING: authctx, authenticator, keyblock */
312     /* Figure out what checksum type to use */ 
313 #if HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
314     key_data = keyblock->contents; 
315     key_len  = keyblock->length; 
316     enctype  = keyblock->enctype; 
317     result = Z_krb5_lookup_cksumtype(enctype, &cksumtype); 
318     if (result) { 
319         krb5_free_keyblock(Z_krb5_ctx, keyblock);
320         krb5_auth_con_free(Z_krb5_ctx, authctx);
321         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
322         return (ZAUTH_FAILED); 
323     } 
324 #else 
325     key_data = keyblock->keyvalue.data; 
326     key_len  = keyblock->keyvalue.length; 
327     { 
328        unsigned int len; 
329        ENCTYPE *val; 
330        int i = 0; 
331  
332        result  = krb5_keytype_to_enctypes(Z_krb5_ctx, keyblock->keytype, 
333                                           &len, &val); 
334        if (result) { 
335            krb5_free_keyblock(Z_krb5_ctx, keyblock);
336            krb5_auth_con_free(Z_krb5_ctx, authctx); 
337            krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); 
338            return (ZAUTH_FAILED);  
339        } 
340  
341        do { 
342            if (i == len) break;
343            result = Z_krb5_lookup_cksumtype(val[i], &cksumtype); 
344            i++;
345        } while (result != 0); 
346
347        if (result) { 
348            krb5_free_keyblock(Z_krb5_ctx, keyblock);
349            krb5_auth_con_free(Z_krb5_ctx, authctx); 
350            krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); 
351            return (ZAUTH_FAILED);  
352        } 
353        enctype = val[i-1]; 
354     } 
355 #endif 
356     /* HOLDING: authctx, authenticator, keyblock */
357  
358     /* Assemble the things to be checksummed */ 
359     /* first part is from start of packet through z_default_format: 
360      * - z_version 
361      * - z_num_other_fields 
362      * - z_kind 
363      * - z_uid 
364      * - z_port 
365      * - z_auth 
366      * - z_authent_len 
367      * - z_ascii_authent 
368      * - z_class 
369      * - z_class_inst 
370      * - z_opcode 
371      * - z_sender 
372      * - z_recipient 
373      * - z_default_format 
374      */ 
375     cksum0_base = notice->z_packet; 
376     x           = notice->z_default_format; 
377     cksum0_len  = x + strlen(x) + 1 - cksum0_base; 
378     /* second part is from z_multinotice through other fields: 
379      * - z_multinotice 
380      * - z_multiuid 
381      * - z_other_fields[] 
382      */ 
383     cksum1_base = notice->z_multinotice; 
384     if (notice->z_num_other_fields) 
385         x = notice->z_other_fields[notice->z_num_other_fields]; 
386     else 
387         x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */ 
388     cksum1_len  = x + strlen(x) + 1 - cksum1_base; 
389  
390     /* last part is the message body */ 
391     cksum2_base = notice->z_message; 
392     cksum2_len  = notice->z_message_len; 
393  
394     if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && 
395         key_len == 8 && 
396         (enctype == ENCTYPE_DES_CBC_CRC || 
397          enctype == ENCTYPE_DES_CBC_MD4 || 
398          enctype == ENCTYPE_DES_CBC_MD5)) { 
399       /* try old-format checksum (covers cksum0 only) */ 
400  
401       ZChecksum_t our_checksum; 
402  
403       our_checksum = des_quad_cksum(cksum0_base, NULL, cksum0_len, 0, 
404                                     key_data); 
405       if (our_checksum == notice->z_checksum) { 
406           krb5_free_keyblock(Z_krb5_ctx, keyblock);
407           krb5_auth_con_free(Z_krb5_ctx, authctx);
408           krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
409           return ZAUTH_YES; 
410       } 
411     } 
412
413     /* HOLDING: authctx, authenticator */
414  
415     cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; 
416     cksumbuf.data = malloc(cksumbuf.length); 
417     if (!cksumbuf.data) { 
418         krb5_free_keyblock(Z_krb5_ctx, keyblock);
419         krb5_auth_con_free(Z_krb5_ctx, authctx);
420         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
421         return ZAUTH_NO; 
422     } 
423     /* HOLDING: authctx, authenticator, cksumbuf.data */ 
424  
425     memcpy(cksumbuf.data, cksum0_base, cksum0_len); 
426     memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len); 
427     memcpy(cksumbuf.data + cksum0_len + cksum1_len, 
428            cksum2_base, cksum2_len); 
429  
430     /* decode zcoded checksum */ 
431     /* The encoded form is always longer than the original */ 
432     asn1_len = strlen(notice->z_ascii_checksum) + 1; 
433     asn1_data = malloc(asn1_len); 
434     if (!asn1_data) { 
435         krb5_free_keyblock(Z_krb5_ctx, keyblock);
436         krb5_auth_con_free(Z_krb5_ctx, authctx);
437         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
438         free(cksumbuf.data); 
439         return ZAUTH_FAILED; 
440     } 
441     /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ 
442     result = ZReadZcode(notice->z_ascii_checksum, 
443                         asn1_data, asn1_len, &asn1_len); 
444     if (result != ZERR_NONE) { 
445         krb5_free_keyblock(Z_krb5_ctx, keyblock);
446         krb5_auth_con_free(Z_krb5_ctx, authctx);
447         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
448         free(asn1_data); 
449         free(cksumbuf.data); 
450         return ZAUTH_FAILED; 
451     } 
452     /* HOLDING: asn1_data, cksumbuf.data */ 
453  
454 #if HAVE_KRB5_C_MAKE_CHECKSUM 
455     /* Verify the checksum -- MIT crypto API */ 
456     memset(&checksum, 0, sizeof(checksum)); 
457     checksum.length = asn1_len; 
458     checksum.contents = asn1_data; 
459     checksum.checksum_type = cksumtype;
460     result = krb5_c_verify_checksum(Z_krb5_ctx, 
461                                     keyblock, Z_KEYUSAGE_SRV_CKSUM, 
462                                     &cksumbuf, &checksum, &valid); 
463     free(asn1_data); 
464     krb5_auth_con_free(Z_krb5_ctx, authctx);
465     krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
466     krb5_free_keyblock(Z_krb5_ctx, keyblock);
467     free(cksumbuf.data); 
468     if (!result && valid) 
469         return (ZAUTH_YES); 
470     else 
471         return (ZAUTH_FAILED); 
472 #else 
473     /* Verify the checksum -- heimdal crypto API */ 
474     checksum.checksum.length = asn1_len;
475     checksum.checksum.data = asn1_data;
476     checksum.cksumtype = cksumtype;
477
478     /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
479
480     result = krb5_crypto_init(Z_krb5_ctx, keyblock, enctype, &cryptctx); 
481     if (result) { 
482         krb5_auth_con_free(Z_krb5_ctx, authctx);
483         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
484         krb5_free_keyblock(Z_krb5_ctx, keyblock);
485         free(asn1_data);
486         free(cksumbuf.data); 
487         return result; 
488     } 
489     /* HOLDING: authctx, authenticator, cryptctx, cksumbuf.data, checksum */ 
490     result = krb5_verify_checksum(Z_krb5_ctx, cryptctx, 
491                                   Z_KEYUSAGE_SRV_CKSUM, 
492                                   cksumbuf.data, cksumbuf.length, 
493                                   &checksum); 
494     krb5_free_keyblock(Z_krb5_ctx, keyblock);
495     krb5_crypto_destroy(Z_krb5_ctx, cryptctx); 
496     krb5_auth_con_free(Z_krb5_ctx, authctx);
497     krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
498     free(asn1_data);
499     free(cksumbuf.data); 
500     if (result) 
501         return (ZAUTH_FAILED); 
502     else 
503         return (ZAUTH_YES); 
504 #endif
505 #else
506     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
507 #endif
508 }
509
510 Code_t
511 ZCheckAuthentication(notice, from)
512     ZNotice_t *notice;
513     struct sockaddr_in *from;
514 {       
515 #ifdef HAVE_KRB5
516     char *authbuf;
517     krb5_principal princ;
518     krb5_data packet;
519     krb5_ticket *tkt;
520     char *name;
521     krb5_error_code result;
522     krb5_principal server;
523     krb5_keytab keytabid = 0;
524     krb5_auth_context authctx;
525     krb5_keyblock *keyblock; 
526     krb5_enctype enctype; 
527     krb5_cksumtype cksumtype; 
528     krb5_data cksumbuf;
529     int valid;
530     char *cksum0_base, *cksum1_base, *cksum2_base; 
531     char *svcinst, *x, *y; 
532     char *asn1_data, *key_data; 
533     int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len; 
534 #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
535     krb5_authenticator *authenticator;
536 #define KRB5AUTHENT authenticator
537 #else
538     krb5_authenticator authenticator;
539 #define KRB5AUTHENT &authenticator
540 #endif
541     int len;
542
543     if (!notice->z_auth)
544         return ZAUTH_NO;
545
546     /* Check for bogus authentication data length. */
547     if (notice->z_authent_len <= 1)
548         return ZAUTH_FAILED;
549
550 #ifdef HAVE_KRB4
551     if (notice->z_ascii_authent[0] != 'Z')
552       return ZCheckAuthentication4(notice, from);
553 #endif
554     
555     len = strlen(notice->z_ascii_authent)+1;
556     authbuf=malloc(len);
557
558     /* Read in the authentication data. */
559     if (ZReadZcode(notice->z_ascii_authent, 
560                    authbuf,
561                    len, &len) == ZERR_BADFIELD) {
562         return ZAUTH_FAILED;
563     }
564
565     packet.length = len;
566     packet.data = authbuf;
567
568     result = krb5_kt_resolve(Z_krb5_ctx, 
569                         keytab_file, &keytabid);
570     if (result) {
571       free(authbuf);
572       return (result);
573     }
574
575     /* HOLDING: authbuf, keytabid */
576     /* Create the auth context */
577     result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
578     if (result) {
579         krb5_kt_close(Z_krb5_ctx, keytabid);
580         free(authbuf);
581         return (result);
582     }
583
584     /* HOLDING: authbuf, authctx */
585     result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), 
586                                   __Zephyr_realm, SERVER_SERVICE, 
587                                   SERVER_INSTANCE, NULL);
588     if (!result) {
589         result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, 
590                              keytabid, 0, &tkt);
591         krb5_free_principal(Z_krb5_ctx, server);
592     }
593     krb5_kt_close(Z_krb5_ctx, keytabid);
594
595     if (result) {
596       if (result == KRB5KRB_AP_ERR_REPEAT)
597         syslog(LOG_DEBUG, "k5 auth failed: %s", error_message(result));
598       else
599         syslog(LOG_WARNING,"k5 auth failed: %s", error_message(result));
600         free(authbuf);
601         krb5_auth_con_free(Z_krb5_ctx, authctx);
602         return ZAUTH_FAILED;
603     }
604
605     /* HOLDING: authbuf, authctx, tkt */
606
607     if (tkt == 0 || !Z_tktprincp(tkt)) {
608        if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
609        free(authbuf);
610        krb5_auth_con_free(Z_krb5_ctx, authctx);
611        return ZAUTH_FAILED;
612     }
613     princ = Z_tktprinc(tkt);
614
615     if (princ == 0) {
616         krb5_free_ticket(Z_krb5_ctx, tkt);
617         free(authbuf);
618         krb5_auth_con_free(Z_krb5_ctx, authctx);
619         return ZAUTH_FAILED;
620     }
621
622     /* HOLDING: authbuf, authctx, tkt */
623     result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
624     if (result) {
625         syslog(LOG_WARNING, "k5 unparse_name failed: %s",
626                error_message(result));
627         free(authbuf);
628         krb5_auth_con_free(Z_krb5_ctx, authctx);
629         krb5_free_ticket(Z_krb5_ctx, tkt);
630         return ZAUTH_FAILED;
631     }
632
633     krb5_free_ticket(Z_krb5_ctx, tkt);
634
635     /* HOLDING: authbuf, authctx, name */
636     if (strcmp(name, notice->z_sender)) {
637         syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
638                name, notice->z_sender);
639         krb5_auth_con_free(Z_krb5_ctx, authctx);
640         free(name);
641         free(authbuf);
642         return ZAUTH_FAILED;
643     }
644     free(name);
645     free(authbuf);
646
647     /* HOLDING: authctx */
648     /* Get an authenticator so we can get the keyblock */
649     result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
650                                              &authenticator);
651     if(result) {
652         krb5_auth_con_free(Z_krb5_ctx, authctx);
653         return result;
654     }
655
656     /* HOLDING: authctx, authenticator */
657     result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
658     if (result) {
659       krb5_auth_con_free(Z_krb5_ctx, authctx);
660       krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
661       return (ZAUTH_FAILED);
662     }
663     
664     /* HOLDING: authctx, authenticator, keyblock */
665     /* Figure out what checksum type to use */
666     key_data = Z_keydata(keyblock);
667     key_len = Z_keylen(keyblock);
668     result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
669     if (result) { 
670         krb5_free_keyblock(Z_krb5_ctx, keyblock);
671         krb5_auth_con_free(Z_krb5_ctx, authctx);
672         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
673         return (ZAUTH_FAILED); 
674     } 
675     /* HOLDING: authctx, authenticator, keyblock */
676
677     memcpy(__Zephyr_session, key_data, sizeof(C_Block)); /* XXX */
678  
679     /* Assemble the things to be checksummed */ 
680     /* first part is from start of packet through z_default_format: 
681      * - z_version 
682      * - z_num_other_fields 
683      * - z_kind 
684      * - z_uid 
685      * - z_port 
686      * - z_auth 
687      * - z_authent_len 
688      * - z_ascii_authent 
689      * - z_class 
690      * - z_class_inst 
691      * - z_opcode 
692      * - z_sender 
693      * - z_recipient 
694      * - z_default_format 
695      */ 
696     cksum0_base = notice->z_packet; 
697     x           = notice->z_default_format; 
698     cksum0_len  = x + strlen(x) + 1 - cksum0_base; 
699     /* second part is from z_multinotice through other fields: 
700      * - z_multinotice 
701      * - z_multiuid 
702      * - z_other_fields[] 
703      */ 
704     cksum1_base = notice->z_multinotice; 
705     if (notice->z_num_other_fields) 
706         x = notice->z_other_fields[notice->z_num_other_fields]; 
707     else 
708         x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */ 
709     cksum1_len  = x + strlen(x) + 1 - cksum1_base; 
710  
711     /* last part is the message body */ 
712     cksum2_base = notice->z_message; 
713     cksum2_len  = notice->z_message_len; 
714  
715     if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && 
716         key_len == 8 && 
717         (enctype == ENCTYPE_DES_CBC_CRC || 
718          enctype == ENCTYPE_DES_CBC_MD4 || 
719          enctype == ENCTYPE_DES_CBC_MD5)) { 
720       /* try old-format checksum (covers cksum0 only) */ 
721  
722       ZChecksum_t our_checksum; 
723  
724       our_checksum = compute_checksum(notice, key_data);
725       
726       krb5_free_keyblock(Z_krb5_ctx, keyblock);
727       krb5_auth_con_free(Z_krb5_ctx, authctx);
728       krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
729
730       if (our_checksum == notice->z_checksum)
731         return ZAUTH_YES; 
732       else
733         return ZAUTH_FAILED;
734     } 
735
736     /* HOLDING: authctx, authenticator */
737  
738     cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; 
739     cksumbuf.data = malloc(cksumbuf.length); 
740     if (!cksumbuf.data) { 
741         krb5_free_keyblock(Z_krb5_ctx, keyblock);
742         krb5_auth_con_free(Z_krb5_ctx, authctx);
743         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
744         return ZAUTH_NO; 
745     } 
746     /* HOLDING: authctx, authenticator, cksumbuf.data */ 
747  
748     memcpy(cksumbuf.data, cksum0_base, cksum0_len); 
749     memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len); 
750     memcpy(cksumbuf.data + cksum0_len + cksum1_len, 
751            cksum2_base, cksum2_len); 
752  
753     /* decode zcoded checksum */ 
754     /* The encoded form is always longer than the original */ 
755     asn1_len = strlen(notice->z_ascii_checksum) + 1; 
756     asn1_data = malloc(asn1_len); 
757     if (!asn1_data) { 
758         krb5_free_keyblock(Z_krb5_ctx, keyblock);
759         krb5_auth_con_free(Z_krb5_ctx, authctx);
760         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
761         free(cksumbuf.data); 
762         return ZAUTH_FAILED; 
763     } 
764     /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ 
765     result = ZReadZcode(notice->z_ascii_checksum, 
766                         asn1_data, asn1_len, &asn1_len); 
767     if (result != ZERR_NONE) { 
768         krb5_free_keyblock(Z_krb5_ctx, keyblock);
769         krb5_auth_con_free(Z_krb5_ctx, authctx);
770         krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
771         free(asn1_data); 
772         free(cksumbuf.data); 
773         return ZAUTH_FAILED; 
774     } 
775     /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */ 
776
777     valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, asn1_data, asn1_len);
778
779     free(asn1_data); 
780     krb5_auth_con_free(Z_krb5_ctx, authctx);
781     krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
782     krb5_free_keyblock(Z_krb5_ctx, keyblock);
783     free(cksumbuf.data); 
784     
785     if (valid) 
786         return (ZAUTH_YES); 
787     else 
788         return (ZAUTH_FAILED); 
789 #else
790     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
791 #endif
792 }
793
794 #undef KRB5AUTHENT
795
796 Code_t
797 ZCheckAuthentication4(notice, from)
798     ZNotice_t *notice;
799     struct sockaddr_in *from;
800 {       
801 #ifdef HAVE_KRB4
802     int result;
803     char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
804     KTEXT_ST authent, ticket;
805     AUTH_DAT dat;
806     ZChecksum_t checksum;
807     C_Block session_key;
808     char instance[INST_SZ+1];
809
810     if (!notice->z_auth)
811         return ZAUTH_NO;
812
813     /* Check for bogus authentication data length. */
814     if (notice->z_authent_len <= 0)
815         return ZAUTH_FAILED;
816
817     /* Read in the authentication data. */
818     if (ZReadAscii(notice->z_ascii_authent, 
819                    strlen(notice->z_ascii_authent)+1, 
820                    (unsigned char *)authent.dat, 
821                    notice->z_authent_len) == ZERR_BADFIELD) {
822         return ZAUTH_FAILED;
823     }
824     authent.length = notice->z_authent_len;
825
826     strcpy(instance, SERVER_INSTANCE);
827
828     /* We don't have the session key cached; do it the long way. */
829     result = krb_rd_req(&authent, SERVER_SERVICE, instance,
830                         from->sin_addr.s_addr, &dat, srvtab_file);
831     if (result == RD_AP_OK) {
832         memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
833         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
834                 dat.pinst, dat.prealm);
835         if (strcmp(srcprincipal, notice->z_sender))
836             return ZAUTH_FAILED;
837     } else {
838         return ZAUTH_FAILED;    /* didn't decode correctly */
839     }
840
841     /* Check the cryptographic checksum. */
842 #ifdef NOENCRYPTION
843     checksum = 0;
844 #else
845     checksum = compute_checksum(notice, dat.session);
846 #endif
847     if (checksum != notice->z_checksum)
848         return ZAUTH_FAILED;
849
850     return ZAUTH_YES;
851
852 #else /* !HAVE_KRB4 */
853     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
854 #endif
855 }
856
857
858 #ifdef HAVE_KRB4
859 static ZChecksum_t compute_checksum(notice, session_key)
860     ZNotice_t *notice;
861     C_Block session_key;
862 {
863 #ifdef NOENCRYPTION
864     return 0;
865 #else
866     ZChecksum_t checksum;
867     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
868
869     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
870     cend = cstart + strlen(cstart) + 1;
871     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
872     checksum ^= des_quad_cksum(cend, NULL, hend - cend, 0, session_key);
873     checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
874                                0, session_key);
875     return checksum;
876 #endif
877 }
878
879 static ZChecksum_t compute_rlm_checksum(notice, session_key)
880     ZNotice_t *notice;
881     C_Block session_key;
882 {
883 #ifdef NOENCRYPTION
884     return 0;
885 #else
886     ZChecksum_t checksum;
887     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
888
889     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
890     cend = cstart + strlen(cstart) + 1;
891     checksum = des_quad_cksum(hstart, NULL, cstart - hstart, 0, session_key);
892     return checksum;
893 #endif
894 }
895
896 void sweep_ticket_hash_table(arg)
897     void *arg;
898 {
899     int i;
900     Hash_entry **ptr, *entry;
901
902     for (i = 0; i < HASHTAB_SIZE; i++) {
903         ptr = &hashtab[i];
904         while (*ptr) {
905             entry = *ptr;
906             if (entry->expires < NOW) {
907                 *ptr = entry->next;
908                 free(entry);
909             } else {
910                 ptr = &(*ptr)->next;
911             }
912         }
913     }
914     timer_set_rel(SWEEP_INTERVAL, sweep_ticket_hash_table, NULL);
915 }
916
917 #endif /* HAVE_KRB4 */
918