]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/kstuff.c
r4275@bucket (orig r265): kcr | 2008-01-21 02:57:32 -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(ZNotice_t *, C_Block);
26 static ZChecksum_t compute_rlm_checksum(ZNotice_t *, C_Block);
27 static Code_t ZCheckAuthentication4(ZNotice_t *notice, struct sockaddr_in *from);
28
29 /*
30  * GetKerberosData
31  *
32  * get ticket from file descriptor and decode it.
33  * Return KFAILURE if we barf on reading the ticket, else return
34  * the value of rd_ap_req() applied to the ticket.
35  */
36 int
37 GetKerberosData(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(int fd,        /* file descriptor to write onto */
98                  KTEXT ticket,  /* where to put ticket (return) */
99                  char *service, /* service name, foreign host */
100                  char *host)
101                  
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     char *dst;
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     char *dst;
192
193     for (i=0; i<20; i++) {
194         if (read(fd, &p[i], 1) != 1) {
195             p[i] = 0;
196             syslog(LOG_WARNING,"bad read reply len @%d (got \"%s\")", i, p);
197             return(KFAILURE);
198         }
199         if (p[i] == ' ') {
200             p[i] = '\0';
201             break;
202         }
203     }
204     if (i == 20 || strncmp(p, "V5-", 3) || !atoi(p+3)) {
205         syslog(LOG_WARNING,"bad reply len");
206         return ZSRV_PKSHORT;
207     }
208     data->length = atoi(p+3);
209     data->data = malloc(data->length);
210     if (! data->data) {
211        data->length = 0;
212        return errno;
213     }
214     dst=data->data;
215     for (i=0; i < data->length; i++) {
216         if (read(fd, dst++, 1) != 1) {
217             free(data->data);
218             memset((char *)data, 0, sizeof(krb5_data));
219             syslog(LOG_WARNING,"bad read reply string");
220             return ZSRV_PKSHORT;
221         }
222     }
223     return 0;
224 }
225
226 Code_t
227 SendKrb5Data(int fd, krb5_data *data) {
228     char p[32];
229     int written, size_to_write;
230     sprintf(p, "V5-%d ", data->length);
231     size_to_write = strlen (p);
232     if (size_to_write != (written = write(fd, p, size_to_write)) ||
233         data->length != (written = write(fd, data->data, data->length))) {
234         return (written < 0) ? errno : ZSRV_PKSHORT; 
235     }    
236     return 0;
237 }
238 #endif
239
240 Code_t
241 ZCheckRealmAuthentication(ZNotice_t *notice,
242                           struct sockaddr_in *from,
243                           char *realm)
244 {       
245 #ifdef HAVE_KRB5
246     char *authbuf;
247     char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4+1024];
248     krb5_principal princ;
249     krb5_data packet;
250     krb5_ticket *tkt;
251     char *name;
252     krb5_error_code result;
253     krb5_principal server;
254     krb5_keytab keytabid = 0;
255     krb5_auth_context authctx;
256     krb5_keyblock *keyblock; 
257     krb5_enctype enctype; 
258     krb5_cksumtype cksumtype; 
259     krb5_data cksumbuf;
260     int valid;
261     char *cksum0_base, *cksum1_base, *cksum2_base; 
262     char *x; 
263     unsigned char *asn1_data;
264     unsigned char *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((unsigned char *)notice->z_ascii_authent, 
287                    (unsigned char *)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((unsigned char *)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     unsigned 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 *x; 
545     unsigned 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((unsigned char *)notice->z_ascii_authent, 
573                    authbuf,
574                    len, &len) == ZERR_BADFIELD) {
575         return ZAUTH_FAILED;
576     }
577
578     packet.length = len;
579     packet.data = (char *)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((unsigned char *)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 static 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;
818     AUTH_DAT dat;
819     ZChecksum_t checksum;
820     char instance[INST_SZ+1];
821
822     if (!notice->z_auth)
823         return ZAUTH_NO;
824
825     /* Check for bogus authentication data length. */
826     if (notice->z_authent_len <= 0)
827         return ZAUTH_FAILED;
828
829     /* Read in the authentication data. */
830     if (ZReadAscii(notice->z_ascii_authent, 
831                    strlen(notice->z_ascii_authent)+1, 
832                    (unsigned char *)authent.dat, 
833                    notice->z_authent_len) == ZERR_BADFIELD) {
834         return ZAUTH_FAILED;
835     }
836     authent.length = notice->z_authent_len;
837
838     strcpy(instance, SERVER_INSTANCE);
839
840     /* We don't have the session key cached; do it the long way. */
841     result = krb_rd_req(&authent, SERVER_SERVICE, instance,
842                         from->sin_addr.s_addr, &dat, srvtab_file);
843     if (result == RD_AP_OK) {
844         ZSetSessionDES(&dat.session);
845         sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
846                 dat.pinst, dat.prealm);
847         if (strcmp(srcprincipal, notice->z_sender))
848             return ZAUTH_FAILED;
849     } else {
850         return ZAUTH_FAILED;    /* didn't decode correctly */
851     }
852
853     /* Check the cryptographic checksum. */
854 #ifdef NOENCRYPTION
855     checksum = 0;
856 #else
857     checksum = compute_checksum(notice, dat.session);
858 #endif
859     if (checksum != notice->z_checksum)
860         return ZAUTH_FAILED;
861
862     return ZAUTH_YES;
863
864 #else /* !HAVE_KRB4 */
865     return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
866 #endif
867 }
868
869
870 #ifdef HAVE_KRB4
871 static ZChecksum_t
872 compute_checksum(ZNotice_t *notice,
873                  C_Block session_key)
874 {
875 #ifdef NOENCRYPTION
876     return 0;
877 #else
878     ZChecksum_t checksum;
879     char *cstart, *cend, *hstart = notice->z_packet, *hend = notice->z_message;
880
881     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
882     cend = cstart + strlen(cstart) + 1;
883     checksum = des_quad_cksum((unsigned char *)hstart, NULL, cstart - hstart, 0, session_key);
884     checksum ^= des_quad_cksum((unsigned char *)cend, NULL, hend - cend, 0, session_key);
885     checksum ^= des_quad_cksum((unsigned char *)notice->z_message, NULL, notice->z_message_len,
886                                0, session_key);
887     return checksum;
888 #endif
889 }
890
891 static ZChecksum_t compute_rlm_checksum(ZNotice_t *notice,
892                                         C_Block session_key)
893 {
894 #ifdef NOENCRYPTION
895     return 0;
896 #else
897     ZChecksum_t checksum;
898     char *cstart, *cend, *hstart = notice->z_packet;
899
900     cstart = notice->z_default_format + strlen(notice->z_default_format) + 1;
901     cend = cstart + strlen(cstart) + 1;
902     checksum = des_quad_cksum((unsigned char *)hstart, NULL, cstart - hstart, 0, session_key);
903     return checksum;
904 #endif
905 }
906
907 #ifdef HAVE_KRB5
908 krb5_error_code 
909 Z_krb5_init_keyblock(krb5_context context,
910         krb5_enctype type,
911         size_t size,
912         krb5_keyblock **key)
913 {
914 #ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
915         return krb5_init_keyblock(context, type, size, key);
916 #else
917         krb5_error_code ret;
918         krb5_keyblock *tmp, tmp_ss;
919         tmp = &tmp_ss;
920
921         *key = NULL;
922         Z_enctype(tmp) = type;
923         Z_keylen(tmp) = size;
924         Z_keydata(tmp) = malloc(size);
925         if (!Z_keydata(tmp))
926                 return ENOMEM;
927         ret =  krb5_copy_keyblock(context, tmp, key);
928         free(Z_keydata(tmp));
929         return ret;
930 #endif
931 }
932
933 #if 0
934 void ZLogKey(char *label, krb5_keyblock *keyblock) {
935    char *p, *buf;
936    unsigned char *k;
937    int i;
938    buf = malloc(5 *Z_keylen(keyblock)+1);
939
940    k=Z_keydata(keyblock);
941    for (i=0,p=buf; i < Z_keylen(keyblock); i++,p+=strlen(p))
942        sprintf(p, " 0x%02x", k[i]);
943    syslog(LOG_ERR, "key %s is type %d, %d bytes, %s", label, Z_enctype(keyblock), Z_keylen(keyblock), buf);
944    free(buf);
945 }
946 #endif
947
948 void
949 ZSetSession(krb5_keyblock *keyblock) {
950 #if 1
951     krb5_error_code result;
952
953     if (__Zephyr_keyblock) {
954          krb5_free_keyblock_contents(Z_krb5_ctx, __Zephyr_keyblock);
955          result = krb5_copy_keyblock_contents(Z_krb5_ctx, keyblock, __Zephyr_keyblock);
956     } else {
957          result = krb5_copy_keyblock(Z_krb5_ctx, keyblock, &__Zephyr_keyblock);
958     }
959     
960     if (result) /*XXX we're out of memory? */
961         ;
962 #else
963     memcpy(__Zephyr_session, Z_keydata(keyblock), sizeof(C_Block));
964 #endif
965 }
966 #endif
967 #ifdef HAVE_KRB4
968 void
969 ZSetSessionDES(C_Block *key) {
970 #ifdef HAVE_KRB5
971      Code_t result;
972      if (__Zephyr_keyblock) {
973           krb5_free_keyblock(Z_krb5_ctx, __Zephyr_keyblock);
974           __Zephyr_keyblock=NULL;
975      }
976      result = Z_krb5_init_keyblock(Z_krb5_ctx, ENCTYPE_DES_CBC_CRC, 
977                                  sizeof(C_Block),
978                                  &__Zephyr_keyblock);
979      if (result) /*XXX we're out of memory? */
980         return;
981
982      memcpy(Z_keydata(__Zephyr_keyblock), key, sizeof(C_Block));
983 #else
984     memcpy(__Zephyr_session, key, sizeof(C_Block));
985 #endif
986 }
987 #endif
988
989 #endif /* HAVE_KRB4 */
990