Large patch from Frank Cusack <fcusack@fcusack.com> to add proper
[ppp.git] / pppd / chap.c
1 /*
2  * chap_ms.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.27 2002/03/01 14:39:18 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:
583                 if (remmd_len != MD5_SIGNATURE_SIZE)
584                     break;                      /* 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 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 #ifdef CHAPMS
597             case CHAP_MICROSOFT:
598             {
599                 int response_offset, response_size;
600
601                 if (remmd_len != MS_CHAP_RESPONSE_LEN)
602                     break;                      /* not even the right length */
603                 ChapMS(cstate, cstate->challenge, cstate->chal_len,
604                        secret, secret_len);
605
606                 /* Determine which part of response to verify against */
607                 if ((u_char *) (remmd + offsetof(MS_ChapResponse, UseNT))) {
608                     response_offset = offsetof(MS_ChapResponse, NTResp);
609                     response_size = sizeof(((MS_ChapResponse *) remmd)->NTResp);
610                 } else {
611                     response_offset = offsetof(MS_ChapResponse, LANManResp);
612                     response_size =
613                         sizeof(((MS_ChapResponse *) remmd)->LANManResp);
614                 }
615
616                 /* compare MDs and send the appropriate status */
617                 if (memcmp(cstate->response + response_offset,
618                     remmd + response_offset, response_size) == 0)
619                     code = CHAP_SUCCESS;        /* they are the same! */
620                 break;
621             }
622 #endif /* CHAPMS */
623
624             default:
625                 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
626             }
627         }
628
629         BZERO(secret, sizeof(secret));
630     }
631     ChapSendStatus(cstate, code);
632
633     if (code == CHAP_SUCCESS) {
634         old_state = cstate->serverstate;
635         cstate->serverstate = CHAPSS_OPEN;
636         if (old_state == CHAPSS_INITIAL_CHAL) {
637             auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
638         }
639         if (cstate->chal_interval != 0)
640             TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
641         notice("CHAP peer authentication succeeded for %q", rhostname);
642
643     } else {
644         error("CHAP peer authentication failed for remote host %q", rhostname);
645         cstate->serverstate = CHAPSS_BADAUTH;
646         auth_peer_fail(cstate->unit, PPP_CHAP);
647     }
648 }
649
650 /*
651  * ChapReceiveSuccess - Receive Success
652  */
653 static void
654 ChapReceiveSuccess(cstate, inp, id, len)
655     chap_state *cstate;
656     u_char *inp;
657     u_char id;
658     int len;
659 {
660
661     if (cstate->clientstate == CHAPCS_OPEN)
662         /* presumably an answer to a duplicate response */
663         return;
664
665     if (cstate->clientstate != CHAPCS_RESPONSE) {
666         /* don't know what this is */
667         CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
668         return;
669     }
670
671     UNTIMEOUT(ChapResponseTimeout, cstate);
672
673     /*
674      * Print message.
675      */
676     if (len > 0)
677         PRINTMSG(inp, len);
678
679     cstate->clientstate = CHAPCS_OPEN;
680
681     auth_withpeer_success(cstate->unit, PPP_CHAP);
682 }
683
684
685 /*
686  * ChapReceiveFailure - Receive failure.
687  */
688 static void
689 ChapReceiveFailure(cstate, inp, id, len)
690     chap_state *cstate;
691     u_char *inp;
692     u_char id;
693     int len;
694 {
695     if (cstate->clientstate != CHAPCS_RESPONSE) {
696         /* don't know what this is */
697         CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
698         return;
699     }
700
701     UNTIMEOUT(ChapResponseTimeout, cstate);
702
703     /*
704      * Print message.
705      */
706     if (len > 0)
707         PRINTMSG(inp, len);
708
709     error("CHAP authentication failed");
710     auth_withpeer_fail(cstate->unit, PPP_CHAP);
711 }
712
713
714 /*
715  * ChapSendChallenge - Send an Authenticate challenge.
716  */
717 static void
718 ChapSendChallenge(cstate)
719     chap_state *cstate;
720 {
721     u_char *outp;
722     int chal_len, name_len;
723     int outlen;
724
725     chal_len = cstate->chal_len;
726     name_len = strlen(cstate->chal_name);
727     outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
728     outp = outpacket_buf;
729
730     MAKEHEADER(outp, PPP_CHAP);         /* paste in a CHAP header */
731
732     PUTCHAR(CHAP_CHALLENGE, outp);
733     PUTCHAR(cstate->chal_id, outp);
734     PUTSHORT(outlen, outp);
735
736     PUTCHAR(chal_len, outp);            /* put length of challenge */
737     BCOPY(cstate->challenge, outp, chal_len);
738     INCPTR(chal_len, outp);
739
740     BCOPY(cstate->chal_name, outp, name_len);   /* append hostname */
741
742     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
743
744     TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
745     ++cstate->chal_transmits;
746 }
747
748
749 /*
750  * ChapSendStatus - Send a status response (ack or nak).
751  */
752 static void
753 ChapSendStatus(cstate, code)
754     chap_state *cstate;
755     int code;
756 {
757     u_char *outp;
758     int outlen, msglen;
759     char msg[256];
760
761     if (code == CHAP_SUCCESS)
762         slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
763     else
764         slprintf(msg, sizeof(msg), "I don't like you.  Go 'way.");
765     msglen = strlen(msg);
766
767     outlen = CHAP_HEADERLEN + msglen;
768     outp = outpacket_buf;
769
770     MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
771
772     PUTCHAR(code, outp);
773     PUTCHAR(cstate->chal_id, outp);
774     PUTSHORT(outlen, outp);
775     BCOPY(msg, outp, msglen);
776     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
777 }
778
779 /*
780  * ChapGenChallenge is used to generate a pseudo-random challenge string of
781  * a pseudo-random length between min_len and max_len.  The challenge
782  * string and its length are stored in *cstate, and various other fields of
783  * *cstate are initialized.
784  */
785
786 static void
787 ChapGenChallenge(cstate)
788     chap_state *cstate;
789 {
790     int chal_len = 0; /* Avoid compiler warning */
791     u_char *ptr = cstate->challenge;
792     int i;
793
794     switch (cstate->chal_type) {
795     case CHAP_DIGEST_MD5:
796         /*
797          * pick a random challenge length between MIN_CHALLENGE_LENGTH and
798          * MAX_CHALLENGE_LENGTH
799          */
800         chal_len = (unsigned) ((drand48() *
801                                 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
802                                 MIN_CHALLENGE_LENGTH);
803         break;
804
805 #ifdef CHAPMS
806     case CHAP_MICROSOFT:
807         /* MS-CHAP is fixed to an 8 octet challenge. */
808         chal_len = 8;
809         break;
810 #endif
811     default:
812         fatal("ChapGenChallenge: Unsupported challenge type %d",
813               (int) cstate->chal_type);
814         break;
815     }
816
817     cstate->chal_len = chal_len;
818     cstate->chal_id = ++cstate->id;
819     cstate->chal_transmits = 0;
820
821     /* generate a random string */
822     for (i = 0; i < chal_len; i++)
823         *ptr++ = (char) (drand48() * 0xff);
824 }
825
826 /*
827  * ChapSendResponse - send a response packet with values as specified
828  * in *cstate.
829  */
830 /* ARGSUSED */
831 static void
832 ChapSendResponse(cstate)
833     chap_state *cstate;
834 {
835     u_char *outp;
836     int outlen, md_len, name_len;
837
838     md_len = cstate->resp_length;
839     name_len = strlen(cstate->resp_name);
840     outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
841     outp = outpacket_buf;
842
843     MAKEHEADER(outp, PPP_CHAP);
844
845     PUTCHAR(CHAP_RESPONSE, outp);       /* we are a response */
846     PUTCHAR(cstate->resp_id, outp);     /* copy id from challenge packet */
847     PUTSHORT(outlen, outp);             /* packet length */
848
849     PUTCHAR(md_len, outp);              /* length of MD */
850     BCOPY(cstate->response, outp, md_len);      /* copy MD to buffer */
851     INCPTR(md_len, outp);
852
853     BCOPY(cstate->resp_name, outp, name_len); /* append our name */
854
855     /* send the packet */
856     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
857
858     cstate->clientstate = CHAPCS_RESPONSE;
859     TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
860     ++cstate->resp_transmits;
861 }
862
863 /*
864  * ChapPrintPkt - print the contents of a CHAP packet.
865  */
866 static char *ChapCodenames[] = {
867     "Challenge", "Response", "Success", "Failure"
868 };
869
870 static int
871 ChapPrintPkt(p, plen, printer, arg)
872     u_char *p;
873     int plen;
874     void (*printer) __P((void *, char *, ...));
875     void *arg;
876 {
877     int code, id, len;
878     int clen, nlen;
879     u_char x;
880
881     if (plen < CHAP_HEADERLEN)
882         return 0;
883     GETCHAR(code, p);
884     GETCHAR(id, p);
885     GETSHORT(len, p);
886     if (len < CHAP_HEADERLEN || len > plen)
887         return 0;
888
889     if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
890         printer(arg, " %s", ChapCodenames[code-1]);
891     else
892         printer(arg, " code=0x%x", code);
893     printer(arg, " id=0x%x", id);
894     len -= CHAP_HEADERLEN;
895     switch (code) {
896     case CHAP_CHALLENGE:
897     case CHAP_RESPONSE:
898         if (len < 1)
899             break;
900         clen = p[0];
901         if (len < clen + 1)
902             break;
903         ++p;
904         nlen = len - clen - 1;
905         printer(arg, " <");
906         for (; clen > 0; --clen) {
907             GETCHAR(x, p);
908             printer(arg, "%.2x", x);
909         }
910         printer(arg, ">, name = ");
911         print_string((char *)p, nlen, printer, arg);
912         break;
913     case CHAP_FAILURE:
914     case CHAP_SUCCESS:
915         printer(arg, " ");
916         print_string((char *)p, len, printer, arg);
917         break;
918     default:
919         for (clen = len; clen > 0; --clen) {
920             GETCHAR(x, p);
921             printer(arg, " %.2x", x);
922         }
923     }
924
925     return len + CHAP_HEADERLEN;
926 }