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