1 /* $OpenBSD: chap.c,v 1.20 2023/03/08 04:43:14 guenther Exp $ */
2
3 /*
4 * chap.c - Challenge Handshake Authentication Protocol.
5 *
6 * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The name(s) of the authors of this software must not be used to
21 * endorse or promote products derived from this software without
22 * prior written permission.
23 *
24 * 4. Redistributions of any form whatsoever must retain the following
25 * acknowledgment:
26 * "This product includes software developed by Paul Mackerras
27 * <paulus@samba.org>".
28 *
29 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
30 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
31 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
32 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
33 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
34 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
35 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
36 *
37 * Copyright (c) 1991 Gregory M. Christy.
38 * All rights reserved.
39 *
40 * Redistribution and use in source and binary forms are permitted
41 * provided that the above copyright notice and this paragraph are
42 * duplicated in all such forms and that any documentation,
43 * advertising materials, and other materials related to such
44 * distribution and use acknowledge that the software was developed
45 * by Gregory M. Christy. The name of the author may not be used to
46 * endorse or promote products derived from this software without
47 * specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
50 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
51 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
52 */
53
54 /*
55 * TODO:
56 */
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sys/types.h>
62 #include <sys/time.h>
63 #include <syslog.h>
64 #include <md5.h>
65
66 #include "pppd.h"
67 #include "chap.h"
68
69 /*
70 * Protocol entry points.
71 */
72 static void ChapInit(int);
73 static void ChapLowerUp(int);
74 static void ChapLowerDown(int);
75 static void ChapInput(int, u_char *, int);
76 static void ChapProtocolReject(int);
77 static int ChapPrintPkt(u_char *, int, void (*)(void *, char *, ...), void *);
78
79 struct protent chap_protent = {
80 PPP_CHAP,
81 ChapInit,
82 ChapInput,
83 ChapProtocolReject,
84 ChapLowerUp,
85 ChapLowerDown,
86 NULL,
87 NULL,
88 ChapPrintPkt,
89 NULL,
90 1,
91 "CHAP",
92 NULL,
93 NULL,
94 NULL
95 };
96
97 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
98
99 static void ChapChallengeTimeout(void *);
100 static void ChapResponseTimeout(void *);
101 static void ChapReceiveChallenge(chap_state *, u_char *, int, int);
102 static void ChapRechallenge(void *);
103 static void ChapReceiveResponse(chap_state *, u_char *, int, int);
104 static void ChapReceiveSuccess(chap_state *, u_char *, int, int);
105 static void ChapReceiveFailure(chap_state *, u_char *, int, int);
106 static void ChapSendStatus(chap_state *, int);
107 static void ChapSendChallenge(chap_state *);
108 static void ChapSendResponse(chap_state *);
109 static void ChapGenChallenge(chap_state *);
110
111 /*
112 * ChapInit - Initialize a CHAP unit.
113 */
114 static void
ChapInit(unit)115 ChapInit(unit)
116 int unit;
117 {
118 chap_state *cstate = &chap[unit];
119
120 BZERO(cstate, sizeof(*cstate));
121 cstate->unit = unit;
122 cstate->clientstate = CHAPCS_INITIAL;
123 cstate->serverstate = CHAPSS_INITIAL;
124 cstate->timeouttime = CHAP_DEFTIMEOUT;
125 cstate->max_transmits = CHAP_DEFTRANSMITS;
126 /* random number generator is initialized in magic_init */
127 }
128
129
130 /*
131 * ChapAuthWithPeer - Authenticate us with our peer (start client).
132 *
133 */
134 void
ChapAuthWithPeer(unit,our_name,digest)135 ChapAuthWithPeer(unit, our_name, digest)
136 int unit;
137 char *our_name;
138 int digest;
139 {
140 chap_state *cstate = &chap[unit];
141
142 cstate->resp_name = our_name;
143 cstate->resp_type = digest;
144
145 if (cstate->clientstate == CHAPCS_INITIAL ||
146 cstate->clientstate == CHAPCS_PENDING) {
147 /* lower layer isn't up - wait until later */
148 cstate->clientstate = CHAPCS_PENDING;
149 return;
150 }
151
152 /*
153 * We get here as a result of LCP coming up.
154 * So even if CHAP was open before, we will
155 * have to re-authenticate ourselves.
156 */
157 cstate->clientstate = CHAPCS_LISTEN;
158 }
159
160
161 /*
162 * ChapAuthPeer - Authenticate our peer (start server).
163 */
164 void
ChapAuthPeer(unit,our_name,digest)165 ChapAuthPeer(unit, our_name, digest)
166 int unit;
167 char *our_name;
168 int digest;
169 {
170 chap_state *cstate = &chap[unit];
171
172 cstate->chal_name = our_name;
173 cstate->chal_type = digest;
174
175 if (cstate->serverstate == CHAPSS_INITIAL ||
176 cstate->serverstate == CHAPSS_PENDING) {
177 /* lower layer isn't up - wait until later */
178 cstate->serverstate = CHAPSS_PENDING;
179 return;
180 }
181
182 ChapGenChallenge(cstate);
183 ChapSendChallenge(cstate); /* crank it up dude! */
184 cstate->serverstate = CHAPSS_INITIAL_CHAL;
185 }
186
187
188 /*
189 * ChapChallengeTimeout - Timeout expired on sending challenge.
190 */
191 static void
ChapChallengeTimeout(arg)192 ChapChallengeTimeout(arg)
193 void *arg;
194 {
195 chap_state *cstate = (chap_state *) arg;
196
197 /* if we aren't sending challenges, don't worry. then again we */
198 /* probably shouldn't be here either */
199 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
200 cstate->serverstate != CHAPSS_RECHALLENGE)
201 return;
202
203 if (cstate->chal_transmits >= cstate->max_transmits) {
204 /* give up on peer */
205 syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
206 cstate->serverstate = CHAPSS_BADAUTH;
207 auth_peer_fail(cstate->unit, PPP_CHAP);
208 return;
209 }
210
211 ChapSendChallenge(cstate); /* Re-send challenge */
212 }
213
214
215 /*
216 * ChapResponseTimeout - Timeout expired on sending response.
217 */
218 static void
ChapResponseTimeout(arg)219 ChapResponseTimeout(arg)
220 void *arg;
221 {
222 chap_state *cstate = (chap_state *) arg;
223
224 /* if we aren't sending a response, don't worry. */
225 if (cstate->clientstate != CHAPCS_RESPONSE)
226 return;
227
228 ChapSendResponse(cstate); /* re-send response */
229 }
230
231
232 /*
233 * ChapRechallenge - Time to challenge the peer again.
234 */
235 static void
ChapRechallenge(arg)236 ChapRechallenge(arg)
237 void *arg;
238 {
239 chap_state *cstate = (chap_state *) arg;
240
241 /* if we aren't sending a response, don't worry. */
242 if (cstate->serverstate != CHAPSS_OPEN)
243 return;
244
245 ChapGenChallenge(cstate);
246 ChapSendChallenge(cstate);
247 cstate->serverstate = CHAPSS_RECHALLENGE;
248 }
249
250
251 /*
252 * ChapLowerUp - The lower layer is up.
253 *
254 * Start up if we have pending requests.
255 */
256 static void
ChapLowerUp(unit)257 ChapLowerUp(unit)
258 int unit;
259 {
260 chap_state *cstate = &chap[unit];
261
262 if (cstate->clientstate == CHAPCS_INITIAL)
263 cstate->clientstate = CHAPCS_CLOSED;
264 else if (cstate->clientstate == CHAPCS_PENDING)
265 cstate->clientstate = CHAPCS_LISTEN;
266
267 if (cstate->serverstate == CHAPSS_INITIAL)
268 cstate->serverstate = CHAPSS_CLOSED;
269 else if (cstate->serverstate == CHAPSS_PENDING) {
270 ChapGenChallenge(cstate);
271 ChapSendChallenge(cstate);
272 cstate->serverstate = CHAPSS_INITIAL_CHAL;
273 }
274 }
275
276
277 /*
278 * ChapLowerDown - The lower layer is down.
279 *
280 * Cancel all timeouts.
281 */
282 static void
ChapLowerDown(unit)283 ChapLowerDown(unit)
284 int unit;
285 {
286 chap_state *cstate = &chap[unit];
287
288 /* Timeout(s) pending? Cancel if so. */
289 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
290 cstate->serverstate == CHAPSS_RECHALLENGE)
291 UNTIMEOUT(ChapChallengeTimeout, cstate);
292 else if (cstate->serverstate == CHAPSS_OPEN
293 && cstate->chal_interval != 0)
294 UNTIMEOUT(ChapRechallenge, cstate);
295 if (cstate->clientstate == CHAPCS_RESPONSE)
296 UNTIMEOUT(ChapResponseTimeout, cstate);
297
298 cstate->clientstate = CHAPCS_INITIAL;
299 cstate->serverstate = CHAPSS_INITIAL;
300 }
301
302
303 /*
304 * ChapProtocolReject - Peer doesn't grok CHAP.
305 */
306 static void
ChapProtocolReject(unit)307 ChapProtocolReject(unit)
308 int unit;
309 {
310 chap_state *cstate = &chap[unit];
311
312 if (cstate->serverstate != CHAPSS_INITIAL &&
313 cstate->serverstate != CHAPSS_CLOSED)
314 auth_peer_fail(unit, PPP_CHAP);
315 if (cstate->clientstate != CHAPCS_INITIAL &&
316 cstate->clientstate != CHAPCS_CLOSED)
317 auth_withpeer_fail(unit, PPP_CHAP);
318 ChapLowerDown(unit); /* shutdown chap */
319 }
320
321
322 /*
323 * ChapInput - Input CHAP packet.
324 */
325 static void
ChapInput(unit,inpacket,packet_len)326 ChapInput(unit, inpacket, packet_len)
327 int unit;
328 u_char *inpacket;
329 int packet_len;
330 {
331 chap_state *cstate = &chap[unit];
332 u_char *inp;
333 u_char code, id;
334 int len;
335
336 /*
337 * Parse header (code, id and length).
338 * If packet too short, drop it.
339 */
340 inp = inpacket;
341 if (packet_len < CHAP_HEADERLEN) {
342 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
343 return;
344 }
345 GETCHAR(code, inp);
346 GETCHAR(id, inp);
347 GETSHORT(len, inp);
348 if (len < CHAP_HEADERLEN) {
349 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
350 return;
351 }
352 if (len > packet_len) {
353 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
354 return;
355 }
356 len -= CHAP_HEADERLEN;
357
358 /*
359 * Action depends on code (as in fact it usually does :-).
360 */
361 switch (code) {
362 case CHAP_CHALLENGE:
363 ChapReceiveChallenge(cstate, inp, id, len);
364 break;
365
366 case CHAP_RESPONSE:
367 ChapReceiveResponse(cstate, inp, id, len);
368 break;
369
370 case CHAP_FAILURE:
371 ChapReceiveFailure(cstate, inp, id, len);
372 break;
373
374 case CHAP_SUCCESS:
375 ChapReceiveSuccess(cstate, inp, id, len);
376 break;
377
378 default: /* Need code reject? */
379 syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
380 break;
381 }
382 }
383
384
385 /*
386 * ChapReceiveChallenge - Receive Challenge and send Response.
387 */
388 static void
ChapReceiveChallenge(cstate,inp,id,len)389 ChapReceiveChallenge(cstate, inp, id, len)
390 chap_state *cstate;
391 u_char *inp;
392 int id;
393 int len;
394 {
395 int rchallenge_len;
396 u_char *rchallenge;
397 int secret_len;
398 char secret[MAXSECRETLEN];
399 char rhostname[256];
400 MD5_CTX mdContext;
401 u_char hash[MD5_SIGNATURE_SIZE];
402
403 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
404 if (cstate->clientstate == CHAPCS_CLOSED ||
405 cstate->clientstate == CHAPCS_PENDING) {
406 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
407 cstate->clientstate));
408 return;
409 }
410
411 if (len < 2) {
412 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
413 return;
414 }
415
416 GETCHAR(rchallenge_len, inp);
417 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
418 if (len < 0) {
419 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
420 return;
421 }
422 rchallenge = inp;
423 INCPTR(rchallenge_len, inp);
424
425 if (len >= sizeof(rhostname))
426 len = sizeof(rhostname) - 1;
427 BCOPY(inp, rhostname, len);
428 rhostname[len] = '\000';
429
430 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'",
431 rhostname));
432
433 /* Microsoft doesn't send their name back in the PPP packet */
434 if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
435 strlcpy(rhostname, remote_name, sizeof(rhostname));
436 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name",
437 rhostname));
438 }
439
440 /* get secret for authenticating ourselves with the specified host */
441 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
442 secret, &secret_len, 0)) {
443 secret_len = 0; /* assume null secret if can't find one */
444 syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
445 rhostname);
446 }
447
448 /* cancel response send timeout if necessary */
449 if (cstate->clientstate == CHAPCS_RESPONSE)
450 UNTIMEOUT(ChapResponseTimeout, cstate);
451
452 cstate->resp_id = id;
453 cstate->resp_transmits = 0;
454
455 /* generate MD based on negotiated type */
456 switch (cstate->resp_type) {
457
458 case CHAP_DIGEST_MD5:
459 MD5Init(&mdContext);
460 MD5Update(&mdContext, &cstate->resp_id, 1);
461 MD5Update(&mdContext, secret, secret_len);
462 MD5Update(&mdContext, rchallenge, rchallenge_len);
463 MD5Final(hash, &mdContext);
464 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
465 cstate->resp_length = MD5_SIGNATURE_SIZE;
466 break;
467
468 default:
469 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
470 return;
471 }
472
473 EXPLICIT_BZERO(secret, sizeof(secret));
474 ChapSendResponse(cstate);
475 }
476
477
478 /*
479 * ChapReceiveResponse - Receive and process response.
480 */
481 static void
ChapReceiveResponse(cstate,inp,id,len)482 ChapReceiveResponse(cstate, inp, id, len)
483 chap_state *cstate;
484 u_char *inp;
485 int id;
486 int len;
487 {
488 u_char *remmd, remmd_len;
489 int secret_len, old_state;
490 int code;
491 char rhostname[256];
492 MD5_CTX mdContext;
493 char secret[MAXSECRETLEN];
494 u_char hash[MD5_SIGNATURE_SIZE];
495
496 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
497
498 if (cstate->serverstate == CHAPSS_CLOSED ||
499 cstate->serverstate == CHAPSS_PENDING) {
500 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
501 cstate->serverstate));
502 return;
503 }
504
505 if (id != cstate->chal_id)
506 return; /* doesn't match ID of last challenge */
507
508 /*
509 * If we have received a duplicate or bogus Response,
510 * we have to send the same answer (Success/Failure)
511 * as we did for the first Response we saw.
512 */
513 if (cstate->serverstate == CHAPSS_OPEN) {
514 ChapSendStatus(cstate, CHAP_SUCCESS);
515 return;
516 }
517 if (cstate->serverstate == CHAPSS_BADAUTH) {
518 ChapSendStatus(cstate, CHAP_FAILURE);
519 return;
520 }
521
522 if (len < 2) {
523 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
524 return;
525 }
526 GETCHAR(remmd_len, inp); /* get length of MD */
527 remmd = inp; /* get pointer to MD */
528 INCPTR(remmd_len, inp);
529
530 len -= sizeof (u_char) + remmd_len;
531 if (len < 0) {
532 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
533 return;
534 }
535
536 UNTIMEOUT(ChapChallengeTimeout, cstate);
537
538 if (len >= sizeof(rhostname))
539 len = sizeof(rhostname) - 1;
540 BCOPY(inp, rhostname, len);
541 rhostname[len] = '\000';
542
543 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
544 rhostname));
545
546 /*
547 * Get secret for authenticating them with us,
548 * do the hash ourselves, and compare the result.
549 */
550 code = CHAP_FAILURE;
551 if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
552 secret, &secret_len, 1)) {
553 syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
554 rhostname);
555 } else {
556
557 /* generate MD based on negotiated type */
558 switch (cstate->chal_type) {
559
560 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
561 if (remmd_len != MD5_SIGNATURE_SIZE)
562 break; /* it's not even the right length */
563 MD5Init(&mdContext);
564 MD5Update(&mdContext, &cstate->chal_id, 1);
565 MD5Update(&mdContext, secret, secret_len);
566 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
567 MD5Final(hash, &mdContext);
568
569 /* compare local and remote MDs and send the appropriate status */
570 if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
571 code = CHAP_SUCCESS; /* they are the same! */
572 break;
573
574 default:
575 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
576 }
577 }
578
579 EXPLICIT_BZERO(secret, sizeof(secret));
580 ChapSendStatus(cstate, code);
581
582 if (code == CHAP_SUCCESS) {
583 old_state = cstate->serverstate;
584 cstate->serverstate = CHAPSS_OPEN;
585 if (old_state == CHAPSS_INITIAL_CHAL) {
586 auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
587 }
588 if (cstate->chal_interval != 0)
589 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
590 syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s",
591 rhostname);
592
593 } else {
594 syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s",
595 rhostname);
596 cstate->serverstate = CHAPSS_BADAUTH;
597 auth_peer_fail(cstate->unit, PPP_CHAP);
598 }
599 }
600
601 /*
602 * ChapReceiveSuccess - Receive Success
603 */
604 static void
ChapReceiveSuccess(cstate,inp,id,len)605 ChapReceiveSuccess(cstate, inp, id, len)
606 chap_state *cstate;
607 u_char *inp;
608 u_char id;
609 int len;
610 {
611
612 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
613
614 if (cstate->clientstate == CHAPCS_OPEN)
615 /* presumably an answer to a duplicate response */
616 return;
617
618 if (cstate->clientstate != CHAPCS_RESPONSE) {
619 /* don't know what this is */
620 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
621 cstate->clientstate));
622 return;
623 }
624
625 UNTIMEOUT(ChapResponseTimeout, cstate);
626
627 /*
628 * Print message.
629 */
630 if (len > 0)
631 PRINTMSG(inp, len);
632
633 cstate->clientstate = CHAPCS_OPEN;
634
635 auth_withpeer_success(cstate->unit, PPP_CHAP);
636 }
637
638
639 /*
640 * ChapReceiveFailure - Receive failure.
641 */
642 static void
ChapReceiveFailure(cstate,inp,id,len)643 ChapReceiveFailure(cstate, inp, id, len)
644 chap_state *cstate;
645 u_char *inp;
646 u_char id;
647 int len;
648 {
649 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
650
651 if (cstate->clientstate != CHAPCS_RESPONSE) {
652 /* don't know what this is */
653 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
654 cstate->clientstate));
655 return;
656 }
657
658 UNTIMEOUT(ChapResponseTimeout, cstate);
659
660 /*
661 * Print message.
662 */
663 if (len > 0)
664 PRINTMSG(inp, len);
665
666 syslog(LOG_ERR, "CHAP authentication failed");
667 auth_withpeer_fail(cstate->unit, PPP_CHAP);
668 }
669
670
671 /*
672 * ChapSendChallenge - Send an Authenticate challenge.
673 */
674 static void
ChapSendChallenge(cstate)675 ChapSendChallenge(cstate)
676 chap_state *cstate;
677 {
678 u_char *outp;
679 int chal_len, name_len;
680 int outlen;
681
682 chal_len = cstate->chal_len;
683 name_len = strlen(cstate->chal_name);
684 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
685 outp = outpacket_buf;
686
687 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
688
689 PUTCHAR(CHAP_CHALLENGE, outp);
690 PUTCHAR(cstate->chal_id, outp);
691 PUTSHORT(outlen, outp);
692
693 PUTCHAR(chal_len, outp); /* put length of challenge */
694 BCOPY(cstate->challenge, outp, chal_len);
695 INCPTR(chal_len, outp);
696
697 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
698
699 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
700
701 CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
702
703 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
704 ++cstate->chal_transmits;
705 }
706
707
708 /*
709 * ChapSendStatus - Send a status response (ack or nak).
710 */
711 static void
ChapSendStatus(cstate,code)712 ChapSendStatus(cstate, code)
713 chap_state *cstate;
714 int code;
715 {
716 u_char *outp;
717 int outlen, msglen;
718 char msg[256];
719
720 if (code == CHAP_SUCCESS)
721 snprintf(msg, sizeof msg, "Welcome to %s.", hostname);
722 else
723 snprintf(msg, sizeof msg, "I don't like you. Go 'way.");
724 msglen = strlen(msg);
725
726 outlen = CHAP_HEADERLEN + msglen;
727 outp = outpacket_buf;
728
729 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
730
731 PUTCHAR(code, outp);
732 PUTCHAR(cstate->chal_id, outp);
733 PUTSHORT(outlen, outp);
734 BCOPY(msg, outp, msglen);
735 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
736
737 CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
738 cstate->chal_id));
739 }
740
741 /*
742 * ChapGenChallenge is used to generate a pseudo-random challenge string of
743 * a pseudo-random length between min_len and max_len. The challenge
744 * string and its length are stored in *cstate, and various other fields of
745 * *cstate are initialized.
746 */
747
748 static void
ChapGenChallenge(cstate)749 ChapGenChallenge(cstate)
750 chap_state *cstate;
751 {
752 int chal_len;
753
754 /* pick a random challenge length >= MIN_CHALLENGE_LENGTH and
755 <= MAX_CHALLENGE_LENGTH */
756 chal_len = MIN_CHALLENGE_LENGTH +
757 arc4random_uniform(MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH + 1);
758
759 cstate->chal_len = chal_len;
760 cstate->chal_id = ++cstate->id;
761 cstate->chal_transmits = 0;
762
763 /* generate a random string */
764 arc4random_buf(cstate->challenge, chal_len);
765 }
766
767 /*
768 * ChapSendResponse - send a response packet with values as specified
769 * in *cstate.
770 */
771 static void
ChapSendResponse(cstate)772 ChapSendResponse(cstate)
773 chap_state *cstate;
774 {
775 u_char *outp;
776 int outlen, md_len, name_len;
777
778 md_len = cstate->resp_length;
779 name_len = strlen(cstate->resp_name);
780 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
781 outp = outpacket_buf;
782
783 MAKEHEADER(outp, PPP_CHAP);
784
785 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
786 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
787 PUTSHORT(outlen, outp); /* packet length */
788
789 PUTCHAR(md_len, outp); /* length of MD */
790 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
791 INCPTR(md_len, outp);
792
793 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
794
795 /* send the packet */
796 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
797
798 cstate->clientstate = CHAPCS_RESPONSE;
799 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
800 ++cstate->resp_transmits;
801 }
802
803 /*
804 * ChapPrintPkt - print the contents of a CHAP packet.
805 */
806 static char *ChapCodenames[] = {
807 "Challenge", "Response", "Success", "Failure"
808 };
809
810 static int
ChapPrintPkt(p,plen,printer,arg)811 ChapPrintPkt(p, plen, printer, arg)
812 u_char *p;
813 int plen;
814 void (*printer)(void *, char *, ...);
815 void *arg;
816 {
817 int code, id, len;
818 int clen, nlen;
819 u_char x;
820
821 if (plen < CHAP_HEADERLEN)
822 return 0;
823 GETCHAR(code, p);
824 GETCHAR(id, p);
825 GETSHORT(len, p);
826 if (len < CHAP_HEADERLEN || len > plen)
827 return 0;
828
829 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
830 printer(arg, " %s", ChapCodenames[code-1]);
831 else
832 printer(arg, " code=0x%x", code);
833 printer(arg, " id=0x%x", id);
834 len -= CHAP_HEADERLEN;
835 switch (code) {
836 case CHAP_CHALLENGE:
837 case CHAP_RESPONSE:
838 if (len < 1)
839 break;
840 clen = p[0];
841 if (len < clen + 1)
842 break;
843 ++p;
844 nlen = len - clen - 1;
845 printer(arg, " <");
846 for (; clen > 0; --clen) {
847 GETCHAR(x, p);
848 printer(arg, "%.2x", x);
849 }
850 printer(arg, ">, name = ");
851 print_string((char *)p, nlen, printer, arg);
852 break;
853 case CHAP_FAILURE:
854 case CHAP_SUCCESS:
855 printer(arg, " ");
856 print_string((char *)p, len, printer, arg);
857 break;
858 default:
859 for (clen = len; clen > 0; --clen) {
860 GETCHAR(x, p);
861 printer(arg, " %.2x", x);
862 }
863 }
864
865 return len + CHAP_HEADERLEN;
866 }
867