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