xref: /openbsd/usr.sbin/pppd/chap.c (revision 6798d9df)
1 /*	$OpenBSD: chap.c,v 1.21 2024/08/09 05:16:13 deraadt 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(int unit)115 ChapInit(int unit)
116 {
117     chap_state *cstate = &chap[unit];
118 
119     BZERO(cstate, sizeof(*cstate));
120     cstate->unit = unit;
121     cstate->clientstate = CHAPCS_INITIAL;
122     cstate->serverstate = CHAPSS_INITIAL;
123     cstate->timeouttime = CHAP_DEFTIMEOUT;
124     cstate->max_transmits = CHAP_DEFTRANSMITS;
125     /* random number generator is initialized in magic_init */
126 }
127 
128 
129 /*
130  * ChapAuthWithPeer - Authenticate us with our peer (start client).
131  *
132  */
133 void
ChapAuthWithPeer(int unit,char * our_name,int digest)134 ChapAuthWithPeer(int unit, char *our_name, int digest)
135 {
136     chap_state *cstate = &chap[unit];
137 
138     cstate->resp_name = our_name;
139     cstate->resp_type = digest;
140 
141     if (cstate->clientstate == CHAPCS_INITIAL ||
142 	cstate->clientstate == CHAPCS_PENDING) {
143 	/* lower layer isn't up - wait until later */
144 	cstate->clientstate = CHAPCS_PENDING;
145 	return;
146     }
147 
148     /*
149      * We get here as a result of LCP coming up.
150      * So even if CHAP was open before, we will
151      * have to re-authenticate ourselves.
152      */
153     cstate->clientstate = CHAPCS_LISTEN;
154 }
155 
156 
157 /*
158  * ChapAuthPeer - Authenticate our peer (start server).
159  */
160 void
ChapAuthPeer(int unit,char * our_name,int digest)161 ChapAuthPeer(int unit, char *our_name, int digest)
162 {
163     chap_state *cstate = &chap[unit];
164 
165     cstate->chal_name = our_name;
166     cstate->chal_type = digest;
167 
168     if (cstate->serverstate == CHAPSS_INITIAL ||
169 	cstate->serverstate == CHAPSS_PENDING) {
170 	/* lower layer isn't up - wait until later */
171 	cstate->serverstate = CHAPSS_PENDING;
172 	return;
173     }
174 
175     ChapGenChallenge(cstate);
176     ChapSendChallenge(cstate);		/* crank it up dude! */
177     cstate->serverstate = CHAPSS_INITIAL_CHAL;
178 }
179 
180 
181 /*
182  * ChapChallengeTimeout - Timeout expired on sending challenge.
183  */
184 static void
ChapChallengeTimeout(void * arg)185 ChapChallengeTimeout(void *arg)
186 {
187     chap_state *cstate = (chap_state *) arg;
188 
189     /* if we aren't sending challenges, don't worry.  then again we */
190     /* probably shouldn't be here either */
191     if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
192 	cstate->serverstate != CHAPSS_RECHALLENGE)
193 	return;
194 
195     if (cstate->chal_transmits >= cstate->max_transmits) {
196 	/* give up on peer */
197 	syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
198 	cstate->serverstate = CHAPSS_BADAUTH;
199 	auth_peer_fail(cstate->unit, PPP_CHAP);
200 	return;
201     }
202 
203     ChapSendChallenge(cstate);		/* Re-send challenge */
204 }
205 
206 
207 /*
208  * ChapResponseTimeout - Timeout expired on sending response.
209  */
210 static void
ChapResponseTimeout(void * arg)211 ChapResponseTimeout(void *arg)
212 {
213     chap_state *cstate = (chap_state *) arg;
214 
215     /* if we aren't sending a response, don't worry. */
216     if (cstate->clientstate != CHAPCS_RESPONSE)
217 	return;
218 
219     ChapSendResponse(cstate);		/* re-send response */
220 }
221 
222 
223 /*
224  * ChapRechallenge - Time to challenge the peer again.
225  */
226 static void
ChapRechallenge(void * arg)227 ChapRechallenge(void *arg)
228 {
229     chap_state *cstate = (chap_state *) arg;
230 
231     /* if we aren't sending a response, don't worry. */
232     if (cstate->serverstate != CHAPSS_OPEN)
233 	return;
234 
235     ChapGenChallenge(cstate);
236     ChapSendChallenge(cstate);
237     cstate->serverstate = CHAPSS_RECHALLENGE;
238 }
239 
240 
241 /*
242  * ChapLowerUp - The lower layer is up.
243  *
244  * Start up if we have pending requests.
245  */
246 static void
ChapLowerUp(int unit)247 ChapLowerUp(int unit)
248 {
249     chap_state *cstate = &chap[unit];
250 
251     if (cstate->clientstate == CHAPCS_INITIAL)
252 	cstate->clientstate = CHAPCS_CLOSED;
253     else if (cstate->clientstate == CHAPCS_PENDING)
254 	cstate->clientstate = CHAPCS_LISTEN;
255 
256     if (cstate->serverstate == CHAPSS_INITIAL)
257 	cstate->serverstate = CHAPSS_CLOSED;
258     else if (cstate->serverstate == CHAPSS_PENDING) {
259 	ChapGenChallenge(cstate);
260 	ChapSendChallenge(cstate);
261 	cstate->serverstate = CHAPSS_INITIAL_CHAL;
262     }
263 }
264 
265 
266 /*
267  * ChapLowerDown - The lower layer is down.
268  *
269  * Cancel all timeouts.
270  */
271 static void
ChapLowerDown(int unit)272 ChapLowerDown(int unit)
273 {
274     chap_state *cstate = &chap[unit];
275 
276     /* Timeout(s) pending?  Cancel if so. */
277     if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
278 	cstate->serverstate == CHAPSS_RECHALLENGE)
279 	UNTIMEOUT(ChapChallengeTimeout, cstate);
280     else if (cstate->serverstate == CHAPSS_OPEN
281 	     && cstate->chal_interval != 0)
282 	UNTIMEOUT(ChapRechallenge, cstate);
283     if (cstate->clientstate == CHAPCS_RESPONSE)
284 	UNTIMEOUT(ChapResponseTimeout, cstate);
285 
286     cstate->clientstate = CHAPCS_INITIAL;
287     cstate->serverstate = CHAPSS_INITIAL;
288 }
289 
290 
291 /*
292  * ChapProtocolReject - Peer doesn't grok CHAP.
293  */
294 static void
ChapProtocolReject(int unit)295 ChapProtocolReject(int unit)
296 {
297     chap_state *cstate = &chap[unit];
298 
299     if (cstate->serverstate != CHAPSS_INITIAL &&
300 	cstate->serverstate != CHAPSS_CLOSED)
301 	auth_peer_fail(unit, PPP_CHAP);
302     if (cstate->clientstate != CHAPCS_INITIAL &&
303 	cstate->clientstate != CHAPCS_CLOSED)
304 	auth_withpeer_fail(unit, PPP_CHAP);
305     ChapLowerDown(unit);		/* shutdown chap */
306 }
307 
308 
309 /*
310  * ChapInput - Input CHAP packet.
311  */
312 static void
ChapInput(int unit,u_char * inpacket,int packet_len)313 ChapInput(int unit, u_char *inpacket, int packet_len)
314 {
315     chap_state *cstate = &chap[unit];
316     u_char *inp;
317     u_char code, id;
318     int len;
319 
320     /*
321      * Parse header (code, id and length).
322      * If packet too short, drop it.
323      */
324     inp = inpacket;
325     if (packet_len < CHAP_HEADERLEN) {
326 	CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
327 	return;
328     }
329     GETCHAR(code, inp);
330     GETCHAR(id, inp);
331     GETSHORT(len, inp);
332     if (len < CHAP_HEADERLEN) {
333 	CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
334 	return;
335     }
336     if (len > packet_len) {
337 	CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
338 	return;
339     }
340     len -= CHAP_HEADERLEN;
341 
342     /*
343      * Action depends on code (as in fact it usually does :-).
344      */
345     switch (code) {
346     case CHAP_CHALLENGE:
347 	ChapReceiveChallenge(cstate, inp, id, len);
348 	break;
349 
350     case CHAP_RESPONSE:
351 	ChapReceiveResponse(cstate, inp, id, len);
352 	break;
353 
354     case CHAP_FAILURE:
355 	ChapReceiveFailure(cstate, inp, id, len);
356 	break;
357 
358     case CHAP_SUCCESS:
359 	ChapReceiveSuccess(cstate, inp, id, len);
360 	break;
361 
362     default:				/* Need code reject? */
363 	syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
364 	break;
365     }
366 }
367 
368 
369 /*
370  * ChapReceiveChallenge - Receive Challenge and send Response.
371  */
372 static void
ChapReceiveChallenge(chap_state * cstate,u_char * inp,int id,int len)373 ChapReceiveChallenge(chap_state *cstate, u_char *inp, int id, int len)
374 {
375     int rchallenge_len;
376     u_char *rchallenge;
377     int secret_len;
378     char secret[MAXSECRETLEN];
379     char rhostname[256];
380     MD5_CTX mdContext;
381     u_char hash[MD5_SIGNATURE_SIZE];
382 
383     CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
384     if (cstate->clientstate == CHAPCS_CLOSED ||
385 	cstate->clientstate == CHAPCS_PENDING) {
386 	CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
387 		   cstate->clientstate));
388 	return;
389     }
390 
391     if (len < 2) {
392 	CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
393 	return;
394     }
395 
396     GETCHAR(rchallenge_len, inp);
397     len -= sizeof (u_char) + rchallenge_len;	/* now name field length */
398     if (len < 0) {
399 	CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
400 	return;
401     }
402     rchallenge = inp;
403     INCPTR(rchallenge_len, inp);
404 
405     if (len >= sizeof(rhostname))
406 	len = sizeof(rhostname) - 1;
407     BCOPY(inp, rhostname, len);
408     rhostname[len] = '\000';
409 
410     CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'",
411 	rhostname));
412 
413     /* Microsoft doesn't send their name back in the PPP packet */
414     if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
415 	strlcpy(rhostname, remote_name, sizeof(rhostname));
416 	CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name",
417 		  rhostname));
418     }
419 
420     /* get secret for authenticating ourselves with the specified host */
421     if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
422 		    secret, &secret_len, 0)) {
423 	secret_len = 0;		/* assume null secret if can't find one */
424 	syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
425 	       rhostname);
426     }
427 
428     /* cancel response send timeout if necessary */
429     if (cstate->clientstate == CHAPCS_RESPONSE)
430 	UNTIMEOUT(ChapResponseTimeout, cstate);
431 
432     cstate->resp_id = id;
433     cstate->resp_transmits = 0;
434 
435     /*  generate MD based on negotiated type */
436     switch (cstate->resp_type) {
437 
438     case CHAP_DIGEST_MD5:
439 	MD5Init(&mdContext);
440 	MD5Update(&mdContext, &cstate->resp_id, 1);
441 	MD5Update(&mdContext, secret, secret_len);
442 	MD5Update(&mdContext, rchallenge, rchallenge_len);
443 	MD5Final(hash, &mdContext);
444 	BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
445 	cstate->resp_length = MD5_SIGNATURE_SIZE;
446 	break;
447 
448     default:
449 	CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
450 	return;
451     }
452 
453     EXPLICIT_BZERO(secret, sizeof(secret));
454     ChapSendResponse(cstate);
455 }
456 
457 
458 /*
459  * ChapReceiveResponse - Receive and process response.
460  */
461 static void
ChapReceiveResponse(chap_state * cstate,u_char * inp,int id,int len)462 ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len)
463 {
464     u_char *remmd, remmd_len;
465     int secret_len, old_state;
466     int code;
467     char rhostname[256];
468     MD5_CTX mdContext;
469     char secret[MAXSECRETLEN];
470     u_char hash[MD5_SIGNATURE_SIZE];
471 
472     CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
473 
474     if (cstate->serverstate == CHAPSS_CLOSED ||
475 	cstate->serverstate == CHAPSS_PENDING) {
476 	CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
477 		   cstate->serverstate));
478 	return;
479     }
480 
481     if (id != cstate->chal_id)
482 	return;			/* doesn't match ID of last challenge */
483 
484     /*
485      * If we have received a duplicate or bogus Response,
486      * we have to send the same answer (Success/Failure)
487      * as we did for the first Response we saw.
488      */
489     if (cstate->serverstate == CHAPSS_OPEN) {
490 	ChapSendStatus(cstate, CHAP_SUCCESS);
491 	return;
492     }
493     if (cstate->serverstate == CHAPSS_BADAUTH) {
494 	ChapSendStatus(cstate, CHAP_FAILURE);
495 	return;
496     }
497 
498     if (len < 2) {
499 	CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
500 	return;
501     }
502     GETCHAR(remmd_len, inp);		/* get length of MD */
503     remmd = inp;			/* get pointer to MD */
504     INCPTR(remmd_len, inp);
505 
506     len -= sizeof (u_char) + remmd_len;
507     if (len < 0) {
508 	CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
509 	return;
510     }
511 
512     UNTIMEOUT(ChapChallengeTimeout, cstate);
513 
514     if (len >= sizeof(rhostname))
515 	len = sizeof(rhostname) - 1;
516     BCOPY(inp, rhostname, len);
517     rhostname[len] = '\000';
518 
519     CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
520 	       rhostname));
521 
522     /*
523      * Get secret for authenticating them with us,
524      * do the hash ourselves, and compare the result.
525      */
526     code = CHAP_FAILURE;
527     if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
528 		   secret, &secret_len, 1)) {
529 	syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
530 	       rhostname);
531     } else {
532 
533 	/*  generate MD based on negotiated type */
534 	switch (cstate->chal_type) {
535 
536 	case CHAP_DIGEST_MD5:		/* only MD5 is defined for now */
537 	    if (remmd_len != MD5_SIGNATURE_SIZE)
538 		break;			/* it's not even the right length */
539 	    MD5Init(&mdContext);
540 	    MD5Update(&mdContext, &cstate->chal_id, 1);
541 	    MD5Update(&mdContext, secret, secret_len);
542 	    MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
543 	    MD5Final(hash, &mdContext);
544 
545 	    /* compare local and remote MDs and send the appropriate status */
546 	    if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
547 		code = CHAP_SUCCESS;	/* they are the same! */
548 	    break;
549 
550 	default:
551 	    CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
552 	}
553     }
554 
555     EXPLICIT_BZERO(secret, sizeof(secret));
556     ChapSendStatus(cstate, code);
557 
558     if (code == CHAP_SUCCESS) {
559 	old_state = cstate->serverstate;
560 	cstate->serverstate = CHAPSS_OPEN;
561 	if (old_state == CHAPSS_INITIAL_CHAL) {
562             auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
563 	}
564 	if (cstate->chal_interval != 0)
565 	    TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
566 	syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s",
567 		rhostname);
568 
569     } else {
570 	syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s",
571 		rhostname);
572 	cstate->serverstate = CHAPSS_BADAUTH;
573 	auth_peer_fail(cstate->unit, PPP_CHAP);
574     }
575 }
576 
577 /*
578  * ChapReceiveSuccess - Receive Success
579  */
580 static void
ChapReceiveSuccess(chap_state * cstate,u_char * inp,int id,int len)581 ChapReceiveSuccess(chap_state *cstate, u_char *inp, int id, int len)
582 {
583 
584     CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
585 
586     if (cstate->clientstate == CHAPCS_OPEN)
587 	/* presumably an answer to a duplicate response */
588 	return;
589 
590     if (cstate->clientstate != CHAPCS_RESPONSE) {
591 	/* don't know what this is */
592 	CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
593 		   cstate->clientstate));
594 	return;
595     }
596 
597     UNTIMEOUT(ChapResponseTimeout, cstate);
598 
599     /*
600      * Print message.
601      */
602     if (len > 0)
603 	PRINTMSG(inp, len);
604 
605     cstate->clientstate = CHAPCS_OPEN;
606 
607     auth_withpeer_success(cstate->unit, PPP_CHAP);
608 }
609 
610 
611 /*
612  * ChapReceiveFailure - Receive failure.
613  */
614 static void
ChapReceiveFailure(chap_state * cstate,u_char * inp,int id,int len)615 ChapReceiveFailure(chap_state *cstate, u_char *inp, int id, int len)
616 {
617     CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
618 
619     if (cstate->clientstate != CHAPCS_RESPONSE) {
620 	/* don't know what this is */
621 	CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
622 		   cstate->clientstate));
623 	return;
624     }
625 
626     UNTIMEOUT(ChapResponseTimeout, cstate);
627 
628     /*
629      * Print message.
630      */
631     if (len > 0)
632 	PRINTMSG(inp, len);
633 
634     syslog(LOG_ERR, "CHAP authentication failed");
635     auth_withpeer_fail(cstate->unit, PPP_CHAP);
636 }
637 
638 
639 /*
640  * ChapSendChallenge - Send an Authenticate challenge.
641  */
642 static void
ChapSendChallenge(chap_state * cstate)643 ChapSendChallenge(chap_state *cstate)
644 {
645     u_char *outp;
646     int chal_len, name_len;
647     int outlen;
648 
649     chal_len = cstate->chal_len;
650     name_len = strlen(cstate->chal_name);
651     outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
652     outp = outpacket_buf;
653 
654     MAKEHEADER(outp, PPP_CHAP);		/* paste in a CHAP header */
655 
656     PUTCHAR(CHAP_CHALLENGE, outp);
657     PUTCHAR(cstate->chal_id, outp);
658     PUTSHORT(outlen, outp);
659 
660     PUTCHAR(chal_len, outp);		/* put length of challenge */
661     BCOPY(cstate->challenge, outp, chal_len);
662     INCPTR(chal_len, outp);
663 
664     BCOPY(cstate->chal_name, outp, name_len);	/* append hostname */
665 
666     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
667 
668     CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
669 
670     TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
671     ++cstate->chal_transmits;
672 }
673 
674 
675 /*
676  * ChapSendStatus - Send a status response (ack or nak).
677  */
678 static void
ChapSendStatus(chap_state * cstate,int code)679 ChapSendStatus(chap_state *cstate, int code)
680 {
681     u_char *outp;
682     int outlen, msglen;
683     char msg[256];
684 
685     if (code == CHAP_SUCCESS)
686 	snprintf(msg, sizeof msg, "Welcome to %s.", hostname);
687     else
688 	snprintf(msg, sizeof msg, "I don't like you.  Go 'way.");
689     msglen = strlen(msg);
690 
691     outlen = CHAP_HEADERLEN + msglen;
692     outp = outpacket_buf;
693 
694     MAKEHEADER(outp, PPP_CHAP);	/* paste in a header */
695 
696     PUTCHAR(code, outp);
697     PUTCHAR(cstate->chal_id, outp);
698     PUTSHORT(outlen, outp);
699     BCOPY(msg, outp, msglen);
700     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
701 
702     CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
703 	       cstate->chal_id));
704 }
705 
706 /*
707  * ChapGenChallenge is used to generate a pseudo-random challenge string of
708  * a pseudo-random length between min_len and max_len.  The challenge
709  * string and its length are stored in *cstate, and various other fields of
710  * *cstate are initialized.
711  */
712 
713 static void
ChapGenChallenge(chap_state * cstate)714 ChapGenChallenge(chap_state *cstate)
715 {
716     int chal_len;
717 
718     /* pick a random challenge length >= MIN_CHALLENGE_LENGTH and
719        <= MAX_CHALLENGE_LENGTH */
720     chal_len = MIN_CHALLENGE_LENGTH +
721 	arc4random_uniform(MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH + 1);
722 
723     cstate->chal_len = chal_len;
724     cstate->chal_id = ++cstate->id;
725     cstate->chal_transmits = 0;
726 
727     /* generate a random string */
728     arc4random_buf(cstate->challenge, chal_len);
729 }
730 
731 /*
732  * ChapSendResponse - send a response packet with values as specified
733  * in *cstate.
734  */
735 static void
ChapSendResponse(chap_state * cstate)736 ChapSendResponse(chap_state *cstate)
737 {
738     u_char *outp;
739     int outlen, md_len, name_len;
740 
741     md_len = cstate->resp_length;
742     name_len = strlen(cstate->resp_name);
743     outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
744     outp = outpacket_buf;
745 
746     MAKEHEADER(outp, PPP_CHAP);
747 
748     PUTCHAR(CHAP_RESPONSE, outp);	/* we are a response */
749     PUTCHAR(cstate->resp_id, outp);	/* copy id from challenge packet */
750     PUTSHORT(outlen, outp);		/* packet length */
751 
752     PUTCHAR(md_len, outp);		/* length of MD */
753     BCOPY(cstate->response, outp, md_len);	/* copy MD to buffer */
754     INCPTR(md_len, outp);
755 
756     BCOPY(cstate->resp_name, outp, name_len); /* append our name */
757 
758     /* send the packet */
759     output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
760 
761     cstate->clientstate = CHAPCS_RESPONSE;
762     TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
763     ++cstate->resp_transmits;
764 }
765 
766 /*
767  * ChapPrintPkt - print the contents of a CHAP packet.
768  */
769 static char *ChapCodenames[] = {
770     "Challenge", "Response", "Success", "Failure"
771 };
772 
773 static int
ChapPrintPkt(u_char * p,int plen,void (* printer)(void *,char *,...),void * arg)774 ChapPrintPkt(u_char *p, int plen, void (*printer)(void *, char *, ...), void *arg)
775 {
776     int code, id, len;
777     int clen, nlen;
778     u_char x;
779 
780     if (plen < CHAP_HEADERLEN)
781 	return 0;
782     GETCHAR(code, p);
783     GETCHAR(id, p);
784     GETSHORT(len, p);
785     if (len < CHAP_HEADERLEN || len > plen)
786 	return 0;
787 
788     if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
789 	printer(arg, " %s", ChapCodenames[code-1]);
790     else
791 	printer(arg, " code=0x%x", code);
792     printer(arg, " id=0x%x", id);
793     len -= CHAP_HEADERLEN;
794     switch (code) {
795     case CHAP_CHALLENGE:
796     case CHAP_RESPONSE:
797 	if (len < 1)
798 	    break;
799 	clen = p[0];
800 	if (len < clen + 1)
801 	    break;
802 	++p;
803 	nlen = len - clen - 1;
804 	printer(arg, " <");
805 	for (; clen > 0; --clen) {
806 	    GETCHAR(x, p);
807 	    printer(arg, "%.2x", x);
808 	}
809 	printer(arg, ">, name = ");
810 	print_string((char *)p, nlen, printer, arg);
811 	break;
812     case CHAP_FAILURE:
813     case CHAP_SUCCESS:
814 	printer(arg, " ");
815 	print_string((char *)p, len, printer, arg);
816 	break;
817     default:
818 	for (clen = len; clen > 0; --clen) {
819 	    GETCHAR(x, p);
820 	    printer(arg, " %.2x", x);
821 	}
822     }
823 
824     return len + CHAP_HEADERLEN;
825 }
826