/*
- * chap.c - Challenge Handshake Authentication Protocol.
+ * chap_ms.c - Challenge Handshake Authentication Protocol.
*
* Copyright (c) 1993 The Australian National University.
* All rights reserved.
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#ifndef lint
-static char rcsid[] = "$Id: chap.c,v 1.20 1999/03/16 22:54:38 paulus Exp $";
-#endif
+#define RCSID "$Id: chap.c,v 1.27 2002/03/01 14:39:18 dfs Exp $"
/*
* TODO:
#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,
+ int remmd_len,
+ chap_state *cstate)) = NULL;
+
+static const char rcsid[] = RCSID;
+
/*
* Command-line options.
*/
static option_t chap_option_list[] = {
{ "chap-restart", o_int, &chap[0].timeouttime,
- "Set timeout for CHAP" },
+ "Set timeout for CHAP", OPT_PRIO },
{ "chap-max-challenge", o_int, &chap[0].max_transmits,
- "Set max #xmits for challenge" },
+ "Set max #xmits for challenge", OPT_PRIO },
{ "chap-interval", o_int, &chap[0].chal_interval,
- "Set interval for rechallenge" },
+ "Set interval for rechallenge", OPT_PRIO },
#ifdef MSLANMAN
{ "ms-lanman", o_bool, &ms_lanman,
"Use LanMan passwd when using MS-CHAP", 1 },
/*
* We get here as a result of LCP coming up.
- * So even if CHAP was open before, we will
+ * So even if CHAP was open before, we will
* have to re-authenticate ourselves.
*/
cstate->clientstate = CHAPCS_LISTEN;
int digest;
{
chap_state *cstate = &chap[unit];
-
+
cstate->chal_name = our_name;
cstate->chal_type = digest;
void *arg;
{
chap_state *cstate = (chap_state *) arg;
-
+
/* if we aren't sending challenges, don't worry. then again we */
/* probably shouldn't be here either */
if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
int unit;
{
chap_state *cstate = &chap[unit];
-
+
if (cstate->clientstate == CHAPCS_INITIAL)
cstate->clientstate = CHAPCS_CLOSED;
else if (cstate->clientstate == CHAPCS_PENDING)
int unit;
{
chap_state *cstate = &chap[unit];
-
+
/* Timeout(s) pending? Cancel if so. */
if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
cstate->serverstate == CHAPSS_RECHALLENGE)
u_char *inp;
u_char code, id;
int len;
-
+
/*
* Parse header (code, id and length).
* If packet too short, drop it.
return;
}
len -= CHAP_HEADERLEN;
-
+
/*
* Action depends on code (as in fact it usually does :-).
*/
case CHAP_CHALLENGE:
ChapReceiveChallenge(cstate, inp, id, len);
break;
-
+
case CHAP_RESPONSE:
ChapReceiveResponse(cstate, inp, id, len);
break;
-
+
case CHAP_FAILURE:
ChapReceiveFailure(cstate, inp, id, len);
break;
char rhostname[256];
MD5_CTX mdContext;
u_char hash[MD5_SIGNATURE_SIZE];
-
+
if (cstate->clientstate == CHAPCS_CLOSED ||
cstate->clientstate == CHAPCS_PENDING) {
CHAPDEBUG(("ChapReceiveChallenge: in state %d", cstate->clientstate));
rhostname[len] = '\000';
/* Microsoft doesn't send their name back in the PPP packet */
- if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
- strlcpy(rhostname, sizeof(rhostname), remote_name);
+ if (explicit_remote || (remote_name[0] != 0 && rhostname[0] == 0)) {
+ strlcpy(rhostname, remote_name, sizeof(rhostname));
CHAPDEBUG(("ChapReceiveChallenge: using '%q' as remote name",
rhostname));
}
cstate->resp_transmits = 0;
/* generate MD based on negotiated type */
- switch (cstate->resp_type) {
+ switch (cstate->resp_type) {
case CHAP_DIGEST_MD5:
MD5Init(&mdContext);
* do the hash ourselves, and compare the result.
*/
code = CHAP_FAILURE;
- if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
- secret, &secret_len, 1)) {
- warn("No CHAP secret found for authenticating %q", rhostname);
- } else {
- /* generate MD based on negotiated type */
- switch (cstate->chal_type) {
-
- case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
- if (remmd_len != MD5_SIGNATURE_SIZE)
- break; /* it's not even the right length */
- MD5Init(&mdContext);
- MD5Update(&mdContext, &cstate->chal_id, 1);
- MD5Update(&mdContext, secret, secret_len);
- MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
- MD5Final(hash, &mdContext);
-
- /* compare local and remote MDs and send the appropriate status */
- if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
- code = CHAP_SUCCESS; /* they are the same! */
- break;
+ /* If a plugin will verify the response, let the plugin do it. */
+ if (chap_auth_hook) {
+ code = (*chap_auth_hook) ( (explicit_remote ? remote_name : rhostname),
+ remmd, (int) remmd_len,
+ cstate );
+ } else {
+ if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname),
+ cstate->chal_name, secret, &secret_len, 1)) {
+ warn("No CHAP secret found for authenticating %q", rhostname);
+ } else {
+
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5:
+ if (remmd_len != MD5_SIGNATURE_SIZE)
+ break; /* not even the right length */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
- default:
- CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ {
+ int response_offset, response_size;
+
+ if (remmd_len != MS_CHAP_RESPONSE_LEN)
+ break; /* not even the right length */
+ ChapMS(cstate, cstate->challenge, cstate->chal_len,
+ secret, secret_len);
+
+ /* Determine which part of response to verify against */
+ if ((u_char *) (remmd + offsetof(MS_ChapResponse, UseNT))) {
+ response_offset = offsetof(MS_ChapResponse, NTResp);
+ response_size = sizeof(((MS_ChapResponse *) remmd)->NTResp);
+ } else {
+ response_offset = offsetof(MS_ChapResponse, LANManResp);
+ response_size =
+ sizeof(((MS_ChapResponse *) remmd)->LANManResp);
+ }
+
+ /* compare MDs and send the appropriate status */
+ if (memcmp(cstate->response + response_offset,
+ remmd + response_offset, response_size) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
+ }
+#endif /* CHAPMS */
+
+ default:
+ CHAPDEBUG(("unknown digest type %d", cstate->chal_type));
+ }
}
- }
- BZERO(secret, sizeof(secret));
+ BZERO(secret, sizeof(secret));
+ }
ChapSendStatus(cstate, code);
if (code == CHAP_SUCCESS) {
BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
-
+
TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
++cstate->chal_transmits;
}
outp = outpacket_buf;
MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
-
+
PUTCHAR(code, outp);
PUTCHAR(cstate->chal_id, outp);
PUTSHORT(outlen, outp);
ChapGenChallenge(cstate)
chap_state *cstate;
{
- int chal_len;
+ int chal_len = 0; /* Avoid compiler warning */
u_char *ptr = cstate->challenge;
- unsigned int i;
+ int i;
+
+ switch (cstate->chal_type) {
+ case CHAP_DIGEST_MD5:
+ /*
+ * pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ * MAX_CHALLENGE_LENGTH
+ */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ /* MS-CHAP is fixed to an 8 octet challenge. */
+ chal_len = 8;
+ break;
+#endif
+ default:
+ fatal("ChapGenChallenge: Unsupported challenge type %d",
+ (int) cstate->chal_type);
+ break;
+ }
- /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
- MAX_CHALLENGE_LENGTH */
- chal_len = (unsigned) ((drand48() *
- (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
- MIN_CHALLENGE_LENGTH);
cstate->chal_len = chal_len;
cstate->chal_id = ++cstate->id;
cstate->chal_transmits = 0;
/* generate a random string */
- for (i = 0; i < chal_len; i++ )
+ for (i = 0; i < chal_len; i++)
*ptr++ = (char) (drand48() * 0xff);
}