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