diff options
Diffstat (limited to 'usr.sbin/ppp/chap.c')
-rw-r--r-- | usr.sbin/ppp/chap.c | 492 |
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); } |