2 * chap.c - Challenge Handshake Authentication Protocol.
4 * Copyright (c) 1993 The Australian National University.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by the Australian National University. The name of the University
13 * may not be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 * Copyright (c) 1991 Gregory M. Christy.
20 * All rights reserved.
22 * Redistribution and use in source and binary forms are permitted
23 * provided that the above copyright notice and this paragraph are
24 * duplicated in all such forms and that any documentation,
25 * advertising materials, and other materials related to such
26 * distribution and use acknowledge that the software was developed
27 * by Gregory M. Christy. The name of the author may not be used to
28 * endorse or promote products derived from this software without
29 * specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
32 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
33 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
37 static char rcsid[] = "$Id: chap.c,v 1.19 1999/03/16 03:15:13 paulus Exp $";
46 #include <sys/types.h>
57 * Command-line options.
59 static option_t chap_option_list[] = {
60 { "chap-restart", o_int, &chap[0].timeouttime,
61 "Set timeout for CHAP" },
62 { "chap-max-challenge", o_int, &chap[0].max_transmits,
63 "Set max #xmits for challenge" },
64 { "chap-interval", o_int, &chap[0].chal_interval,
65 "Set interval for rechallenge" },
67 { "ms-lanman", o_bool, &ms_lanman,
68 "Use LanMan passwd when using MS-CHAP", 1 },
74 * Protocol entry points.
76 static void ChapInit __P((int));
77 static void ChapLowerUp __P((int));
78 static void ChapLowerDown __P((int));
79 static void ChapInput __P((int, u_char *, int));
80 static void ChapProtocolReject __P((int));
81 static int ChapPrintPkt __P((u_char *, int,
82 void (*) __P((void *, char *, ...)), void *));
84 struct protent chap_protent = {
103 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
105 static void ChapChallengeTimeout __P((void *));
106 static void ChapResponseTimeout __P((void *));
107 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
108 static void ChapRechallenge __P((void *));
109 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
110 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
111 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
112 static void ChapSendStatus __P((chap_state *, int));
113 static void ChapSendChallenge __P((chap_state *));
114 static void ChapSendResponse __P((chap_state *));
115 static void ChapGenChallenge __P((chap_state *));
117 extern double drand48 __P((void));
118 extern void srand48 __P((long));
121 * ChapInit - Initialize a CHAP unit.
127 chap_state *cstate = &chap[unit];
129 BZERO(cstate, sizeof(*cstate));
131 cstate->clientstate = CHAPCS_INITIAL;
132 cstate->serverstate = CHAPSS_INITIAL;
133 cstate->timeouttime = CHAP_DEFTIMEOUT;
134 cstate->max_transmits = CHAP_DEFTRANSMITS;
135 /* random number generator is initialized in magic_init */
140 * ChapAuthWithPeer - Authenticate us with our peer (start client).
144 ChapAuthWithPeer(unit, our_name, digest)
149 chap_state *cstate = &chap[unit];
151 cstate->resp_name = our_name;
152 cstate->resp_type = digest;
154 if (cstate->clientstate == CHAPCS_INITIAL ||
155 cstate->clientstate == CHAPCS_PENDING) {
156 /* lower layer isn't up - wait until later */
157 cstate->clientstate = CHAPCS_PENDING;
162 * We get here as a result of LCP coming up.
163 * So even if CHAP was open before, we will
164 * have to re-authenticate ourselves.
166 cstate->clientstate = CHAPCS_LISTEN;
171 * ChapAuthPeer - Authenticate our peer (start server).
174 ChapAuthPeer(unit, our_name, digest)
179 chap_state *cstate = &chap[unit];
181 cstate->chal_name = our_name;
182 cstate->chal_type = digest;
184 if (cstate->serverstate == CHAPSS_INITIAL ||
185 cstate->serverstate == CHAPSS_PENDING) {
186 /* lower layer isn't up - wait until later */
187 cstate->serverstate = CHAPSS_PENDING;
191 ChapGenChallenge(cstate);
192 ChapSendChallenge(cstate); /* crank it up dude! */
193 cstate->serverstate = CHAPSS_INITIAL_CHAL;
198 * ChapChallengeTimeout - Timeout expired on sending challenge.
201 ChapChallengeTimeout(arg)
204 chap_state *cstate = (chap_state *) arg;
206 /* if we aren't sending challenges, don't worry. then again we */
207 /* probably shouldn't be here either */
208 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
209 cstate->serverstate != CHAPSS_RECHALLENGE)
212 if (cstate->chal_transmits >= cstate->max_transmits) {
213 /* give up on peer */
214 error("Peer failed to respond to CHAP challenge");
215 cstate->serverstate = CHAPSS_BADAUTH;
216 auth_peer_fail(cstate->unit, PPP_CHAP);
220 ChapSendChallenge(cstate); /* Re-send challenge */
225 * ChapResponseTimeout - Timeout expired on sending response.
228 ChapResponseTimeout(arg)
231 chap_state *cstate = (chap_state *) arg;
233 /* if we aren't sending a response, don't worry. */
234 if (cstate->clientstate != CHAPCS_RESPONSE)
237 ChapSendResponse(cstate); /* re-send response */
242 * ChapRechallenge - Time to challenge the peer again.
248 chap_state *cstate = (chap_state *) arg;
250 /* if we aren't sending a response, don't worry. */
251 if (cstate->serverstate != CHAPSS_OPEN)
254 ChapGenChallenge(cstate);
255 ChapSendChallenge(cstate);
256 cstate->serverstate = CHAPSS_RECHALLENGE;
261 * ChapLowerUp - The lower layer is up.
263 * Start up if we have pending requests.
269 chap_state *cstate = &chap[unit];
271 if (cstate->clientstate == CHAPCS_INITIAL)
272 cstate->clientstate = CHAPCS_CLOSED;
273 else if (cstate->clientstate == CHAPCS_PENDING)
274 cstate->clientstate = CHAPCS_LISTEN;
276 if (cstate->serverstate == CHAPSS_INITIAL)
277 cstate->serverstate = CHAPSS_CLOSED;
278 else if (cstate->serverstate == CHAPSS_PENDING) {
279 ChapGenChallenge(cstate);
280 ChapSendChallenge(cstate);
281 cstate->serverstate = CHAPSS_INITIAL_CHAL;
287 * ChapLowerDown - The lower layer is down.
289 * Cancel all timeouts.
295 chap_state *cstate = &chap[unit];
297 /* Timeout(s) pending? Cancel if so. */
298 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
299 cstate->serverstate == CHAPSS_RECHALLENGE)
300 UNTIMEOUT(ChapChallengeTimeout, cstate);
301 else if (cstate->serverstate == CHAPSS_OPEN
302 && cstate->chal_interval != 0)
303 UNTIMEOUT(ChapRechallenge, cstate);
304 if (cstate->clientstate == CHAPCS_RESPONSE)
305 UNTIMEOUT(ChapResponseTimeout, cstate);
307 cstate->clientstate = CHAPCS_INITIAL;
308 cstate->serverstate = CHAPSS_INITIAL;
313 * ChapProtocolReject - Peer doesn't grok CHAP.
316 ChapProtocolReject(unit)
319 chap_state *cstate = &chap[unit];
321 if (cstate->serverstate != CHAPSS_INITIAL &&
322 cstate->serverstate != CHAPSS_CLOSED)
323 auth_peer_fail(unit, PPP_CHAP);
324 if (cstate->clientstate != CHAPCS_INITIAL &&
325 cstate->clientstate != CHAPCS_CLOSED)
326 auth_withpeer_fail(unit, PPP_CHAP);
327 ChapLowerDown(unit); /* shutdown chap */
332 * ChapInput - Input CHAP packet.
335 ChapInput(unit, inpacket, packet_len)
340 chap_state *cstate = &chap[unit];
346 * Parse header (code, id and length).
347 * If packet too short, drop it.
350 if (packet_len < CHAP_HEADERLEN) {
351 CHAPDEBUG(("ChapInput: rcvd short header."));
357 if (len < CHAP_HEADERLEN) {
358 CHAPDEBUG(("ChapInput: rcvd illegal length."));
361 if (len > packet_len) {
362 CHAPDEBUG(("ChapInput: rcvd short packet."));
365 len -= CHAP_HEADERLEN;
368 * Action depends on code (as in fact it usually does :-).
372 ChapReceiveChallenge(cstate, inp, id, len);
376 ChapReceiveResponse(cstate, inp, id, len);
380 ChapReceiveFailure(cstate, inp, id, len);
384 ChapReceiveSuccess(cstate, inp, id, len);
387 default: /* Need code reject? */
388 warn("Unknown CHAP code (%d) received.", code);
395 * ChapReceiveChallenge - Receive Challenge and send Response.
398 ChapReceiveChallenge(cstate, inp, id, len)
407 char secret[MAXSECRETLEN];
410 u_char hash[MD5_SIGNATURE_SIZE];
412 if (cstate->clientstate == CHAPCS_CLOSED ||
413 cstate->clientstate == CHAPCS_PENDING) {
414 CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
419 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
423 GETCHAR(rchallenge_len, inp);
424 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
426 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
430 INCPTR(rchallenge_len, inp);
432 if (len >= sizeof(rhostname))
433 len = sizeof(rhostname) - 1;
434 BCOPY(inp, rhostname, len);
435 rhostname[len] = '\000';
437 /* Microsoft doesn't send their name back in the PPP packet */
438 if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
439 strlcpy(rhostname, sizeof(rhostname), remote_name);
440 CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
444 /* get secret for authenticating ourselves with the specified host */
445 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
446 secret, &secret_len, 0)) {
447 secret_len = 0; /* assume null secret if can't find one */
448 warn("No CHAP secret found for authenticating us to %q", rhostname);
451 /* cancel response send timeout if necessary */
452 if (cstate->clientstate == CHAPCS_RESPONSE)
453 UNTIMEOUT(ChapResponseTimeout, cstate);
455 cstate->resp_id = id;
456 cstate->resp_transmits = 0;
458 /* generate MD based on negotiated type */
459 switch (cstate->resp_type) {
461 case CHAP_DIGEST_MD5:
463 MD5Update(&mdContext, &cstate->resp_id, 1);
464 MD5Update(&mdContext, secret, secret_len);
465 MD5Update(&mdContext, rchallenge, rchallenge_len);
466 MD5Final(hash, &mdContext);
467 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
468 cstate->resp_length = MD5_SIGNATURE_SIZE;
473 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
478 CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
482 BZERO(secret, sizeof(secret));
483 ChapSendResponse(cstate);
488 * ChapReceiveResponse - Receive and process response.
491 ChapReceiveResponse(cstate, inp, id, len)
497 u_char *remmd, remmd_len;
498 int secret_len, old_state;
502 char secret[MAXSECRETLEN];
503 u_char hash[MD5_SIGNATURE_SIZE];
505 if (cstate->serverstate == CHAPSS_CLOSED ||
506 cstate->serverstate == CHAPSS_PENDING) {
507 CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
511 if (id != cstate->chal_id)
512 return; /* doesn't match ID of last challenge */
515 * If we have received a duplicate or bogus Response,
516 * we have to send the same answer (Success/Failure)
517 * as we did for the first Response we saw.
519 if (cstate->serverstate == CHAPSS_OPEN) {
520 ChapSendStatus(cstate, CHAP_SUCCESS);
523 if (cstate->serverstate == CHAPSS_BADAUTH) {
524 ChapSendStatus(cstate, CHAP_FAILURE);
529 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
532 GETCHAR(remmd_len, inp); /* get length of MD */
533 remmd = inp; /* get pointer to MD */
534 INCPTR(remmd_len, inp);
536 len -= sizeof (u_char) + remmd_len;
538 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
542 UNTIMEOUT(ChapChallengeTimeout, cstate);
544 if (len >= sizeof(rhostname))
545 len = sizeof(rhostname) - 1;
546 BCOPY(inp, rhostname, len);
547 rhostname[len] = '\000';
550 * Get secret for authenticating them with us,
551 * do the hash ourselves, and compare the result.
554 if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
555 secret, &secret_len, 1)) {
556 warn("No CHAP secret found for authenticating %q", rhostname);
559 /* generate MD based on negotiated type */
560 switch (cstate->chal_type) {
562 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
563 if (remmd_len != MD5_SIGNATURE_SIZE)
564 break; /* it's not even the right length */
566 MD5Update(&mdContext, &cstate->chal_id, 1);
567 MD5Update(&mdContext, secret, secret_len);
568 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
569 MD5Final(hash, &mdContext);
571 /* compare local and remote MDs and send the appropriate status */
572 if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
573 code = CHAP_SUCCESS; /* they are the same! */
577 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
581 BZERO(secret, sizeof(secret));
582 ChapSendStatus(cstate, code);
584 if (code == CHAP_SUCCESS) {
585 old_state = cstate->serverstate;
586 cstate->serverstate = CHAPSS_OPEN;
587 if (old_state == CHAPSS_INITIAL_CHAL) {
588 auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
590 if (cstate->chal_interval != 0)
591 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
592 notice("CHAP peer authentication succeeded for %q", rhostname);
595 error("CHAP peer authentication failed for remote host %q", rhostname);
596 cstate->serverstate = CHAPSS_BADAUTH;
597 auth_peer_fail(cstate->unit, PPP_CHAP);
602 * ChapReceiveSuccess - Receive Success
605 ChapReceiveSuccess(cstate, inp, id, len)
612 if (cstate->clientstate == CHAPCS_OPEN)
613 /* presumably an answer to a duplicate response */
616 if (cstate->clientstate != CHAPCS_RESPONSE) {
617 /* don't know what this is */
618 CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
622 UNTIMEOUT(ChapResponseTimeout, cstate);
630 cstate->clientstate = CHAPCS_OPEN;
632 auth_withpeer_success(cstate->unit, PPP_CHAP);
637 * ChapReceiveFailure - Receive failure.
640 ChapReceiveFailure(cstate, inp, id, len)
646 if (cstate->clientstate != CHAPCS_RESPONSE) {
647 /* don't know what this is */
648 CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
652 UNTIMEOUT(ChapResponseTimeout, cstate);
660 error("CHAP authentication failed");
661 auth_withpeer_fail(cstate->unit, PPP_CHAP);
666 * ChapSendChallenge - Send an Authenticate challenge.
669 ChapSendChallenge(cstate)
673 int chal_len, name_len;
676 chal_len = cstate->chal_len;
677 name_len = strlen(cstate->chal_name);
678 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
679 outp = outpacket_buf;
681 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
683 PUTCHAR(CHAP_CHALLENGE, outp);
684 PUTCHAR(cstate->chal_id, outp);
685 PUTSHORT(outlen, outp);
687 PUTCHAR(chal_len, outp); /* put length of challenge */
688 BCOPY(cstate->challenge, outp, chal_len);
689 INCPTR(chal_len, outp);
691 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
693 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
695 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
696 ++cstate->chal_transmits;
701 * ChapSendStatus - Send a status response (ack or nak).
704 ChapSendStatus(cstate, code)
712 if (code == CHAP_SUCCESS)
713 slprintf(msg, sizeof(msg), "Welcome to %s.", hostname);
715 slprintf(msg, sizeof(msg), "I don't like you. Go 'way.");
716 msglen = strlen(msg);
718 outlen = CHAP_HEADERLEN + msglen;
719 outp = outpacket_buf;
721 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
724 PUTCHAR(cstate->chal_id, outp);
725 PUTSHORT(outlen, outp);
726 BCOPY(msg, outp, msglen);
727 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
731 * ChapGenChallenge is used to generate a pseudo-random challenge string of
732 * a pseudo-random length between min_len and max_len. The challenge
733 * string and its length are stored in *cstate, and various other fields of
734 * *cstate are initialized.
738 ChapGenChallenge(cstate)
742 u_char *ptr = cstate->challenge;
745 /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
746 MAX_CHALLENGE_LENGTH */
747 chal_len = (unsigned) ((drand48() *
748 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
749 MIN_CHALLENGE_LENGTH);
750 cstate->chal_len = chal_len;
751 cstate->chal_id = ++cstate->id;
752 cstate->chal_transmits = 0;
754 /* generate a random string */
755 for (i = 0; i < chal_len; i++ )
756 *ptr++ = (char) (drand48() * 0xff);
760 * ChapSendResponse - send a response packet with values as specified
765 ChapSendResponse(cstate)
769 int outlen, md_len, name_len;
771 md_len = cstate->resp_length;
772 name_len = strlen(cstate->resp_name);
773 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
774 outp = outpacket_buf;
776 MAKEHEADER(outp, PPP_CHAP);
778 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
779 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
780 PUTSHORT(outlen, outp); /* packet length */
782 PUTCHAR(md_len, outp); /* length of MD */
783 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
784 INCPTR(md_len, outp);
786 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
788 /* send the packet */
789 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
791 cstate->clientstate = CHAPCS_RESPONSE;
792 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
793 ++cstate->resp_transmits;
797 * ChapPrintPkt - print the contents of a CHAP packet.
799 static char *ChapCodenames[] = {
800 "Challenge", "Response", "Success", "Failure"
804 ChapPrintPkt(p, plen, printer, arg)
807 void (*printer) __P((void *, char *, ...));
814 if (plen < CHAP_HEADERLEN)
819 if (len < CHAP_HEADERLEN || len > plen)
822 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
823 printer(arg, " %s", ChapCodenames[code-1]);
825 printer(arg, " code=0x%x", code);
826 printer(arg, " id=0x%x", id);
827 len -= CHAP_HEADERLEN;
837 nlen = len - clen - 1;
839 for (; clen > 0; --clen) {
841 printer(arg, "%.2x", x);
843 printer(arg, ">, name = ");
844 print_string((char *)p, nlen, printer, arg);
849 print_string((char *)p, len, printer, arg);
852 for (clen = len; clen > 0; --clen) {
854 printer(arg, " %.2x", x);
858 return len + CHAP_HEADERLEN;