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