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