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