xref: /openbsd/usr.sbin/pppd/chap.c (revision 5b133f3f)
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