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