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