2 * chap_ms.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.
36 #define RCSID "$Id: chap.c,v 1.37 2002/10/12 20:09:36 fcusack Exp $"
45 #include <sys/types.h>
55 /* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
56 int (*chap_check_hook) __P((void)) = NULL;
58 /* Hook for a plugin to get the CHAP password for authenticating us */
59 int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL;
61 /* Hook for a plugin to validate CHAP challenge */
62 int (*chap_auth_hook) __P((char *user,
65 chap_state *cstate)) = NULL;
67 static const char rcsid[] = RCSID;
71 /* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
72 static char *mschap_challenge = NULL;
73 /* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
74 static char *mschap2_peer_challenge = NULL;
78 * Command-line options.
80 static option_t chap_option_list[] = {
81 { "chap-restart", o_int, &chap[0].timeouttime,
82 "Set timeout for CHAP", OPT_PRIO },
83 { "chap-max-challenge", o_int, &chap[0].max_transmits,
84 "Set max #xmits for challenge", OPT_PRIO },
85 { "chap-interval", o_int, &chap[0].chal_interval,
86 "Set interval for rechallenge", OPT_PRIO },
88 { "ms-lanman", o_bool, &ms_lanman,
89 "Use LanMan passwd when using MS-CHAP", 1 },
92 { "mschap-challenge", o_string, &mschap_challenge,
93 "specify CHAP challenge" },
94 { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
95 "specify CHAP peer challenge" },
101 * Protocol entry points.
103 static void ChapInit __P((int));
104 static void ChapLowerUp __P((int));
105 static void ChapLowerDown __P((int));
106 static void ChapInput __P((int, u_char *, int));
107 static void ChapProtocolReject __P((int));
108 static int ChapPrintPkt __P((u_char *, int,
109 void (*) __P((void *, char *, ...)), void *));
111 struct protent chap_protent = {
131 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
133 static void ChapChallengeTimeout __P((void *));
134 static void ChapResponseTimeout __P((void *));
135 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
136 static void ChapRechallenge __P((void *));
137 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
138 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
139 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
140 static void ChapSendStatus __P((chap_state *, int));
141 static void ChapSendChallenge __P((chap_state *));
142 static void ChapSendResponse __P((chap_state *));
143 static void ChapGenChallenge __P((chap_state *));
145 extern double drand48 __P((void));
146 extern void srand48 __P((long));
149 * ChapInit - Initialize a CHAP unit.
155 chap_state *cstate = &chap[unit];
157 BZERO(cstate, sizeof(*cstate));
159 cstate->clientstate = CHAPCS_INITIAL;
160 cstate->serverstate = CHAPSS_INITIAL;
161 cstate->timeouttime = CHAP_DEFTIMEOUT;
162 cstate->max_transmits = CHAP_DEFTRANSMITS;
163 /* random number generator is initialized in magic_init */
168 * ChapAuthWithPeer - Authenticate us with our peer (start client).
172 ChapAuthWithPeer(unit, our_name, digest)
177 chap_state *cstate = &chap[unit];
179 cstate->resp_name = our_name;
180 cstate->resp_type = digest;
182 if (cstate->clientstate == CHAPCS_INITIAL ||
183 cstate->clientstate == CHAPCS_PENDING) {
184 /* lower layer isn't up - wait until later */
185 cstate->clientstate = CHAPCS_PENDING;
190 * We get here as a result of LCP coming up.
191 * So even if CHAP was open before, we will
192 * have to re-authenticate ourselves.
194 cstate->clientstate = CHAPCS_LISTEN;
199 * ChapAuthPeer - Authenticate our peer (start server).
202 ChapAuthPeer(unit, our_name, digest)
207 chap_state *cstate = &chap[unit];
209 cstate->chal_name = our_name;
210 cstate->chal_type = digest;
212 if (cstate->serverstate == CHAPSS_INITIAL ||
213 cstate->serverstate == CHAPSS_PENDING) {
214 /* lower layer isn't up - wait until later */
215 cstate->serverstate = CHAPSS_PENDING;
219 ChapGenChallenge(cstate);
220 ChapSendChallenge(cstate); /* crank it up dude! */
221 cstate->serverstate = CHAPSS_INITIAL_CHAL;
226 * ChapChallengeTimeout - Timeout expired on sending challenge.
229 ChapChallengeTimeout(arg)
232 chap_state *cstate = (chap_state *) arg;
234 /* if we aren't sending challenges, don't worry. then again we */
235 /* probably shouldn't be here either */
236 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
237 cstate->serverstate != CHAPSS_RECHALLENGE)
240 if (cstate->chal_transmits >= cstate->max_transmits) {
241 /* give up on peer */
242 error("Peer failed to respond to CHAP challenge");
243 cstate->serverstate = CHAPSS_BADAUTH;
244 auth_peer_fail(cstate->unit, PPP_CHAP);
248 ChapSendChallenge(cstate); /* Re-send challenge */
253 * ChapResponseTimeout - Timeout expired on sending response.
256 ChapResponseTimeout(arg)
259 chap_state *cstate = (chap_state *) arg;
261 /* if we aren't sending a response, don't worry. */
262 if (cstate->clientstate != CHAPCS_RESPONSE)
265 ChapSendResponse(cstate); /* re-send response */
270 * ChapRechallenge - Time to challenge the peer again.
276 chap_state *cstate = (chap_state *) arg;
278 /* if we aren't sending a response, don't worry. */
279 if (cstate->serverstate != CHAPSS_OPEN)
282 ChapGenChallenge(cstate);
283 ChapSendChallenge(cstate);
284 cstate->serverstate = CHAPSS_RECHALLENGE;
289 * ChapLowerUp - The lower layer is up.
291 * Start up if we have pending requests.
297 chap_state *cstate = &chap[unit];
299 if (cstate->clientstate == CHAPCS_INITIAL)
300 cstate->clientstate = CHAPCS_CLOSED;
301 else if (cstate->clientstate == CHAPCS_PENDING)
302 cstate->clientstate = CHAPCS_LISTEN;
304 if (cstate->serverstate == CHAPSS_INITIAL)
305 cstate->serverstate = CHAPSS_CLOSED;
306 else if (cstate->serverstate == CHAPSS_PENDING) {
307 ChapGenChallenge(cstate);
308 ChapSendChallenge(cstate);
309 cstate->serverstate = CHAPSS_INITIAL_CHAL;
315 * ChapLowerDown - The lower layer is down.
317 * Cancel all timeouts.
323 chap_state *cstate = &chap[unit];
325 /* Timeout(s) pending? Cancel if so. */
326 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
327 cstate->serverstate == CHAPSS_RECHALLENGE)
328 UNTIMEOUT(ChapChallengeTimeout, cstate);
329 else if (cstate->serverstate == CHAPSS_OPEN
330 && cstate->chal_interval != 0)
331 UNTIMEOUT(ChapRechallenge, cstate);
332 if (cstate->clientstate == CHAPCS_RESPONSE)
333 UNTIMEOUT(ChapResponseTimeout, cstate);
335 cstate->clientstate = CHAPCS_INITIAL;
336 cstate->serverstate = CHAPSS_INITIAL;
341 * ChapProtocolReject - Peer doesn't grok CHAP.
344 ChapProtocolReject(unit)
347 chap_state *cstate = &chap[unit];
349 if (cstate->serverstate != CHAPSS_INITIAL &&
350 cstate->serverstate != CHAPSS_CLOSED)
351 auth_peer_fail(unit, PPP_CHAP);
352 if (cstate->clientstate != CHAPCS_INITIAL &&
353 cstate->clientstate != CHAPCS_CLOSED)
354 auth_withpeer_fail(unit, PPP_CHAP);
355 ChapLowerDown(unit); /* shutdown chap */
360 * ChapInput - Input CHAP packet.
363 ChapInput(unit, inpacket, packet_len)
368 chap_state *cstate = &chap[unit];
374 * Parse header (code, id and length).
375 * If packet too short, drop it.
378 if (packet_len < CHAP_HEADERLEN) {
379 CHAPDEBUG(("ChapInput: rcvd short header."));
385 if (len < CHAP_HEADERLEN) {
386 CHAPDEBUG(("ChapInput: rcvd illegal length."));
389 if (len > packet_len) {
390 CHAPDEBUG(("ChapInput: rcvd short packet."));
393 len -= CHAP_HEADERLEN;
396 * Action depends on code (as in fact it usually does :-).
400 ChapReceiveChallenge(cstate, inp, id, len);
404 ChapReceiveResponse(cstate, inp, id, len);
408 ChapReceiveFailure(cstate, inp, id, len);
412 ChapReceiveSuccess(cstate, inp, id, len);
415 default: /* Need code reject? */
416 warn("Unknown CHAP code (%d) received.", code);
423 * ChapReceiveChallenge - Receive Challenge and send Response.
426 ChapReceiveChallenge(cstate, inp, id, len)
435 char secret[MAXSECRETLEN];
438 u_char hash[MD5_SIGNATURE_SIZE];
440 if (cstate->clientstate == CHAPCS_CLOSED ||
441 cstate->clientstate == CHAPCS_PENDING) {
442 CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
447 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
451 GETCHAR(rchallenge_len, inp);
452 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
454 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
458 INCPTR(rchallenge_len, inp);
460 /* Null terminate and clean remote name. */
461 slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
463 /* Microsoft doesn't send their name back in the PPP packet */
464 if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
465 strlcpy(rhostname, remote_name, sizeof(rhostname));
466 CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
470 /* get secret for authenticating ourselves with the specified host */
471 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
472 secret, &secret_len, 0)) {
473 secret_len = 0; /* assume null secret if can't find one */
474 warn("No CHAP secret found for authenticating us to %q", rhostname);
477 /* cancel response send timeout if necessary */
478 if (cstate->clientstate == CHAPCS_RESPONSE)
479 UNTIMEOUT(ChapResponseTimeout, cstate);
481 cstate->resp_id = id;
482 cstate->resp_transmits = 0;
484 /* generate MD based on negotiated type */
485 switch (cstate->resp_type) {
487 case CHAP_DIGEST_MD5:
489 MD5Update(&mdContext, &cstate->resp_id, 1);
490 MD5Update(&mdContext, secret, secret_len);
491 MD5Update(&mdContext, rchallenge, rchallenge_len);
492 MD5Final(hash, &mdContext);
493 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
494 cstate->resp_length = MD5_SIGNATURE_SIZE;
499 ChapMS(cstate, rchallenge, secret, secret_len,
500 (MS_ChapResponse *) cstate->response);
503 case CHAP_MICROSOFT_V2:
504 ChapMS2(cstate, rchallenge,
505 mschap2_peer_challenge? mschap2_peer_challenge: NULL,
506 cstate->resp_name, secret, secret_len,
507 (MS_Chap2Response *) cstate->response, cstate->earesponse,
508 MS_CHAP2_AUTHENTICATEE);
513 CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
517 BZERO(secret, sizeof(secret));
518 ChapSendResponse(cstate);
523 * ChapReceiveResponse - Receive and process response.
526 ChapReceiveResponse(cstate, inp, id, len)
532 u_char *remmd, remmd_len;
533 int secret_len, old_state;
537 char secret[MAXSECRETLEN];
538 u_char hash[MD5_SIGNATURE_SIZE];
540 if (cstate->serverstate == CHAPSS_CLOSED ||
541 cstate->serverstate == CHAPSS_PENDING) {
542 CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
546 if (id != cstate->chal_id)
547 return; /* doesn't match ID of last challenge */
550 * If we have received a duplicate or bogus Response,
551 * we have to send the same answer (Success/Failure)
552 * as we did for the first Response we saw.
554 if (cstate->serverstate == CHAPSS_OPEN) {
555 ChapSendStatus(cstate, CHAP_SUCCESS);
558 if (cstate->serverstate == CHAPSS_BADAUTH) {
559 ChapSendStatus(cstate, CHAP_FAILURE);
564 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
567 GETCHAR(remmd_len, inp); /* get length of MD */
568 remmd = inp; /* get pointer to MD */
569 INCPTR(remmd_len, inp);
571 len -= sizeof (u_char) + remmd_len;
573 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
577 UNTIMEOUT(ChapChallengeTimeout, cstate);
579 /* Null terminate and clean remote name. */
580 slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp);
583 /* copy the flags into cstate for use elsewhere */
584 if (cstate->chal_type == CHAP_MICROSOFT_V2)
585 cstate->resp_flags = ((MS_Chap2Response *) remmd)->Flags[0];
588 * Get secret for authenticating them with us,
589 * do the hash ourselves, and compare the result.
593 /* If a plugin will verify the response, let the plugin do it. */
594 if (chap_auth_hook) {
595 code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
596 remmd, (int) remmd_len,
599 * Check remote number authorization. A plugin may have filled in
600 * the remote number or added an allowed number, and rather than
601 * return an authenticate failure, is leaving it for us to verify.
603 if (code == CHAP_SUCCESS) {
604 if (!auth_number()) {
605 /* We do not want to leak info about the chap result. */
606 code = CHAP_FAILURE; /* XXX exit value will be "wrong" */
607 warn("calling number %q is not authorized", remote_number);
612 if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
613 cstate->chal_name, secret, &secret_len, 1)) {
614 warn("No CHAP secret found for authenticating %q", rhostname);
617 /* generate MD based on negotiated type */
618 switch (cstate->chal_type) {
620 case CHAP_DIGEST_MD5:
621 if (remmd_len != MD5_SIGNATURE_SIZE)
622 break; /* not even the right length */
624 MD5Update(&mdContext, &cstate->chal_id, 1);
625 MD5Update(&mdContext, secret, secret_len);
626 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
627 MD5Final(hash, &mdContext);
629 /* compare MDs and send the appropriate status */
630 if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0)
631 code = CHAP_SUCCESS; /* they are the same! */
637 int response_offset, response_size;
638 MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
641 if (remmd_len != MS_CHAP_RESPONSE_LEN)
642 break; /* not even the right length */
644 /* Determine which part of response to verify against */
646 response_offset = offsetof(MS_ChapResponse, NTResp);
647 response_size = sizeof(rmd->NTResp);
650 response_offset = offsetof(MS_ChapResponse, LANManResp);
651 response_size = sizeof(rmd->LANManResp);
653 /* Should really propagate this into the error packet. */
654 notice("Peer request for LANMAN auth not supported");
656 #endif /* MSLANMAN */
659 /* Generate the expected response. */
660 ChapMS(cstate, cstate->challenge, secret, secret_len, &md);
662 /* compare MDs and send the appropriate status */
663 if (memcmp((u_char *) &md + response_offset,
664 (u_char *) remmd + response_offset,
666 code = CHAP_SUCCESS; /* they are the same! */
670 case CHAP_MICROSOFT_V2:
672 MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
675 if (remmd_len != MS_CHAP2_RESPONSE_LEN)
676 break; /* not even the right length */
678 /* Generate the expected response and our mutual auth. */
679 ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
680 (explicit_remote? remote_name: rhostname),
681 secret, secret_len, &md,
682 cstate->saresponse, MS_CHAP2_AUTHENTICATOR);
684 /* compare MDs and send the appropriate status */
685 if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
686 code = CHAP_SUCCESS; /* yay! */
692 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
696 BZERO(secret, sizeof(secret));
698 ChapSendStatus(cstate, code);
700 if (code == CHAP_SUCCESS) {
701 old_state = cstate->serverstate;
702 cstate->serverstate = CHAPSS_OPEN;
703 if (old_state == CHAPSS_INITIAL_CHAL) {
704 auth_peer_success(cstate->unit, PPP_CHAP, cstate->chal_type,
707 if (cstate->chal_interval != 0)
708 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
709 notice("CHAP peer authentication succeeded for %q", rhostname);
712 warn("CHAP peer authentication failed for %q", rhostname);
713 cstate->serverstate = CHAPSS_BADAUTH;
714 auth_peer_fail(cstate->unit, PPP_CHAP);
719 * ChapReceiveSuccess - Receive Success
722 ChapReceiveSuccess(cstate, inp, id, len)
729 if (cstate->clientstate == CHAPCS_OPEN)
730 /* presumably an answer to a duplicate response */
733 if (cstate->clientstate != CHAPCS_RESPONSE) {
734 /* don't know what this is */
735 CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
739 UNTIMEOUT(ChapResponseTimeout, cstate);
743 * For MS-CHAPv2, we must verify that the peer knows our secret.
745 if (cstate->resp_type == CHAP_MICROSOFT_V2) {
746 if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) {
748 if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) {
749 /* Authenticator Response matches. */
750 inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
751 len -= MS_AUTH_RESPONSE_LENGTH;
752 if ((len >= 3) && !strncmp(inp, " M=", 3)) {
753 inp += 3; len -= 3; /* Eat the delimiter */
755 /* Packet has extra text which does not begin " M=" */
756 error("MS-CHAPv2 Success packet is badly formed.");
757 auth_withpeer_fail(cstate->unit, PPP_CHAP);
760 /* Authenticator Response did not match expected. */
761 error("MS-CHAPv2 mutual authentication failed.");
762 auth_withpeer_fail(cstate->unit, PPP_CHAP);
765 /* Packet does not start with "S=" */
766 error("MS-CHAPv2 Success packet is badly formed.");
767 auth_withpeer_fail(cstate->unit, PPP_CHAP);
778 cstate->clientstate = CHAPCS_OPEN;
780 notice("CHAP authentication succeeded");
781 auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type);
786 * ChapReceiveFailure - Receive failure.
789 ChapReceiveFailure(cstate, inp, id, len)
798 if (cstate->clientstate != CHAPCS_RESPONSE) {
799 /* don't know what this is */
800 CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
805 /* We want a null-terminated string for strxxx(). */
806 msg = malloc(len + 1);
809 notice("Out of memory in ChapReceiveFailure");
812 BCOPY(inp, msg, len);
813 p = msg + len; *p = '\0'; p = msg;
816 UNTIMEOUT(ChapResponseTimeout, cstate);
819 if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
820 (cstate->resp_type == CHAP_MICROSOFT)) {
824 * Deal with MS-CHAP formatted failure messages; just print the
825 * M=<message> part (if any). For MS-CHAP we're not really supposed
826 * to use M=<message>, but it shouldn't hurt. See ChapSendStatus().
828 if (!strncmp(p, "E=", 2))
829 error = (int) strtol(p, NULL, 10); /* Remember the error code. */
831 goto print_msg; /* Message is badly formatted. */
833 if (len && ((p = strstr(p, " M=")) != NULL)) {
834 /* M=<message> field found. */
837 /* No M=<message>; use the error code. */
839 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
840 p = "E=646 Restricted logon hours";
843 case MS_CHAP_ERROR_ACCT_DISABLED:
844 p = "E=647 Account disabled";
847 case MS_CHAP_ERROR_PASSWD_EXPIRED:
848 p = "E=648 Password expired";
851 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
852 p = "E=649 No dialin permission";
855 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
856 p = "E=691 Authentication failure";
859 case MS_CHAP_ERROR_CHANGING_PASSWORD:
860 /* Should never see this, we don't support Change Password. */
861 p = "E=709 Error changing password";
866 p = msg = malloc(len + 33);
868 novm("ChapReceiveFailure");
871 slprintf(p, len + 33, "Unknown authentication failure: %.*s",
884 if (len > 0 && p != NULL)
887 error("CHAP authentication failed");
888 auth_withpeer_fail(cstate->unit, PPP_CHAP);
896 * ChapSendChallenge - Send an Authenticate challenge.
899 ChapSendChallenge(cstate)
903 int chal_len, name_len;
906 chal_len = cstate->chal_len;
907 name_len = strlen(cstate->chal_name);
908 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
909 outp = outpacket_buf;
911 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
913 PUTCHAR(CHAP_CHALLENGE, outp);
914 PUTCHAR(cstate->chal_id, outp);
915 PUTSHORT(outlen, outp);
917 PUTCHAR(chal_len, outp); /* put length of challenge */
918 BCOPY(cstate->challenge, outp, chal_len);
919 INCPTR(chal_len, outp);
921 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
923 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
925 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
926 ++cstate->chal_transmits;
931 * ChapSendStatus - Send a status response (ack or nak).
932 * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats.
935 ChapSendStatus(cstate, code)
940 int i, outlen, msglen;
945 q = p + sizeof(msg); /* points 1 byte past msg */
947 if (code == CHAP_SUCCESS) {
949 if (cstate->chal_type == CHAP_MICROSOFT_V2) {
951 * Per RFC 2759, success message must be formatted as
952 * "S=<auth_string> M=<message>"
954 * <auth_string> is the Authenticator Response (mutual auth)
955 * <message> is a text message
957 * However, some versions of Windows (win98 tested) do not know
958 * about the M=<message> part (required per RFC 2759) and flag
959 * it as an error (reported incorrectly as an encryption error
960 * to the user). Since the RFC requires it, and it can be
961 * useful information, we supply it if the peer is a conforming
962 * system. Luckily (?), win98 sets the Flags field to 0x04
963 * (contrary to RFC requirements) so we can use that to
964 * distinguish between conforming and non-conforming systems.
966 * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
967 * help debugging this.
969 slprintf(p, q - p, "S=");
971 slprintf(p, q - p, "%s", cstate->saresponse);
972 p += strlen(cstate->saresponse);
973 if (cstate->resp_flags != 0)
975 slprintf(p, q - p, " M=");
980 slprintf(p, q - p, "Welcome to %s.", hostname);
983 if ((cstate->chal_type == CHAP_MICROSOFT_V2) ||
984 (cstate->chal_type == CHAP_MICROSOFT)) {
986 * Failure message must be formatted as
987 * "E=e R=r C=c V=v M=m"
989 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
990 * r = retry (we use 1, ok to retry)
991 * c = challenge to use for next response, we reuse previous
992 * v = Change Password version supported, we use 0
995 * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore
996 * any extra text according to RFC 2433. So we'll go the easy
997 * (read: lazy) route and include it always. Neither win2k nor
998 * win98 (others untested) display the message to the user anyway.
999 * They also both ignore the E=e code.
1001 * Note that it's safe to reuse the same challenge as we don't
1002 * actually accept another response based on the error message
1003 * (and no clients try to resend a response anyway).
1005 * Basically, this whole bit is useless code, even the small
1006 * implementation here is only because of overspecification.
1008 slprintf(p, q - p, "E=691 R=1 C=");
1010 for (i = 0; i < cstate->chal_len; i++)
1011 sprintf(p + i * 2, "%02X", cstate->challenge[i]);
1012 p += cstate->chal_len * 2;
1013 slprintf(p, q - p, " V=0 M=");
1018 slprintf(p, q - p, "I don't like you. Go 'way.");
1021 msglen = strlen(msg);
1023 outlen = CHAP_HEADERLEN + msglen;
1024 outp = outpacket_buf;
1026 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
1028 PUTCHAR(code, outp);
1029 PUTCHAR(cstate->chal_id, outp);
1030 PUTSHORT(outlen, outp);
1031 BCOPY(msg, outp, msglen);
1032 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1036 * ChapGenChallenge is used to generate a pseudo-random challenge string of
1037 * a pseudo-random length between min_len and max_len. The challenge
1038 * string and its length are stored in *cstate, and various other fields of
1039 * *cstate are initialized.
1043 ChapGenChallenge(cstate)
1046 int chal_len = 0; /* Avoid compiler warning */
1047 u_char *ptr = cstate->challenge;
1050 switch (cstate->chal_type) {
1051 case CHAP_DIGEST_MD5:
1053 * pick a random challenge length between MIN_CHALLENGE_LENGTH and
1054 * MAX_CHALLENGE_LENGTH
1056 chal_len = (unsigned) ((drand48() *
1057 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
1058 MIN_CHALLENGE_LENGTH);
1062 case CHAP_MICROSOFT:
1063 /* MS-CHAP is fixed to an 8 octet challenge. */
1067 case CHAP_MICROSOFT_V2:
1068 /* MS-CHAPv2 is fixed to a 16 octet challenge. */
1073 fatal("ChapGenChallenge: Unsupported challenge type %d",
1074 (int) cstate->chal_type);
1078 cstate->chal_len = chal_len;
1079 cstate->chal_id = ++cstate->id;
1080 cstate->chal_transmits = 0;
1083 if (mschap_challenge)
1084 for (i = 0; i < chal_len; i++)
1085 *ptr++ = mschap_challenge[i];
1088 /* generate a random string */
1089 for (i = 0; i < chal_len; i++)
1090 *ptr++ = (char) (drand48() * 0xff);
1094 * ChapSendResponse - send a response packet with values as specified
1099 ChapSendResponse(cstate)
1103 int outlen, md_len, name_len;
1105 md_len = cstate->resp_length;
1106 name_len = strlen(cstate->resp_name);
1107 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
1108 outp = outpacket_buf;
1110 MAKEHEADER(outp, PPP_CHAP);
1112 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
1113 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
1114 PUTSHORT(outlen, outp); /* packet length */
1116 PUTCHAR(md_len, outp); /* length of MD */
1117 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
1118 INCPTR(md_len, outp);
1120 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
1122 /* send the packet */
1123 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1125 cstate->clientstate = CHAPCS_RESPONSE;
1126 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
1127 ++cstate->resp_transmits;
1131 * ChapPrintPkt - print the contents of a CHAP packet.
1133 static char *ChapCodenames[] = {
1134 "Challenge", "Response", "Success", "Failure"
1138 ChapPrintPkt(p, plen, printer, arg)
1141 void (*printer) __P((void *, char *, ...));
1148 if (plen < CHAP_HEADERLEN)
1153 if (len < CHAP_HEADERLEN || len > plen)
1156 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
1157 printer(arg, " %s", ChapCodenames[code-1]);
1159 printer(arg, " code=0x%x", code);
1160 printer(arg, " id=0x%x", id);
1161 len -= CHAP_HEADERLEN;
1163 case CHAP_CHALLENGE:
1171 nlen = len - clen - 1;
1173 for (; clen > 0; --clen) {
1175 printer(arg, "%.2x", x);
1177 printer(arg, ">, name = ");
1178 print_string((char *)p, nlen, printer, arg);
1183 print_string((char *)p, len, printer, arg);
1186 for (clen = len; clen > 0; --clen) {
1188 printer(arg, " %.2x", x);
1192 return len + CHAP_HEADERLEN;