X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=pppd%2Fchap.c;h=aa07d0e941e8dfede3df882c86bf818769136921;hb=96a47326d0073d66e287fb05fa34d119f46744ed;hp=114ed4d6bbfd7e05ee64a7b82d58d21309f3017e;hpb=b38527fb14af5ebe3d2559e2f861575c722a1ce9;p=ppp.git diff --git a/pppd/chap.c b/pppd/chap.c index 114ed4d..aa07d0e 100644 --- a/pppd/chap.c +++ b/pppd/chap.c @@ -1,20 +1,36 @@ /* - * chap_ms.c - Challenge Handshake Authentication Protocol. + * chap.c - Challenge Handshake Authentication Protocol. * - * Copyright (c) 1993 The Australian National University. - * All rights reserved. + * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved. * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the Australian National University. The name of the University - * may not be used to endorse or promote products derived from this - * software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Copyright (c) 1991 Gregory M. Christy. * All rights reserved. @@ -33,7 +49,7 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#define RCSID "$Id: chap.c,v 1.30 2002/04/02 13:54:59 dfs Exp $" +#define RCSID "$Id: chap.c,v 1.41 2003/07/28 12:25:41 carlsonj Exp $" /* * TODO: @@ -52,12 +68,6 @@ #include "chap_ms.h" #endif -/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */ -int (*chap_check_hook) __P((void)) = NULL; - -/* Hook for a plugin to get the CHAP password for authenticating us */ -int (*chap_passwd_hook) __P((char *user, char *passwd)) = NULL; - /* Hook for a plugin to validate CHAP challenge */ int (*chap_auth_hook) __P((char *user, u_char *remmd, @@ -84,6 +94,7 @@ static option_t chap_option_list[] = { "Set max #xmits for challenge", OPT_PRIO }, { "chap-interval", o_int, &chap[0].chal_interval, "Set interval for rechallenge", OPT_PRIO }, +#ifdef MSCHAP #ifdef MSLANMAN { "ms-lanman", o_bool, &ms_lanman, "Use LanMan passwd when using MS-CHAP", 1 }, @@ -93,6 +104,7 @@ static option_t chap_option_list[] = { "specify CHAP challenge" }, { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge, "specify CHAP peer challenge" }, +#endif #endif { NULL } }; @@ -165,14 +177,11 @@ ChapInit(unit) /* - * ChapAuthWithPeer - Authenticate us with our peer (start client). + * chap_auth_with_peer - Authenticate us with our peer (start client). * */ void -ChapAuthWithPeer(unit, our_name, digest) - int unit; - char *our_name; - int digest; +chap_auth_with_peer(int unit, char *our_name, int digest) { chap_state *cstate = &chap[unit]; @@ -196,13 +205,10 @@ ChapAuthWithPeer(unit, our_name, digest) /* - * ChapAuthPeer - Authenticate our peer (start server). + * chap_auth_peer - Authenticate our peer (start server). */ void -ChapAuthPeer(unit, our_name, digest) - int unit; - char *our_name; - int digest; +chap_auth_peer(int unit, char *our_name, int digest) { chap_state *cstate = &chap[unit]; @@ -457,10 +463,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)) { @@ -500,7 +504,6 @@ 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: @@ -509,7 +512,6 @@ ChapReceiveChallenge(cstate, inp, id, len) cstate->resp_name, secret, secret_len, (MS_Chap2Response *) cstate->response, cstate->earesponse, MS_CHAP2_AUTHENTICATEE); - cstate->resp_length = MS_CHAP2_RESPONSE_LEN; break; #endif /* CHAPMS */ @@ -580,11 +582,14 @@ ChapReceiveResponse(cstate, inp, id, len) UNTIMEOUT(ChapChallengeTimeout, cstate); - 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); +#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. @@ -596,6 +601,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" */ + warn("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)) { @@ -648,8 +666,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; } @@ -696,7 +715,7 @@ ChapReceiveResponse(cstate, inp, id, len) 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); } @@ -764,6 +783,7 @@ ChapReceiveSuccess(cstate, inp, id, len) cstate->clientstate = CHAPCS_OPEN; + notice("CHAP authentication succeeded"); auth_withpeer_success(cstate->unit, PPP_CHAP, cstate->resp_type); } @@ -778,7 +798,9 @@ ChapReceiveFailure(cstate, inp, id, len) u_char id; int len; { +#ifdef CHAPMS u_char *msg; +#endif u_char *p = inp; if (cstate->clientstate != CHAPCS_RESPONSE) { @@ -823,35 +845,35 @@ ChapReceiveFailure(cstate, inp, id, len) /* No M=; use the error code. */ 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", @@ -866,7 +888,9 @@ ChapReceiveFailure(cstate, inp, id, len) /* * Print message. */ +#ifdef CHAPMS print_msg: +#endif if (len > 0 && p != NULL) PRINTMSG(p, len); @@ -923,7 +947,7 @@ ChapSendStatus(cstate, code) int code; { u_char *outp; - int i, outlen, msglen; + int outlen, msglen; char msg[256]; char *p, *q; @@ -934,16 +958,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; } @@ -966,8 +1004,19 @@ 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. */ + int i; + slprintf(p, q - p, "E=691 R=1 C="); p += 12; for (i = 0; i < cstate->chal_len; i++) @@ -980,6 +1029,9 @@ ChapSendStatus(cstate, code) slprintf(p, q - p, "I don't like you. Go 'way."); } +#ifdef CHAPMS +msgdone: +#endif msglen = strlen(msg); outlen = CHAP_HEADERLEN + msglen;