1 /*
2 * chap.c - Challenge Handshake Authentication Protocol.
3 *
4 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
6 *
7 * Copyright (c) 1993 The Australian National University.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the Australian National University. The name of the University
16 * may not be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 *
22 * Copyright (c) 1991 Gregory M. Christy.
23 * All rights reserved.
24 *
25 * Redistribution and use in source and binary forms are permitted
26 * provided that the above copyright notice and this paragraph are
27 * duplicated in all such forms and that any documentation,
28 * advertising materials, and other materials related to such
29 * distribution and use acknowledge that the software was developed
30 * by Gregory M. Christy. The name of the author may not be used to
31 * endorse or promote products derived from this software without
32 * specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
35 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
36 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
37 */
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <sys/time.h>
44
45 #include "pppd.h"
46 #include "chap.h"
47 #include "md5.h"
48 #if defined(CHAPMS) || defined(CHAPMSV2)
49 #include "chap_ms.h"
50 #endif
51
52 /*
53 * Command-line options.
54 */
55 static option_t chap_option_list[] = {
56 { "chap-restart", o_int, &chap[0].timeouttime,
57 "Set CHAP Challenge retry interval" },
58 { "chap-max-challenge", o_int, &chap[0].max_transmits,
59 "Max number of Challenges sent without a valid response" },
60 { "chap-interval", o_int, &chap[0].chal_interval,
61 "Set interval for rechallenge" },
62 #if defined(CHAPMS) && defined(MSLANMAN)
63 { "ms-lanman", o_bool, &ms_lanman,
64 "Use obsolete LAN Manager password when using MS-CHAP", 1 },
65 #endif
66 { NULL }
67 };
68
69 /*
70 * Protocol entry points.
71 */
72 static void ChapInit __P((int));
73 static void ChapLowerUp __P((int));
74 static void ChapLowerDown __P((int));
75 static void ChapInput __P((int, u_char *, int));
76 static void ChapProtocolReject __P((int));
77 static int ChapPrintPkt __P((u_char *, int,
78 void (*) __P((void *, const char *, ...)), void *));
79
80 struct protent chap_protent = {
81 PPP_CHAP, /* PPP protocol number */
82 ChapInit, /* Initialization procedure */
83 ChapInput, /* Process a received packet */
84 ChapProtocolReject, /* Process a received protocol-reject */
85 ChapLowerUp, /* Lower layer has come up */
86 ChapLowerDown, /* Lower layer has gone down */
87 NULL, /* Open the protocol */
88 NULL, /* Close the protocol */
89 ChapPrintPkt, /* Print a packet in readable form */
90 NULL, /* Process a received data packet */
91 1, /* 0 iff protocol is disabled */
92 "CHAP", /* Text name of protocol */
93 NULL, /* Text name of corresponding data protocol */
94 chap_option_list, /* List of command-line options */
95 NULL, /* Check requested options, assign defaults */
96 NULL, /* Configure interface for demand-dial */
97 NULL /* Say whether to bring up link for this pkt */
98 };
99
100 /* Not static'd for plug-ins */
101 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
102
103 static void ChapChallengeTimeout __P((void *));
104 static void ChapResponseTimeout __P((void *));
105 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
106 static void ChapRechallenge __P((void *));
107 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
108 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
109 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
110 static void ChapSendStatus __P((chap_state *, int));
111 static void ChapSendChallenge __P((chap_state *));
112 static void ChapSendResponse __P((chap_state *));
113 static void ChapGenChallenge __P((chap_state *));
114
115 static const char *
chap_cstate(int clientstate)116 chap_cstate(int clientstate)
117 {
118 static const char *cstate[] = { CHAPCS__LIST };
119 static char buf[32];
120
121 if (clientstate < 0 || clientstate >= Dim(cstate)) {
122 (void) slprintf(buf, sizeof(buf), "#%d", clientstate);
123 return (buf);
124 }
125 return cstate[clientstate];
126 }
127
128 static const char *
chap_sstate(int serverstate)129 chap_sstate(int serverstate)
130 {
131 static const char *sstate[] = { CHAPSS__LIST };
132 static char buf[32];
133
134 if (serverstate < 0 || serverstate >= Dim(sstate)) {
135 (void) slprintf(buf, sizeof(buf), "#%d", serverstate);
136 return (buf);
137 }
138 return sstate[serverstate];
139 }
140
141 /*
142 * ChapInit - Initialize a CHAP unit.
143 */
144 static void
ChapInit(unit)145 ChapInit(unit)
146 int unit;
147 {
148 chap_state *cstate = &chap[unit];
149
150 BZERO(cstate, sizeof(*cstate));
151 cstate->unit = unit;
152 cstate->clientstate = CHAPCS_INITIAL;
153 cstate->serverstate = CHAPSS_INITIAL;
154 cstate->timeouttime = CHAP_DEFTIMEOUT;
155 cstate->max_transmits = CHAP_DEFTRANSMITS;
156
157 /* XXX save get_first_hwaddr() here. */
158 /* random number generator is initialized in magic_init */
159 }
160
161
162 /*
163 * ChapAuthWithPeer - Authenticate us with our peer (start client).
164 *
165 */
166 void
ChapAuthWithPeer(unit,our_name,digest)167 ChapAuthWithPeer(unit, our_name, digest)
168 int unit;
169 char *our_name;
170 int digest;
171 {
172 chap_state *cstate = &chap[unit];
173
174 cstate->resp_name = our_name;
175 cstate->resp_type = digest;
176
177 if (cstate->clientstate == CHAPCS_INITIAL ||
178 cstate->clientstate == CHAPCS_PENDING) {
179 /* lower layer isn't up - wait until later */
180 cstate->clientstate = CHAPCS_PENDING;
181 return;
182 }
183
184 /*
185 * We get here as a result of LCP coming up.
186 * So even if CHAP was open before, we will
187 * have to re-authenticate ourselves.
188 */
189 cstate->clientstate = CHAPCS_LISTEN;
190 }
191
192
193 /*
194 * ChapAuthPeer - Authenticate our peer (start server).
195 */
196 void
ChapAuthPeer(unit,our_name,digest)197 ChapAuthPeer(unit, our_name, digest)
198 int unit;
199 char *our_name;
200 int digest;
201 {
202 chap_state *cstate = &chap[unit];
203
204 cstate->chal_name = our_name;
205 cstate->chal_type = digest;
206
207 if (cstate->serverstate == CHAPSS_INITIAL ||
208 cstate->serverstate == CHAPSS_PENDING) {
209 /* lower layer isn't up - wait until later */
210 cstate->serverstate = CHAPSS_PENDING;
211 return;
212 }
213
214 ChapGenChallenge(cstate);
215 ChapSendChallenge(cstate); /* crank it up dude! */
216 cstate->serverstate = CHAPSS_INITIAL_CHAL;
217 }
218
219
220 /*
221 * ChapChallengeTimeout - Timeout expired on sending challenge.
222 */
223 static void
ChapChallengeTimeout(arg)224 ChapChallengeTimeout(arg)
225 void *arg;
226 {
227 chap_state *cstate = (chap_state *) arg;
228
229 /* if we aren't sending challenges, don't worry. then again we */
230 /* probably shouldn't be here either */
231 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
232 cstate->serverstate != CHAPSS_RECHALLENGE)
233 return;
234
235 if (cstate->chal_transmits >= cstate->max_transmits) {
236 /* give up on peer */
237 error("Peer failed to respond to CHAP challenge");
238 cstate->serverstate = CHAPSS_BADAUTH;
239 auth_peer_fail(cstate->unit, PPP_CHAP);
240 return;
241 }
242
243 ChapSendChallenge(cstate); /* Re-send challenge */
244 }
245
246
247 /*
248 * ChapResponseTimeout - Timeout expired on sending response.
249 */
250 static void
ChapResponseTimeout(arg)251 ChapResponseTimeout(arg)
252 void *arg;
253 {
254 chap_state *cstate = (chap_state *) arg;
255
256 /* if we aren't sending a response, don't worry. */
257 if (cstate->clientstate != CHAPCS_RESPONSE)
258 return;
259
260 ChapSendResponse(cstate); /* re-send response */
261 }
262
263
264 /*
265 * ChapRechallenge - Time to challenge the peer again.
266 */
267 static void
ChapRechallenge(arg)268 ChapRechallenge(arg)
269 void *arg;
270 {
271 chap_state *cstate = (chap_state *) arg;
272
273 /* if we aren't sending a response, don't worry. */
274 if (cstate->serverstate != CHAPSS_OPEN)
275 return;
276
277 ChapGenChallenge(cstate);
278 ChapSendChallenge(cstate);
279 cstate->serverstate = CHAPSS_RECHALLENGE;
280 }
281
282
283 /*
284 * ChapLowerUp - The lower layer is up.
285 *
286 * Start up if we have pending requests.
287 */
288 static void
ChapLowerUp(unit)289 ChapLowerUp(unit)
290 int unit;
291 {
292 chap_state *cstate = &chap[unit];
293
294 if (cstate->clientstate == CHAPCS_INITIAL)
295 cstate->clientstate = CHAPCS_CLOSED;
296 else if (cstate->clientstate == CHAPCS_PENDING)
297 cstate->clientstate = CHAPCS_LISTEN;
298
299 if (cstate->serverstate == CHAPSS_INITIAL)
300 cstate->serverstate = CHAPSS_CLOSED;
301 else if (cstate->serverstate == CHAPSS_PENDING) {
302 ChapGenChallenge(cstate);
303 ChapSendChallenge(cstate);
304 cstate->serverstate = CHAPSS_INITIAL_CHAL;
305 }
306 }
307
308
309 /*
310 * ChapLowerDown - The lower layer is down.
311 *
312 * Cancel all timeouts.
313 */
314 static void
ChapLowerDown(unit)315 ChapLowerDown(unit)
316 int unit;
317 {
318 chap_state *cstate = &chap[unit];
319
320 /* Timeout(s) pending? Cancel if so. */
321 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
322 cstate->serverstate == CHAPSS_RECHALLENGE)
323 UNTIMEOUT(ChapChallengeTimeout, cstate);
324 else if (cstate->serverstate == CHAPSS_OPEN
325 && cstate->chal_interval != 0)
326 UNTIMEOUT(ChapRechallenge, cstate);
327 if (cstate->clientstate == CHAPCS_RESPONSE)
328 UNTIMEOUT(ChapResponseTimeout, cstate);
329
330 cstate->clientstate = CHAPCS_INITIAL;
331 cstate->serverstate = CHAPSS_INITIAL;
332 }
333
334
335 /*
336 * ChapProtocolReject - Peer doesn't grok CHAP.
337 */
338 static void
ChapProtocolReject(unit)339 ChapProtocolReject(unit)
340 int unit;
341 {
342 chap_state *cstate = &chap[unit];
343
344 if (cstate->serverstate != CHAPSS_INITIAL &&
345 cstate->serverstate != CHAPSS_CLOSED)
346 auth_peer_fail(unit, PPP_CHAP);
347 if (cstate->clientstate != CHAPCS_INITIAL &&
348 cstate->clientstate != CHAPCS_CLOSED)
349 auth_withpeer_fail(unit, PPP_CHAP);
350 ChapLowerDown(unit); /* shutdown chap */
351 }
352
353
354 /*
355 * ChapInput - Input CHAP packet.
356 */
357 static void
ChapInput(unit,inpacket,packet_len)358 ChapInput(unit, inpacket, packet_len)
359 int unit;
360 u_char *inpacket;
361 int packet_len;
362 {
363 chap_state *cstate = &chap[unit];
364 u_char *inp;
365 u_char code, id;
366 int len;
367
368 /*
369 * Parse header (code, id and length).
370 * If packet too short, drop it.
371 */
372 inp = inpacket;
373 if (packet_len < CHAP_HEADERLEN) {
374 error("CHAP: packet is too small (%d < %d)", packet_len,
375 CHAP_HEADERLEN);
376 return;
377 }
378 GETCHAR(code, inp);
379 GETCHAR(id, inp);
380 GETSHORT(len, inp);
381 if (len < CHAP_HEADERLEN || len > packet_len) {
382 error("CHAP: packet has illegal length %d (%d..%d)",
383 len, CHAP_HEADERLEN, packet_len);
384 return;
385 }
386 len -= CHAP_HEADERLEN;
387
388 /*
389 * Action depends on code (as in fact it usually does :-).
390 */
391 switch (code) {
392 case CHAP_CHALLENGE:
393 ChapReceiveChallenge(cstate, inp, id, len);
394 break;
395
396 case CHAP_RESPONSE:
397 ChapReceiveResponse(cstate, inp, id, len);
398 break;
399
400 case CHAP_FAILURE:
401 ChapReceiveFailure(cstate, inp, id, len);
402 break;
403
404 case CHAP_SUCCESS:
405 ChapReceiveSuccess(cstate, inp, id, len);
406 break;
407
408 default:
409 /* CHAP does not define a code reject. */
410 warn("Unknown CHAP code (%d) received.", code);
411 break;
412 }
413 }
414
415
416 /*
417 * ChapReceiveChallenge - Receive Challenge and send Response.
418 */
419 static void
ChapReceiveChallenge(cstate,inp,id,len)420 ChapReceiveChallenge(cstate, inp, id, len)
421 chap_state *cstate;
422 u_char *inp;
423 int id;
424 int len;
425 {
426 int rchallenge_len;
427 u_char *rchallenge;
428 int secret_len;
429 char rhostname[MAXNAMELEN];
430 char secret[MAXSECRETLEN];
431 MD5_CTX mdContext;
432 u_char hash[MD5_SIGNATURE_SIZE];
433 int fake_response = 0;
434
435 if (cstate->clientstate == CHAPCS_CLOSED ||
436 cstate->clientstate == CHAPCS_PENDING) {
437 if (debug)
438 dbglog("CHAP Challenge unexpectedly received in state %s",
439 chap_cstate(cstate->clientstate));
440 return;
441 }
442
443 if (len < 2) {
444 error("CHAP: Challenge message length %d", len);
445 return;
446 }
447
448 GETCHAR(rchallenge_len, inp);
449 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
450 if (len < 0) {
451 error("CHAP: Challenge truncated");
452 return;
453 }
454 rchallenge = inp;
455 INCPTR(rchallenge_len, inp);
456
457 if (len >= sizeof(rhostname))
458 len = sizeof(rhostname) - 1;
459 if (len > 0) {
460 BCOPY(inp, rhostname, len);
461 }
462 rhostname[len] = '\0';
463
464 #ifdef CHECK_CHALLENGE_LENGTH
465 if (rchallenge_len < CHECK_CHALLENGE_LENGTH) {
466 warn("CHAP: Challenge from %s unreasonably short (%d octets)",
467 rhostname, rchallenge_len);
468 fake_response = 1;
469 }
470 #endif
471
472 /* XXX compare against saved get_first_hwaddr() here. */
473
474 /* Microsoft NT doesn't send a name in the CHAP Challenge message */
475 if (explicit_remote ||
476 (remote_name[0] != '\0' && rhostname[0] == '\0')) {
477 (void) strlcpy(rhostname, remote_name, sizeof(rhostname));
478 if (debug)
479 dbglog("CHAP: Peer gave no name; using '%q' as remote name",
480 rhostname);
481 }
482
483 if (cstate->peercname[0] == '\0') {
484 (void) strlcpy(cstate->peercname, rhostname,
485 sizeof (cstate->peercname));
486 } else if (strcmp(rhostname, cstate->peercname) != 0) {
487 if (++cstate->rename_count == 1) {
488 info("CHAP: peer challenge name changed from '%q' to '%q'",
489 cstate->peercname, rhostname);
490 (void) strlcpy(cstate->peercname, rhostname,
491 sizeof (cstate->peercname));
492 } else {
493 fake_response = 1;
494 if (cstate->rename_count == 2)
495 warn("CHAP: peer challenge name changed again to '%q'",
496 rhostname);
497 }
498 }
499
500 /* get secret for authenticating ourselves with the specified host */
501 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
502 secret, &secret_len, 0)) {
503 secret_len = 0; /* assume null secret if can't find one */
504 warn("No CHAP secret found for authenticating us (%q) to %q",
505 cstate->resp_name, rhostname);
506 }
507
508 /* cancel response send timeout if necessary */
509 if (cstate->clientstate == CHAPCS_RESPONSE)
510 UNTIMEOUT(ChapResponseTimeout, cstate);
511
512 cstate->resp_id = id;
513 cstate->resp_transmits = 0;
514
515 /* generate MD based on negotiated type */
516 switch (cstate->resp_type) {
517
518 case CHAP_DIGEST_MD5:
519 MD5Init(&mdContext);
520 MD5Update(&mdContext, &cstate->resp_id, 1);
521 MD5Update(&mdContext, (u_char *)secret, (unsigned)secret_len);
522 MD5Update(&mdContext, rchallenge, rchallenge_len);
523 MD5Final(hash, &mdContext);
524 if (fake_response) {
525 for (len = 0; len < MD5_SIGNATURE_SIZE; len++)
526 cstate->response[len] = (char) (drand48() * 0xff);
527 } else {
528 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
529 }
530 cstate->resp_length = MD5_SIGNATURE_SIZE;
531 break;
532
533 #ifdef CHAPMS
534 case CHAP_MICROSOFT:
535 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
536 break;
537 #endif
538
539 #ifdef CHAPMSV2
540 case CHAP_MICROSOFT_V2:
541 ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len);
542 break;
543 #endif
544
545 default:
546 error("CHAP: unknown digest type %d", cstate->resp_type);
547 return;
548 }
549
550 BZERO(secret, sizeof(secret));
551 ChapSendResponse(cstate);
552 }
553
554
555 /*
556 * ChapReceiveResponse - Receive and process response.
557 */
558 static void
ChapReceiveResponse(cstate,inp,id,len)559 ChapReceiveResponse(cstate, inp, id, len)
560 chap_state *cstate;
561 u_char *inp;
562 int id;
563 int len;
564 {
565 u_char *remmd, remmd_len;
566 int secret_len, old_state;
567 int code;
568 char rhostname[MAXNAMELEN], *rhn;
569 MD5_CTX mdContext;
570 char secret[MAXSECRETLEN];
571 u_char hash[MD5_SIGNATURE_SIZE];
572
573 if (cstate->serverstate == CHAPSS_CLOSED ||
574 cstate->serverstate == CHAPSS_PENDING) {
575 if (debug)
576 dbglog("CHAP Response unexpectedly received in state %s",
577 chap_sstate(cstate->serverstate));
578 return;
579 }
580
581 if (id != cstate->chal_id) {
582 if (debug)
583 dbglog("CHAP: discard response %d; expecting %d", id,
584 cstate->chal_id);
585 return; /* doesn't match ID of last challenge */
586 }
587
588 /*
589 * If we have received a duplicate or bogus Response,
590 * we have to send the same answer (Success/Failure)
591 * as we did for the first Response we saw.
592 */
593 if (cstate->serverstate == CHAPSS_OPEN) {
594 if (debug)
595 dbglog("CHAP ignoring response and resending success message");
596 ChapSendStatus(cstate, CHAP_SUCCESS);
597 return;
598 }
599 if (cstate->serverstate == CHAPSS_BADAUTH) {
600 if (debug)
601 dbglog("CHAP ignoring response and resending failure message");
602 ChapSendStatus(cstate, CHAP_FAILURE);
603 return;
604 }
605
606 if (len < 2) {
607 error("CHAP: Response message length %d", len);
608 return;
609 }
610 GETCHAR(remmd_len, inp); /* get length of MD */
611 remmd = inp; /* get pointer to MD */
612 INCPTR(remmd_len, inp);
613
614 len -= sizeof (u_char) + remmd_len;
615 if (len < 0) {
616 error("CHAP: Response truncated");
617 return;
618 }
619
620 UNTIMEOUT(ChapChallengeTimeout, cstate);
621
622 if (len >= sizeof(rhostname))
623 len = sizeof(rhostname) - 1;
624 BCOPY(inp, rhostname, len);
625 rhostname[len] = '\0';
626
627 /*
628 * Get secret for authenticating them with us,
629 * do the hash ourselves, and compare the result.
630 */
631 code = CHAP_FAILURE;
632 rhn = (explicit_remote? remote_name: rhostname);
633 if (cstate->serverstate == CHAPSS_RECHALLENGE &&
634 strcmp(rhostname, peer_authname) != 0) {
635 warn("Peer changed his name from '%q' to '%q' on rechallenge",
636 peer_authname, rhostname);
637 } else if (!get_secret(cstate->unit, rhn, cstate->chal_name, secret,
638 &secret_len, 1)) {
639 warn("No CHAP secret found for authenticating %q to us (%q)",
640 rhn, cstate->chal_name);
641 } else {
642
643 /* generate MD based on negotiated type */
644 switch (cstate->chal_type) {
645
646 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
647 if (remmd_len != MD5_SIGNATURE_SIZE)
648 break; /* it's not even the right length */
649 MD5Init(&mdContext);
650 MD5Update(&mdContext, &cstate->chal_id, 1);
651 MD5Update(&mdContext, (u_char *)secret, secret_len);
652 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
653 MD5Final(hash, &mdContext);
654
655 /* compare local and remote MDs and send the appropriate status */
656 if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
657 code = CHAP_SUCCESS; /* they are the same! */
658 break;
659
660 #ifdef CHAPMS
661 case CHAP_MICROSOFT:
662 if (ChapMSValidate(cstate, remmd, remmd_len, secret,
663 secret_len))
664 code = CHAP_SUCCESS;
665 break;
666 #endif
667
668 #ifdef CHAPMSV2
669 case CHAP_MICROSOFT_V2:
670 if (ChapMSv2Validate(cstate, rhostname, remmd, remmd_len,
671 secret, secret_len))
672 code = CHAP_SUCCESS;
673 break;
674 #endif
675
676 default:
677 error("CHAP: unknown digest type %d", cstate->chal_type);
678 }
679 }
680
681 BZERO(secret, sizeof(secret));
682 ChapSendStatus(cstate, code);
683
684 if (code == CHAP_SUCCESS) {
685 old_state = cstate->serverstate;
686 cstate->serverstate = CHAPSS_OPEN;
687 if (old_state == CHAPSS_INITIAL_CHAL) {
688 auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
689 }
690 if (cstate->chal_interval != 0)
691 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
692 if (old_state == CHAPSS_INITIAL_CHAL)
693 notice("CHAP peer authentication succeeded for %q", rhostname);
694 else if (debug)
695 dbglog("CHAP peer reauthentication succeeded for %q", rhostname);
696 } else {
697 error("CHAP peer %sauthentication failed for remote host %q",
698 (cstate->serverstate == CHAPSS_INITIAL_CHAL ? "" : "re"),
699 rhostname);
700 cstate->serverstate = CHAPSS_BADAUTH;
701 auth_peer_fail(cstate->unit, PPP_CHAP);
702 }
703 }
704
705 /*
706 * ChapReceiveSuccess - Receive Success
707 */
708 /*ARGSUSED*/
709 static void
ChapReceiveSuccess(cstate,inp,id,len)710 ChapReceiveSuccess(cstate, inp, id, len)
711 chap_state *cstate;
712 u_char *inp;
713 u_char id;
714 int len;
715 {
716
717 if (cstate->clientstate == CHAPCS_OPEN) {
718 /* presumably an answer to a duplicate response */
719 if (debug)
720 dbglog("CHAP duplicate Success message ignored");
721 return;
722 }
723
724 if (cstate->clientstate != CHAPCS_RESPONSE) {
725 /* don't know what this is */
726 if (debug)
727 dbglog("CHAP Success unexpectedly received in state %s",
728 chap_cstate(cstate->clientstate));
729 return;
730 }
731
732 UNTIMEOUT(ChapResponseTimeout, cstate);
733
734 /*
735 * Print message.
736 */
737 if (len > 0)
738 PRINTMSG(inp, len);
739
740 cstate->clientstate = CHAPCS_OPEN;
741
742 auth_withpeer_success(cstate->unit, PPP_CHAP);
743 }
744
745
746 /*
747 * ChapReceiveFailure - Receive failure.
748 */
749 /*ARGSUSED*/
750 static void
ChapReceiveFailure(cstate,inp,id,len)751 ChapReceiveFailure(cstate, inp, id, len)
752 chap_state *cstate;
753 u_char *inp;
754 u_char id;
755 int len;
756 {
757 if (cstate->clientstate != CHAPCS_RESPONSE) {
758 /* don't know what this is */
759 if (debug)
760 dbglog("CHAP Failure unexpectedly received in state %s",
761 chap_cstate(cstate->clientstate));
762 return;
763 }
764
765 UNTIMEOUT(ChapResponseTimeout, cstate);
766
767 /*
768 * Print message.
769 */
770 if (len > 0)
771 PRINTMSG(inp, len);
772
773 error("CHAP authentication failed");
774 auth_withpeer_fail(cstate->unit, PPP_CHAP);
775 }
776
777
778 /*
779 * ChapSendChallenge - Send an Authenticate challenge.
780 */
781 static void
ChapSendChallenge(cstate)782 ChapSendChallenge(cstate)
783 chap_state *cstate;
784 {
785 u_char *outp;
786 int chal_len, name_len;
787 int outlen;
788
789 chal_len = cstate->chal_len;
790 name_len = strlen(cstate->chal_name);
791 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
792 outp = outpacket_buf;
793
794 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
795
796 PUTCHAR(CHAP_CHALLENGE, outp);
797 PUTCHAR(cstate->chal_id, outp);
798 PUTSHORT(outlen, outp);
799
800 PUTCHAR(chal_len, outp); /* put length of challenge */
801 BCOPY(cstate->challenge, outp, chal_len);
802 INCPTR(chal_len, outp);
803
804 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
805
806 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
807
808 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
809 ++cstate->chal_transmits;
810 }
811
812
813 /*
814 * ChapSendStatus - Send a status response (ack or nak).
815 */
816 static void
ChapSendStatus(cstate,code)817 ChapSendStatus(cstate, code)
818 chap_state *cstate;
819 int code;
820 {
821 u_char *outp;
822 int outlen, msglen;
823 char msg[256], *msgp;
824
825 if ((msgp = cstate->stat_message) != NULL) {
826 msglen = cstate->stat_length;
827 } else {
828 if (code == CHAP_SUCCESS)
829 (void) slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
830 else
831 (void) slprintf(msg, sizeof(msg), "I don't like you. Go 'way.");
832 msglen = strlen(msg);
833 msgp = msg;
834 }
835
836 outlen = CHAP_HEADERLEN + msglen;
837 outp = outpacket_buf;
838
839 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
840
841 PUTCHAR(code, outp);
842 PUTCHAR(cstate->chal_id, outp);
843 PUTSHORT(outlen, outp);
844 BCOPY(msgp, outp, msglen);
845 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
846 }
847
848 /*
849 * ChapGenChallenge is used to generate a pseudo-random challenge string of
850 * a pseudo-random length between min_len and max_len. The challenge
851 * string and its length are stored in *cstate, and various other fields of
852 * *cstate are initialized.
853 */
854
855 static void
ChapGenChallenge(cstate)856 ChapGenChallenge(cstate)
857 chap_state *cstate;
858 {
859 int chal_len;
860 u_char *ptr = cstate->challenge;
861 int i;
862
863 /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
864 MAX_CHALLENGE_LENGTH */
865 chal_len = (unsigned) ((drand48() *
866 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
867 MIN_CHALLENGE_LENGTH);
868 cstate->chal_len = chal_len;
869 cstate->chal_id = ++cstate->id;
870 cstate->chal_transmits = 0;
871
872 /* XXX tack on saved get_first_hwaddr() here. */
873
874 /* generate a random string */
875 for (i = 0; i < chal_len; i++)
876 *ptr++ = (char) (drand48() * 0xff);
877 }
878
879 /*
880 * ChapSendResponse - send a response packet with values as specified
881 * in *cstate.
882 */
883 /* ARGSUSED */
884 static void
ChapSendResponse(cstate)885 ChapSendResponse(cstate)
886 chap_state *cstate;
887 {
888 u_char *outp;
889 int outlen, md_len, name_len;
890
891 md_len = cstate->resp_length;
892 name_len = strlen(cstate->resp_name);
893 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
894 outp = outpacket_buf;
895
896 MAKEHEADER(outp, PPP_CHAP);
897
898 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
899 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
900 PUTSHORT(outlen, outp); /* packet length */
901
902 PUTCHAR(md_len, outp); /* length of MD */
903 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
904 INCPTR(md_len, outp);
905
906 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
907
908 /* send the packet */
909 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
910
911 cstate->clientstate = CHAPCS_RESPONSE;
912 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
913 ++cstate->resp_transmits;
914 }
915
916 /*
917 * ChapPrintPkt - print the contents of a CHAP packet.
918 */
919 static char *ChapCodenames[] = {
920 "Challenge", "Response", "Success", "Failure"
921 };
922
923 static int
ChapPrintPkt(p,plen,printer,arg)924 ChapPrintPkt(p, plen, printer, arg)
925 u_char *p;
926 int plen;
927 void (*printer) __P((void *, const char *, ...));
928 void *arg;
929 {
930 int code, id, len;
931 int clen, nlen;
932 u_char x;
933
934 if (plen < CHAP_HEADERLEN)
935 return 0;
936 GETCHAR(code, p);
937 GETCHAR(id, p);
938 GETSHORT(len, p);
939 if (len < CHAP_HEADERLEN || len > plen)
940 return 0;
941
942 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
943 printer(arg, " %s", ChapCodenames[code-1]);
944 else
945 printer(arg, " code=0x%x", code);
946 printer(arg, " id=0x%x", id);
947 len -= CHAP_HEADERLEN;
948 switch (code) {
949 case CHAP_CHALLENGE:
950 case CHAP_RESPONSE:
951 if (len < 1)
952 break;
953 clen = p[0];
954 if (len < clen + 1)
955 break;
956 ++p;
957 nlen = len - clen - 1;
958 printer(arg, " <");
959 for (; clen > 0; --clen) {
960 GETCHAR(x, p);
961 printer(arg, "%.2x", x);
962 }
963 printer(arg, ">, name = ");
964 print_string((char *)p, nlen, printer, arg);
965 break;
966 case CHAP_FAILURE:
967 case CHAP_SUCCESS:
968 printer(arg, " ");
969 print_string((char *)p, len, printer, arg);
970 break;
971 default:
972 for (clen = len; clen > 0; --clen) {
973 GETCHAR(x, p);
974 printer(arg, " %.2x", x);
975 }
976 }
977
978 return len + CHAP_HEADERLEN;
979 }
980