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