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