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