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