]> git.ozlabs.org Git - ppp.git/blob - pppd/chap.c
Remove references to the old CHAP code.
[ppp.git] / pppd / chap.c
1 /*
2  * chap.c - Challenge Handshake Authentication Protocol.
3  *
4  * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name(s) of the authors of this software must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission.
21  *
22  * 4. Redistributions of any form whatsoever must retain the following
23  *    acknowledgment:
24  *    "This product includes software developed by Paul Mackerras
25  *     <paulus@samba.org>".
26  *
27  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
28  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
29  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
30  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
31  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
32  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
33  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34  *
35  * Copyright (c) 1991 Gregory M. Christy.
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms are permitted
39  * provided that the above copyright notice and this paragraph are
40  * duplicated in all such forms and that any documentation,
41  * advertising materials, and other materials related to such
42  * distribution and use acknowledge that the software was developed
43  * by Gregory M. Christy.  The name of the author may not be used to
44  * endorse or promote products derived from this software without
45  * specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50  */
51
52 #define RCSID   "$Id: chap.c,v 1.41 2003/07/28 12:25:41 carlsonj Exp $"
53
54 /*
55  * TODO:
56  */
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sys/types.h>
62 #include <sys/time.h>
63
64 #include "pppd.h"
65 #include "chap.h"
66 #include "md5.h"
67 #ifdef CHAPMS
68 #include "chap_ms.h"
69 #endif
70
71 /* Hook for a plugin to validate CHAP challenge */
72 int (*chap_auth_hook) __P((char *user,
73                            u_char *remmd,
74                            int remmd_len,
75                            chap_state *cstate)) = NULL;
76
77 static const char rcsid[] = RCSID;
78
79 #ifdef CHAPMS
80 /* For MPPE debug */
81 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
82 static char *mschap_challenge = NULL;
83 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
84 static char *mschap2_peer_challenge = NULL;
85 #endif
86
87 /*
88  * Command-line options.
89  */
90 static option_t chap_option_list[] = {
91     { "chap-restart", o_int, &chap[0].timeouttime,
92       "Set timeout for CHAP", OPT_PRIO },
93     { "chap-max-challenge", o_int, &chap[0].max_transmits,
94       "Set max #xmits for challenge", OPT_PRIO },
95     { "chap-interval", o_int, &chap[0].chal_interval,
96       "Set interval for rechallenge", OPT_PRIO },
97 #ifdef MSCHAP
98 #ifdef MSLANMAN
99     { "ms-lanman", o_bool, &ms_lanman,
100       "Use LanMan passwd when using MS-CHAP", 1 },
101 #endif
102 #ifdef DEBUGMPPEKEY
103     { "mschap-challenge", o_string, &mschap_challenge,
104       "specify CHAP challenge" },
105     { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
106       "specify CHAP peer challenge" },
107 #endif
108 #endif
109     { NULL }
110 };
111
112 /*
113  * Protocol entry points.
114  */
115 static void ChapInit __P((int));
116 static void ChapLowerUp __P((int));
117 static void ChapLowerDown __P((int));
118 static void ChapInput __P((int, u_char *, int));
119 static void ChapProtocolReject __P((int));
120 static int  ChapPrintPkt __P((u_char *, int,
121                               void (*) __P((void *, char *, ...)), void *));
122
123 struct protent chap_protent = {
124     PPP_CHAP,
125     ChapInit,
126     ChapInput,
127     ChapProtocolReject,
128     ChapLowerUp,
129     ChapLowerDown,
130     NULL,
131     NULL,
132     ChapPrintPkt,
133     NULL,
134     1,
135     "CHAP",
136     NULL,
137     chap_option_list,
138     NULL,
139     NULL,
140     NULL
141 };
142
143 chap_state chap[NUM_PPP];               /* CHAP state; one for each unit */
144
145 static void ChapChallengeTimeout __P((void *));
146 static void ChapResponseTimeout __P((void *));
147 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
148 static void ChapRechallenge __P((void *));
149 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
150 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
151 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
152 static void ChapSendStatus __P((chap_state *, int));
153 static void ChapSendChallenge __P((chap_state *));
154 static void ChapSendResponse __P((chap_state *));
155 static void ChapGenChallenge __P((chap_state *));
156
157 extern double drand48 __P((void));
158 extern void srand48 __P((long));
159
160 /*
161  * ChapInit - Initialize a CHAP unit.
162  */
163 static void
164 ChapInit(unit)
165     int unit;
166 {
167     chap_state *cstate = &chap[unit];
168
169     BZERO(cstate, sizeof(*cstate));
170     cstate->unit = unit;
171     cstate->clientstate = CHAPCS_INITIAL;
172     cstate->serverstate = CHAPSS_INITIAL;
173     cstate->timeouttime = CHAP_DEFTIMEOUT;
174     cstate->max_transmits = CHAP_DEFTRANSMITS;
175     /* random number generator is initialized in magic_init */
176 }
177
178
179 /*
180  * chap_auth_with_peer - Authenticate us with our peer (start client).
181  *
182  */
183 void
184 chap_auth_with_peer(int unit, char *our_name, int digest)
185 {
186     chap_state *cstate = &chap[unit];
187
188     cstate->resp_name = our_name;
189     cstate->resp_type = digest;
190
191     if (cstate->clientstate == CHAPCS_INITIAL ||
192         cstate->clientstate == CHAPCS_PENDING) {
193         /* lower layer isn't up - wait until later */
194         cstate->clientstate = CHAPCS_PENDING;
195         return;
196     }
197
198     /*
199      * We get here as a result of LCP coming up.
200      * So even if CHAP was open before, we will
201      * have to re-authenticate ourselves.
202      */
203     cstate->clientstate = CHAPCS_LISTEN;
204 }
205
206
207 /*
208  * chap_auth_peer - Authenticate our peer (start server).
209  */
210 void
211 chap_auth_peer(int unit, char *our_name, int digest)
212 {
213     chap_state *cstate = &chap[unit];
214
215     cstate->chal_name = our_name;
216     cstate->chal_type = digest;
217
218     if (cstate->serverstate == CHAPSS_INITIAL ||
219         cstate->serverstate == CHAPSS_PENDING) {
220         /* lower layer isn't up - wait until later */
221         cstate->serverstate = CHAPSS_PENDING;
222         return;
223     }
224
225     ChapGenChallenge(cstate);
226     ChapSendChallenge(cstate);          /* crank it up dude! */
227     cstate->serverstate = CHAPSS_INITIAL_CHAL;
228 }
229
230
231 /*
232  * ChapChallengeTimeout - Timeout expired on sending challenge.
233  */
234 static void
235 ChapChallengeTimeout(arg)
236     void *arg;
237 {
238     chap_state *cstate = (chap_state *) arg;
239
240     /* if we aren't sending challenges, don't worry.  then again we */
241     /* probably shouldn't be here either */
242     if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
243         cstate->serverstate != CHAPSS_RECHALLENGE)
244         return;
245
246     if (cstate->chal_transmits >= cstate->max_transmits) {
247         /* give up on peer */
248         error("Peer failed to respond to CHAP challenge");
249         cstate->serverstate = CHAPSS_BADAUTH;
250         auth_peer_fail(cstate->unit, PPP_CHAP);
251         return;
252     }
253
254     ChapSendChallenge(cstate);          /* Re-send challenge */
255 }
256
257
258 /*
259  * ChapResponseTimeout - Timeout expired on sending response.
260  */
261 static void
262 ChapResponseTimeout(arg)
263     void *arg;
264 {
265     chap_state *cstate = (chap_state *) arg;
266
267     /* if we aren't sending a response, don't worry. */
268     if (cstate->clientstate != CHAPCS_RESPONSE)
269         return;
270
271     ChapSendResponse(cstate);           /* re-send response */
272 }
273
274
275 /*
276  * ChapRechallenge - Time to challenge the peer again.
277  */
278 static void
279 ChapRechallenge(arg)
280     void *arg;
281 {
282     chap_state *cstate = (chap_state *) arg;
283
284     /* if we aren't sending a response, don't worry. */
285     if (cstate->serverstate != CHAPSS_OPEN)
286         return;
287
288     ChapGenChallenge(cstate);
289     ChapSendChallenge(cstate);
290     cstate->serverstate = CHAPSS_RECHALLENGE;
291 }
292
293
294 /*
295  * ChapLowerUp - The lower layer is up.
296  *
297  * Start up if we have pending requests.
298  */
299 static void
300 ChapLowerUp(unit)
301     int unit;
302 {
303     chap_state *cstate = &chap[unit];
304
305     if (cstate->clientstate == CHAPCS_INITIAL)
306         cstate->clientstate = CHAPCS_CLOSED;
307     else if (cstate->clientstate == CHAPCS_PENDING)
308         cstate->clientstate = CHAPCS_LISTEN;
309
310     if (cstate->serverstate == CHAPSS_INITIAL)
311         cstate->serverstate = CHAPSS_CLOSED;
312     else if (cstate->serverstate == CHAPSS_PENDING) {
313         ChapGenChallenge(cstate);
314         ChapSendChallenge(cstate);
315         cstate->serverstate = CHAPSS_INITIAL_CHAL;
316     }
317 }
318
319
320 /*
321  * ChapLowerDown - The lower layer is down.
322  *
323  * Cancel all timeouts.
324  */
325 static void
326 ChapLowerDown(unit)
327     int unit;
328 {
329     chap_state *cstate = &chap[unit];
330
331     /* Timeout(s) pending?  Cancel if so. */
332     if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
333         cstate->serverstate == CHAPSS_RECHALLENGE)
334         UNTIMEOUT(ChapChallengeTimeout, cstate);
335     else if (cstate->serverstate == CHAPSS_OPEN
336              && cstate->chal_interval != 0)
337         UNTIMEOUT(ChapRechallenge, cstate);
338     if (cstate->clientstate == CHAPCS_RESPONSE)
339         UNTIMEOUT(ChapResponseTimeout, cstate);
340
341     cstate->clientstate = CHAPCS_INITIAL;
342     cstate->serverstate = CHAPSS_INITIAL;
343 }
344
345
346 /*
347  * ChapProtocolReject - Peer doesn't grok CHAP.
348  */
349 static void
350 ChapProtocolReject(unit)
351     int unit;
352 {
353     chap_state *cstate = &chap[unit];
354
355     if (cstate->serverstate != CHAPSS_INITIAL &&
356         cstate->serverstate != CHAPSS_CLOSED)
357         auth_peer_fail(unit, PPP_CHAP);
358     if (cstate->clientstate != CHAPCS_INITIAL &&
359         cstate->clientstate != CHAPCS_CLOSED)
360         auth_withpeer_fail(unit, PPP_CHAP);
361     ChapLowerDown(unit);                /* shutdown chap */
362 }
363
364
365 /*
366  * ChapInput - Input CHAP packet.
367  */
368 static void
369 ChapInput(unit, inpacket, packet_len)
370     int unit;
371     u_char *inpacket;
372     int packet_len;
373 {
374     chap_state *cstate = &chap[unit];
375     u_char *inp;
376     u_char code, id;
377     int len;
378
379     /*
380      * Parse header (code, id and length).
381      * If packet too short, drop it.
382      */
383     inp = inpacket;
384     if (packet_len < CHAP_HEADERLEN) {
385         CHAPDEBUG(("ChapInput: rcvd short header."));
386         return;
387     }
388     GETCHAR(code, inp);
389     GETCHAR(id, inp);
390     GETSHORT(len, inp);
391     if (len < CHAP_HEADERLEN) {
392         CHAPDEBUG(("ChapInput: rcvd illegal length."));
393         return;
394     }
395     if (len > packet_len) {
396         CHAPDEBUG(("ChapInput: rcvd short packet."));
397         return;
398     }
399     len -= CHAP_HEADERLEN;
400
401     /*
402      * Action depends on code (as in fact it usually does :-).
403      */
404     switch (code) {
405     case CHAP_CHALLENGE:
406         ChapReceiveChallenge(cstate, inp, id, len);
407         break;
408
409     case CHAP_RESPONSE:
410         ChapReceiveResponse(cstate, inp, id, len);
411         break;
412
413     case CHAP_FAILURE:
414         ChapReceiveFailure(cstate, inp, id, len);
415         break;
416
417     case CHAP_SUCCESS:
418         ChapReceiveSuccess(cstate, inp, id, len);
419         break;
420
421     default:                            /* Need code reject? */
422         warn("Unknown CHAP code (%d) received.", code);
423         break;
424     }
425 }
426
427
428 /*
429  * ChapReceiveChallenge - Receive Challenge and send Response.
430  */
431 static void
432 ChapReceiveChallenge(cstate, inp, id, len)
433     chap_state *cstate;
434     u_char *inp;
435     int id;
436     int len;
437 {
438     int rchallenge_len;
439     u_char *rchallenge;
440     int secret_len;
441     char secret[MAXSECRETLEN];
442     char rhostname[256];
443     MD5_CTX mdContext;
444     u_char hash[MD5_SIGNATURE_SIZE];
445
446     if (cstate->clientstate == CHAPCS_CLOSED ||
447         cstate->clientstate == CHAPCS_PENDING) {
448         CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
449         return;
450     }
451
452     if (len < 2) {
453         CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
454         return;
455     }
456
457     GETCHAR(rchallenge_len, inp);
458     len -= sizeof (u_char) + rchallenge_len;    /* now name field length */
459     if (len < 0) {
460         CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
461         return;
462     }
463     rchallenge = inp;
464     INCPTR(rchallenge_len, inp);
465
466     /* Null terminate and clean remote name. */
467     slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
468
469     /* Microsoft doesn't send their name back in the PPP packet */
470     if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
471         strlcpy(rhostname, remote_name, sizeof(rhostname));
472         CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
473                    rhostname));
474     }
475
476     /* get secret for authenticating ourselves with the specified host */
477     if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
478                     secret, &secret_len, 0)) {
479         secret_len = 0;         /* assume null secret if can't find one */
480         warn("No CHAP secret found for authenticating us to %q", rhostname);
481     }
482
483     /* cancel response send timeout if necessary */
484     if (cstate->clientstate == CHAPCS_RESPONSE)
485         UNTIMEOUT(ChapResponseTimeout, cstate);
486
487     cstate->resp_id = id;
488     cstate->resp_transmits = 0;
489
490     /*  generate MD based on negotiated type */
491     switch (cstate->resp_type) {
492
493     case CHAP_DIGEST_MD5:
494         MD5Init(&mdContext);
495         MD5Update(&mdContext, &cstate->resp_id, 1);
496         MD5Update(&mdContext, secret, secret_len);
497         MD5Update(&mdContext, rchallenge, rchallenge_len);
498         MD5Final(hash, &mdContext);
499         BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
500         cstate->resp_length = MD5_SIGNATURE_SIZE;
501         break;
502
503 #ifdef CHAPMS
504     case CHAP_MICROSOFT:
505         ChapMS(cstate, rchallenge, secret, secret_len,
506                (MS_ChapResponse *) cstate->response);
507         break;
508
509     case CHAP_MICROSOFT_V2:
510         ChapMS2(cstate, rchallenge,
511                 mschap2_peer_challenge? mschap2_peer_challenge: NULL,
512                 cstate->resp_name, secret, secret_len,
513                 (MS_Chap2Response *) cstate->response, cstate->earesponse,
514                  MS_CHAP2_AUTHENTICATEE);
515         break;
516 #endif /* CHAPMS */
517
518     default:
519         CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
520         return;
521     }
522
523     BZERO(secret, sizeof(secret));
524     ChapSendResponse(cstate);
525 }
526
527
528 /*
529  * ChapReceiveResponse - Receive and process response.
530  */
531 static void
532 ChapReceiveResponse(cstate, inp, id, len)
533     chap_state *cstate;
534     u_char *inp;
535     int id;
536     int len;
537 {
538     u_char *remmd, remmd_len;
539     int secret_len, old_state;
540     int code;
541     char rhostname[256];
542     MD5_CTX mdContext;
543     char secret[MAXSECRETLEN];
544     u_char hash[MD5_SIGNATURE_SIZE];
545
546     if (cstate->serverstate == CHAPSS_CLOSED ||
547         cstate->serverstate == CHAPSS_PENDING) {
548         CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
549         return;
550     }
551
552     if (id != cstate->chal_id)
553         return;                 /* doesn't match ID of last challenge */
554
555     /*
556      * If we have received a duplicate or bogus Response,
557      * we have to send the same answer (Success/Failure)
558      * as we did for the first Response we saw.
559      */
560     if (cstate->serverstate == CHAPSS_OPEN) {
561         ChapSendStatus(cstate, CHAP_SUCCESS);
562         return;
563     }
564     if (cstate->serverstate == CHAPSS_BADAUTH) {
565         ChapSendStatus(cstate, CHAP_FAILURE);
566         return;
567     }
568
569     if (len < 2) {
570         CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
571         return;
572     }
573     GETCHAR(remmd_len, inp);            /* get length of MD */
574     remmd = inp;                        /* get pointer to MD */
575     INCPTR(remmd_len, inp);
576
577     len -= sizeof (u_char) + remmd_len;
578     if (len < 0) {
579         CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
580         return;
581     }
582
583     UNTIMEOUT(ChapChallengeTimeout, cstate);
584
585     /* Null terminate and clean remote name. */
586     slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
587
588 #ifdef CHAPMS
589     /* copy the flags into cstate for use elsewhere */
590     if (cstate->chal_type == CHAP_MICROSOFT_V2)
591         cstate->resp_flags = ((MS_Chap2Response *) remmd)->Flags[0];
592 #endif /* CHAPMS */
593     /*
594      * Get secret for authenticating them with us,
595      * do the hash ourselves, and compare the result.
596      */
597     code = CHAP_FAILURE;
598
599     /* If a plugin will verify the response, let the plugin do it. */
600     if (chap_auth_hook) {
601         code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
602                                    remmd, (int) remmd_len,
603                                    cstate );
604         /*
605          * Check remote number authorization.  A plugin may have filled in
606          * the remote number or added an allowed number, and rather than
607          * return an authenticate failure, is leaving it for us to verify.
608          */
609         if (code == CHAP_SUCCESS) {
610             if (!auth_number()) {
611                 /* We do not want to leak info about the chap result. */
612                 code = CHAP_FAILURE; /* XXX exit value will be "wrong" */
613                 warn("calling number %q is not authorized", remote_number);
614             }
615         }
616
617     } else {
618         if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
619                         cstate->chal_name, secret, &secret_len, 1)) {
620             warn("No CHAP secret found for authenticating %q", rhostname);
621         } else {
622
623             /*  generate MD based on negotiated type */
624             switch (cstate->chal_type) {
625
626             case CHAP_DIGEST_MD5:
627                 if (remmd_len != MD5_SIGNATURE_SIZE)
628                     break;                      /* not even the right length */
629                 MD5Init(&mdContext);
630                 MD5Update(&mdContext, &cstate->chal_id, 1);
631                 MD5Update(&mdContext, secret, secret_len);
632                 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
633                 MD5Final(hash, &mdContext);
634
635                 /* compare MDs and send the appropriate status */
636                 if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0)
637                     code = CHAP_SUCCESS;        /* they are the same! */
638                 break;
639
640 #ifdef CHAPMS
641             case CHAP_MICROSOFT:
642             {
643                 int response_offset, response_size;
644                 MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
645                 MS_ChapResponse md;
646
647                 if (remmd_len != MS_CHAP_RESPONSE_LEN)
648                     break;                      /* not even the right length */
649
650                 /* Determine which part of response to verify against */
651                 if (rmd->UseNT[0]) {
652                     response_offset = offsetof(MS_ChapResponse, NTResp);
653                     response_size = sizeof(rmd->NTResp);
654                 } else {
655 #ifdef MSLANMAN
656                     response_offset = offsetof(MS_ChapResponse, LANManResp);
657                     response_size = sizeof(rmd->LANManResp);
658 #else
659                     /* Should really propagate this into the error packet. */
660                     notice("Peer request for LANMAN auth not supported");
661                     break;
662 #endif /* MSLANMAN */
663                 }
664
665                 /* Generate the expected response. */
666                 ChapMS(cstate, cstate->challenge, secret, secret_len, &md);
667
668                 /* compare MDs and send the appropriate status */
669                 if (memcmp((u_char *) &md + response_offset,
670                            (u_char *) remmd + response_offset,
671                            response_size) == 0)
672                     code = CHAP_SUCCESS;        /* they are the same! */
673                 break;
674             }
675
676             case CHAP_MICROSOFT_V2:
677             {
678                 MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
679                 MS_Chap2Response md;
680
681                 if (remmd_len != MS_CHAP2_RESPONSE_LEN)
682                     break;                      /* not even the right length */
683
684                 /* Generate the expected response and our mutual auth. */
685                 ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
686                         (explicit_remote? remote_name: rhostname),
687                         secret, secret_len, &md,
688                         cstate->saresponse, MS_CHAP2_AUTHENTICATOR);
689
690                 /* compare MDs and send the appropriate status */
691                 if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
692                     code = CHAP_SUCCESS;        /* yay! */
693                 break;
694             }
695 #endif /* CHAPMS */
696
697             default:
698                 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
699             }
700         }
701
702         BZERO(secret, sizeof(secret));
703     }
704     ChapSendStatus(cstate, code);
705
706     if (code == CHAP_SUCCESS) {
707         old_state = cstate->serverstate;
708         cstate->serverstate = CHAPSS_OPEN;
709         if (old_state == CHAPSS_INITIAL_CHAL) {
710             auth_peer_success(cstate->unit, PPP_CHAP, cstate->chal_type,
711                               rhostname, len);
712         }
713         if (cstate->chal_interval != 0)
714             TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
715         notice("CHAP peer authentication succeeded for %q", rhostname);
716
717     } else {
718         warn("CHAP peer authentication failed for %q", rhostname);
719         cstate->serverstate = CHAPSS_BADAUTH;
720         auth_peer_fail(cstate->unit, PPP_CHAP);
721     }
722 }
723
724 /*
725  * ChapReceiveSuccess - Receive Success
726  */
727 static void
728 ChapReceiveSuccess(cstate, inp, id, len)
729     chap_state *cstate;
730     u_char *inp;
731     u_char id;
732     int len;
733 {
734
735     if (cstate->clientstate == CHAPCS_OPEN)
736         /* presumably an answer to a duplicate response */
737         return;
738
739     if (cstate->clientstate != CHAPCS_RESPONSE) {
740         /* don't know what this is */
741         CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
742         return;
743     }
744
745     UNTIMEOUT(ChapResponseTimeout, cstate);
746
747 #ifdef CHAPMS
748     /*
749      * For MS-CHAPv2, we must verify that the peer knows our secret.
750      */
751     if (cstate->resp_type == CHAP_MICROSOFT_V2) {
752         if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) {
753             inp += 2; len -= 2;
754             if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) {
755                 /* Authenticator Response matches. */
756                 inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
757                 len -= MS_AUTH_RESPONSE_LENGTH;
758                 if ((len >= 3) && !strncmp(inp, " M=", 3)) {
759                     inp += 3; len -= 3; /* Eat the delimiter */
760                 } else if (len) {
761                     /* Packet has extra text which does not begin " M=" */
762                     error("MS-CHAPv2 Success packet is badly formed.");
763                     auth_withpeer_fail(cstate->unit, PPP_CHAP);
764                 }
765             } else {
766                 /* Authenticator Response did not match expected. */
767                 error("MS-CHAPv2 mutual authentication failed.");
768                 auth_withpeer_fail(cstate->unit, PPP_CHAP);
769             }
770         } else {
771             /* Packet does not start with "S=" */
772             error("MS-CHAPv2 Success packet is badly formed.");
773             auth_withpeer_fail(cstate->unit, PPP_CHAP);
774         }
775     }
776 #endif
777
778     /*
779      * Print message.
780      */
781     if (len > 0)
782         PRINTMSG(inp, len);
783
784     cstate->clientstate = CHAPCS_OPEN;
785
786     notice("CHAP authentication succeeded");
787     auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type);
788 }
789
790
791 /*
792  * ChapReceiveFailure - Receive failure.
793  */
794 static void
795 ChapReceiveFailure(cstate, inp, id, len)
796     chap_state *cstate;
797     u_char *inp;
798     u_char id;
799     int len;
800 {
801 #ifdef CHAPMS
802     u_char *msg;
803 #endif
804     u_char *p = inp;
805
806     if (cstate->clientstate != CHAPCS_RESPONSE) {
807         /* don't know what this is */
808         CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
809         return;
810     }
811
812 #ifdef CHAPMS
813     /* We want a null-terminated string for strxxx(). */
814     msg = malloc(len + 1);
815     if (!msg) {
816         p = NULL;
817         notice("Out of memory in ChapReceiveFailure");
818         goto print_msg;
819     }
820     BCOPY(inp, msg, len);
821     p = msg + len; *p = '\0'; p = msg;
822 #endif
823
824     UNTIMEOUT(ChapResponseTimeout, cstate);
825
826 #ifdef CHAPMS
827     if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
828         (cstate->resp_type == CHAP_MICROSOFT)) {
829         int error;
830
831         /*
832          * Deal with MS-CHAP formatted failure messages; just print the
833          * M=<message> part (if any).  For MS-CHAP we're not really supposed
834          * to use M=<message>, but it shouldn't hurt.  See ChapSendStatus().
835          */
836         if (!strncmp(p, "E=", 2))
837             error = (int) strtol(p, NULL, 10); /* Remember the error code. */
838         else
839             goto print_msg; /* Message is badly formatted. */
840
841         if (len && ((p = strstr(p, " M=")) != NULL)) {
842             /* M=<message> field found. */
843             p += 3;
844         } else {
845             /* No M=<message>; use the error code. */
846             switch(error) {
847             case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
848                 p = "E=646 Restricted logon hours";
849                 break;
850
851             case MS_CHAP_ERROR_ACCT_DISABLED:
852                 p = "E=647 Account disabled";
853                 break;
854
855             case MS_CHAP_ERROR_PASSWD_EXPIRED:
856                 p = "E=648 Password expired";
857                 break;
858
859             case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
860                 p = "E=649 No dialin permission";
861                 break;
862
863             case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
864                 p = "E=691 Authentication failure";
865                 break;
866
867             case MS_CHAP_ERROR_CHANGING_PASSWORD:
868                 /* Should never see this, we don't support Change Password. */
869                 p = "E=709 Error changing password";
870                 break;
871
872             default:
873                 free(msg);
874                 p = msg = malloc(len + 33);
875                 if (!msg) {
876                     novm("ChapReceiveFailure");
877                     goto print_msg;
878                 }
879                 slprintf(p, len + 33, "Unknown authentication failure: %.*s",
880                          len, inp);
881                 break;
882             }
883         }
884         len = strlen(p);
885     }
886 #endif
887
888     /*
889      * Print message.
890      */
891 #ifdef CHAPMS
892 print_msg:
893 #endif
894     if (len > 0 && p != NULL)
895         PRINTMSG(p, len);
896
897     error("CHAP authentication failed");
898     auth_withpeer_fail(cstate->unit, PPP_CHAP);
899 #ifdef CHAPMS
900     if (msg) free(msg);
901 #endif
902 }
903
904
905 /*
906  * ChapSendChallenge - Send an Authenticate challenge.
907  */
908 static void
909 ChapSendChallenge(cstate)
910     chap_state *cstate;
911 {
912     u_char *outp;
913     int chal_len, name_len;
914     int outlen;
915
916     chal_len = cstate->chal_len;
917     name_len = strlen(cstate->chal_name);
918     outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
919     outp = outpacket_buf;
920
921     MAKEHEADER(outp, PPP_CHAP);         /* paste in a CHAP header */
922
923     PUTCHAR(CHAP_CHALLENGE, outp);
924     PUTCHAR(cstate->chal_id, outp);
925     PUTSHORT(outlen, outp);
926
927     PUTCHAR(chal_len, outp);            /* put length of challenge */
928     BCOPY(cstate->challenge, outp, chal_len);
929     INCPTR(chal_len, outp);
930
931     BCOPY(cstate->chal_name, outp, name_len);   /* append hostname */
932
933     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
934
935     TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
936     ++cstate->chal_transmits;
937 }
938
939
940 /*
941  * ChapSendStatus - Send a status response (ack or nak).
942  * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats.
943  */
944 static void
945 ChapSendStatus(cstate, code)
946     chap_state *cstate;
947     int code;
948 {
949     u_char *outp;
950     int outlen, msglen;
951     char msg[256];
952     char *p, *q;
953
954     p = msg;
955     q = p + sizeof(msg); /* points 1 byte past msg */
956
957     if (code == CHAP_SUCCESS) {
958 #ifdef CHAPMS
959         if (cstate->chal_type == CHAP_MICROSOFT_V2) {
960             /*
961              * Per RFC 2759, success message must be formatted as
962              *     "S=<auth_string> M=<message>"
963              * where
964              *     <auth_string> is the Authenticator Response (mutual auth)
965              *     <message> is a text message
966              *
967              * However, some versions of Windows (win98 tested) do not know
968              * about the M=<message> part (required per RFC 2759) and flag
969              * it as an error (reported incorrectly as an encryption error
970              * to the user).  Since the RFC requires it, and it can be
971              * useful information, we supply it if the peer is a conforming
972              * system.  Luckily (?), win98 sets the Flags field to 0x04
973              * (contrary to RFC requirements) so we can use that to
974              * distinguish between conforming and non-conforming systems.
975              *
976              * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
977              * help debugging this.
978              */
979             slprintf(p, q - p, "S=");
980             p += 2;
981             slprintf(p, q - p, "%s", cstate->saresponse);
982             p += strlen(cstate->saresponse);
983             if (cstate->resp_flags != 0)
984                 goto msgdone;
985             slprintf(p, q - p, " M=");
986             p += 3;
987         }
988 #endif /* CHAPMS */
989
990         slprintf(p, q - p, "Welcome to %s.", hostname);
991     } else {
992 #ifdef CHAPMS
993         if ((cstate->chal_type == CHAP_MICROSOFT_V2) ||
994             (cstate->chal_type == CHAP_MICROSOFT)) {
995             /*
996              * Failure message must be formatted as
997              *     "E=e R=r C=c V=v M=m"
998              * where
999              *     e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
1000              *     r = retry (we use 1, ok to retry)
1001              *     c = challenge to use for next response, we reuse previous
1002              *     v = Change Password version supported, we use 0
1003              *     m = text message
1004              *
1005              * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore
1006              * any extra text according to RFC 2433.  So we'll go the easy
1007              * (read: lazy) route and include it always.  Neither win2k nor
1008              * win98 (others untested) display the message to the user anyway.
1009              * They also both ignore the E=e code.
1010              *
1011              * Note that it's safe to reuse the same challenge as we don't
1012              * actually accept another response based on the error message
1013              * (and no clients try to resend a response anyway).
1014              *
1015              * Basically, this whole bit is useless code, even the small
1016              * implementation here is only because of overspecification.
1017              */
1018             int i;
1019
1020             slprintf(p, q - p, "E=691 R=1 C=");
1021             p += 12;
1022             for (i = 0; i < cstate->chal_len; i++)
1023                 sprintf(p + i * 2, "%02X", cstate->challenge[i]);
1024             p += cstate->chal_len * 2;
1025             slprintf(p, q - p, " V=0 M=");
1026             p += 7;
1027         }
1028 #endif /* CHAPMS */
1029
1030         slprintf(p, q - p, "I don't like you.  Go 'way.");
1031     }
1032 #ifdef CHAPMS
1033 msgdone:
1034 #endif
1035     msglen = strlen(msg);
1036
1037     outlen = CHAP_HEADERLEN + msglen;
1038     outp = outpacket_buf;
1039
1040     MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
1041
1042     PUTCHAR(code, outp);
1043     PUTCHAR(cstate->chal_id, outp);
1044     PUTSHORT(outlen, outp);
1045     BCOPY(msg, outp, msglen);
1046     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1047 }
1048
1049 /*
1050  * ChapGenChallenge is used to generate a pseudo-random challenge string of
1051  * a pseudo-random length between min_len and max_len.  The challenge
1052  * string and its length are stored in *cstate, and various other fields of
1053  * *cstate are initialized.
1054  */
1055
1056 static void
1057 ChapGenChallenge(cstate)
1058     chap_state *cstate;
1059 {
1060     int chal_len = 0; /* Avoid compiler warning */
1061     u_char *ptr = cstate->challenge;
1062     int i;
1063
1064     switch (cstate->chal_type) {
1065     case CHAP_DIGEST_MD5:
1066         /*
1067          * pick a random challenge length between MIN_CHALLENGE_LENGTH and
1068          * MAX_CHALLENGE_LENGTH
1069          */
1070         chal_len = (unsigned) ((drand48() *
1071                                 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
1072                                 MIN_CHALLENGE_LENGTH);
1073         break;
1074
1075 #ifdef CHAPMS
1076     case CHAP_MICROSOFT:
1077         /* MS-CHAP is fixed to an 8 octet challenge. */
1078         chal_len = 8;
1079         break;
1080
1081     case CHAP_MICROSOFT_V2:
1082         /* MS-CHAPv2 is fixed to a 16 octet challenge. */
1083         chal_len = 16;
1084         break;
1085 #endif
1086     default:
1087         fatal("ChapGenChallenge: Unsupported challenge type %d",
1088               (int) cstate->chal_type);
1089         break;
1090     }
1091
1092     cstate->chal_len = chal_len;
1093     cstate->chal_id = ++cstate->id;
1094     cstate->chal_transmits = 0;
1095
1096 #ifdef CHAPMS
1097     if (mschap_challenge)
1098         for (i = 0; i < chal_len; i++)
1099             *ptr++ = mschap_challenge[i];
1100     else
1101 #endif
1102         /* generate a random string */
1103         for (i = 0; i < chal_len; i++)
1104             *ptr++ = (char) (drand48() * 0xff);
1105 }
1106
1107 /*
1108  * ChapSendResponse - send a response packet with values as specified
1109  * in *cstate.
1110  */
1111 /* ARGSUSED */
1112 static void
1113 ChapSendResponse(cstate)
1114     chap_state *cstate;
1115 {
1116     u_char *outp;
1117     int outlen, md_len, name_len;
1118
1119     md_len = cstate->resp_length;
1120     name_len = strlen(cstate->resp_name);
1121     outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
1122     outp = outpacket_buf;
1123
1124     MAKEHEADER(outp, PPP_CHAP);
1125
1126     PUTCHAR(CHAP_RESPONSE, outp);       /* we are a response */
1127     PUTCHAR(cstate->resp_id, outp);     /* copy id from challenge packet */
1128     PUTSHORT(outlen, outp);             /* packet length */
1129
1130     PUTCHAR(md_len, outp);              /* length of MD */
1131     BCOPY(cstate->response, outp, md_len);      /* copy MD to buffer */
1132     INCPTR(md_len, outp);
1133
1134     BCOPY(cstate->resp_name, outp, name_len); /* append our name */
1135
1136     /* send the packet */
1137     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1138
1139     cstate->clientstate = CHAPCS_RESPONSE;
1140     TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
1141     ++cstate->resp_transmits;
1142 }
1143
1144 /*
1145  * ChapPrintPkt - print the contents of a CHAP packet.
1146  */
1147 static char *ChapCodenames[] = {
1148     "Challenge", "Response", "Success", "Failure"
1149 };
1150
1151 static int
1152 ChapPrintPkt(p, plen, printer, arg)
1153     u_char *p;
1154     int plen;
1155     void (*printer) __P((void *, char *, ...));
1156     void *arg;
1157 {
1158     int code, id, len;
1159     int clen, nlen;
1160     u_char x;
1161
1162     if (plen < CHAP_HEADERLEN)
1163         return 0;
1164     GETCHAR(code, p);
1165     GETCHAR(id, p);
1166     GETSHORT(len, p);
1167     if (len < CHAP_HEADERLEN || len > plen)
1168         return 0;
1169
1170     if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
1171         printer(arg, " %s", ChapCodenames[code-1]);
1172     else
1173         printer(arg, " code=0x%x", code);
1174     printer(arg, " id=0x%x", id);
1175     len -= CHAP_HEADERLEN;
1176     switch (code) {
1177     case CHAP_CHALLENGE:
1178     case CHAP_RESPONSE:
1179         if (len < 1)
1180             break;
1181         clen = p[0];
1182         if (len < clen + 1)
1183             break;
1184         ++p;
1185         nlen = len - clen - 1;
1186         printer(arg, " <");
1187         for (; clen > 0; --clen) {
1188             GETCHAR(x, p);
1189             printer(arg, "%.2x", x);
1190         }
1191         printer(arg, ">, name = ");
1192         print_string((char *)p, nlen, printer, arg);
1193         break;
1194     case CHAP_FAILURE:
1195     case CHAP_SUCCESS:
1196         printer(arg, " ");
1197         print_string((char *)p, len, printer, arg);
1198         break;
1199     default:
1200         for (clen = len; clen > 0; --clen) {
1201             GETCHAR(x, p);
1202             printer(arg, " %.2x", x);
1203         }
1204     }
1205
1206     return len + CHAP_HEADERLEN;
1207 }