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.29 2002/03/05 15:14:04 dfs 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;
70 * Command-line options.
72 static option_t chap_option_list[] = {
73 { "chap-restart", o_int, &chap[0].timeouttime,
74 "Set timeout for CHAP", OPT_PRIO },
75 { "chap-max-challenge", o_int, &chap[0].max_transmits,
76 "Set max #xmits for challenge", OPT_PRIO },
77 { "chap-interval", o_int, &chap[0].chal_interval,
78 "Set interval for rechallenge", OPT_PRIO },
80 { "ms-lanman", o_bool, &ms_lanman,
81 "Use LanMan passwd when using MS-CHAP", 1 },
87 * Protocol entry points.
89 static void ChapInit __P((int));
90 static void ChapLowerUp __P((int));
91 static void ChapLowerDown __P((int));
92 static void ChapInput __P((int, u_char *, int));
93 static void ChapProtocolReject __P((int));
94 static int ChapPrintPkt __P((u_char *, int,
95 void (*) __P((void *, char *, ...)), void *));
97 struct protent chap_protent = {
117 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
119 static void ChapChallengeTimeout __P((void *));
120 static void ChapResponseTimeout __P((void *));
121 static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
122 static void ChapRechallenge __P((void *));
123 static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
124 static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
125 static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
126 static void ChapSendStatus __P((chap_state *, int));
127 static void ChapSendChallenge __P((chap_state *));
128 static void ChapSendResponse __P((chap_state *));
129 static void ChapGenChallenge __P((chap_state *));
131 extern double drand48 __P((void));
132 extern void srand48 __P((long));
135 * ChapInit - Initialize a CHAP unit.
141 chap_state *cstate = &chap[unit];
143 BZERO(cstate, sizeof(*cstate));
145 cstate->clientstate = CHAPCS_INITIAL;
146 cstate->serverstate = CHAPSS_INITIAL;
147 cstate->timeouttime = CHAP_DEFTIMEOUT;
148 cstate->max_transmits = CHAP_DEFTRANSMITS;
149 /* random number generator is initialized in magic_init */
154 * ChapAuthWithPeer - Authenticate us with our peer (start client).
158 ChapAuthWithPeer(unit, our_name, digest)
163 chap_state *cstate = &chap[unit];
165 cstate->resp_name = our_name;
166 cstate->resp_type = digest;
168 if (cstate->clientstate == CHAPCS_INITIAL ||
169 cstate->clientstate == CHAPCS_PENDING) {
170 /* lower layer isn't up - wait until later */
171 cstate->clientstate = CHAPCS_PENDING;
176 * We get here as a result of LCP coming up.
177 * So even if CHAP was open before, we will
178 * have to re-authenticate ourselves.
180 cstate->clientstate = CHAPCS_LISTEN;
185 * ChapAuthPeer - Authenticate our peer (start server).
188 ChapAuthPeer(unit, our_name, digest)
193 chap_state *cstate = &chap[unit];
195 cstate->chal_name = our_name;
196 cstate->chal_type = digest;
198 if (cstate->serverstate == CHAPSS_INITIAL ||
199 cstate->serverstate == CHAPSS_PENDING) {
200 /* lower layer isn't up - wait until later */
201 cstate->serverstate = CHAPSS_PENDING;
205 ChapGenChallenge(cstate);
206 ChapSendChallenge(cstate); /* crank it up dude! */
207 cstate->serverstate = CHAPSS_INITIAL_CHAL;
212 * ChapChallengeTimeout - Timeout expired on sending challenge.
215 ChapChallengeTimeout(arg)
218 chap_state *cstate = (chap_state *) arg;
220 /* if we aren't sending challenges, don't worry. then again we */
221 /* probably shouldn't be here either */
222 if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
223 cstate->serverstate != CHAPSS_RECHALLENGE)
226 if (cstate->chal_transmits >= cstate->max_transmits) {
227 /* give up on peer */
228 error("Peer failed to respond to CHAP challenge");
229 cstate->serverstate = CHAPSS_BADAUTH;
230 auth_peer_fail(cstate->unit, PPP_CHAP);
234 ChapSendChallenge(cstate); /* Re-send challenge */
239 * ChapResponseTimeout - Timeout expired on sending response.
242 ChapResponseTimeout(arg)
245 chap_state *cstate = (chap_state *) arg;
247 /* if we aren't sending a response, don't worry. */
248 if (cstate->clientstate != CHAPCS_RESPONSE)
251 ChapSendResponse(cstate); /* re-send response */
256 * ChapRechallenge - Time to challenge the peer again.
262 chap_state *cstate = (chap_state *) arg;
264 /* if we aren't sending a response, don't worry. */
265 if (cstate->serverstate != CHAPSS_OPEN)
268 ChapGenChallenge(cstate);
269 ChapSendChallenge(cstate);
270 cstate->serverstate = CHAPSS_RECHALLENGE;
275 * ChapLowerUp - The lower layer is up.
277 * Start up if we have pending requests.
283 chap_state *cstate = &chap[unit];
285 if (cstate->clientstate == CHAPCS_INITIAL)
286 cstate->clientstate = CHAPCS_CLOSED;
287 else if (cstate->clientstate == CHAPCS_PENDING)
288 cstate->clientstate = CHAPCS_LISTEN;
290 if (cstate->serverstate == CHAPSS_INITIAL)
291 cstate->serverstate = CHAPSS_CLOSED;
292 else if (cstate->serverstate == CHAPSS_PENDING) {
293 ChapGenChallenge(cstate);
294 ChapSendChallenge(cstate);
295 cstate->serverstate = CHAPSS_INITIAL_CHAL;
301 * ChapLowerDown - The lower layer is down.
303 * Cancel all timeouts.
309 chap_state *cstate = &chap[unit];
311 /* Timeout(s) pending? Cancel if so. */
312 if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
313 cstate->serverstate == CHAPSS_RECHALLENGE)
314 UNTIMEOUT(ChapChallengeTimeout, cstate);
315 else if (cstate->serverstate == CHAPSS_OPEN
316 && cstate->chal_interval != 0)
317 UNTIMEOUT(ChapRechallenge, cstate);
318 if (cstate->clientstate == CHAPCS_RESPONSE)
319 UNTIMEOUT(ChapResponseTimeout, cstate);
321 cstate->clientstate = CHAPCS_INITIAL;
322 cstate->serverstate = CHAPSS_INITIAL;
327 * ChapProtocolReject - Peer doesn't grok CHAP.
330 ChapProtocolReject(unit)
333 chap_state *cstate = &chap[unit];
335 if (cstate->serverstate != CHAPSS_INITIAL &&
336 cstate->serverstate != CHAPSS_CLOSED)
337 auth_peer_fail(unit, PPP_CHAP);
338 if (cstate->clientstate != CHAPCS_INITIAL &&
339 cstate->clientstate != CHAPCS_CLOSED)
340 auth_withpeer_fail(unit, PPP_CHAP);
341 ChapLowerDown(unit); /* shutdown chap */
346 * ChapInput - Input CHAP packet.
349 ChapInput(unit, inpacket, packet_len)
354 chap_state *cstate = &chap[unit];
360 * Parse header (code, id and length).
361 * If packet too short, drop it.
364 if (packet_len < CHAP_HEADERLEN) {
365 CHAPDEBUG(("ChapInput: rcvd short header."));
371 if (len < CHAP_HEADERLEN) {
372 CHAPDEBUG(("ChapInput: rcvd illegal length."));
375 if (len > packet_len) {
376 CHAPDEBUG(("ChapInput: rcvd short packet."));
379 len -= CHAP_HEADERLEN;
382 * Action depends on code (as in fact it usually does :-).
386 ChapReceiveChallenge(cstate, inp, id, len);
390 ChapReceiveResponse(cstate, inp, id, len);
394 ChapReceiveFailure(cstate, inp, id, len);
398 ChapReceiveSuccess(cstate, inp, id, len);
401 default: /* Need code reject? */
402 warn("Unknown CHAP code (%d) received.", code);
409 * ChapReceiveChallenge - Receive Challenge and send Response.
412 ChapReceiveChallenge(cstate, inp, id, len)
421 char secret[MAXSECRETLEN];
424 u_char hash[MD5_SIGNATURE_SIZE];
426 if (cstate->clientstate == CHAPCS_CLOSED ||
427 cstate->clientstate == CHAPCS_PENDING) {
428 CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
433 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
437 GETCHAR(rchallenge_len, inp);
438 len -= sizeof (u_char) + rchallenge_len; /* now name field length */
440 CHAPDEBUG(("ChapReceiveChallenge: rcvd short packet."));
444 INCPTR(rchallenge_len, inp);
446 if (len >= sizeof(rhostname))
447 len = sizeof(rhostname) - 1;
448 BCOPY(inp, rhostname, len);
449 rhostname[len] = '\000';
451 /* Microsoft doesn't send their name back in the PPP packet */
452 if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
453 strlcpy(rhostname, remote_name, sizeof(rhostname));
454 CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
458 /* get secret for authenticating ourselves with the specified host */
459 if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
460 secret, &secret_len, 0)) {
461 secret_len = 0; /* assume null secret if can't find one */
462 warn("No CHAP secret found for authenticating us to %q", rhostname);
465 /* cancel response send timeout if necessary */
466 if (cstate->clientstate == CHAPCS_RESPONSE)
467 UNTIMEOUT(ChapResponseTimeout, cstate);
469 cstate->resp_id = id;
470 cstate->resp_transmits = 0;
472 /* generate MD based on negotiated type */
473 switch (cstate->resp_type) {
475 case CHAP_DIGEST_MD5:
477 MD5Update(&mdContext, &cstate->resp_id, 1);
478 MD5Update(&mdContext, secret, secret_len);
479 MD5Update(&mdContext, rchallenge, rchallenge_len);
480 MD5Final(hash, &mdContext);
481 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
482 cstate->resp_length = MD5_SIGNATURE_SIZE;
487 ChapMS(cstate, rchallenge, secret, secret_len,
488 (MS_ChapResponse *) cstate->response);
489 cstate->resp_length = MS_CHAP_RESPONSE_LEN;
492 case CHAP_MICROSOFT_V2:
493 ChapMS2(cstate, rchallenge, NULL, cstate->resp_name, secret, secret_len,
494 (MS_Chap2Response *) cstate->response, cstate->earesponse);
495 cstate->resp_length = MS_CHAP2_RESPONSE_LEN;
500 CHAPDEBUG(("unknown digest type %d", cstate->resp_type));
504 BZERO(secret, sizeof(secret));
505 ChapSendResponse(cstate);
510 * ChapReceiveResponse - Receive and process response.
513 ChapReceiveResponse(cstate, inp, id, len)
519 u_char *remmd, remmd_len;
520 int secret_len, old_state;
524 char secret[MAXSECRETLEN];
525 u_char hash[MD5_SIGNATURE_SIZE];
527 if (cstate->serverstate == CHAPSS_CLOSED ||
528 cstate->serverstate == CHAPSS_PENDING) {
529 CHAPDEBUG(("ChapReceiveResponse: in state %d", cstate->serverstate));
533 if (id != cstate->chal_id)
534 return; /* doesn't match ID of last challenge */
537 * If we have received a duplicate or bogus Response,
538 * we have to send the same answer (Success/Failure)
539 * as we did for the first Response we saw.
541 if (cstate->serverstate == CHAPSS_OPEN) {
542 ChapSendStatus(cstate, CHAP_SUCCESS);
545 if (cstate->serverstate == CHAPSS_BADAUTH) {
546 ChapSendStatus(cstate, CHAP_FAILURE);
551 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
554 GETCHAR(remmd_len, inp); /* get length of MD */
555 remmd = inp; /* get pointer to MD */
556 INCPTR(remmd_len, inp);
558 len -= sizeof (u_char) + remmd_len;
560 CHAPDEBUG(("ChapReceiveResponse: rcvd short packet."));
564 UNTIMEOUT(ChapChallengeTimeout, cstate);
566 if (len >= sizeof(rhostname))
567 len = sizeof(rhostname) - 1;
568 BCOPY(inp, rhostname, len);
569 rhostname[len] = '\000';
572 * Get secret for authenticating them with us,
573 * do the hash ourselves, and compare the result.
577 /* If a plugin will verify the response, let the plugin do it. */
578 if (chap_auth_hook) {
579 code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
580 remmd, (int) remmd_len,
583 if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
584 cstate->chal_name, secret, &secret_len, 1)) {
585 warn("No CHAP secret found for authenticating %q", rhostname);
588 /* generate MD based on negotiated type */
589 switch (cstate->chal_type) {
591 case CHAP_DIGEST_MD5:
592 if (remmd_len != MD5_SIGNATURE_SIZE)
593 break; /* not even the right length */
595 MD5Update(&mdContext, &cstate->chal_id, 1);
596 MD5Update(&mdContext, secret, secret_len);
597 MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
598 MD5Final(hash, &mdContext);
600 /* compare MDs and send the appropriate status */
601 if (memcmp(hash, remmd, MD5_SIGNATURE_SIZE) == 0)
602 code = CHAP_SUCCESS; /* they are the same! */
608 int response_offset, response_size;
609 MS_ChapResponse *rmd = (MS_ChapResponse *) remmd;
612 if (remmd_len != MS_CHAP_RESPONSE_LEN)
613 break; /* not even the right length */
615 /* Determine which part of response to verify against */
617 response_offset = offsetof(MS_ChapResponse, NTResp);
618 response_size = sizeof(rmd->NTResp);
621 response_offset = offsetof(MS_ChapResponse, LANManResp);
622 response_size = sizeof(rmd->LANManResp);
624 /* Should really propagate this into the error packet. */
625 notice("Peer request for LANMAN auth not supported");
627 #endif /* MSLANMAN */
630 /* Generate the expected response. */
631 ChapMS(cstate, cstate->challenge, secret, secret_len, &md);
633 /* compare MDs and send the appropriate status */
634 if (memcmp(&md + response_offset,
635 remmd + response_offset, response_size) == 0)
636 code = CHAP_SUCCESS; /* they are the same! */
640 case CHAP_MICROSOFT_V2:
642 MS_Chap2Response *rmd = (MS_Chap2Response *) remmd;
645 if (remmd_len != MS_CHAP2_RESPONSE_LEN)
646 break; /* not even the right length */
648 /* Generate the expected response and our mutual auth. */
649 ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge,
650 (explicit_remote? remote_name: rhostname),
651 secret, secret_len, &md,
654 /* compare MDs and send the appropriate status */
655 if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0)
656 code = CHAP_SUCCESS; /* yay! */
662 CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
666 BZERO(secret, sizeof(secret));
668 ChapSendStatus(cstate, code);
670 if (code == CHAP_SUCCESS) {
671 old_state = cstate->serverstate;
672 cstate->serverstate = CHAPSS_OPEN;
673 if (old_state == CHAPSS_INITIAL_CHAL) {
674 auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
676 if (cstate->chal_interval != 0)
677 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
678 notice("CHAP peer authentication succeeded for %q", rhostname);
681 error("CHAP peer authentication failed for remote host %q", rhostname);
682 cstate->serverstate = CHAPSS_BADAUTH;
683 auth_peer_fail(cstate->unit, PPP_CHAP);
688 * ChapReceiveSuccess - Receive Success
691 ChapReceiveSuccess(cstate, inp, id, len)
698 if (cstate->clientstate == CHAPCS_OPEN)
699 /* presumably an answer to a duplicate response */
702 if (cstate->clientstate != CHAPCS_RESPONSE) {
703 /* don't know what this is */
704 CHAPDEBUG(("ChapReceiveSuccess: in state %d\n", cstate->clientstate));
708 UNTIMEOUT(ChapResponseTimeout, cstate);
712 * For MS-CHAPv2, we must verify that the peer knows our secret.
714 if (cstate->resp_type == CHAP_MICROSOFT_V2) {
715 if ((len >= MS_AUTH_RESPONSE_LENGTH + 2) && !strncmp(inp, "S=", 2)) {
717 if (!memcmp(inp, cstate->earesponse, MS_AUTH_RESPONSE_LENGTH)) {
718 /* Authenticator Response matches. */
719 inp += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
720 len -= MS_AUTH_RESPONSE_LENGTH;
721 if ((len >= 3) && !strncmp(inp, " M=", 3)) {
722 inp += 3; len -= 3; /* Eat the delimiter */
724 /* Packet has extra text which does not begin " M=" */
725 error("MS-CHAPv2 Success packet is badly formed.");
726 auth_withpeer_fail(cstate->unit, PPP_CHAP);
729 /* Authenticator Response did not match expected. */
730 error("MS-CHAPv2 mutual authentication failed.");
731 auth_withpeer_fail(cstate->unit, PPP_CHAP);
734 /* Packet does not start with "S=" */
735 error("MS-CHAPv2 Success packet is badly formed.");
736 auth_withpeer_fail(cstate->unit, PPP_CHAP);
747 cstate->clientstate = CHAPCS_OPEN;
749 auth_withpeer_success(cstate->unit, PPP_CHAP);
754 * ChapReceiveFailure - Receive failure.
757 ChapReceiveFailure(cstate, inp, id, len)
766 if (cstate->clientstate != CHAPCS_RESPONSE) {
767 /* don't know what this is */
768 CHAPDEBUG(("ChapReceiveFailure: in state %d\n", cstate->clientstate));
773 /* We want a null-terminated string for strxxx(). */
774 msg = malloc(len + 1);
777 notice("Out of memory in ChapReceiveFailure");
780 BCOPY(inp, msg, len);
781 p = msg + len; *p = '\0'; p = msg;
784 UNTIMEOUT(ChapResponseTimeout, cstate);
787 if ((cstate->resp_type == CHAP_MICROSOFT_V2) ||
788 (cstate->resp_type == CHAP_MICROSOFT)) {
792 * Deal with MS-CHAP formatted failure messages; just print the
793 * M=<message> part (if any). For MS-CHAP we're not really supposed
794 * to use M=<message>, but it shouldn't hurt. See ChapSendStatus().
796 if (!strncmp(p, "E=", 2))
797 error = strtol(p, NULL, 10); /* Remember the error code. */
799 goto print_msg; /* Message is badly formatted. */
801 if (len && ((p = strstr(p, " M=")) != NULL)) {
802 /* M=<message> field found. */
805 /* No M=<message>; use the error code. */
806 switch(error - MS_CHAP_ERROR_BASE) {
807 case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
808 p = "Restricted logon hours";
811 case MS_CHAP_ERROR_ACCT_DISABLED:
812 p = "Account disabled";
815 case MS_CHAP_ERROR_PASSWD_EXPIRED:
816 p = "Password expired";
819 case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
820 p = "No dialin permission";
823 case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
824 p = "Authentication failure";
827 case MS_CHAP_ERROR_CHANGING_PASSWORD:
828 /* Should never see this, we don't support Change Password. */
829 p = "Error changing password";
834 p = msg = malloc(len + 33);
836 notice("Out of memory in ChapReceiveFailure");
839 slprintf(p, len + 33, "Unknown authentication failure: %.*s",
852 if (len > 0 && p != NULL)
855 error("CHAP authentication failed");
856 auth_withpeer_fail(cstate->unit, PPP_CHAP);
864 * ChapSendChallenge - Send an Authenticate challenge.
867 ChapSendChallenge(cstate)
871 int chal_len, name_len;
874 chal_len = cstate->chal_len;
875 name_len = strlen(cstate->chal_name);
876 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
877 outp = outpacket_buf;
879 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
881 PUTCHAR(CHAP_CHALLENGE, outp);
882 PUTCHAR(cstate->chal_id, outp);
883 PUTSHORT(outlen, outp);
885 PUTCHAR(chal_len, outp); /* put length of challenge */
886 BCOPY(cstate->challenge, outp, chal_len);
887 INCPTR(chal_len, outp);
889 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
891 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
893 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
894 ++cstate->chal_transmits;
899 * ChapSendStatus - Send a status response (ack or nak).
900 * See RFC 2433 and RFC 2759 for MS-CHAP and MS-CHAPv2 message formats.
903 ChapSendStatus(cstate, code)
908 int i, outlen, msglen;
913 q = p + sizeof(msg); /* points 1 byte past msg */
915 if (code == CHAP_SUCCESS) {
917 if (cstate->chal_type == CHAP_MICROSOFT_V2) {
919 * Success message must be formatted as
920 * "S=<auth_string> M=<message>"
922 * <auth_string> is the Authenticator Response (mutual auth)
923 * <message> is a text message
925 slprintf(p, q - p, "S=");
927 slprintf(p, q - p, "%s", cstate->saresponse);
928 p += strlen(cstate->saresponse);
929 slprintf(p, q - p, " M=");
934 slprintf(p, q - p, "Welcome to %s.", hostname);
937 if ((cstate->chal_type == CHAP_MICROSOFT_V2) ||
938 (cstate->chal_type == CHAP_MICROSOFT)) {
940 * Failure message must be formatted as
941 * "E=e R=r C=c V=v M=m"
943 * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
944 * r = retry (we use 1, ok to retry)
945 * c = challenge to use for next response, we reuse previous
946 * v = Change Password version supported, we use 0
949 * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore
950 * any extra text according to RFC 2433. So we'll go the easy
951 * (read: lazy) route and include it always.
953 slprintf(p, q - p, "E=691 R=1 C=");
955 for (i = 0; i < cstate->chal_len; i++)
956 sprintf(p + i * 2, "%02X", cstate->challenge[i]);
957 p += cstate->chal_len * 2;
958 slprintf(p, q - p, " V=0 M=");
963 slprintf(p, q - p, "I don't like you. Go 'way.");
965 msglen = strlen(msg);
967 outlen = CHAP_HEADERLEN + msglen;
968 outp = outpacket_buf;
970 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
973 PUTCHAR(cstate->chal_id, outp);
974 PUTSHORT(outlen, outp);
975 BCOPY(msg, outp, msglen);
976 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
980 * ChapGenChallenge is used to generate a pseudo-random challenge string of
981 * a pseudo-random length between min_len and max_len. The challenge
982 * string and its length are stored in *cstate, and various other fields of
983 * *cstate are initialized.
987 ChapGenChallenge(cstate)
990 int chal_len = 0; /* Avoid compiler warning */
991 u_char *ptr = cstate->challenge;
994 switch (cstate->chal_type) {
995 case CHAP_DIGEST_MD5:
997 * pick a random challenge length between MIN_CHALLENGE_LENGTH and
998 * MAX_CHALLENGE_LENGTH
1000 chal_len = (unsigned) ((drand48() *
1001 (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
1002 MIN_CHALLENGE_LENGTH);
1006 case CHAP_MICROSOFT:
1007 /* MS-CHAP is fixed to an 8 octet challenge. */
1011 case CHAP_MICROSOFT_V2:
1012 /* MS-CHAPv2 is fixed to a 16 octet challenge. */
1017 fatal("ChapGenChallenge: Unsupported challenge type %d",
1018 (int) cstate->chal_type);
1022 cstate->chal_len = chal_len;
1023 cstate->chal_id = ++cstate->id;
1024 cstate->chal_transmits = 0;
1026 /* generate a random string */
1027 for (i = 0; i < chal_len; i++)
1028 *ptr++ = (char) (drand48() * 0xff);
1032 * ChapSendResponse - send a response packet with values as specified
1037 ChapSendResponse(cstate)
1041 int outlen, md_len, name_len;
1043 md_len = cstate->resp_length;
1044 name_len = strlen(cstate->resp_name);
1045 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
1046 outp = outpacket_buf;
1048 MAKEHEADER(outp, PPP_CHAP);
1050 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
1051 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
1052 PUTSHORT(outlen, outp); /* packet length */
1054 PUTCHAR(md_len, outp); /* length of MD */
1055 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
1056 INCPTR(md_len, outp);
1058 BCOPY(cstate->resp_name, outp, name_len); /* append our name */
1060 /* send the packet */
1061 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
1063 cstate->clientstate = CHAPCS_RESPONSE;
1064 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
1065 ++cstate->resp_transmits;
1069 * ChapPrintPkt - print the contents of a CHAP packet.
1071 static char *ChapCodenames[] = {
1072 "Challenge", "Response", "Success", "Failure"
1076 ChapPrintPkt(p, plen, printer, arg)
1079 void (*printer) __P((void *, char *, ...));
1086 if (plen < CHAP_HEADERLEN)
1091 if (len < CHAP_HEADERLEN || len > plen)
1094 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
1095 printer(arg, " %s", ChapCodenames[code-1]);
1097 printer(arg, " code=0x%x", code);
1098 printer(arg, " id=0x%x", id);
1099 len -= CHAP_HEADERLEN;
1101 case CHAP_CHALLENGE:
1109 nlen = len - clen - 1;
1111 for (; clen > 0; --clen) {
1113 printer(arg, "%.2x", x);
1115 printer(arg, ">, name = ");
1116 print_string((char *)p, nlen, printer, arg);
1121 print_string((char *)p, len, printer, arg);
1124 for (clen = len; clen > 0; --clen) {
1126 printer(arg, " %.2x", x);
1130 return len + CHAP_HEADERLEN;