]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/lib/Zinternal.c
r4254@bucket (orig r244): kcr | 2008-01-20 14:40:42 -0500
[1ts-debian.git] / zephyr / lib / Zinternal.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains source for the internal Zephyr routines.
3  *
4  *      Created by:     Robert French
5  *
6  *      $Id$
7  *
8  *      Copyright (c) 1987,1988,1991 by the Massachusetts Institute of
9  *      Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <internal.h>
15 #include <arpa/inet.h>
16 #include <sys/socket.h>
17 #include <utmp.h>
18
19 #ifndef lint
20 static const char rcsid_Zinternal_c[] =
21   "$Id$";
22 static const char copyright[] =
23   "Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.";
24 #endif
25
26 int __Zephyr_fd = -1;
27 int __Zephyr_open;
28 int __Zephyr_port = -1;
29 struct in_addr __My_addr;
30 int __Q_CompleteLength;
31 int __Q_Size;
32 struct _Z_InputQ *__Q_Head, *__Q_Tail;
33 struct sockaddr_in __HM_addr;
34 struct sockaddr_in __HM_addr_real;
35 int __HM_set;
36 int __Zephyr_server;
37 ZLocations_t *__locate_list;
38 int __locate_num;
39 int __locate_next;
40 ZSubscription_t *__subscriptions_list;
41 int __subscriptions_num;
42 int __subscriptions_next;
43 int Z_discarded_packets = 0;
44
45 #ifdef HAVE_KRB5
46 /* This context is used throughout */
47 krb5_context Z_krb5_ctx;
48
49 static struct cksum_map_s {
50   krb5_enctype e;
51   krb5_cksumtype c;
52 } cksum_map[] = {
53   /* per RFC1510 and draft-ietf-krb-wg-crypto-02.txt */
54   { ENCTYPE_NULL,                     CKSUMTYPE_RSA_MD5 },
55   { ENCTYPE_DES_CBC_CRC,              CKSUMTYPE_RSA_MD5_DES },
56   { ENCTYPE_DES_CBC_MD4,              CKSUMTYPE_RSA_MD4_DES },
57   { ENCTYPE_DES_CBC_MD5,              CKSUMTYPE_RSA_MD5_DES },
58
59   /* 
60    * The implementors hate us, and are inconsistent with names for
61    * most things defined after RFC1510.  Note that des3-cbc-sha1
62    * and des3-cbc-sha1-kd are listed by number to avoid confusion 
63    * caused by inconsistency between the names used in the specs
64    * and those used by implementations.
65    * -- jhutz, 30-Nov-2002
66    */
67
68   /* source lost in history (an expired internet-draft) */
69   { 5 /* des3-cbc-md5 */,             9  /* rsa-md5-des3 */ },
70   { 7 /* des3-cbc-sha1 */,            13 /* hmac-sha1-des3 */ },
71
72   /* per draft-ietf-krb-wg-crypto-02.txt */
73   { 16 /* des3-cbc-sha1-kd */,        12 /* hmac-sha1-des3-kd */ },
74
75   /* per draft-raeburn-krb-rijndael-krb-02.txt */
76   { 17 /* aes128-cts-hmac-sha1-96 */, 15 /* hmac-sha1-96-aes128 */ },
77   { 18 /* aes256-cts-hmac-sha1-96 */, 16 /* hmac-sha1-96-aes256 */ },
78
79   /* per draft-brezak-win2k-krb-rc4-hmac-04.txt */
80   { 23 /* rc4-hmac */,                -138 /* hmac-md5 */ },
81   { 24 /* rc4-hmac-exp */,            -138 /* hmac-md5 */ },
82 };
83 #define N_CKSUM_MAP (sizeof(cksum_map) / sizeof(struct cksum_map_s))
84
85 Code_t
86 Z_krb5_lookup_cksumtype(krb5_enctype e,
87                         krb5_cksumtype *c)
88 {
89   int i;
90
91   for (i = 0; i < N_CKSUM_MAP; i++) {
92     if (cksum_map[i].e == e) {
93       *c = cksum_map[i].c;
94       return ZERR_NONE;
95     }
96   }
97   return KRB5_PROG_ETYPE_NOSUPP;
98 }
99 #endif /* HAVE_KRB5 */
100
101 char __Zephyr_realm[REALM_SZ];
102
103 #ifdef Z_DEBUG
104 void (*__Z_debug_print)(const char *fmt, va_list args, void *closure);
105 void *__Z_debug_print_closure;
106 #endif
107
108 #define min(a,b) ((a)<(b)?(a):(b))
109
110 static int Z_AddField(char **ptr, char *field, char *end);
111 static int find_or_insert_uid(ZUnique_Id_t *uid, ZNotice_Kind_t kind);
112 static Code_t Z_ZcodeFormatRawHeader(ZNotice_t *, char *, int, int *, char **, 
113                                      int *, char **, char **, int cksumtype);
114
115 /* Find or insert uid in the old uids buffer.  The buffer is a sorted
116  * circular queue.  We make the assumption that most packets arrive in
117  * order, so we can usually search for a uid or insert it into the buffer
118  * by looking back just a few entries from the end.  Since this code is
119  * only executed by the client, the implementation isn't microoptimized. */
120 static int
121 find_or_insert_uid(ZUnique_Id_t *uid,
122                    ZNotice_Kind_t kind)
123 {
124     static struct _filter {
125         ZUnique_Id_t    uid;
126         ZNotice_Kind_t  kind;
127         time_t          t;
128     } *buffer;
129     static long size;
130     static long start;
131     static long num;
132
133     time_t now;
134     struct _filter *new;
135     long i, j, new_size;
136     int result;
137
138     /* Initialize the uid buffer if it hasn't been done already. */
139     if (!buffer) {
140         size = Z_INITFILTERSIZE;
141         buffer = (struct _filter *) malloc(size * sizeof(*buffer));
142         if (!buffer)
143             return 0;
144     }
145
146     /* Age the uid buffer, discarding any uids older than the clock skew. */
147     time(&now);
148     while (num && (now - buffer[start % size].t) > CLOCK_SKEW)
149         start++, num--;
150     start %= size;
151
152     /* Make room for a new uid, since we'll probably have to insert one. */
153     if (num == size) {
154         new_size = size * 2 + 2;
155         new = (struct _filter *) malloc(new_size * sizeof(*new));
156         if (!new)
157             return 0;
158         for (i = 0; i < num; i++)
159             new[i] = buffer[(start + i) % size];
160         free(buffer);
161         buffer = new;
162         size = new_size;
163         start = 0;
164     }
165
166     /* Search for this uid in the buffer, starting from the end. */
167     for (i = start + num - 1; i >= start; i--) {
168         result = memcmp(uid, &buffer[i % size].uid, sizeof(*uid));
169         if (result == 0 && buffer[i % size].kind == kind)
170             return 1;
171         if (result > 0)
172             break;
173     }
174
175     /* We didn't find it; insert the uid into the buffer after i. */
176     i++;
177     for (j = start + num; j > i; j--)
178         buffer[j % size] = buffer[(j - 1) % size];
179     buffer[i % size].uid = *uid;
180     buffer[i % size].kind = kind;
181     buffer[i % size].t = now;
182     num++;
183
184     return 0;
185 }
186
187
188 /* Return 1 if there is a packet waiting, 0 otherwise */
189
190 int
191 Z_PacketWaiting(void)
192 {
193     struct timeval tv;
194     fd_set read;
195
196     tv.tv_sec = tv.tv_usec = 0;
197     FD_ZERO(&read);
198     FD_SET(ZGetFD(), &read);
199     return (select(ZGetFD() + 1, &read, NULL, NULL, &tv));
200
201
202
203 /* Wait for a complete notice to become available */
204
205 Code_t
206 Z_WaitForComplete(void)
207 {
208     Code_t retval;
209
210     if (__Q_CompleteLength)
211         return (Z_ReadEnqueue());
212
213     while (!__Q_CompleteLength)
214         if ((retval = Z_ReadWait()) != ZERR_NONE)
215             return (retval);
216
217     return (ZERR_NONE);
218 }
219
220
221 /* Read any available packets and enqueue them */
222
223 Code_t
224 Z_ReadEnqueue(void)
225 {
226     Code_t retval;
227
228     if (ZGetFD() < 0)
229         return (ZERR_NOPORT);
230     
231     while (Z_PacketWaiting())
232         if ((retval = Z_ReadWait()) != ZERR_NONE)
233             return (retval);
234
235     return (ZERR_NONE);
236 }
237
238
239 /*
240  * Search the queue for a notice with the proper multiuid - remove any
241  * notices that haven't been touched in a while
242  */
243
244 struct _Z_InputQ *
245 Z_SearchQueue(ZUnique_Id_t *uid,
246               ZNotice_Kind_t kind)
247 {
248     register struct _Z_InputQ *qptr;
249     struct _Z_InputQ *next;
250     struct timeval tv;
251
252     (void) gettimeofday(&tv, (struct timezone *)0);
253
254     qptr = __Q_Head;
255
256     while (qptr) {
257         if (ZCompareUID(uid, &qptr->uid) && qptr->kind == kind)
258             return (qptr);
259         next = qptr->next;
260         if (qptr->timep && (qptr->timep+Z_NOTICETIMELIMIT < tv.tv_sec))
261             Z_RemQueue(qptr);
262         qptr = next;
263     }
264     return (NULL);
265 }
266
267 /*
268  * Now we delve into really convoluted queue handling and
269  * fragmentation reassembly algorithms and other stuff you probably
270  * don't want to look at...
271  *
272  * This routine does NOT guarantee a complete packet will be ready when it
273  * returns.
274  */
275
276 Code_t
277 Z_ReadWait(void)
278 {
279     register struct _Z_InputQ *qptr;
280     ZNotice_t notice;
281     ZPacket_t packet;
282     struct sockaddr_in olddest, from;
283     int from_len, packet_len, zvlen, part, partof;
284     char *slash;
285     Code_t retval;
286     fd_set fds;
287     struct timeval tv;
288
289     if (ZGetFD() < 0)
290         return (ZERR_NOPORT);
291         
292     FD_ZERO(&fds);
293     FD_SET(ZGetFD(), &fds);
294     tv.tv_sec = 60;
295     tv.tv_usec = 0;
296
297     if (select(ZGetFD() + 1, &fds, NULL, NULL, &tv) < 0)
298       return (errno);
299     if (!FD_ISSET(ZGetFD(), &fds))
300       return ETIMEDOUT;
301
302     from_len = sizeof(struct sockaddr_in);
303
304     packet_len = recvfrom(ZGetFD(), packet, sizeof(packet), 0, 
305                           (struct sockaddr *)&from, &from_len);
306
307     if (packet_len < 0)
308         return (errno);
309
310     if (!packet_len)
311         return (ZERR_EOF);
312
313     /* Ignore obviously non-Zephyr packets. */
314     zvlen = sizeof(ZVERSIONHDR) - 1;
315     if (packet_len < zvlen || memcmp(packet, ZVERSIONHDR, zvlen) != 0) {
316         Z_discarded_packets++;
317         return (ZERR_NONE);
318     }   
319
320     /* Parse the notice */
321     if ((retval = ZParseNotice(packet, packet_len, &notice)) != ZERR_NONE)
322         return (retval);
323
324     /*
325      * If we're not a server and the notice is of an appropriate kind,
326      * send back a CLIENTACK to whoever sent it to say we got it.
327      */
328     if (!__Zephyr_server) {
329         if (notice.z_kind != HMACK && notice.z_kind != SERVACK &&
330             notice.z_kind != SERVNAK && notice.z_kind != CLIENTACK) {
331             ZNotice_t tmpnotice;
332             ZPacket_t pkt;
333             int len;
334
335             tmpnotice = notice;
336             tmpnotice.z_kind = CLIENTACK;
337             tmpnotice.z_message_len = 0;
338             olddest = __HM_addr;
339             __HM_addr = from;
340             if ((retval = ZFormatSmallRawNotice(&tmpnotice, pkt, &len))
341                 != ZERR_NONE)
342                 return(retval);
343             if ((retval = ZSendPacket(pkt, len, 0)) != ZERR_NONE)
344                 return (retval);
345             __HM_addr = olddest;
346         }
347         if (find_or_insert_uid(&notice.z_uid, notice.z_kind))
348             return(ZERR_NONE);
349
350         /* Check authentication on the notice. */
351         notice.z_checked_auth = ZCheckAuthentication(&notice, &from);
352     }
353
354
355     /*
356      * Parse apart the z_multinotice field - if the field is blank for
357      * some reason, assume this packet stands by itself.
358      */
359     slash = strchr(notice.z_multinotice, '/');
360     if (slash) {
361         part = atoi(notice.z_multinotice);
362         partof = atoi(slash+1);
363         if (part > partof || partof == 0) {
364             part = 0;
365             partof = notice.z_message_len;
366         }
367     }
368     else {
369         part = 0;
370         partof = notice.z_message_len;
371     }
372
373     /* Too big a packet...just ignore it! */
374     if (partof > Z_MAXNOTICESIZE)
375         return (ZERR_NONE);
376
377     /*
378      * If we aren't a server and we can find a notice in the queue
379      * with the same multiuid field, insert the current fragment as
380      * appropriate.
381      */
382     switch (notice.z_kind) {
383     case SERVACK:
384     case SERVNAK:
385         /* The SERVACK and SERVNAK replies shouldn't be reassembled
386            (they have no parts).  Instead, we should hold on to the reply
387            ONLY if it's the first part of a fragmented message, i.e.
388            multi_uid == uid.  This allows programs to wait for the uid
389            of the first packet, and get a response when that notice
390            arrives.  Acknowledgements of the other fragments are discarded
391            (XXX we assume here that they all carry the same information
392            regarding failure/success)
393          */
394         if (!__Zephyr_server &&
395             !ZCompareUID(&notice.z_multiuid, &notice.z_uid))
396             /* they're not the same... throw away this packet. */
397             return(ZERR_NONE);
398         /* fall thru & process it */
399     default:
400         /* for HMACK types, we assume no packet loss (local loopback
401            connections).  The other types can be fragmented and MUST
402            run through this code. */
403         if (!__Zephyr_server && (qptr = Z_SearchQueue(&notice.z_multiuid,
404                                                       notice.z_kind))) {
405             /*
406              * If this is the first fragment, and we haven't already
407              * gotten a first fragment, grab the header from it.
408              */
409             if (part == 0 && !qptr->header) {
410                 qptr->header_len = packet_len-notice.z_message_len;
411                 qptr->header = (char *) malloc((unsigned) qptr->header_len);
412                 if (!qptr->header)
413                     return (ENOMEM);
414                 (void) memcpy(qptr->header, packet, qptr->header_len);
415             }
416             return (Z_AddNoticeToEntry(qptr, &notice, part));
417         }
418     }
419
420     /*
421      * We'll have to create a new entry...make sure the queue isn't
422      * going to get too big.
423      */
424     if (__Q_Size+(__Zephyr_server ? notice.z_message_len : partof) > Z_MAXQUEUESIZE)
425         return (ZERR_NONE);
426
427     /*
428      * This is a notice we haven't heard of, so create a new queue
429      * entry for it and zero it out.
430      */
431     qptr = (struct _Z_InputQ *)malloc(sizeof(struct _Z_InputQ));
432     if (!qptr)
433         return (ENOMEM);
434     (void) memset((char *)qptr, 0, sizeof(struct _Z_InputQ));
435
436     /* Insert the entry at the end of the queue */
437     qptr->next = NULL;
438     qptr->prev = __Q_Tail;
439     if (__Q_Tail)
440         __Q_Tail->next = qptr;
441     __Q_Tail = qptr;
442
443     if (!__Q_Head)
444         __Q_Head = qptr;
445
446     
447     /* Copy the from field, multiuid, kind, and checked authentication. */
448     qptr->from = from;
449     qptr->uid = notice.z_multiuid;
450     qptr->kind = notice.z_kind;
451     qptr->auth = notice.z_checked_auth;
452     
453     /*
454      * If this is the first part of the notice, we take the header
455      * from it.  We only take it if this is the first fragment so that
456      * the Unique ID's will be predictable.
457      *
458      * If a Zephyr Server, we always take the header.
459      */
460     if (__Zephyr_server || part == 0) {
461         qptr->header_len = packet_len-notice.z_message_len;
462         qptr->header = (char *) malloc((unsigned) qptr->header_len);
463         if (!qptr->header)
464             return ENOMEM;
465         (void) memcpy(qptr->header, packet, qptr->header_len);
466     }
467
468     /*
469      * If this is not a fragmented notice, then don't bother with a
470      * hole list.
471      * If we are a Zephyr server, all notices are treated as complete.
472      */
473     if (__Zephyr_server || (part == 0 && notice.z_message_len == partof)) {
474         __Q_CompleteLength++;
475         qptr->holelist = (struct _Z_Hole *) 0;
476         qptr->complete = 1;
477         /* allocate a msg buf for this piece */
478         if (notice.z_message_len == 0)
479             qptr->msg = 0;
480         else if (!(qptr->msg = (char *) malloc((unsigned) notice.z_message_len)))
481             return(ENOMEM);
482         else
483             (void) memcpy(qptr->msg, notice.z_message, notice.z_message_len);
484         qptr->msg_len = notice.z_message_len;
485         __Q_Size += notice.z_message_len;
486         qptr->packet_len = qptr->header_len+qptr->msg_len;
487         if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len)))
488             return (ENOMEM);
489         (void) memcpy(qptr->packet, qptr->header, qptr->header_len);
490         if(qptr->msg)
491             (void) memcpy(qptr->packet+qptr->header_len, qptr->msg,
492                            qptr->msg_len);
493         return (ZERR_NONE);
494     }
495
496     /*
497      * We know how long the message is going to be (this is better
498      * than IP fragmentation...), so go ahead and allocate it all.
499      */
500     if (!(qptr->msg = (char *) malloc((unsigned) partof)) && partof)
501         return (ENOMEM);
502     qptr->msg_len = partof;
503     __Q_Size += partof;
504
505     /*
506      * Well, it's a fragmented notice...allocate a hole list and
507      * initialize it to the full packet size.  Then insert the
508      * current fragment.
509      */
510     if (!(qptr->holelist = (struct _Z_Hole *)
511           malloc(sizeof(struct _Z_Hole))))
512         return (ENOMEM);
513     qptr->holelist->next = (struct _Z_Hole *) 0;
514     qptr->holelist->first = 0;
515     qptr->holelist->last = partof-1;
516     return (Z_AddNoticeToEntry(qptr, &notice, part));
517 }
518
519
520 /* Fragment management routines - compliments, more or less, of RFC815 */
521
522 Code_t
523 Z_AddNoticeToEntry(struct _Z_InputQ *qptr,
524                    ZNotice_t *notice,
525                    int part)
526 {
527     int last, oldfirst, oldlast;
528     struct _Z_Hole *hole, *lasthole;
529     struct timeval tv;
530
531     /* Bounds check. */
532     if (part < 0 || notice->z_message_len < 0 || part > qptr->msg_len
533         || notice->z_message_len > qptr->msg_len - part)
534       return (ZERR_NONE);
535
536     /* Incorporate this notice's checked authentication. */
537     if (notice->z_checked_auth == ZAUTH_FAILED)
538         qptr->auth = ZAUTH_FAILED;
539     else if (notice->z_checked_auth == ZAUTH_NO && qptr->auth != ZAUTH_FAILED)
540         qptr->auth = ZAUTH_NO;
541
542     (void) gettimeofday(&tv, (struct timezone *)0);
543     qptr->timep = tv.tv_sec;
544     
545     last = part+notice->z_message_len-1;
546
547     hole = qptr->holelist;
548     lasthole = (struct _Z_Hole *) 0;
549
550     /* copy in the message body */
551     (void) memcpy(qptr->msg+part, notice->z_message, notice->z_message_len);
552
553     /* Search for a hole that overlaps with the current fragment */
554     while (hole) {
555         if (part <= hole->last && last >= hole->first)
556             break;
557         lasthole = hole;
558         hole = hole->next;
559     }
560
561     /* If we found one, delete it and reconstruct a new hole */
562     if (hole) {
563         oldfirst = hole->first;
564         oldlast = hole->last;
565         if (lasthole)
566             lasthole->next = hole->next;
567         else
568             qptr->holelist = hole->next;
569         free((char *)hole);
570         /*
571          * Now create a new hole that is the original hole without the
572          * current fragment.
573          */
574         if (part > oldfirst) {
575             /* Search for the end of the hole list */
576             hole = qptr->holelist;
577             lasthole = (struct _Z_Hole *) 0;
578             while (hole) {
579                 lasthole = hole;
580                 hole = hole->next;
581             }
582             if (lasthole) {
583                 if (!(lasthole->next = (struct _Z_Hole *)
584                       malloc(sizeof(struct _Z_InputQ))))
585                     return (ENOMEM);
586                 hole = lasthole->next;
587             }
588             else {
589                 if (!(qptr->holelist = (struct _Z_Hole *)
590                       malloc(sizeof(struct _Z_InputQ))))
591                     return (ENOMEM);
592                 hole = qptr->holelist;
593             }
594             hole->next = NULL;
595             hole->first = oldfirst;
596             hole->last = part-1;
597         }
598         if (last < oldlast) {
599             /* Search for the end of the hole list */
600             hole = qptr->holelist;
601             lasthole = (struct _Z_Hole *) 0;
602             while (hole) {
603                 lasthole = hole;
604                 hole = hole->next;
605             }
606             if (lasthole) {
607                 if (!(lasthole->next = (struct _Z_Hole *)
608                       malloc(sizeof(struct _Z_InputQ))))
609                     return (ENOMEM);
610                 hole = lasthole->next;
611             }
612             else {
613                 if (!(qptr->holelist = (struct _Z_Hole *)
614                       malloc(sizeof(struct _Z_InputQ))))
615                     return (ENOMEM);
616                 hole = qptr->holelist;
617             }
618             hole->next = (struct _Z_Hole *) 0;
619             hole->first = last+1;
620             hole->last = oldlast;
621         }
622     }
623
624     if (!qptr->holelist) {
625         if (!qptr->complete)
626             __Q_CompleteLength++;
627         qptr->complete = 1;
628         qptr->timep = 0;                /* don't time out anymore */
629         qptr->packet_len = qptr->header_len+qptr->msg_len;
630         if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len)))
631             return (ENOMEM);
632         (void) memcpy(qptr->packet, qptr->header, qptr->header_len);
633         (void) memcpy(qptr->packet+qptr->header_len, qptr->msg,
634                        qptr->msg_len);
635     }
636     
637     return (ZERR_NONE);
638 }
639
640 void
641 Z_gettimeofday(struct _ZTimeval *ztv,
642                struct timezone *tz)
643 {
644         struct timeval tv;
645         (void) gettimeofday(&tv, tz); /* yeah, yeah, I know */
646         ztv->tv_sec=tv.tv_sec;
647         ztv->tv_usec=tv.tv_usec;
648 }
649
650 Code_t
651 Z_FormatHeader(ZNotice_t *notice,
652                char *buffer,
653                int buffer_len,
654                int *len,
655                Z_AuthProc cert_routine)
656 {
657     Code_t retval;
658     static char version[BUFSIZ]; /* default init should be all \0 */
659     struct sockaddr_in name;
660     int namelen = sizeof(name);
661
662     if (!notice->z_sender)
663         notice->z_sender = ZGetSender();
664
665     if (notice->z_port == 0) {
666         if (ZGetFD() < 0) {
667             retval = ZOpenPort((u_short *)0);
668             if (retval != ZERR_NONE)
669                 return (retval);
670         }
671         retval = getsockname(ZGetFD(), (struct sockaddr *) &name, &namelen);
672         if (retval != 0)
673             return (retval);
674         notice->z_port = name.sin_port;
675     }
676
677     notice->z_multinotice = "";
678     
679     (void) Z_gettimeofday(&notice->z_uid.tv, (struct timezone *)0);
680     notice->z_uid.tv.tv_sec = htonl((u_long) notice->z_uid.tv.tv_sec);
681     notice->z_uid.tv.tv_usec = htonl((u_long) notice->z_uid.tv.tv_usec);
682     
683     (void) memcpy(&notice->z_uid.zuid_addr, &__My_addr, sizeof(__My_addr));
684
685     notice->z_multiuid = notice->z_uid;
686
687     if (!version[0])
688             (void) sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
689                            ZVERSIONMINOR);
690     notice->z_version = version;
691
692     return Z_FormatAuthHeader(notice, buffer, buffer_len, len, cert_routine);
693 }
694
695 Code_t
696 Z_NewFormatHeader(ZNotice_t *notice,
697                   char *buffer,
698                   int buffer_len,
699                   int *len,
700                   Z_AuthProc cert_routine)
701 {
702     Code_t retval;
703     static char version[BUFSIZ]; /* default init should be all \0 */
704     struct sockaddr_in name;
705     struct timeval tv;
706     int namelen = sizeof(name);
707
708     if (!notice->z_sender)
709         notice->z_sender = ZGetSender();
710
711     if (notice->z_port == 0) {
712         if (ZGetFD() < 0) {
713             retval = ZOpenPort((u_short *)0);
714             if (retval != ZERR_NONE)
715                 return (retval);
716         }
717         retval = getsockname(ZGetFD(), (struct sockaddr *) &name, &namelen);
718         if (retval != 0)
719             return (retval);
720         notice->z_port = name.sin_port;
721     }
722
723     notice->z_multinotice = "";
724     
725     (void) gettimeofday(&tv, (struct timezone *)0);
726     notice->z_uid.tv.tv_sec = htonl((u_long) tv.tv_sec);
727     notice->z_uid.tv.tv_usec = htonl((u_long) tv.tv_usec);
728     
729     (void) memcpy(&notice->z_uid.zuid_addr, &__My_addr, sizeof(__My_addr));
730
731     notice->z_multiuid = notice->z_uid;
732
733     if (!version[0])
734             (void) sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR,
735                            ZVERSIONMINOR);
736     notice->z_version = version;
737
738     return Z_NewFormatAuthHeader(notice, buffer, buffer_len, len, cert_routine);
739 }
740
741 Code_t
742 Z_FormatAuthHeader(ZNotice_t *notice,
743                    char *buffer,
744                    int buffer_len,
745                    int *len,
746                    Z_AuthProc cert_routine)
747 {
748     if (!cert_routine) {
749         notice->z_auth = 0;
750         notice->z_authent_len = 0;
751         notice->z_ascii_authent = "";
752         notice->z_checksum = 0;
753         return (Z_FormatRawHeader(notice, buffer, buffer_len,
754                                   len, NULL, NULL));
755     }
756     
757     return ((*cert_routine)(notice, buffer, buffer_len, len));
758 }
759
760 Code_t
761 Z_NewFormatAuthHeader(ZNotice_t *notice,
762                       char *buffer,
763                       int buffer_len,
764                       int *len,
765                       Z_AuthProc cert_routine)
766 {
767     if (!cert_routine) {
768         notice->z_auth = 0;
769         notice->z_authent_len = 0;
770         notice->z_ascii_authent = "";
771         notice->z_checksum = 0;
772         return (Z_FormatRawHeader(notice, buffer, buffer_len,
773                                   len, NULL, NULL));
774     }
775     
776     return ((*cert_routine)(notice, buffer, buffer_len, len));
777
778         
779 Code_t
780 Z_NewFormatRawHeader(ZNotice_t *notice,
781                      char *buffer,
782                      int buffer_len,
783                      int *hdr_len,
784                      char **cksum_start,
785                      int *cksum_len,
786                      char **cstart,
787                      char **cend)
788 {
789    return(Z_ZcodeFormatRawHeader(notice, buffer, buffer_len, hdr_len,
790                                  cksum_start, cksum_len, cstart, cend, 0));
791 }
792
793 Code_t
794 Z_AsciiFormatRawHeader(ZNotice_t *notice,
795                        char *buffer,
796                        int buffer_len,
797                        int *hdr_len,
798                        char **cksum_start,
799                        int *cksum_len,
800                        char **cstart,
801                        char **cend)
802 {
803    return(Z_ZcodeFormatRawHeader(notice, buffer, buffer_len, hdr_len,
804                                  cksum_start, cksum_len, cstart, cend, 1));
805 }
806
807 static Code_t
808 Z_ZcodeFormatRawHeader(ZNotice_t *notice,
809                        char *buffer,
810                        int buffer_len,
811                        int *hdr_len,
812                        char **cksum_start,
813                        int *cksum_len,
814                        char **cstart,
815                        char **cend,
816                        int cksumstyle)
817 {
818     static char version_nogalaxy[BUFSIZ]; /* default init should be all \0 */
819     char newrecip[BUFSIZ];
820     char *ptr, *end;
821     int i;
822
823     if (!notice->z_class)
824             notice->z_class = "";
825
826     if (!notice->z_class_inst)
827             notice->z_class_inst = "";
828
829     if (!notice->z_opcode)
830             notice->z_opcode = "";
831
832     if (!notice->z_recipient)
833             notice->z_recipient = "";
834
835     if (!notice->z_default_format)
836             notice->z_default_format = "";
837
838     ptr = buffer;
839     end = buffer+buffer_len;
840
841     if (cksum_start)
842         *cksum_start = ptr;
843
844     (void) sprintf(version_nogalaxy, "%s%d.%d", ZVERSIONHDR,
845                    ZVERSIONMAJOR, ZVERSIONMINOR);
846
847     notice->z_version = version_nogalaxy;
848
849     if (Z_AddField(&ptr, version_nogalaxy, end))
850         return (ZERR_HEADERLEN);
851
852     if (ZMakeAscii32(ptr, end-ptr,
853                      Z_NUMFIELDS + notice->z_num_other_fields)
854         == ZERR_FIELDLEN)
855         return (ZERR_HEADERLEN);
856     ptr += strlen(ptr)+1;
857
858     if (ZMakeAscii32(ptr, end-ptr, notice->z_kind) == ZERR_FIELDLEN)
859         return (ZERR_HEADERLEN);
860     ptr += strlen(ptr)+1;
861
862     if (ZMakeAscii(ptr, end-ptr, (unsigned char *)&notice->z_uid, 
863                    sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
864         return (ZERR_HEADERLEN);
865     ptr += strlen(ptr)+1;
866
867     if (ZMakeAscii16(ptr, end-ptr, ntohs(notice->z_port)) == ZERR_FIELDLEN)
868         return (ZERR_HEADERLEN);
869     ptr += strlen(ptr)+1;
870
871     if (ZMakeAscii32(ptr, end-ptr, notice->z_auth) == ZERR_FIELDLEN)
872         return (ZERR_HEADERLEN);
873     ptr += strlen(ptr)+1;
874
875     if (ZMakeAscii32(ptr, end-ptr, notice->z_authent_len) == ZERR_FIELDLEN)
876         return (ZERR_HEADERLEN);
877     ptr += strlen(ptr)+1;
878
879     if (Z_AddField(&ptr, notice->z_ascii_authent, end))
880         return (ZERR_HEADERLEN);
881     if (Z_AddField(&ptr, notice->z_class, end))
882         return (ZERR_HEADERLEN);
883     if (Z_AddField(&ptr, notice->z_class_inst, end))
884         return (ZERR_HEADERLEN);
885     if (Z_AddField(&ptr, notice->z_opcode, end))
886         return (ZERR_HEADERLEN);
887     if (Z_AddField(&ptr, notice->z_sender, end))
888         return (ZERR_HEADERLEN);
889     if (strchr(notice->z_recipient, '@') || !*notice->z_recipient) {
890         if (Z_AddField(&ptr, notice->z_recipient, end))
891             return (ZERR_HEADERLEN);
892     }
893     else {
894         if (strlen(notice->z_recipient) + strlen(__Zephyr_realm) + 2 >
895             sizeof(newrecip))
896             return (ZERR_HEADERLEN);
897         (void) sprintf(newrecip, "%s@%s", notice->z_recipient, __Zephyr_realm);
898         if (Z_AddField(&ptr, newrecip, end))
899             return (ZERR_HEADERLEN);
900     }           
901     if (Z_AddField(&ptr, notice->z_default_format, end))
902         return (ZERR_HEADERLEN);
903
904     /* copy back the end pointer location for crypto checksum */
905     if (cstart)
906         *cstart = ptr;
907     if (cksumstyle == 1) {
908       if (Z_AddField(&ptr, notice->z_ascii_checksum, end))
909          return (ZERR_HEADERLEN);
910     } else {
911 #ifdef xZCODE_K4SUM
912     if (ZMakeZcode32(ptr, end-ptr, notice->z_checksum) == ZERR_FIELDLEN)
913         return ZERR_HEADERLEN;
914 #else
915     if (ZMakeAscii32(ptr, end-ptr, notice->z_checksum) == ZERR_FIELDLEN)
916         return (ZERR_HEADERLEN);
917 #endif
918     ptr += strlen(ptr)+1;
919     }
920     if (cend)
921         *cend = ptr;
922
923     if (Z_AddField(&ptr, notice->z_multinotice, end))
924         return (ZERR_HEADERLEN);
925
926     if (ZMakeAscii(ptr, end-ptr, (unsigned char *)&notice->z_multiuid, 
927                    sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
928         return (ZERR_HEADERLEN);
929     ptr += strlen(ptr)+1;
930         
931     for (i=0;i<notice->z_num_other_fields;i++)
932         if (Z_AddField(&ptr, notice->z_other_fields[i], end))
933             return (ZERR_HEADERLEN);
934     
935     if (cksum_len)
936         *cksum_len = ptr-*cksum_start;
937
938     *hdr_len = ptr-buffer;
939
940 #if 0
941     {
942         printf("Z_FormatRawHeader output:\n");
943         for (i = 0; i < *hdr_len; i += 16) {
944             int i2;
945             printf("%03d:", i);
946             for (i2 = i; i2 < i+16 && i2 < *hdr_len; i2++)
947                 printf(" %02x", buffer[i2] & 0xff);
948             for (; i2 < i+16; i2++)
949                 printf("   ");
950             printf("  ");
951             for (i2 = i; i2 < i+16 && i2 < *hdr_len; i2++)
952                 printf("%c",
953                        ((buffer[i2] > 0 && buffer[i2] < 127 && isprint(buffer[i2]))
954                         ? buffer[i2]
955                         : '.'));
956             printf("\n");
957         }
958     }
959 #endif
960
961     return (ZERR_NONE);
962 }
963
964 Code_t
965 Z_FormatRawHeader(ZNotice_t *notice,
966                   char *buffer,
967                   int buffer_len,
968                   int *len,
969                   char **cstart,
970                   char **cend)
971 {
972     char newrecip[BUFSIZ];
973     char *ptr, *end;
974     int i;
975
976     if (!notice->z_class)
977             notice->z_class = "";
978
979     if (!notice->z_class_inst)
980             notice->z_class_inst = "";
981
982     if (!notice->z_opcode)
983             notice->z_opcode = "";
984
985     if (!notice->z_recipient)
986             notice->z_recipient = "";
987
988     if (!notice->z_default_format)
989             notice->z_default_format = "";
990
991     ptr = buffer;
992     end = buffer+buffer_len;
993
994     if (buffer_len < strlen(notice->z_version)+1)
995         return (ZERR_HEADERLEN);
996
997     (void) strcpy(ptr, notice->z_version);
998     ptr += strlen(ptr)+1;
999
1000     if (ZMakeAscii32(ptr, end-ptr, Z_NUMFIELDS + notice->z_num_other_fields)
1001         == ZERR_FIELDLEN)
1002         return (ZERR_HEADERLEN);
1003     ptr += strlen(ptr)+1;
1004
1005     if (ZMakeAscii32(ptr, end-ptr, notice->z_kind) == ZERR_FIELDLEN)
1006         return (ZERR_HEADERLEN);
1007     ptr += strlen(ptr)+1;
1008
1009     if (ZMakeAscii(ptr, end-ptr, (unsigned char *)&notice->z_uid, 
1010                    sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
1011         return (ZERR_HEADERLEN);
1012     ptr += strlen(ptr)+1;
1013
1014     if (ZMakeAscii16(ptr, end-ptr, ntohs(notice->z_port)) == ZERR_FIELDLEN)
1015         return (ZERR_HEADERLEN);
1016     ptr += strlen(ptr)+1;
1017
1018     if (ZMakeAscii32(ptr, end-ptr, notice->z_auth) == ZERR_FIELDLEN)
1019         return (ZERR_HEADERLEN);
1020     ptr += strlen(ptr)+1;
1021
1022     if (ZMakeAscii32(ptr, end-ptr, notice->z_authent_len) == ZERR_FIELDLEN)
1023         return (ZERR_HEADERLEN);
1024     ptr += strlen(ptr)+1;
1025
1026     if (Z_AddField(&ptr, notice->z_ascii_authent, end))
1027         return (ZERR_HEADERLEN);
1028     if (Z_AddField(&ptr, notice->z_class, end))
1029         return (ZERR_HEADERLEN);
1030     if (Z_AddField(&ptr, notice->z_class_inst, end))
1031         return (ZERR_HEADERLEN);
1032     if (Z_AddField(&ptr, notice->z_opcode, end))
1033         return (ZERR_HEADERLEN);
1034     if (Z_AddField(&ptr, notice->z_sender, end))
1035         return (ZERR_HEADERLEN);
1036     if (strchr(notice->z_recipient, '@') || !*notice->z_recipient) {
1037         if (Z_AddField(&ptr, notice->z_recipient, end))
1038             return (ZERR_HEADERLEN);
1039     }
1040     else {
1041         if (strlen(notice->z_recipient) + strlen(__Zephyr_realm) + 2 >
1042             sizeof(newrecip))
1043             return (ZERR_HEADERLEN);
1044         (void) sprintf(newrecip, "%s@%s", notice->z_recipient, __Zephyr_realm);
1045         if (Z_AddField(&ptr, newrecip, end))
1046             return (ZERR_HEADERLEN);
1047     }           
1048     if (Z_AddField(&ptr, notice->z_default_format, end))
1049         return (ZERR_HEADERLEN);
1050
1051     /* copy back the end pointer location for crypto checksum */
1052     if (cstart)
1053         *cstart = ptr;
1054     if (ZMakeAscii32(ptr, end-ptr, notice->z_checksum) == ZERR_FIELDLEN)
1055         return (ZERR_HEADERLEN);
1056     ptr += strlen(ptr)+1;
1057     if (cend)
1058         *cend = ptr;
1059
1060     if (Z_AddField(&ptr, notice->z_multinotice, end))
1061         return (ZERR_HEADERLEN);
1062
1063     if (ZMakeAscii(ptr, end-ptr, (unsigned char *)&notice->z_multiuid, 
1064                    sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN)
1065         return (ZERR_HEADERLEN);
1066     ptr += strlen(ptr)+1;
1067         
1068     for (i=0;i<notice->z_num_other_fields;i++)
1069         if (Z_AddField(&ptr, notice->z_other_fields[i], end))
1070             return (ZERR_HEADERLEN);
1071     
1072     *len = ptr-buffer;
1073         
1074     return (ZERR_NONE);
1075 }
1076
1077 static int
1078 Z_AddField(char **ptr,
1079            char *field,
1080            char *end)
1081 {
1082     register int len;
1083
1084     len = field ? strlen (field) + 1 : 1;
1085
1086     if (*ptr+len > end)
1087         return 1;
1088     if (field)
1089         (void) strcpy(*ptr, field);
1090     else
1091         **ptr = '\0';
1092     *ptr += len;
1093
1094     return 0;
1095 }
1096
1097 struct _Z_InputQ *
1098 Z_GetFirstComplete(void)
1099 {
1100     struct _Z_InputQ *qptr;
1101
1102     qptr = __Q_Head;
1103
1104     while (qptr) {
1105         if (qptr->complete)
1106             return (qptr);
1107         qptr = qptr->next;
1108     }
1109
1110     return ((struct _Z_InputQ *)0);
1111 }
1112
1113 struct _Z_InputQ *
1114 Z_GetNextComplete(struct _Z_InputQ *qptr)
1115 {
1116     qptr = qptr->next;
1117     while (qptr) {
1118         if (qptr->complete)
1119             return (qptr);
1120         qptr = qptr->next;
1121     }
1122
1123     return ((struct _Z_InputQ *)0);
1124 }
1125
1126 void
1127 Z_RemQueue(struct _Z_InputQ *qptr)
1128 {
1129     struct _Z_Hole *hole, *nexthole;
1130     
1131     if (qptr->complete)
1132         __Q_CompleteLength--;
1133
1134     __Q_Size -= qptr->msg_len;
1135     
1136     if (qptr->header)
1137         free(qptr->header);
1138     if (qptr->msg)
1139         free(qptr->msg);
1140     if (qptr->packet)
1141         free(qptr->packet);
1142     
1143     hole = qptr->holelist;
1144     while (hole) {
1145         nexthole = hole->next;
1146         free((char *)hole);
1147         hole = nexthole;
1148     }
1149     
1150     if (qptr == __Q_Head && __Q_Head == __Q_Tail) {
1151         free ((char *)qptr);
1152         __Q_Head = (struct _Z_InputQ *)0;
1153         __Q_Tail = (struct _Z_InputQ *)0;
1154         return;
1155     }
1156     
1157     if (qptr == __Q_Head) {
1158         __Q_Head = qptr->next;
1159         __Q_Head->prev = (struct _Z_InputQ *)0;
1160         free ((char *)qptr);
1161         return;
1162     } 
1163     if (qptr == __Q_Tail) {
1164         __Q_Tail = qptr->prev;
1165         __Q_Tail->next = (struct _Z_InputQ *)0;
1166         free ((char *)qptr);
1167         return;
1168     }
1169     qptr->prev->next = qptr->next;
1170     qptr->next->prev = qptr->prev;
1171     free ((char *)qptr);
1172     return;
1173 }
1174
1175 Code_t
1176 Z_SendFragmentedNotice(ZNotice_t *notice,
1177                        int len,
1178                        Z_AuthProc cert_func,
1179                        Z_SendProc send_func)
1180 {
1181     ZNotice_t partnotice;
1182     ZPacket_t buffer;
1183     char multi[64];
1184     int offset, hdrsize, fragsize, ret_len, message_len, waitforack;
1185     Code_t retval;
1186     
1187     hdrsize = len-notice->z_message_len;
1188     fragsize = Z_MAXPKTLEN-hdrsize-Z_FRAGFUDGE;
1189     
1190     offset = 0;
1191
1192     waitforack = ((notice->z_kind == UNACKED || notice->z_kind == ACKED)
1193                   && !__Zephyr_server);
1194     
1195     partnotice = *notice;
1196
1197     while (offset < notice->z_message_len || !notice->z_message_len) {
1198         (void) sprintf(multi, "%d/%d", offset, notice->z_message_len);
1199         partnotice.z_multinotice = multi;
1200         if (offset > 0) {
1201             (void) Z_gettimeofday(&partnotice.z_uid.tv,
1202                                   (struct timezone *)0);
1203             partnotice.z_uid.tv.tv_sec =
1204                 htonl((u_long) partnotice.z_uid.tv.tv_sec);
1205             partnotice.z_uid.tv.tv_usec =
1206                 htonl((u_long) partnotice.z_uid.tv.tv_usec);
1207             (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr, 
1208                           sizeof(__My_addr));
1209         }
1210         message_len = min(notice->z_message_len-offset, fragsize);
1211         partnotice.z_message = notice->z_message+offset;
1212         partnotice.z_message_len = message_len;
1213         if ((retval = Z_FormatAuthHeader(&partnotice, buffer, Z_MAXHEADERLEN,
1214                                          &ret_len, cert_func)) != ZERR_NONE) {
1215             return (retval);
1216         }
1217         memcpy(buffer + ret_len, partnotice.z_message, message_len);
1218         if ((retval = (*send_func)(&partnotice, buffer, ret_len+message_len,
1219                                    waitforack)) != ZERR_NONE) {
1220             return (retval);
1221         }
1222         offset += fragsize;
1223         if (!notice->z_message_len)
1224             break;
1225     }
1226
1227     return (ZERR_NONE);
1228 }
1229
1230 /*ARGSUSED*/
1231 Code_t Z_XmitFragment(ZNotice_t *notice,
1232                       char *buf,
1233                       int len,
1234                       int wait)
1235 {
1236     return(ZSendPacket(buf, len, wait));
1237 }
1238
1239 #ifdef Z_DEBUG
1240 /* For debugging printing */
1241 const char *const ZNoticeKinds[] = {
1242     "UNSAFE", "UNACKED", "ACKED", "HMACK", "HMCTL", "SERVACK", "SERVNAK",
1243     "CLIENTACK", "STAT"
1244 };
1245 #endif
1246
1247 #ifdef Z_DEBUG
1248
1249 #undef Z_debug
1250 void
1251 Z_debug(const char *format, ...)
1252 {
1253     va_list pvar;
1254     if (!__Z_debug_print)
1255       return;
1256     va_start (pvar, format);
1257     (*__Z_debug_print) (format, pvar, __Z_debug_print_closure);
1258     va_end (pvar);
1259 }
1260
1261 void
1262 Z_debug_stderr(const char *format,
1263                va_list args,
1264                void *closure)
1265 {
1266 #ifdef HAVE_VPRINTF
1267     vfprintf (stderr, format, args);
1268 #else
1269     _doprnt (format, args, stderr);
1270 #endif
1271     putc ('\n', stderr);
1272 }
1273
1274 #undef ZGetFD
1275 int ZGetFD (void) { return __Zephyr_fd; }
1276
1277 #undef ZQLength
1278 int ZQLength (void) { return __Q_CompleteLength; }
1279
1280 #undef ZGetDestAddr
1281 struct sockaddr_in ZGetDestAddr (void) { return __HM_addr; }
1282
1283 #undef ZGetRealm
1284 Zconst char * ZGetRealm (void) { return __Zephyr_realm; }
1285
1286 #undef ZSetDebug
1287 void
1288 ZSetDebug(void (*proc) __P((const char *, va_list, void *)),
1289           char *arg)
1290 {
1291     __Z_debug_print = proc;
1292     __Z_debug_print_closure = arg;
1293 }
1294 #endif /* Z_DEBUG */
1295
1296 #ifdef HAVE_KRB5
1297 Code_t
1298 Z_Checksum(krb5_data *cksumbuf,
1299            krb5_keyblock *keyblock, 
1300            krb5_cksumtype cksumtype, 
1301            char **asn1_data,
1302            int *asn1_len)
1303 {
1304     krb5_error_code result;
1305     char *data;
1306     int len;
1307 #if HAVE_KRB5_C_MAKE_CHECKSUM
1308     krb5_checksum checksum;
1309 #else
1310     Checksum checksum;
1311     krb5_crypto cryptctx;
1312 #endif
1313     
1314 #if HAVE_KRB5_C_MAKE_CHECKSUM
1315     /* Create the checksum -- MIT crypto API */
1316     result = krb5_c_make_checksum(Z_krb5_ctx, cksumtype,
1317                                   keyblock, Z_KEYUSAGE_CLT_CKSUM,
1318                                   cksumbuf, &checksum);
1319     if (result)
1320         return result;
1321     /* HOLDING: checksum */
1322
1323     data = checksum.contents;
1324     len = checksum.length;
1325 #else
1326     /* Create the checksum -- heimdal crypto API */
1327     result = krb5_crypto_init(Z_krb5_ctx, keyblock, keyblock->keytype, 
1328                               &cryptctx);
1329     if (result)
1330         return result;
1331
1332     /* HOLDING: cryptctx */
1333     result = krb5_create_checksum(Z_krb5_ctx, cryptctx,
1334                                   Z_KEYUSAGE_CLT_CKSUM, cksumtype,
1335                                   cksumbuf->data, cksumbuf->length,
1336                                   &checksum);
1337     krb5_crypto_destroy(Z_krb5_ctx, cryptctx);
1338     if (result)
1339         return result;
1340
1341     len = checksum.checksum.length;
1342     data = checksum.checksum.data;
1343     /* HOLDING: checksum */
1344 #endif
1345
1346     *asn1_data = malloc(len);
1347     if (*asn1_data == NULL)
1348         return errno;
1349     memcpy(*asn1_data, data, len);
1350     *asn1_len = len;
1351
1352 #if HAVE_KRB5_C_MAKE_CHECKSUM
1353     krb5_free_checksum_contents(Z_krb5_ctx, &checksum);
1354 #else
1355     free_Checksum(&checksum);
1356 #endif
1357
1358     return 0;
1359 }
1360
1361 Code_t
1362 Z_InsertZcodeChecksum(krb5_keyblock *keyblock,
1363                       ZNotice_t *notice, 
1364                       char *buffer,
1365                       char *cksum_start,
1366                       int cksum_len, 
1367                       char *cstart,
1368                       char *cend,
1369                       int buffer_len, 
1370                       int *length_adjust)
1371 {
1372      int plain_len;   /* length of part not to be checksummed */
1373      int cksum0_len;  /* length of part before checksum */
1374      int cksum1_len;  /* length of part after checksum */
1375      krb5_data cksumbuf;
1376      krb5_data cksum;
1377      char *key_data;
1378      int key_len;
1379      krb5_enctype enctype;
1380      krb5_cksumtype cksumtype;
1381      Code_t result;
1382      
1383      key_data = Z_keydata(keyblock);
1384      key_len = Z_keylen(keyblock);
1385      result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
1386      if (result)
1387           return (ZAUTH_FAILED);
1388      
1389      /* Assemble the things to be checksummed */
1390      plain_len  = cksum_start - buffer;
1391      cksum0_len = cstart - cksum_start;
1392      cksum1_len = (cksum_start + cksum_len) - cend;
1393      memset(&cksumbuf, 0, sizeof(cksumbuf));
1394      cksumbuf.length = cksum0_len + cksum1_len + notice->z_message_len;
1395      cksumbuf.data = malloc(cksumbuf.length);
1396      if (!cksumbuf.data)
1397           return ENOMEM;
1398      memcpy(cksumbuf.data, cksum_start, cksum0_len);
1399      memcpy(cksumbuf.data + cksum0_len, cend, cksum1_len);
1400      memcpy(cksumbuf.data + cksum0_len + cksum1_len,
1401             notice->z_message, notice->z_message_len);
1402      /* compute the checksum */
1403      result = Z_Checksum(&cksumbuf, keyblock, cksumtype, 
1404                         (char **)&cksum.data, &cksum.length);
1405      if (result) {
1406           free(cksumbuf.data);
1407           return result;
1408      }
1409      
1410      /*
1411       * OK....  we can zcode to a space starting at 'cstart',
1412       * with a length of buffer_len - (plain_len + cksum_len).
1413       * Then we tack on the end part, which is located at
1414       * cksumbuf.data + cksum0_len and has length cksum1_len
1415       */
1416      
1417      result = ZMakeZcode(cstart, buffer_len - (plain_len + cksum_len),
1418                          cksum.data, cksum.length);
1419      free(cksum.data);
1420      if (!result) {
1421           int zcode_len = strlen(cstart) + 1;
1422           memcpy(cstart + zcode_len, cksumbuf.data + cksum0_len, cksum1_len);
1423           *length_adjust = zcode_len - cksum_len + (cksum0_len + cksum1_len);
1424      }
1425      free(cksumbuf.data);
1426      return result;
1427 }
1428
1429 Code_t
1430 Z_ExtractEncCksum(krb5_keyblock *keyblock,
1431                   krb5_enctype *enctype, 
1432                   krb5_cksumtype *cksumtype)
1433 {
1434     *enctype  = Z_enctype(keyblock); 
1435     return Z_krb5_lookup_cksumtype(*enctype, cksumtype); 
1436 }
1437 #endif
1438
1439 #ifdef HAVE_KRB5
1440 /* returns 0 if invalid or losing, 1 if valid, *sigh* */
1441 int
1442 Z_krb5_verify_cksum(krb5_keyblock *keyblock,
1443                     krb5_data *cksumbuf, 
1444                     krb5_cksumtype cksumtype,
1445                     char *asn1_data, 
1446                     int asn1_len)
1447 {
1448     krb5_error_code result;
1449 #if HAVE_KRB5_C_MAKE_CHECKSUM
1450     krb5_checksum checksum;
1451     krb5_boolean valid;
1452 #else
1453     krb5_crypto cryptctx;
1454     Checksum checksum;
1455     size_t xlen;
1456 #endif
1457
1458     memset(&checksum, 0, sizeof(checksum));
1459 #if HAVE_KRB5_C_MAKE_CHECKSUM
1460     /* Verify the checksum -- MIT crypto API */
1461     checksum.length = asn1_len;
1462     checksum.contents = asn1_data;
1463     checksum.checksum_type = cksumtype;
1464     result = krb5_c_verify_checksum(Z_krb5_ctx,
1465                                     keyblock, Z_KEYUSAGE_SRV_CKSUM,
1466                                     cksumbuf, &checksum, &valid);
1467     if (!result && valid)
1468         return 1;
1469     else
1470         return 0;
1471 #else
1472     checksum.checksum.length = asn1_len;
1473     checksum.checksum.data = asn1_data;
1474     checksum.cksumtype = cksumtype;
1475
1476     result = krb5_crypto_init(Z_krb5_ctx, keyblock, keyblock->keytype, &cryptctx);
1477     if (result)
1478         return result;
1479     
1480     /* HOLDING: cryptctx */
1481     result = krb5_verify_checksum(Z_krb5_ctx, cryptctx,
1482                                   Z_KEYUSAGE_SRV_CKSUM,
1483                                   cksumbuf->data, cksumbuf->length,
1484                                   &checksum);
1485     krb5_crypto_destroy(Z_krb5_ctx, cryptctx);
1486     if (result)
1487         return 0;
1488     else
1489         return 1;
1490 #endif
1491 }
1492 #endif