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