X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=pppd%2Fchap.c;h=e47d52f6ba68c76e610c229210e8f7fd8aaf81ab;hp=21b0280323577b5bc10896237a5bb267248a7729;hb=cccb82a2e9bbc20f343bf4ef7550f79566f84758;hpb=bcfa20820fc9ff3b25bcf62308e3e737c1897dc6 diff --git a/pppd/chap.c b/pppd/chap.c index 21b0280..e47d52f 100644 --- a/pppd/chap.c +++ b/pppd/chap.c @@ -33,7 +33,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: chap.c,v 1.29 2002/03/05 15:14:04 dfs Exp $" +#define RCSID "$Id: chap.c,v 1.35 2002/10/12 01:28:05 fcusack Exp $" /* * TODO: @@ -66,6 +66,14 @@ int (*chap_auth_hook) __P((char *user, static const char rcsid[] = RCSID; +#ifdef CHAPMS +/* For MPPE debug */ +/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */ +static char *mschap_challenge = NULL; +/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */ +static char *mschap2_peer_challenge = NULL; +#endif + /* * Command-line options. */ @@ -79,6 +87,12 @@ static option_t chap_option_list[] = { #ifdef MSLANMAN { "ms-lanman", o_bool, &ms_lanman, "Use LanMan passwd when using MS-CHAP", 1 }, +#endif +#ifdef DEBUGMPPEKEY + { "mschap-challenge", o_string, &mschap_challenge, + "specify CHAP challenge" }, + { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge, + "specify CHAP peer challenge" }, #endif { NULL } }; @@ -443,10 +457,8 @@ ChapReceiveChallenge(cstate, inp, id, len) rchallenge = inp; INCPTR(rchallenge_len, inp); - if (len >= sizeof(rhostname)) - len = sizeof(rhostname) - 1; - BCOPY(inp, rhostname, len); - rhostname[len] = '\000'; + /* Null terminate and clean remote name. */ + slprintf(rhostname, sizeof(rhostname), "%.*v", len, inp); /* Microsoft doesn't send their name back in the PPP packet */ if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) { @@ -486,13 +498,14 @@ ChapReceiveChallenge(cstate, inp, id, len) case CHAP_MICROSOFT: ChapMS(cstate, rchallenge, secret, secret_len, (MS_ChapResponse *) cstate->response); - cstate->resp_length = MS_CHAP_RESPONSE_LEN; break; case CHAP_MICROSOFT_V2: - ChapMS2(cstate, rchallenge, NULL, cstate->resp_name, secret, secret_len, - (MS_Chap2Response *) cstate->response, cstate->earesponse); - cstate->resp_length = MS_CHAP2_RESPONSE_LEN; + ChapMS2(cstate, rchallenge, + mschap2_peer_challenge? mschap2_peer_challenge: NULL, + cstate->resp_name, secret, secret_len, + (MS_Chap2Response *) cstate->response, cstate->earesponse, + MS_CHAP2_AUTHENTICATEE); break; #endif /* CHAPMS */ @@ -568,6 +581,11 @@ ChapReceiveResponse(cstate, inp, id, len) BCOPY(inp, rhostname, len); rhostname[len] = '\000'; +#ifdef CHAPMS + /* copy the flags into cstate for use elsewhere */ + if (cstate->chal_type == CHAP_MICROSOFT_V2) + cstate->resp_flags = ((MS_Chap2Response *) remmd)->Flags[0]; +#endif /* CHAPMS */ /* * Get secret for authenticating them with us, * do the hash ourselves, and compare the result. @@ -579,6 +597,19 @@ ChapReceiveResponse(cstate, inp, id, len) code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname), remmd, (int) remmd_len, cstate ); + /* + * Check remote number authorization. A plugin may have filled in + * the remote number or added an allowed number, and rather than + * return an authenticate failure, is leaving it for us to verify. + */ + if (code == CHAP_SUCCESS) { + if (!auth_number()) { + /* We do not want to leak info about the chap result. */ + code = CHAP_FAILURE; /* XXX exit value will be "wrong" */ + error("calling number %q is not authorized", remote_number); + } + } + } else { if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname), cstate->chal_name, secret, &secret_len, 1)) { @@ -631,8 +662,9 @@ ChapReceiveResponse(cstate, inp, id, len) ChapMS(cstate, cstate->challenge, secret, secret_len, &md); /* compare MDs and send the appropriate status */ - if (memcmp(&md + response_offset, - remmd + response_offset, response_size) == 0) + if (memcmp((u_char *) &md + response_offset, + (u_char *) remmd + response_offset, + response_size) == 0) code = CHAP_SUCCESS; /* they are the same! */ break; } @@ -649,7 +681,7 @@ ChapReceiveResponse(cstate, inp, id, len) ChapMS2(cstate, cstate->challenge, rmd->PeerChallenge, (explicit_remote? remote_name: rhostname), secret, secret_len, &md, - cstate->saresponse); + cstate->saresponse, MS_CHAP2_AUTHENTICATOR); /* compare MDs and send the appropriate status */ if (memcmp(md.NTResp, rmd->NTResp, sizeof(md.NTResp)) == 0) @@ -671,14 +703,15 @@ ChapReceiveResponse(cstate, inp, id, len) old_state = cstate->serverstate; cstate->serverstate = CHAPSS_OPEN; if (old_state == CHAPSS_INITIAL_CHAL) { - auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + auth_peer_success(cstate->unit, PPP_CHAP, cstate->chal_type, + rhostname, len); } if (cstate->chal_interval != 0) TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); notice("CHAP peer authentication succeeded for %q", rhostname); } else { - error("CHAP peer authentication failed for remote host %q", rhostname); + warn("CHAP peer authentication failed for %q", rhostname); cstate->serverstate = CHAPSS_BADAUTH; auth_peer_fail(cstate->unit, PPP_CHAP); } @@ -746,7 +779,8 @@ ChapReceiveSuccess(cstate, inp, id, len) cstate->clientstate = CHAPCS_OPEN; - auth_withpeer_success(cstate->unit, PPP_CHAP); + notice("CHAP authentication succeeded"); + auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type); } @@ -786,7 +820,7 @@ ChapReceiveFailure(cstate, inp, id, len) #ifdef CHAPMS if ((cstate->resp_type == CHAP_MICROSOFT_V2) || (cstate->resp_type == CHAP_MICROSOFT)) { - long error; + int error; /* * Deal with MS-CHAP formatted failure messages; just print the @@ -794,7 +828,7 @@ ChapReceiveFailure(cstate, inp, id, len) * to use M=, but it shouldn't hurt. See ChapSendStatus(). */ if (!strncmp(p, "E=", 2)) - error = strtol(p, NULL, 10); /* Remember the error code. */ + error = (int) strtol(p, NULL, 10); /* Remember the error code. */ else goto print_msg; /* Message is badly formatted. */ @@ -803,37 +837,37 @@ ChapReceiveFailure(cstate, inp, id, len) p += 3; } else { /* No M=; use the error code. */ - switch(error - MS_CHAP_ERROR_BASE) { + switch(error) { case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS: - p = "Restricted logon hours"; + p = "E=646 Restricted logon hours"; break; case MS_CHAP_ERROR_ACCT_DISABLED: - p = "Account disabled"; + p = "E=647 Account disabled"; break; case MS_CHAP_ERROR_PASSWD_EXPIRED: - p = "Password expired"; + p = "E=648 Password expired"; break; case MS_CHAP_ERROR_NO_DIALIN_PERMISSION: - p = "No dialin permission"; + p = "E=649 No dialin permission"; break; case MS_CHAP_ERROR_AUTHENTICATION_FAILURE: - p = "Authentication failure"; + p = "E=691 Authentication failure"; break; case MS_CHAP_ERROR_CHANGING_PASSWORD: /* Should never see this, we don't support Change Password. */ - p = "Error changing password"; + p = "E=709 Error changing password"; break; default: free(msg); p = msg = malloc(len + 33); if (!msg) { - notice("Out of memory in ChapReceiveFailure"); + novm("ChapReceiveFailure"); goto print_msg; } slprintf(p, len + 33, "Unknown authentication failure: %.*s", @@ -916,16 +950,30 @@ ChapSendStatus(cstate, code) #ifdef CHAPMS if (cstate->chal_type == CHAP_MICROSOFT_V2) { /* - * Success message must be formatted as + * Per RFC 2759, success message must be formatted as * "S= M=" * where * is the Authenticator Response (mutual auth) * is a text message + * + * However, some versions of Windows (win98 tested) do not know + * about the M= part (required per RFC 2759) and flag + * it as an error (reported incorrectly as an encryption error + * to the user). Since the RFC requires it, and it can be + * useful information, we supply it if the peer is a conforming + * system. Luckily (?), win98 sets the Flags field to 0x04 + * (contrary to RFC requirements) so we can use that to + * distinguish between conforming and non-conforming systems. + * + * Special thanks to Alex Swiridov for + * help debugging this. */ slprintf(p, q - p, "S="); p += 2; slprintf(p, q - p, "%s", cstate->saresponse); p += strlen(cstate->saresponse); + if (cstate->resp_flags != 0) + goto msgdone; slprintf(p, q - p, " M="); p += 3; } @@ -948,7 +996,16 @@ ChapSendStatus(cstate, code) * * The M=m part is only for MS-CHAPv2, but MS-CHAP should ignore * any extra text according to RFC 2433. So we'll go the easy - * (read: lazy) route and include it always. + * (read: lazy) route and include it always. Neither win2k nor + * win98 (others untested) display the message to the user anyway. + * They also both ignore the E=e code. + * + * Note that it's safe to reuse the same challenge as we don't + * actually accept another response based on the error message + * (and no clients try to resend a response anyway). + * + * Basically, this whole bit is useless code, even the small + * implementation here is only because of overspecification. */ slprintf(p, q - p, "E=691 R=1 C="); p += 12; @@ -962,6 +1019,7 @@ ChapSendStatus(cstate, code) slprintf(p, q - p, "I don't like you. Go 'way."); } +msgdone: msglen = strlen(msg); outlen = CHAP_HEADERLEN + msglen; @@ -1023,9 +1081,15 @@ ChapGenChallenge(cstate) cstate->chal_id = ++cstate->id; cstate->chal_transmits = 0; - /* generate a random string */ - for (i = 0; i < chal_len; i++) - *ptr++ = (char) (drand48() * 0xff); +#ifdef CHAPMS + if (mschap_challenge) + for (i = 0; i < chal_len; i++) + *ptr++ = mschap_challenge[i]; + else +#endif + /* generate a random string */ + for (i = 0; i < chal_len; i++) + *ptr++ = (char) (drand48() * 0xff); } /*