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