summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ppp/chap.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>1999-02-06 02:54:47 +0000
committerbrian <brian@FreeBSD.org>1999-02-06 02:54:47 +0000
commit4435d086fa4374c8a6b2091bf4acccf59f7db3d0 (patch)
treed1336bbacc5640555b58b1ebfb93986ba3586343 /usr.sbin/ppp/chap.c
parentbd140b22548c2a839c934b79c6dab11f47d412d3 (diff)
downloadFreeBSD-src-4435d086fa4374c8a6b2091bf4acccf59f7db3d0.zip
FreeBSD-src-4435d086fa4374c8a6b2091bf4acccf59f7db3d0.tar.gz
Decouple pap & chap output routines from the corresponding
input routines and take advantage of the new init/continue interface in libradius. This allows a timely response on other links in an MP setup while RADIUS requests are in progress as well as the ability to handle other data from the peer in parallel. It should also make the future addition of PAM support trivial. While I'm in there, validate pap & chap header IDs if ``idcheck'' is enabled (the default) for other FSM packet types. NOTE: This involved integrating the generation of chap challenges and the validation of chap responses (and commenting what's going on in those routines). I currently have no way of testing ppps ability to respond to M$Chap CHALLENGEs correctly, so if someone could do the honours, it'd be much appreciated (it *looks* ok!). Sponsored by: Internet Business Solutions Ltd., Switzerland
Diffstat (limited to 'usr.sbin/ppp/chap.c')
-rw-r--r--usr.sbin/ppp/chap.c492
1 files changed, 273 insertions, 219 deletions
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
index a539155..895b34e 100644
--- a/usr.sbin/ppp/chap.c
+++ b/usr.sbin/ppp/chap.c
@@ -17,7 +17,7 @@
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: chap.c,v 1.38 1999/01/28 01:56:31 brian Exp $
+ * $Id: chap.c,v 1.39 1999/01/29 22:46:31 brian Exp $
*
* TODO:
*/
@@ -29,10 +29,10 @@
#ifdef HAVE_DES
#include <md4.h>
+#include <string.h>
#endif
#include <md5.h>
#include <stdlib.h>
-#include <string.h>
#include <termios.h>
#include "mbuf.h"
@@ -71,10 +71,11 @@
static const char *chapcodes[] = {
"???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
};
+#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
static void
ChapOutput(struct physical *physical, u_int code, u_int id,
- const u_char * ptr, int count, const char *text)
+ const u_char *ptr, int count, const char *text)
{
int plen;
struct fsmheader lh;
@@ -96,258 +97,311 @@ ChapOutput(struct physical *physical, u_int code, u_int id,
hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
}
-void
-chap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
+static char *
+chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, int MSChap)
{
- struct chap *chap = auth2chap(auth);
+ char *result, *digest;
+ size_t nlen, klen;
+
+ nlen = strlen(name);
+ klen = strlen(key);
+
+#ifdef HAVE_DES
+ if (MSChap) {
+ char expkey[AUTHLEN << 2];
+ MD4_CTX MD4context;
+ int f;
+
+ if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
+ return result;
+
+ digest = result; /* this is the response */
+ *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */
+ memset(digest, '\0', 24);
+ digest += 24;
+
+ for (f = klen; f; f--) {
+ expkey[2*f-2] = key[f-1];
+ expkey[2*f-1] = 0;
+ }
+
+ /*
+ * -----------
+ * answer = | k\0e\0y\0 |
+ * -----------
+ */
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, expkey, klen << 1);
+ MD4Final(digest, &MD4context);
+ memcpy(digest + 25, name, nlen);
+
+ /*
+ * ``result'' is:
+ * ---- --------- -------------------- ------
+ * result = | 49 | 24 * \0 | digest (pad to 25) | name |
+ * ---- --------- -------------------- ------
+ */
+ chap_MS(digest, challenge + 1, *challenge);
+
+ /*
+ * ---- --------- ---------------- --- ----------
+ * result = | 49 | 24 * \0 | 24 byte digest | 1 | authname |
+ * ---- --------- ---------------- --- ----------
+ */
+ } else
+#endif
+ if ((result = malloc(nlen + 17)) != NULL) {
+ /* Normal MD5 stuff */
+ MD5_CTX MD5context;
+
+ digest = result;
+ *digest++ = 16; /* value size */
+
+ MD5Init(&MD5context);
+log_Printf(LogPHASE, "Build with 0x%x, %s & %.*s\n", id, key, *challenge, challenge+1);
+ MD5Update(&MD5context, &id, 1);
+ MD5Update(&MD5context, key, klen);
+ MD5Update(&MD5context, challenge + 1, *challenge);
+ MD5Final(digest, &MD5context);
+
+ memcpy(digest + 16, name, nlen);
+ /*
+ * ---- -------- ------
+ * result = | 16 | digest | name |
+ * ---- -------- ------
+ */
+ }
+
+ return result;
+}
+
+static void
+chap_Challenge(struct authinfo *authp)
+{
+ struct chap *chap = auth2chap(authp);
int len, i;
char *cp;
randinit();
- cp = chap->challenge_data;
+ cp = chap->challenge;
#ifndef NORADIUS
- if (*physical->dl->bundle->radius.cfg.file) {
+ if (*authp->physical->dl->bundle->radius.cfg.file) {
/* For radius, our challenge is 16 readable NUL terminated bytes :*/
- *cp++ = chap->challenge_len = 16;
- for (i = 0; i < chap->challenge_len; i++)
+ *cp++ = 16;
+ for (i = 0; i < 16; i++)
*cp++ = (random() % 10) + '0';
} else
#endif
{
- *cp++ = chap->challenge_len = random() % (CHAPCHALLENGELEN-16) + 16;
- for (i = 0; i < chap->challenge_len; i++)
+ *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
+ for (i = 0; i < *chap->challenge; i++)
*cp++ = random() & 0xff;
}
- len = strlen(physical->dl->bundle->cfg.auth.name);
- memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+ memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
cp += len;
- ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
- cp - chap->challenge_data, NULL);
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
+ cp - chap->challenge, NULL);
}
static void
-RecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
- struct physical *physical)
+chap_Success(struct authinfo *authp)
{
- int valsize, len, arglen, keylen, namelen, success;
- char *cp, *argp, *ap, *name, *digest;
- char *keyp;
- MD5_CTX MD5context; /* context for MD5 */
- char answer[CHAPDIGESTLEN];
- char cdigest[16];
-#ifdef HAVE_DES
- int ix;
- MD4_CTX MD4context; /* context for MD4 */
-#endif
-
- len = ntohs(chp->length);
- log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
- arglen = len - sizeof(struct fsmheader);
- cp = (char *) MBUF_CTOP(bp);
- valsize = *cp++ & 255;
- name = cp + valsize;
- namelen = arglen - valsize - 1;
- name[namelen] = 0;
-
- log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
- chapcodes[chp->code], name);
-
- switch (chp->code) {
- case CHAP_CHALLENGE:
- keyp = bundle->cfg.auth.key;
- keylen = strlen(bundle->cfg.auth.key);
- name = bundle->cfg.auth.name;
- namelen = strlen(bundle->cfg.auth.name);
-
-#ifdef HAVE_DES
- if (physical->dl->chap.using_MSChap)
- argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
- else
-#endif
- argp = malloc(1 + valsize + namelen + 16);
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+ ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
+ physical_Login(authp->physical, authp->in.name);
- if (argp == NULL) {
- ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL);
- return;
- }
-#ifdef HAVE_DES
- if (physical->dl->chap.using_MSChap) {
- digest = argp; /* this is the response */
- *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */
- memset(digest, '\0', 24);
- digest += 24;
-
- ap = answer; /* this is the challenge */
- memcpy(ap, keyp, keylen);
- ap += 2 * keylen;
- memcpy(ap, cp, valsize);
- log_DumpBuff(LogDEBUG, "recv", ap, valsize);
- ap += valsize;
- for (ix = keylen; ix > 0 ; ix--) {
- answer[2*ix-2] = answer[ix-1];
- answer[2*ix-1] = 0;
- }
- MD4Init(&MD4context);
- MD4Update(&MD4context, answer, 2 * keylen);
- MD4Final(digest, &MD4context);
- memcpy(digest + 25, name, namelen);
- ap += 2 * keylen;
- chap_MS(digest, answer + 2 * keylen, valsize);
- log_DumpBuff(LogDEBUG, "answer", digest, 24);
- ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
- namelen + MS_CHAP_RESPONSE_LEN + 1, name);
- } else {
-#endif
- digest = argp;
- *digest++ = 16; /* value size */
- ap = answer;
- *ap++ = chp->id;
- memcpy(ap, keyp, keylen);
- ap += keylen;
- memcpy(ap, cp, valsize);
- log_DumpBuff(LogDEBUG, "recv", ap, valsize);
- ap += valsize;
- MD5Init(&MD5context);
- MD5Update(&MD5context, answer, ap - answer);
- MD5Final(digest, &MD5context);
- log_DumpBuff(LogDEBUG, "answer", digest, 16);
- memcpy(digest + 16, name, namelen);
- ap += namelen;
- /* Send answer to the peer */
- ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name);
-#ifdef HAVE_DES
- }
-#endif
- free(argp);
- if (*name == '\0')
- log_Printf(LogWARN, "Sending empty CHAP authname!\n");
- break;
- case CHAP_RESPONSE:
+ if (authp->physical->link.lcp.auth_iwait == 0)
/*
- * Get a secret key corresponds to the peer
+ * Either I didn't need to authenticate, or I've already been
+ * told that I got the answer right.
*/
- success = 0;
-#ifndef NORADIUS
- if (*bundle->radius.cfg.file) {
- char chapname[AUTHLEN], chal[17];
-
- if (namelen > AUTHLEN - 1)
- namelen = AUTHLEN - 1;
- strncpy(chapname, name, namelen);
- chapname[namelen] = '\0';
- *answer = chp->id;
- strncpy(answer+1, cp, 16);
- answer[17] = '\0';
- strncpy(chal, physical->dl->chap.challenge_data + 1, 16);
- chal[16] = '\0';
-
- if (radius_Authenticate(&bundle->radius, bundle, chapname, answer, chal))
- success = 1; /* And there was much rejoicing ! */
-
- } else
-#endif
- if ((keyp = auth_GetSecret(bundle, name, namelen, physical))) {
- /* Compute correct digest value */
- keylen = strlen(keyp);
- ap = answer;
- *ap++ = chp->id;
- memcpy(ap, keyp, keylen);
- ap += keylen;
- MD5Init(&MD5context);
- MD5Update(&MD5context, answer, ap - answer);
- MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
- physical->dl->chap.challenge_len);
- MD5Final(cdigest, &MD5context);
- log_DumpBuff(LogDEBUG, "got", cp, 16);
- log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
-
- /*
- * Compare with the response
- */
- if (memcmp(cp, cdigest, 16) == 0)
- success = 1;
- }
-
- if (success) {
- datalink_GotAuthname(physical->dl, name, namelen);
- ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL);
- physical->link.lcp.auth_ineed = 0;
- if (Enabled(bundle, OPT_UTMP))
- physical_Login(physical, name);
-
- if (physical->link.lcp.auth_iwait == 0)
- /*
- * Either I didn't need to authenticate, or I've already been
- * told that I got the answer right.
- */
- datalink_AuthOk(physical->dl);
- } else {
- /*
- * Peer is not registerd, or response digest is wrong.
- */
- ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL);
- datalink_AuthNotOk(physical->dl);
- break;
- }
- }
+ datalink_AuthOk(authp->physical->dl);
}
static void
-RecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
- struct physical *physical)
+chap_Failure(struct authinfo *authp)
{
- int len;
-
- len = ntohs(chp->length);
- log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
- if (chp->code == CHAP_SUCCESS) {
- if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
- physical->link.lcp.auth_iwait = 0;
- if (physical->link.lcp.auth_ineed == 0)
- /*
- * We've succeeded in our ``login''
- * If we're not expecting the peer to authenticate (or he already
- * has), proceed to network phase.
- */
- datalink_AuthOk(physical->dl);
- }
- } else {
- /* CHAP failed - it's not going to get any better */
- log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n");
- datalink_AuthNotOk(physical->dl);
- }
+ ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
+ datalink_AuthNotOk(authp->physical->dl);
}
void
-chap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
+chap_Init(struct chap *chap, struct physical *p)
{
- int len = mbuf_Length(bp);
- struct fsmheader *chp;
-
- if (len >= sizeof(struct fsmheader)) {
- chp = (struct fsmheader *) MBUF_CTOP(bp);
- if (len >= ntohs(chp->length)) {
- if (chp->code < 1 || chp->code > 4)
- chp->code = 0;
- bp->offset += sizeof(struct fsmheader);
- bp->cnt -= sizeof(struct fsmheader);
-
- switch (chp->code) {
+ auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
+ *chap->challenge = 0;
+ chap->using_MSChap = 0;
+}
+
+void
+chap_Input(struct physical *p, struct mbuf *bp)
+{
+ struct chap *chap = &p->dl->chap;
+ char *name, *key, *ans, *myans;
+ int len, nlen;
+ u_char alen;
+
+ if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL)
+ log_Printf(LogERROR, "Chap Input: Truncated header !\n");
+ else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
+ log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
+ chap->auth.in.hdr.code);
+ else {
+ len = mbuf_Length(bp);
+ ans = NULL;
+
+ if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
+ chap->auth.id != chap->auth.in.hdr.id &&
+ Enabled(p->dl->bundle, OPT_IDCHECK)) {
+ /* Wrong conversation dude ! */
+ log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
+ chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
+ chap->auth.id);
+ mbuf_Free(bp);
+ return;
+ }
+ chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ bp = mbuf_Read(bp, chap->challenge, 1);
+ len -= *chap->challenge + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
+ mbuf_Free(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, chap->challenge + 1, *chap->challenge);
+ bp = auth_ReadName(&chap->auth, bp, len);
+ break;
+
case CHAP_RESPONSE:
- auth_StopTimer(&physical->dl->chap.auth);
- /* Fall into.. */
+ auth_StopTimer(&chap->auth);
+ bp = mbuf_Read(bp, &alen, 1);
+ len -= alen + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated response !\n");
+ mbuf_Free(bp);
+ return;
+ }
+ if ((ans = malloc(alen + 2)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ mbuf_Free(bp);
+ return;
+ }
+ *ans = chap->auth.id;
+ bp = mbuf_Read(bp, ans + 1, alen);
+ ans[alen+1] = '\0';
+ bp = auth_ReadName(&chap->auth, bp, len);
+ break;
+
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ /* chap->auth.in.name is already set up at CHALLENGE time */
+ if ((ans = malloc(len + 1)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ mbuf_Free(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, ans, len);
+ ans[len] = '\0';
+ break;
+ }
+
+ switch (chap->auth.in.hdr.code) {
case CHAP_CHALLENGE:
- RecvChapTalk(bundle, chp, bp, physical);
- break;
+ case CHAP_RESPONSE:
+ if (*chap->auth.in.name)
+ log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
+ chapcodes[chap->auth.in.hdr.code], chap->auth.in.name);
+ else
+ log_Printf(LogPHASE, "Chap Input: %s\n",
+ chapcodes[chap->auth.in.hdr.code]);
+ break;
+
case CHAP_SUCCESS:
case CHAP_FAILURE:
- log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]);
- RecvChapResult(bundle, chp, bp, physical);
- break;
- }
+ if (*ans)
+ log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
+ chapcodes[chap->auth.in.hdr.code], ans);
+ else
+ log_Printf(LogPHASE, "Chap Input: %s\n",
+ chapcodes[chap->auth.in.hdr.code]);
+ break;
}
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ name = p->dl->bundle->cfg.auth.name;
+ nlen = strlen(name);
+ key = p->dl->bundle->cfg.auth.key;
+ myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, 0);
+ if (myans) {
+ ChapOutput(p, CHAP_RESPONSE, chap->auth.id, myans,
+ *myans + 1 + nlen, name);
+ free(myans);
+ } else
+ ChapOutput(p, CHAP_FAILURE, chap->auth.id, "Out of memory!",
+ 14, NULL);
+ break;
+
+ case CHAP_RESPONSE:
+ name = chap->auth.in.name;
+ nlen = strlen(name);
+#ifndef NORADIUS
+ if (*p->dl->bundle->radius.cfg.file) {
+ chap->challenge[*chap->challenge+1] = '\0';
+log_Printf(LogPHASE, "Challenge %s, answer is %d bytes starting with %d\n", chap->challenge+1, alen+1, *ans);
+ radius_Authenticate(&p->dl->bundle->radius, &chap->auth,
+ chap->auth.in.name, ans, chap->challenge + 1);
+ } else
+#endif
+ {
+ key = auth_GetSecret(p->dl->bundle, name, nlen, p);
+ if (key) {
+ myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge,
+ chap->using_MSChap);
+ if (myans == NULL)
+ key = NULL;
+ else {
+ if (memcmp(myans, ans, 1 + *myans))
+ key = NULL;
+ free(myans);
+ }
+ }
+
+ if (key)
+ chap_Success(&chap->auth);
+ else
+ chap_Failure(&chap->auth);
+ }
+
+ break;
+
+ case CHAP_SUCCESS:
+ if (p->link.lcp.auth_iwait == PROTO_CHAP) {
+ p->link.lcp.auth_iwait = 0;
+ if (p->link.lcp.auth_ineed == 0)
+ /*
+ * We've succeeded in our ``login''
+ * If we're not expecting the peer to authenticate (or he already
+ * has), proceed to network phase.
+ */
+ datalink_AuthOk(p->dl);
+ }
+ break;
+
+ case CHAP_FAILURE:
+ datalink_AuthNotOk(p->dl);
+ break;
+ }
+ free(ans);
}
+
mbuf_Free(bp);
}
OpenPOWER on IntegriCloud