]> git.ozlabs.org Git - ppp.git/blob - pppd/chap.c
extend protocol table
[ppp.git] / pppd / chap.c
1 /*
2  * chap.c - Crytographic Handshake Authentication Protocol.
3  *
4  * Copyright (c) 1991 Gregory M. Christy.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by Gregory M. Christy.  The name of the author may not be used to
13  * endorse or promote products derived from this software without
14  * specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20
21 #ifndef lint
22 static char rcsid[] = "$Id: chap.c,v 1.9 1995/12/18 03:46:19 paulus Exp $";
23 #endif
24
25 /*
26  * TODO:
27  */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <syslog.h>
34
35 #include "pppd.h"
36 #include "chap.h"
37 #include "md5.h"
38
39 struct protent chap_protent = {
40     PPP_CHAP, ChapInit, ChapInput, ChapProtocolReject,
41     ChapLowerUp, ChapLowerDown, NULL, NULL,
42     ChapPrintPkt, NULL, 1, "CHAP"
43 };
44
45 chap_state chap[NUM_PPP];               /* CHAP state; one for each unit */
46
47 static void ChapChallengeTimeout __P((caddr_t));
48 static void ChapResponseTimeout __P((caddr_t));
49 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
50 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
51 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
52 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
53 static void ChapSendStatus __P((chap_state *, int));
54 static void ChapSendChallenge __P((chap_state *));
55 static void ChapSendResponse __P((chap_state *));
56 static void ChapGenChallenge __P((chap_state *));
57
58 extern double drand48 __P((void));
59 extern void srand48 __P((long));
60
61 /*
62  * ChapInit - Initialize a CHAP unit.
63  */
64 void
65 ChapInit(unit)
66     int unit;
67 {
68     chap_state *cstate = &chap[unit];
69
70     BZERO(cstate, sizeof(*cstate));
71     cstate->unit = unit;
72     cstate->clientstate = CHAPCS_INITIAL;
73     cstate->serverstate = CHAPSS_INITIAL;
74     cstate->timeouttime = CHAP_DEFTIMEOUT;
75     cstate->max_transmits = CHAP_DEFTRANSMITS;
76     /* random number generator is initialized in magic_init */
77 }
78
79
80 /*
81  * ChapAuthWithPeer - Authenticate us with our peer (start client).
82  *
83  */
84 void
85 ChapAuthWithPeer(unit, our_name, digest)
86     int unit;
87     char *our_name;
88     int digest;
89 {
90     chap_state *cstate = &chap[unit];
91
92     cstate->resp_name = our_name;
93     cstate->resp_type = digest;
94
95     if (cstate->clientstate == CHAPCS_INITIAL ||
96         cstate->clientstate == CHAPCS_PENDING) {
97         /* lower layer isn't up - wait until later */
98         cstate->clientstate = CHAPCS_PENDING;
99         return;
100     }
101
102     /*
103      * We get here as a result of LCP coming up.
104      * So even if CHAP was open before, we will 
105      * have to re-authenticate ourselves.
106      */
107     cstate->clientstate = CHAPCS_LISTEN;
108 }
109
110
111 /*
112  * ChapAuthPeer - Authenticate our peer (start server).
113  */
114 void
115 ChapAuthPeer(unit, our_name, digest)
116     int unit;
117     char *our_name;
118     int digest;
119 {
120     chap_state *cstate = &chap[unit];
121   
122     cstate->chal_name = our_name;
123     cstate->chal_type = digest;
124
125     if (cstate->serverstate == CHAPSS_INITIAL ||
126         cstate->serverstate == CHAPSS_PENDING) {
127         /* lower layer isn't up - wait until later */
128         cstate->serverstate = CHAPSS_PENDING;
129         return;
130     }
131
132     ChapGenChallenge(cstate);
133     ChapSendChallenge(cstate);          /* crank it up dude! */
134     cstate->serverstate = CHAPSS_INITIAL_CHAL;
135 }
136
137
138 /*
139  * ChapChallengeTimeout - Timeout expired on sending challenge.
140  */
141 static void
142 ChapChallengeTimeout(arg)
143     caddr_t arg;
144 {
145     chap_state *cstate = (chap_state *) arg;
146   
147     /* if we aren't sending challenges, don't worry.  then again we */
148     /* probably shouldn't be here either */
149     if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
150         cstate->serverstate != CHAPSS_RECHALLENGE)
151         return;
152
153     if (cstate->chal_transmits >= cstate->max_transmits) {
154         /* give up on peer */
155         syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
156         cstate->serverstate = CHAPSS_BADAUTH;
157         auth_peer_fail(cstate->unit, PPP_CHAP);
158         return;
159     }
160
161     ChapSendChallenge(cstate);          /* Re-send challenge */
162 }
163
164
165 /*
166  * ChapResponseTimeout - Timeout expired on sending response.
167  */
168 static void
169 ChapResponseTimeout(arg)
170     caddr_t arg;
171 {
172     chap_state *cstate = (chap_state *) arg;
173
174     /* if we aren't sending a response, don't worry. */
175     if (cstate->clientstate != CHAPCS_RESPONSE)
176         return;
177
178     ChapSendResponse(cstate);           /* re-send response */
179 }
180
181
182 /*
183  * ChapRechallenge - Time to challenge the peer again.
184  */
185 static void
186 ChapRechallenge(arg)
187     caddr_t arg;
188 {
189     chap_state *cstate = (chap_state *) arg;
190
191     /* if we aren't sending a response, don't worry. */
192     if (cstate->serverstate != CHAPSS_OPEN)
193         return;
194
195     ChapGenChallenge(cstate);
196     ChapSendChallenge(cstate);
197     cstate->serverstate = CHAPSS_RECHALLENGE;
198 }
199
200
201 /*
202  * ChapLowerUp - The lower layer is up.
203  *
204  * Start up if we have pending requests.
205  */
206 void
207 ChapLowerUp(unit)
208     int unit;
209 {
210     chap_state *cstate = &chap[unit];
211   
212     if (cstate->clientstate == CHAPCS_INITIAL)
213         cstate->clientstate = CHAPCS_CLOSED;
214     else if (cstate->clientstate == CHAPCS_PENDING)
215         cstate->clientstate = CHAPCS_LISTEN;
216
217     if (cstate->serverstate == CHAPSS_INITIAL)
218         cstate->serverstate = CHAPSS_CLOSED;
219     else if (cstate->serverstate == CHAPSS_PENDING) {
220         ChapGenChallenge(cstate);
221         ChapSendChallenge(cstate);
222         cstate->serverstate = CHAPSS_INITIAL_CHAL;
223     }
224 }
225
226
227 /*
228  * ChapLowerDown - The lower layer is down.
229  *
230  * Cancel all timeouts.
231  */
232 void
233 ChapLowerDown(unit)
234     int unit;
235 {
236     chap_state *cstate = &chap[unit];
237   
238     /* Timeout(s) pending?  Cancel if so. */
239     if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
240         cstate->serverstate == CHAPSS_RECHALLENGE)
241         UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
242     else if (cstate->serverstate == CHAPSS_OPEN
243              && cstate->chal_interval != 0)
244         UNTIMEOUT(ChapRechallenge, (caddr_t) cstate);
245     if (cstate->clientstate == CHAPCS_RESPONSE)
246         UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
247
248     cstate->clientstate = CHAPCS_INITIAL;
249     cstate->serverstate = CHAPSS_INITIAL;
250 }
251
252
253 /*
254  * ChapProtocolReject - Peer doesn't grok CHAP.
255  */
256 void
257 ChapProtocolReject(unit)
258     int unit;
259 {
260     chap_state *cstate = &chap[unit];
261
262     if (cstate->serverstate != CHAPSS_INITIAL &&
263         cstate->serverstate != CHAPSS_CLOSED)
264         auth_peer_fail(unit, PPP_CHAP);
265     if (cstate->clientstate != CHAPCS_INITIAL &&
266         cstate->clientstate != CHAPCS_CLOSED)
267         auth_withpeer_fail(unit, PPP_CHAP);
268     ChapLowerDown(unit);                /* shutdown chap */
269 }
270
271
272 /*
273  * ChapInput - Input CHAP packet.
274  */
275 void
276 ChapInput(unit, inpacket, packet_len)
277     int unit;
278     u_char *inpacket;
279     int packet_len;
280 {
281     chap_state *cstate = &chap[unit];
282     u_char *inp;
283     u_char code, id;
284     int len;
285   
286     /*
287      * Parse header (code, id and length).
288      * If packet too short, drop it.
289      */
290     inp = inpacket;
291     if (packet_len < CHAP_HEADERLEN) {
292         CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
293         return;
294     }
295     GETCHAR(code, inp);
296     GETCHAR(id, inp);
297     GETSHORT(len, inp);
298     if (len < CHAP_HEADERLEN) {
299         CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
300         return;
301     }
302     if (len > packet_len) {
303         CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
304         return;
305     }
306     len -= CHAP_HEADERLEN;
307   
308     /*
309      * Action depends on code (as in fact it usually does :-).
310      */
311     switch (code) {
312     case CHAP_CHALLENGE:
313         ChapReceiveChallenge(cstate, inp, id, len);
314         break;
315     
316     case CHAP_RESPONSE:
317         ChapReceiveResponse(cstate, inp, id, len);
318         break;
319     
320     case CHAP_FAILURE:
321         ChapReceiveFailure(cstate, inp, id, len);
322         break;
323
324     case CHAP_SUCCESS:
325         ChapReceiveSuccess(cstate, inp, id, len);
326         break;
327
328     default:                            /* Need code reject? */
329         syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
330         break;
331     }
332 }
333
334
335 /*
336  * ChapReceiveChallenge - Receive Challenge and send Response.
337  */
338 static void
339 ChapReceiveChallenge(cstate, inp, id, len)
340     chap_state *cstate;
341     u_char *inp;
342     int id;
343     int len;
344 {
345     int rchallenge_len;
346     u_char *rchallenge;
347     int secret_len;
348     char secret[MAXSECRETLEN];
349     char rhostname[256];
350     MD5_CTX mdContext;
351  
352     CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
353     if (cstate->clientstate == CHAPCS_CLOSED ||
354         cstate->clientstate == CHAPCS_PENDING) {
355         CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
356                    cstate->clientstate));
357         return;
358     }
359
360     if (len < 2) {
361         CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
362         return;
363     }
364
365     GETCHAR(rchallenge_len, inp);
366     len -= sizeof (u_char) + rchallenge_len;    /* now name field length */
367     if (len < 0) {
368         CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
369         return;
370     }
371     rchallenge = inp;
372     INCPTR(rchallenge_len, inp);
373
374     if (len >= sizeof(rhostname))
375         len = sizeof(rhostname) - 1;
376     BCOPY(inp, rhostname, len);
377     rhostname[len] = '\000';
378
379     CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field: %s",
380                rhostname));
381
382     /* get secret for authenticating ourselves with the specified host */
383     if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
384                     secret, &secret_len, 0)) {
385         secret_len = 0;         /* assume null secret if can't find one */
386         syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
387                rhostname);
388     }
389
390     /* cancel response send timeout if necessary */
391     if (cstate->clientstate == CHAPCS_RESPONSE)
392         UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
393
394     cstate->resp_id = id;
395     cstate->resp_transmits = 0;
396
397     /*  generate MD based on negotiated type */
398     switch (cstate->resp_type) { 
399
400     case CHAP_DIGEST_MD5:               /* only MD5 is defined for now */
401         MD5Init(&mdContext);
402         MD5Update(&mdContext, &cstate->resp_id, 1);
403         MD5Update(&mdContext, secret, secret_len);
404         MD5Update(&mdContext, rchallenge, rchallenge_len);
405         MD5Final(&mdContext);
406         BCOPY(mdContext.digest, cstate->response, MD5_SIGNATURE_SIZE);
407         cstate->resp_length = MD5_SIGNATURE_SIZE;
408         break;
409
410     default:
411         CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
412         return;
413     }
414
415     ChapSendResponse(cstate);
416 }
417
418
419 /*
420  * ChapReceiveResponse - Receive and process response.
421  */
422 static void
423 ChapReceiveResponse(cstate, inp, id, len)
424     chap_state *cstate;
425     u_char *inp;
426     int id;
427     int len;
428 {
429     u_char *remmd, remmd_len;
430     int secret_len, old_state;
431     int code;
432     char rhostname[256];
433     u_char buf[256];
434     MD5_CTX mdContext;
435     u_char msg[256];
436     char secret[MAXSECRETLEN];
437
438     CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
439
440     if (cstate->serverstate == CHAPSS_CLOSED ||
441         cstate->serverstate == CHAPSS_PENDING) {
442         CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
443                    cstate->serverstate));
444         return;
445     }
446
447     if (id != cstate->chal_id)
448         return;                 /* doesn't match ID of last challenge */
449
450     /*
451      * If we have received a duplicate or bogus Response,
452      * we have to send the same answer (Success/Failure)
453      * as we did for the first Response we saw.
454      */
455     if (cstate->serverstate == CHAPSS_OPEN) {
456         ChapSendStatus(cstate, CHAP_SUCCESS);
457         return;
458     }
459     if (cstate->serverstate == CHAPSS_BADAUTH) {
460         ChapSendStatus(cstate, CHAP_FAILURE);
461         return;
462     }
463
464     if (len < 2) {
465         CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
466         return;
467     }
468     GETCHAR(remmd_len, inp);            /* get length of MD */
469     remmd = inp;                        /* get pointer to MD */
470     INCPTR(remmd_len, inp);
471
472     len -= sizeof (u_char) + remmd_len;
473     if (len < 0) {
474         CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
475         return;
476     }
477
478     UNTIMEOUT(ChapChallengeTimeout, (caddr_t) cstate);
479
480     if (len >= sizeof(rhostname))
481         len = sizeof(rhostname) - 1;
482     BCOPY(inp, rhostname, len);
483     rhostname[len] = '\000';
484
485     CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
486                rhostname));
487
488     /*
489      * Get secret for authenticating them with us,
490      * do the hash ourselves, and compare the result.
491      */
492     code = CHAP_FAILURE;
493     if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
494                    secret, &secret_len, 1)) {
495         syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
496                rhostname);
497     } else {
498
499         /*  generate MD based on negotiated type */
500         switch (cstate->chal_type) { 
501
502         case CHAP_DIGEST_MD5:           /* only MD5 is defined for now */
503             if (remmd_len != MD5_SIGNATURE_SIZE)
504                 break;                  /* it's not even the right length */
505             MD5Init(&mdContext);
506             MD5Update(&mdContext, &cstate->chal_id, 1);
507             MD5Update(&mdContext, secret, secret_len);
508             MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
509             MD5Final(&mdContext); 
510
511             /* compare local and remote MDs and send the appropriate status */
512             if (memcmp (mdContext.digest, remmd, MD5_SIGNATURE_SIZE) == 0)
513                 code = CHAP_SUCCESS;    /* they are the same! */
514             break;
515
516         default:
517             CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
518         }
519     }
520
521     ChapSendStatus(cstate, code);
522
523     if (code == CHAP_SUCCESS) {
524         old_state = cstate->serverstate;
525         cstate->serverstate = CHAPSS_OPEN;
526         if (old_state == CHAPSS_INITIAL_CHAL) {
527             auth_peer_success(cstate->unit, PPP_CHAP);
528         }
529         if (cstate->chal_interval != 0)
530             TIMEOUT(ChapRechallenge, (caddr_t) cstate, cstate->chal_interval);
531
532     } else {
533         syslog(LOG_ERR, "CHAP peer authentication failed");
534         cstate->serverstate = CHAPSS_BADAUTH;
535         auth_peer_fail(cstate->unit, PPP_CHAP);
536     }
537 }
538
539 /*
540  * ChapReceiveSuccess - Receive Success
541  */
542 static void
543 ChapReceiveSuccess(cstate, inp, id, len)
544     chap_state *cstate;
545     u_char *inp;
546     u_char id;
547     int len;
548 {
549
550     CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
551
552     if (cstate->clientstate == CHAPCS_OPEN)
553         /* presumably an answer to a duplicate response */
554         return;
555
556     if (cstate->clientstate != CHAPCS_RESPONSE) {
557         /* don't know what this is */
558         CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
559                    cstate->clientstate));
560         return;
561     }
562
563     UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
564
565     /*
566      * Print message.
567      */
568     if (len > 0)
569         PRINTMSG(inp, len);
570
571     cstate->clientstate = CHAPCS_OPEN;
572
573     auth_withpeer_success(cstate->unit, PPP_CHAP);
574 }
575
576
577 /*
578  * ChapReceiveFailure - Receive failure.
579  */
580 static void
581 ChapReceiveFailure(cstate, inp, id, len)
582     chap_state *cstate;
583     u_char *inp;
584     u_char id;
585     int len;
586 {
587     u_char msglen;
588     u_char *msg;
589   
590     CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
591
592     if (cstate->clientstate != CHAPCS_RESPONSE) {
593         /* don't know what this is */
594         CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
595                    cstate->clientstate));
596         return;
597     }
598
599     UNTIMEOUT(ChapResponseTimeout, (caddr_t) cstate);
600
601     /*
602      * Print message.
603      */
604     if (len > 0)
605         PRINTMSG(inp, len);
606
607     syslog(LOG_ERR, "CHAP authentication failed");
608     auth_withpeer_fail(cstate->unit, PPP_CHAP);
609 }
610
611
612 /*
613  * ChapSendChallenge - Send an Authenticate challenge.
614  */
615 static void
616 ChapSendChallenge(cstate)
617     chap_state *cstate;
618 {
619     u_char *outp;
620     int chal_len, name_len;
621     int outlen;
622
623     chal_len = cstate->chal_len;
624     name_len = strlen(cstate->chal_name);
625     outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
626     outp = outpacket_buf;
627
628     MAKEHEADER(outp, PPP_CHAP);         /* paste in a CHAP header */
629
630     PUTCHAR(CHAP_CHALLENGE, outp);
631     PUTCHAR(cstate->chal_id, outp);
632     PUTSHORT(outlen, outp);
633
634     PUTCHAR(chal_len, outp);            /* put length of challenge */
635     BCOPY(cstate->challenge, outp, chal_len);
636     INCPTR(chal_len, outp);
637
638     BCOPY(cstate->chal_name, outp, name_len);   /* append hostname */
639
640     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
641   
642     CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
643
644     TIMEOUT(ChapChallengeTimeout, (caddr_t) cstate, cstate->timeouttime);
645     ++cstate->chal_transmits;
646 }
647
648
649 /*
650  * ChapSendStatus - Send a status response (ack or nak).
651  */
652 static void
653 ChapSendStatus(cstate, code)
654     chap_state *cstate;
655     int code;
656 {
657     u_char *outp;
658     int outlen, msglen;
659     char msg[256];
660
661     if (code == CHAP_SUCCESS)
662         sprintf(msg, "Welcome to %s.", hostname);
663     else
664         sprintf(msg, "I don't like you.  Go 'way.");
665     msglen = strlen(msg);
666
667     outlen = CHAP_HEADERLEN + msglen;
668     outp = outpacket_buf;
669
670     MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
671   
672     PUTCHAR(code, outp);
673     PUTCHAR(cstate->chal_id, outp);
674     PUTSHORT(outlen, outp);
675     BCOPY(msg, outp, msglen);
676     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
677   
678     CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
679                cstate->chal_id));
680 }
681
682 /*
683  * ChapGenChallenge is used to generate a pseudo-random challenge string of
684  * a pseudo-random length between min_len and max_len.  The challenge
685  * string and its length are stored in *cstate, and various other fields of
686  * *cstate are initialized.
687  */
688
689 static void
690 ChapGenChallenge(cstate)
691     chap_state *cstate;
692 {
693     int chal_len;
694     u_char *ptr = cstate->challenge;
695     unsigned int i;
696
697     /* pick a random challenge length between MIN_CHALLENGE_LENGTH and 
698        MAX_CHALLENGE_LENGTH */  
699     chal_len =  (unsigned) ((drand48() *
700                              (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
701                             MIN_CHALLENGE_LENGTH);
702     cstate->chal_len = chal_len;
703     cstate->chal_id = ++cstate->id;
704     cstate->chal_transmits = 0;
705
706     /* generate a random string */
707     for (i = 0; i < chal_len; i++ )
708         *ptr++ = (char) (drand48() * 0xff);
709 }
710
711 /*
712  * ChapSendResponse - send a response packet with values as specified
713  * in *cstate.
714  */
715 /* ARGSUSED */
716 static void
717 ChapSendResponse(cstate)
718     chap_state *cstate;
719 {
720     u_char *outp;
721     int outlen, md_len, name_len;
722
723     md_len = cstate->resp_length;
724     name_len = strlen(cstate->resp_name);
725     outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
726     outp = outpacket_buf;
727
728     MAKEHEADER(outp, PPP_CHAP);
729
730     PUTCHAR(CHAP_RESPONSE, outp);       /* we are a response */
731     PUTCHAR(cstate->resp_id, outp);     /* copy id from challenge packet */
732     PUTSHORT(outlen, outp);             /* packet length */
733
734     PUTCHAR(md_len, outp);              /* length of MD */
735     BCOPY(cstate->response, outp, md_len);      /* copy MD to buffer */
736     INCPTR(md_len, outp);
737
738     BCOPY(cstate->resp_name, outp, name_len); /* append our name */
739
740     /* send the packet */
741     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
742
743     cstate->clientstate = CHAPCS_RESPONSE;
744     TIMEOUT(ChapResponseTimeout, (caddr_t) cstate, cstate->timeouttime);
745     ++cstate->resp_transmits;
746 }
747
748 /*
749  * ChapPrintPkt - print the contents of a CHAP packet.
750  */
751 char *ChapCodenames[] = {
752     "Challenge", "Response", "Success", "Failure"
753 };
754
755 int
756 ChapPrintPkt(p, plen, printer, arg)
757     u_char *p;
758     int plen;
759     void (*printer) __P((void *, char *, ...));
760     void *arg;
761 {
762     int code, id, len;
763     int clen, nlen;
764     u_char x;
765
766     if (plen < CHAP_HEADERLEN)
767         return 0;
768     GETCHAR(code, p);
769     GETCHAR(id, p);
770     GETSHORT(len, p);
771     if (len < CHAP_HEADERLEN || len > plen)
772         return 0;
773
774     if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
775         printer(arg, " %s", ChapCodenames[code-1]);
776     else
777         printer(arg, " code=0x%x", code);
778     printer(arg, " id=0x%x", id);
779     len -= CHAP_HEADERLEN;
780     switch (code) {
781     case CHAP_CHALLENGE:
782     case CHAP_RESPONSE:
783         if (len < 1)
784             break;
785         clen = p[0];
786         if (len < clen + 1)
787             break;
788         ++p;
789         nlen = len - clen - 1;
790         printer(arg, " <");
791         for (; clen > 0; --clen) {
792             GETCHAR(x, p);
793             printer(arg, "%.2x", x);
794         }
795         printer(arg, ">, name = ");
796         print_string((char *)p, nlen, printer, arg);
797         break;
798     case CHAP_FAILURE:
799     case CHAP_SUCCESS:
800         printer(arg, " ");
801         print_string((char *)p, len, printer, arg);
802         break;
803     default:
804         for (clen = len; clen > 0; --clen) {
805             GETCHAR(x, p);
806             printer(arg, " %.2x", x);
807         }
808     }
809
810     return len + CHAP_HEADERLEN;
811 }