summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ppp/chap.c
diff options
context:
space:
mode:
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