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