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