2 * chap.c - Challenge Handshake Authentication Protocol.
4 * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * 3. The name(s) of the authors of this software must not be used to
19 * endorse or promote products derived from this software without
20 * prior written permission.
22 * 4. Redistributions of any form whatsoever must retain the following
24 * "This product includes software developed by Paul Mackerras
25 * <paulus@samba.org>".
27 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
28 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
29 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
30 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
31 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
32 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
33 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 * Copyright (c) 1991 Gregory M. Christy.
36 * All rights reserved.
38 * Redistribution and use in source and binary forms are permitted
39 * provided that the above copyright notice and this paragraph are
40 * duplicated in all such forms and that any documentation,
41 * advertising materials, and other materials related to such
42 * distribution and use acknowledge that the software was developed
43 * by Gregory M. Christy. The name of the author may not be used to
44 * endorse or promote products derived from this software without
45 * specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
52 #define RCSID "$Id: chap.c,v 1.39 2003/02/16 22:32:14 paulus Exp $"
61 #include <sys/types.h>
71 /* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
72 int (*chap_check_hook) __P((void)) = NULL;
74 /* Hook for a plugin to get the CHAP password for authenticating us */
75 int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
77 /* Hook for a plugin to validate CHAP challenge */
78 int (*chap_auth_hook) __P((char *user,
81 chap_state *cstate)) = NULL;
83 static const char rcsid[] = RCSID;
87 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
88 static char *mschap_challenge = NULL;
89 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
90 static char *mschap2_peer_challenge = NULL;
94 * Command-line options.
96 static option_t chap_option_list[] = {
97 { "chap-restart", o_int, &chap[0].timeouttime,
98 "Set timeout for CHAP", OPT_PRIO },
99 { "chap-max-challenge", o_int, &chap[0].max_transmits,
100 "Set max #xmits for challenge", OPT_PRIO },
101 { "chap-interval", o_int, &chap[0].chal_interval,
102 "Set interval for rechallenge", OPT_PRIO },
105 { "ms-lanman", o_bool, &ms_lanman,
106 "Use LanMan passwd when using MS-CHAP", 1 },
109 { "mschap-challenge", o_string, &mschap_challenge,
110 "specify CHAP challenge" },
111 { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
112 "specify CHAP peer challenge" },
119 * Protocol entry points.
121 static void ChapInit __P((int));
122 static void ChapLowerUp __P((int));
123 static void ChapLowerDown __P((int));
124 static void ChapInput __P((int, u_char *, int));
125 static void ChapProtocolReject __P((int));
126 static int ChapPrintPkt __P((u_char *, int,
127 void (*) __P((void *, char *, ...)), void *));
129 struct protent chap_protent = {
149 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
151 static void ChapChallengeTimeout __P((void *));
152 static void ChapResponseTimeout __P((void *));
153 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
154 static void ChapRechallenge __P((void *));
155 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
156 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
157 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
158 static void ChapSendStatus __P((chap_state *, int));
159 static void ChapSendChallenge __P((chap_state *));
160 static void ChapSendResponse __P((chap_state *));
161 static void ChapGenChallenge __P((chap_state *));
163 extern double drand48 __P((void));
164 extern void srand48 __P((long));
167 * ChapInit - Initialize a CHAP unit.
173 chap_state *cstate = &chap[unit];
175 BZERO(cstate, sizeof(*cstate));
177 cstate->clientstate = CHAPCS_INITIAL;
178 cstate->serverstate = CHAPSS_INITIAL;
179 cstate->timeouttime = CHAP_DEFTIMEOUT;
180 cstate->max_transmits = CHAP_DEFTRANSMITS;
181 /* random number generator is initialized in magic_init */
186 * ChapAuthWithPeer - Authenticate us with our peer (start client).
190 ChapAuthWithPeer(unit, our_name, digest)
195 chap_state *cstate = &chap[unit];
197 cstate->resp_name = our_name;
198 cstate->resp_type = digest;
200 if (cstate->clientstate == CHAPCS_INITIAL ||
201 cstate->clientstate == CHAPCS_PENDING) {
202 /* lower layer isn't up - wait until later */
203 cstate->clientstate = CHAPCS_PENDING;
208 * We get here as a result of LCP coming up.
209 * So even if CHAP was open before, we will
210 * have to re-authenticate ourselves.
212 cstate->clientstate = CHAPCS_LISTEN;
217 * ChapAuthPeer - Authenticate our peer (start server).
220 ChapAuthPeer(unit, our_name, digest)
225 chap_state *cstate = &chap[unit];
227 cstate->chal_name = our_name;
228 cstate->chal_type = digest;
230 if (cstate->serverstate == CHAPSS_INITIAL ||
231 cstate->serverstate == CHAPSS_PENDING) {
232 /* lower layer isn't up - wait until later */
233 cstate->serverstate = CHAPSS_PENDING;
237 ChapGenChallenge(cstate);
238 ChapSendChallenge(cstate); /* crank it up dude! */
239 cstate->serverstate = CHAPSS_INITIAL_CHAL;
244 * ChapChallengeTimeout - Timeout expired on sending challenge.
247 ChapChallengeTimeout(arg)
250 chap_state *cstate = (chap_state *) arg;
252 /* if we aren't sending challenges, don't worry. then again we */
253 /* probably shouldn't be here either */
254 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
255 cstate->serverstate != CHAPSS_RECHALLENGE)
258 if (cstate->chal_transmits >= cstate->max_transmits) {
259 /* give up on peer */
260 error("Peer failed to respond to CHAP challenge");
261 cstate->serverstate = CHAPSS_BADAUTH;
262 auth_peer_fail(cstate->unit, PPP_CHAP);
266 ChapSendChallenge(cstate); /* Re-send challenge */
271 * ChapResponseTimeout - Timeout expired on sending response.
274 ChapResponseTimeout(arg)
277 chap_state *cstate = (chap_state *) arg;
279 /* if we aren't sending a response, don't worry. */
280 if (cstate->clientstate != CHAPCS_RESPONSE)
283 ChapSendResponse(cstate); /* re-send response */
288 * ChapRechallenge - Time to challenge the peer again.
294 chap_state *cstate = (chap_state *) arg;
296 /* if we aren't sending a response, don't worry. */
297 if (cstate->serverstate != CHAPSS_OPEN)
300 ChapGenChallenge(cstate);
301 ChapSendChallenge(cstate);
302 cstate->serverstate = CHAPSS_RECHALLENGE;
307 * ChapLowerUp - The lower layer is up.
309 * Start up if we have pending requests.
315 chap_state *cstate = &chap[unit];
317 if (cstate->clientstate == CHAPCS_INITIAL)
318 cstate->clientstate = CHAPCS_CLOSED;
319 else if (cstate->clientstate == CHAPCS_PENDING)
320 cstate->clientstate = CHAPCS_LISTEN;
322 if (cstate->serverstate == CHAPSS_INITIAL)
323 cstate->serverstate = CHAPSS_CLOSED;
324 else if (cstate->serverstate == CHAPSS_PENDING) {
325 ChapGenChallenge(cstate);
326 ChapSendChallenge(cstate);
327 cstate->serverstate = CHAPSS_INITIAL_CHAL;
333 * ChapLowerDown - The lower layer is down.
335 * Cancel all timeouts.
341 chap_state *cstate = &chap[unit];
343 /* Timeout(s) pending? Cancel if so. */
344 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
345 cstate->serverstate == CHAPSS_RECHALLENGE)
346 UNTIMEOUT(ChapChallengeTimeout, cstate);
347 else if (cstate->serverstate == CHAPSS_OPEN
348 && cstate->chal_interval != 0)
349 UNTIMEOUT(ChapRechallenge, cstate);
350 if (cstate->clientstate == CHAPCS_RESPONSE)
351 UNTIMEOUT(ChapResponseTimeout, cstate);
353 cstate->clientstate = CHAPCS_INITIAL;
354 cstate->serverstate = CHAPSS_INITIAL;
359 * ChapProtocolReject - Peer doesn't grok CHAP.
362 ChapProtocolReject(unit)
365 chap_state *cstate = &chap[unit];
367 if (cstate->serverstate != CHAPSS_INITIAL &&
368 cstate->serverstate != CHAPSS_CLOSED)
369 auth_peer_fail(unit, PPP_CHAP);
370 if (cstate->clientstate != CHAPCS_INITIAL &&
371 cstate->clientstate != CHAPCS_CLOSED)
372 auth_withpeer_fail(unit, PPP_CHAP);
373 ChapLowerDown(unit); /* shutdown chap */
378 * ChapInput - Input CHAP packet.
381 ChapInput(unit, inpacket, packet_len)
386 chap_state *cstate = &chap[unit];
392 * Parse header (code, id and length).
393 * If packet too short, drop it.
396 if (packet_len < CHAP_HEADERLEN) {
397 CHAPDEBUG(("ChapInput: rcvd short header."));
403 if (len < CHAP_HEADERLEN) {
404 CHAPDEBUG(("ChapInput: rcvd illegal length."));
407 if (len > packet_len) {
408 CHAPDEBUG(("ChapInput: rcvd short packet."));
411 len -= CHAP_HEADERLEN;
414 * Action depends on code (as in fact it usually does :-).
418 ChapReceiveChallenge(cstate, inp, id, len);
422 ChapReceiveResponse(cstate, inp, id, len);
426 ChapReceiveFailure(cstate, inp, id, len);
430 ChapReceiveSuccess(cstate, inp, id, len);
433 default: /* Need code reject? */
434 warn("Unknown CHAP code (%d) received.", code);
441 * ChapReceiveChallenge - Receive Challenge and send Response.
444 ChapReceiveChallenge(cstate, inp, id, len)
453 char secret[MAXSECRETLEN];
456 u_char hash[MD5_SIGNATURE_SIZE];
458 if (cstate->clientstate == CHAPCS_CLOSED ||
459 cstate->clientstate == CHAPCS_PENDING) {
460 CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
465 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
469 GETCHAR(rchallenge_len, inp);
470 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
472 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
476 INCPTR(rchallenge_len, inp);
478 /* Null terminate and clean remote name. */
479 slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
481 /* Microsoft doesn't send their name back in the PPP packet */
482 if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
483 strlcpy(rhostname, remote_name, sizeof(rhostname));
484 CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
488 /* get secret for authenticating ourselves with the specified host */
489 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
490 secret, &secret_len, 0)) {
491 secret_len = 0; /* assume null secret if can't find one */
492 warn("No CHAP secret found for authenticating us to %q", rhostname);
495 /* cancel response send timeout if necessary */
496 if (cstate->clientstate == CHAPCS_RESPONSE)
497 UNTIMEOUT(ChapResponseTimeout, cstate);
499 cstate->resp_id = id;
500 cstate->resp_transmits = 0;
502 /* generate MD based on negotiated type */
503 switch (cstate->resp_type) {
505 case CHAP_DIGEST_MD5:
507 MD5Update(&mdContext, &cstate->resp_id, 1);
508 MD5Update(&mdContext, secret, secret_len);
509 MD5Update(&mdContext, rchallenge, rchallenge_len);
510 MD5Final(hash, &mdContext);
511 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
512 cstate->resp_length = MD5_SIGNATURE_SIZE;
517 ChapMS(cstate, rchallenge, secret, secret_len,
518 (MS_ChapResponse *) cstate->response);
521 case CHAP_MICROSOFT_V2:
522 ChapMS2(cstate, rchallenge,
523 mschap2_peer_challenge? mschap2_peer_challenge: NULL,
524 cstate->resp_name, secret, secret_len,
525 (MS_Chap2Response *) cstate->response, cstate->earesponse,
526 MS_CHAP2_AUTHENTICATEE);
531 CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
535 BZERO(secret, sizeof(secret));
536 ChapSendResponse(cstate);
541 * ChapReceiveResponse - Receive and process response.
544 ChapReceiveResponse(cstate, inp, id, len)
550 u_char *remmd, remmd_len;
551 int secret_len, old_state;
555 char secret[MAXSECRETLEN];
556 u_char hash[MD5_SIGNATURE_SIZE];
558 if (cstate->serverstate == CHAPSS_CLOSED ||
559 cstate->serverstate == CHAPSS_PENDING) {
560 CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
564 if (id != cstate->chal_id)
565 return; /* doesn't match ID of last challenge */
568 * If we have received a duplicate or bogus Response,
569 * we have to send the same answer (Success/Failure)
570 * as we did for the first Response we saw.
572 if (cstate->serverstate == CHAPSS_OPEN) {
573 ChapSendStatus(cstate, CHAP_SUCCESS);
576 if (cstate->serverstate == CHAPSS_BADAUTH) {
577 ChapSendStatus(cstate, CHAP_FAILURE);
582 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
585 GETCHAR(remmd_len, inp); /* get length of MD */
586 remmd = inp; /* get pointer to MD */
587 INCPTR(remmd_len, inp);
589 len -= sizeof (u_char) + remmd_len;
591 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
595 UNTIMEOUT(ChapChallengeTimeout, cstate);
597 /* Null terminate and clean remote name. */
598 slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
601 /* copy the flags into cstate for use elsewhere */
602 if (cstate->chal_type == CHAP_MICROSOFT_V2)
603 cstate->resp_flags = ((MS_Chap2Response *) remmd)->Flags[0];
606 * Get secret for authenticating them with us,
607 * do the hash ourselves, and compare the result.
611 /* If a plugin will verify the response, let the plugin do it. */
612 if (chap_auth_hook) {
613 code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
614 remmd, (int) remmd_len,
617 * Check remote number authorization. A plugin may have filled in
618 * the remote number or added an allowed number, and rather than
619 * return an authenticate failure, is leaving it for us to verify.
621 if (code == CHAP_SUCCESS) {
622 if (!auth_number()) {
623 /* We do not want to leak info about the chap result. */
624 code = CHAP_FAILURE; /* XXX exit value will be "wrong" */
625 warn("calling number %q is not authorized", remote_number);
630 if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
631 cstate->chal_name, secret, &secret_len, 1)) {
632 warn("No CHAP secret found for authenticating %q", rhostname);
635 /* generate MD based on negotiated type */
636 switch (cstate->chal_type) {
638 case CHAP_DIGEST_MD5:
639 if (remmd_len != MD5_SIGNATURE_SIZE)
640 break; /* not even the right length */
642 MD5Update(&mdContext, &cstate->chal_id, 1);
643 MD5Update(&mdContext, secret, secret_len);
644 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
645 MD5Final(hash, &mdContext);
647 /* compare MDs and send the appropriate status */
648 if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0)
649 code = CHAP_SUCCESS; /* they are the same! */
655 int response_offset, response_size;
656 MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
659 if (remmd_len != MS_CHAP_RESPONSE_LEN)
660 break; /* not even the right length */
662 /* Determine which part of response to verify against */
664 response_offset = offsetof(MS_ChapResponse, NTResp);
665 response_size = sizeof(rmd->NTResp);
668 response_offset = offsetof(MS_ChapResponse, LANManResp);
669 response_size = sizeof(rmd->LANManResp);
671 /* Should really propagate this into the error packet. */
672 notice("Peer request for LANMAN auth not supported");
674 #endif /* MSLANMAN */
677 /* Generate the expected response. */
678 ChapMS(cstate, cstate->challenge, secret, secret_len, &md);
680 /* compare MDs and send the appropriate status */
681 if (memcmp((u_char *) &md + response_offset,
682 (u_char *) remmd + response_offset,
684 code = CHAP_SUCCESS; /* they are the same! */
688 case CHAP_MICROSOFT_V2:
690 MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
693 if (remmd_len != MS_CHAP2_RESPONSE_LEN)
694 break; /* not even the right length */
696 /* Generate the expected response and our mutual auth. */
697 ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
698 (explicit_remote? remote_name: rhostname),
699 secret, secret_len, &md,
700 cstate->saresponse, MS_CHAP2_AUTHENTICATOR);
702 /* compare MDs and send the appropriate status */
703 if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
704 code = CHAP_SUCCESS; /* yay! */
710 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
714 BZERO(secret, sizeof(secret));
716 ChapSendStatus(cstate, code);
718 if (code == CHAP_SUCCESS) {
719 old_state = cstate->serverstate;
720 cstate->serverstate = CHAPSS_OPEN;
721 if (old_state == CHAPSS_INITIAL_CHAL) {
722 auth_peer_success(cstate->unit, PPP_CHAP, cstate->chal_type,
725 if (cstate->chal_interval != 0)
726 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
727 notice("CHAP peer authentication succeeded for %q", rhostname);
730 warn("CHAP peer authentication failed for %q", rhostname);
731 cstate->serverstate = CHAPSS_BADAUTH;
732 auth_peer_fail(cstate->unit, PPP_CHAP);
737 * ChapReceiveSuccess - Receive Success
740 ChapReceiveSuccess(cstate, inp, id, len)
747 if (cstate->clientstate == CHAPCS_OPEN)
748 /* presumably an answer to a duplicate response */
751 if (cstate->clientstate != CHAPCS_RESPONSE) {
752 /* don't know what this is */
753 CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
757 UNTIMEOUT(ChapResponseTimeout, cstate);
761 * For MS-CHAPv2, we must verify that the peer knows our secret.
763 if (cstate->resp_type == CHAP_MICROSOFT_V2) {
764 if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) {
766 if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) {
767 /* Authenticator Response matches. */
768 inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
769 len -= MS_AUTH_RESPONSE_LENGTH;
770 if ((len >= 3) && !strncmp(inp, " M=", 3)) {
771 inp += 3; len -= 3; /* Eat the delimiter */
773 /* Packet has extra text which does not begin " M=" */
774 error("MS-CHAPv2 Success packet is badly formed.");
775 auth_withpeer_fail(cstate->unit, PPP_CHAP);
778 /* Authenticator Response did not match expected. */
779 error("MS-CHAPv2 mutual authentication failed.");
780 auth_withpeer_fail(cstate->unit, PPP_CHAP);
783 /* Packet does not start with "S=" */
784 error("MS-CHAPv2 Success packet is badly formed.");
785 auth_withpeer_fail(cstate->unit, PPP_CHAP);
796 cstate->clientstate = CHAPCS_OPEN;
798 notice("CHAP authentication succeeded");
799 auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type);
804 * ChapReceiveFailure - Receive failure.
807 ChapReceiveFailure(cstate, inp, id, len)
816 if (cstate->clientstate != CHAPCS_RESPONSE) {
817 /* don't know what this is */
818 CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
823 /* We want a null-terminated string for strxxx(). */
824 msg = malloc(len + 1);
827 notice("Out of memory in ChapReceiveFailure");
830 BCOPY(inp, msg, len);
831 p = msg + len; *p = '\0'; p = msg;
834 UNTIMEOUT(ChapResponseTimeout, cstate);
837 if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
838 (cstate->resp_type == CHAP_MICROSOFT)) {
842 * Deal with MS-CHAP formatted failure messages; just print the
843 * M=<message> part (if any). For MS-CHAP we're not really supposed
844 * to use M=<message>, but it shouldn't hurt. See ChapSendStatus().
846 if (!strncmp(p, "E=", 2))
847 error = (int) strtol(p, NULL, 10); /* Remember the error code. */
849 goto print_msg; /* Message is badly formatted. */
851 if (len && ((p = strstr(p, " M=")) != NULL)) {
852 /* M=<message> field found. */
855 /* No M=<message>; use the error code. */
857 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
858 p = "E=646 Restricted logon hours";
861 case MS_CHAP_ERROR_ACCT_DISABLED:
862 p = "E=647 Account disabled";
865 case MS_CHAP_ERROR_PASSWD_EXPIRED:
866 p = "E=648 Password expired";
869 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
870 p = "E=649 No dialin permission";
873 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
874 p = "E=691 Authentication failure";
877 case MS_CHAP_ERROR_CHANGING_PASSWORD:
878 /* Should never see this, we don't support Change Password. */
879 p = "E=709 Error changing password";
884 p = msg = malloc(len + 33);
886 novm("ChapReceiveFailure");
889 slprintf(p, len + 33, "Unknown authentication failure: %.*s",
902 if (len > 0 && p != NULL)
905 error("CHAP authentication failed");
906 auth_withpeer_fail(cstate->unit, PPP_CHAP);
914 * ChapSendChallenge - Send an Authenticate challenge.
917 ChapSendChallenge(cstate)
921 int chal_len, name_len;
924 chal_len = cstate->chal_len;
925 name_len = strlen(cstate->chal_name);
926 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
927 outp = outpacket_buf;
929 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
931 PUTCHAR(CHAP_CHALLENGE, outp);
932 PUTCHAR(cstate->chal_id, outp);
933 PUTSHORT(outlen, outp);
935 PUTCHAR(chal_len, outp); /* put length of challenge */
936 BCOPY(cstate->challenge, outp, chal_len);
937 INCPTR(chal_len, outp);
939 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
941 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
943 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
944 ++cstate->chal_transmits;
949 * ChapSendStatus - Send a status response (ack or nak).
950 * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats.
953 ChapSendStatus(cstate, code)
958 int i, outlen, msglen;
963 q = p + sizeof(msg); /* points 1 byte past msg */
965 if (code == CHAP_SUCCESS) {
967 if (cstate->chal_type == CHAP_MICROSOFT_V2) {
969 * Per RFC 2759, success message must be formatted as
970 * "S=<auth_string> M=<message>"
972 * <auth_string> is the Authenticator Response (mutual auth)
973 * <message> is a text message
975 * However, some versions of Windows (win98 tested) do not know
976 * about the M=<message> part (required per RFC 2759) and flag
977 * it as an error (reported incorrectly as an encryption error
978 * to the user). Since the RFC requires it, and it can be
979 * useful information, we supply it if the peer is a conforming
980 * system. Luckily (?), win98 sets the Flags field to 0x04
981 * (contrary to RFC requirements) so we can use that to
982 * distinguish between conforming and non-conforming systems.
984 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
985 * help debugging this.
987 slprintf(p, q - p, "S=");
989 slprintf(p, q - p, "%s", cstate->saresponse);
990 p += strlen(cstate->saresponse);
991 if (cstate->resp_flags != 0)
993 slprintf(p, q - p, " M=");
998 slprintf(p, q - p, "Welcome to %s.", hostname);
1001 if ((cstate->chal_type == CHAP_MICROSOFT_V2) ||
1002 (cstate->chal_type == CHAP_MICROSOFT)) {
1004 * Failure message must be formatted as
1005 * "E=e R=r C=c V=v M=m"
1007 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
1008 * r = retry (we use 1, ok to retry)
1009 * c = challenge to use for next response, we reuse previous
1010 * v = Change Password version supported, we use 0
1013 * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore
1014 * any extra text according to RFC 2433. So we'll go the easy
1015 * (read: lazy) route and include it always. Neither win2k nor
1016 * win98 (others untested) display the message to the user anyway.
1017 * They also both ignore the E=e code.
1019 * Note that it's safe to reuse the same challenge as we don't
1020 * actually accept another response based on the error message
1021 * (and no clients try to resend a response anyway).
1023 * Basically, this whole bit is useless code, even the small
1024 * implementation here is only because of overspecification.
1026 slprintf(p, q - p, "E=691 R=1 C=");
1028 for (i = 0; i < cstate->chal_len; i++)
1029 sprintf(p + i * 2, "%02X", cstate->challenge[i]);
1030 p += cstate->chal_len * 2;
1031 slprintf(p, q - p, " V=0 M=");
1036 slprintf(p, q - p, "I don't like you. Go 'way.");
1039 msglen = strlen(msg);
1041 outlen = CHAP_HEADERLEN + msglen;
1042 outp = outpacket_buf;
1044 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
1046 PUTCHAR(code, outp);
1047 PUTCHAR(cstate->chal_id, outp);
1048 PUTSHORT(outlen, outp);
1049 BCOPY(msg, outp, msglen);
1050 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1054 * ChapGenChallenge is used to generate a pseudo-random challenge string of
1055 * a pseudo-random length between min_len and max_len. The challenge
1056 * string and its length are stored in *cstate, and various other fields of
1057 * *cstate are initialized.
1061 ChapGenChallenge(cstate)
1064 int chal_len = 0; /* Avoid compiler warning */
1065 u_char *ptr = cstate->challenge;
1068 switch (cstate->chal_type) {
1069 case CHAP_DIGEST_MD5:
1071 * pick a random challenge length between MIN_CHALLENGE_LENGTH and
1072 * MAX_CHALLENGE_LENGTH
1074 chal_len = (unsigned) ((drand48() *
1075 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
1076 MIN_CHALLENGE_LENGTH);
1080 case CHAP_MICROSOFT:
1081 /* MS-CHAP is fixed to an 8 octet challenge. */
1085 case CHAP_MICROSOFT_V2:
1086 /* MS-CHAPv2 is fixed to a 16 octet challenge. */
1091 fatal("ChapGenChallenge: Unsupported challenge type %d",
1092 (int) cstate->chal_type);
1096 cstate->chal_len = chal_len;
1097 cstate->chal_id = ++cstate->id;
1098 cstate->chal_transmits = 0;
1101 if (mschap_challenge)
1102 for (i = 0; i < chal_len; i++)
1103 *ptr++ = mschap_challenge[i];
1106 /* generate a random string */
1107 for (i = 0; i < chal_len; i++)
1108 *ptr++ = (char) (drand48() * 0xff);
1112 * ChapSendResponse - send a response packet with values as specified
1117 ChapSendResponse(cstate)
1121 int outlen, md_len, name_len;
1123 md_len = cstate->resp_length;
1124 name_len = strlen(cstate->resp_name);
1125 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
1126 outp = outpacket_buf;
1128 MAKEHEADER(outp, PPP_CHAP);
1130 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
1131 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
1132 PUTSHORT(outlen, outp); /* packet length */
1134 PUTCHAR(md_len, outp); /* length of MD */
1135 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
1136 INCPTR(md_len, outp);
1138 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
1140 /* send the packet */
1141 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1143 cstate->clientstate = CHAPCS_RESPONSE;
1144 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
1145 ++cstate->resp_transmits;
1149 * ChapPrintPkt - print the contents of a CHAP packet.
1151 static char *ChapCodenames[] = {
1152 "Challenge", "Response", "Success", "Failure"
1156 ChapPrintPkt(p, plen, printer, arg)
1159 void (*printer) __P((void *, char *, ...));
1166 if (plen < CHAP_HEADERLEN)
1171 if (len < CHAP_HEADERLEN || len > plen)
1174 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
1175 printer(arg, " %s", ChapCodenames[code-1]);
1177 printer(arg, " code=0x%x", code);
1178 printer(arg, " id=0x%x", id);
1179 len -= CHAP_HEADERLEN;
1181 case CHAP_CHALLENGE:
1189 nlen = len - clen - 1;
1191 for (; clen > 0; --clen) {
1193 printer(arg, "%.2x", x);
1195 printer(arg, ">, name = ");
1196 print_string((char *)p, nlen, printer, arg);
1201 print_string((char *)p, len, printer, arg);
1204 for (clen = len; clen > 0; --clen) {
1206 printer(arg, " %.2x", x);
1210 return len + CHAP_HEADERLEN;