/*
- * 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
+ * <paulus@samba.org>".
+ *
+ * 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.
* 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.38 2002/12/04 23:03:32 paulus Exp $"
/*
* TODO:
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.
*/
#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 }
};
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)) {
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 */
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.
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)) {
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;
}
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)
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);
}
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);
}
#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
* to use M=<message>, 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. */
p += 3;
} else {
/* No M=<message>; 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",
#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=<auth_string> M=<message>"
* where
* <auth_string> is the Authenticator Response (mutual auth)
* <message> is a text message
+ *
+ * However, some versions of Windows (win98 tested) do not know
+ * about the M=<message> 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 <say@real.kharkov.ua> 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;
}
*
* 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;
slprintf(p, q - p, "I don't like you. Go 'way.");
}
+msgdone:
msglen = strlen(msg);
outlen = CHAP_HEADERLEN + msglen;
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);
}
/*