chap.c revision 30715
16059Samurai/* 26059Samurai * PPP CHAP Module 36059Samurai * 46059Samurai * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 56059Samurai * 66059Samurai * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 76059Samurai * 86059Samurai * Redistribution and use in source and binary forms are permitted 96059Samurai * provided that the above copyright notice and this paragraph are 106059Samurai * duplicated in all such forms and that any documentation, 116059Samurai * advertising materials, and other materials related to such 126059Samurai * distribution and use acknowledge that the software was developed 136059Samurai * by the Internet Initiative Japan, Inc. The name of the 146059Samurai * IIJ may not be used to endorse or promote products derived 156059Samurai * from this software without specific prior written permission. 166059Samurai * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 176059Samurai * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 186059Samurai * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 198857Srgrimes * 2030715Sbrian * $Id: chap.c,v 1.23 1997/09/25 00:52:32 brian Exp $ 218857Srgrimes * 226059Samurai * TODO: 236059Samurai */ 2430715Sbrian#include <sys/param.h> 2530715Sbrian#include <netinet/in.h> 2630715Sbrian 2730715Sbrian#include <ctype.h> 2830715Sbrian#ifdef HAVE_DES 2930715Sbrian#include <md4.h> 3030715Sbrian#endif 3130715Sbrian#include <md5.h> 3230715Sbrian#include <stdio.h> 3330715Sbrian#include <stdlib.h> 3430715Sbrian#include <string.h> 3513379Sphk#include <time.h> 3630715Sbrian#include <unistd.h> 3730715Sbrian#ifdef __OpenBSD__ 3830715Sbrian#include <util.h> 3930715Sbrian#else 4030715Sbrian#include <libutil.h> 4130715Sbrian#endif 4229729Sbrian#include <utmp.h> 4329840Sbrian 4430715Sbrian#include "mbuf.h" 4530715Sbrian#include "log.h" 4630715Sbrian#include "defs.h" 4730715Sbrian#include "timer.h" 486059Samurai#include "fsm.h" 496059Samurai#include "chap.h" 5029840Sbrian#include "chap_ms.h" 516059Samurai#include "lcpproto.h" 526059Samurai#include "lcp.h" 536059Samurai#include "hdlc.h" 546059Samurai#include "phase.h" 5526142Sbrian#include "loadalias.h" 5630715Sbrian#include "command.h" 576059Samurai#include "vars.h" 586735Samurai#include "auth.h" 596059Samurai 606059Samuraistatic char *chapcodes[] = { 6119866Sphk "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 626059Samurai}; 636059Samurai 6430715Sbrianstatic void 6528679SbrianChapOutput(u_int code, u_int id, u_char * ptr, int count) 666059Samurai{ 676059Samurai int plen; 686059Samurai struct fsmheader lh; 696059Samurai struct mbuf *bp; 706059Samurai 7128679Sbrian plen = sizeof(struct fsmheader) + count; 726059Samurai lh.code = code; 736059Samurai lh.id = id; 746059Samurai lh.length = htons(plen); 756059Samurai bp = mballoc(plen, MB_FSM); 7630715Sbrian memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 776059Samurai if (count) 7830715Sbrian memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 7926516Sbrian LogDumpBp(LogDEBUG, "ChapOutput", bp); 8026516Sbrian LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]); 8113733Sdfr HdlcOutput(PRI_LINK, PROTO_CHAP, bp); 826059Samurai} 836059Samurai 846059Samurai 856059Samuraistatic char challenge_data[80]; 8628679Sbrianstatic int challenge_len; 876059Samurai 8830715Sbrianstatic void 8928679SbrianSendChapChallenge(int chapid) 906059Samurai{ 9113379Sphk int len, i; 926059Samurai char *cp; 936059Samurai 9430715Sbrian randinit(); 956059Samurai cp = challenge_data; 966059Samurai *cp++ = challenge_len = random() % 32 + 16; 976059Samurai for (i = 0; i < challenge_len; i++) 986059Samurai *cp++ = random() & 0xff; 996059Samurai len = strlen(VarAuthName); 10030715Sbrian memcpy(cp, VarAuthName, len); 1016059Samurai cp += len; 1026059Samurai ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data); 1036059Samurai} 1046059Samurai 10530715Sbrianstruct authinfo AuthChapInfo = { 10630715Sbrian SendChapChallenge, 10730715Sbrian}; 10830715Sbrian 10930715Sbrianstatic void 11030715SbrianRecvChapTalk(struct fsmheader *chp, struct mbuf *bp) 1116059Samurai{ 1126059Samurai int valsize, len; 1136059Samurai int arglen, keylen, namelen; 1146059Samurai char *cp, *argp, *ap, *name, *digest; 1156059Samurai char *keyp; 11629549Sbrian MD5_CTX MD5context; /* context for MD5 */ 1176059Samurai char answer[100]; 1186059Samurai char cdigest[16]; 11929840Sbrian#ifdef HAVE_DES 12029840Sbrian int ix; 12129840Sbrian MD4_CTX MD4context; /* context for MD4 */ 12229840Sbrian#endif 1236059Samurai 1246059Samurai len = ntohs(chp->length); 12526516Sbrian LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len); 1266059Samurai arglen = len - sizeof(struct fsmheader); 12728679Sbrian cp = (char *) MBUF_CTOP(bp); 1286059Samurai valsize = *cp++ & 255; 1296059Samurai name = cp + valsize; 1306059Samurai namelen = arglen - valsize - 1; 1316059Samurai name[namelen] = 0; 13226516Sbrian LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name); 1336059Samurai 1346059Samurai /* 1356059Samurai * Get a secret key corresponds to the peer 1366059Samurai */ 1376059Samurai keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE); 1386059Samurai 1396059Samurai switch (chp->code) { 1406059Samurai case CHAP_CHALLENGE: 1416059Samurai if (keyp) { 1426059Samurai keylen = strlen(keyp); 1436059Samurai } else { 1446059Samurai keylen = strlen(VarAuthKey); 1456059Samurai keyp = VarAuthKey; 1466059Samurai } 1476059Samurai name = VarAuthName; 1486059Samurai namelen = strlen(VarAuthName); 14929840Sbrian 15029840Sbrian#ifdef HAVE_DES 15129840Sbrian if (VarMSChap) 15229840Sbrian argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 15329840Sbrian else 15429840Sbrian#endif 15529840Sbrian argp = malloc(1 + valsize + namelen + 16); 15629840Sbrian 15725630Sbrian if (argp == NULL) { 15825630Sbrian ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14); 15925630Sbrian return; 16025630Sbrian } 16129840Sbrian#ifdef HAVE_DES 16229840Sbrian if (VarMSChap) { 16329840Sbrian digest = argp; /* this is the response */ 16429840Sbrian *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 16530715Sbrian memset(digest, '\0', 24); 16630715Sbrian digest += 24; 16729840Sbrian 16829840Sbrian ap = answer; /* this is the challenge */ 16930715Sbrian memcpy(ap, keyp, keylen); 17029840Sbrian ap += 2 * keylen; 17130715Sbrian memcpy(ap, cp, valsize); 17229840Sbrian LogDumpBuff(LogDEBUG, "recv", ap, valsize); 17329840Sbrian ap += valsize; 17429840Sbrian for (ix = keylen; ix > 0 ; ix--) { 17529840Sbrian answer[2*ix-2] = answer[ix-1]; 17629840Sbrian answer[2*ix-1] = 0; 17729840Sbrian } 17829549Sbrian MD4Init(&MD4context); 17929840Sbrian MD4Update(&MD4context, answer, 2 * keylen); 18029549Sbrian MD4Final(digest, &MD4context); 18130715Sbrian memcpy(digest + 25, name, namelen); 18229840Sbrian ap += 2 * keylen; 18329840Sbrian ChapMS(digest, answer + 2 * keylen, valsize); 18429840Sbrian LogDumpBuff(LogDEBUG, "answer", digest, 24); 18529840Sbrian ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1); 18629549Sbrian } else { 18729840Sbrian#endif 18829840Sbrian digest = argp; 18929840Sbrian *digest++ = 16; /* value size */ 19029840Sbrian ap = answer; 19129840Sbrian *ap++ = chp->id; 19230715Sbrian memcpy(ap, keyp, keylen); 19329840Sbrian ap += keylen; 19430715Sbrian memcpy(ap, cp, valsize); 19529840Sbrian LogDumpBuff(LogDEBUG, "recv", ap, valsize); 19629840Sbrian ap += valsize; 19729549Sbrian MD5Init(&MD5context); 19829549Sbrian MD5Update(&MD5context, answer, ap - answer); 19929549Sbrian MD5Final(digest, &MD5context); 20029840Sbrian LogDumpBuff(LogDEBUG, "answer", digest, 16); 20130715Sbrian memcpy(digest + 16, name, namelen); 20229840Sbrian ap += namelen; 20329840Sbrian /* Send answer to the peer */ 20429840Sbrian ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17); 20529840Sbrian#ifdef HAVE_DES 20629549Sbrian } 20729840Sbrian#endif 20818885Sjkh free(argp); 2096059Samurai break; 2106059Samurai case CHAP_RESPONSE: 2116059Samurai if (keyp) { 21228679Sbrian 2136059Samurai /* 2146059Samurai * Compute correct digest value 2156059Samurai */ 2166059Samurai keylen = strlen(keyp); 2176059Samurai ap = answer; 2186059Samurai *ap++ = chp->id; 21930715Sbrian memcpy(ap, keyp, keylen); 2206059Samurai ap += keylen; 22129840Sbrian MD5Init(&MD5context); 22229840Sbrian MD5Update(&MD5context, answer, ap - answer); 22329840Sbrian MD5Update(&MD5context, challenge_data + 1, challenge_len); 22429840Sbrian MD5Final(cdigest, &MD5context); 22526516Sbrian LogDumpBuff(LogDEBUG, "got", cp, 16); 22626516Sbrian LogDumpBuff(LogDEBUG, "expect", cdigest, 16); 22728679Sbrian 2286059Samurai /* 2296059Samurai * Compare with the response 2306059Samurai */ 23130715Sbrian if (memcmp(cp, cdigest, 16) == 0) { 23229729Sbrian ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10); 23329729Sbrian if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp)) 23429729Sbrian if (Utmp) 23529729Sbrian LogPrintf(LogERROR, "Oops, already logged in on %s\n", 23629729Sbrian VarBaseDevice); 23729729Sbrian else { 23829729Sbrian struct utmp ut; 23929729Sbrian memset(&ut, 0, sizeof(ut)); 24029729Sbrian time(&ut.ut_time); 24129729Sbrian strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1); 24229729Sbrian strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1); 24329729Sbrian if (logout(ut.ut_line)) 24429729Sbrian logwtmp(ut.ut_line, "", ""); 24529729Sbrian login(&ut); 24629729Sbrian Utmp = 1; 24729729Sbrian } 2486059Samurai NewPhase(PHASE_NETWORK); 2496059Samurai break; 2506059Samurai } 2516059Samurai } 25228679Sbrian 2536059Samurai /* 2546059Samurai * Peer is not registerd, or response digest is wrong. 2556059Samurai */ 2566059Samurai ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9); 25726098Sbrian reconnect(RECON_FALSE); 2586059Samurai LcpClose(); 2596059Samurai break; 2606059Samurai } 2616059Samurai} 2626059Samurai 26330715Sbrianstatic void 26430715SbrianRecvChapResult(struct fsmheader *chp, struct mbuf *bp) 2656059Samurai{ 2666059Samurai int len; 2676059Samurai struct lcpstate *lcp = &LcpInfo; 2686059Samurai 2696059Samurai len = ntohs(chp->length); 27026516Sbrian LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len); 2716059Samurai if (chp->code == CHAP_SUCCESS) { 2726059Samurai if (lcp->auth_iwait == PROTO_CHAP) { 2736059Samurai lcp->auth_iwait = 0; 2746059Samurai if (lcp->auth_ineed == 0) 2756059Samurai NewPhase(PHASE_NETWORK); 2766059Samurai } 2776059Samurai } else { 27828679Sbrian 2796059Samurai /* 2806059Samurai * Maybe, we shoud close LCP. Of cause, peer may take close action, too. 2816059Samurai */ 2826059Samurai ; 2836059Samurai } 2846059Samurai} 2856059Samurai 2866059Samuraivoid 28730715SbrianChapInput(struct mbuf *bp) 2886059Samurai{ 2896059Samurai int len = plength(bp); 2906059Samurai struct fsmheader *chp; 2916059Samurai 2926059Samurai if (len >= sizeof(struct fsmheader)) { 29328679Sbrian chp = (struct fsmheader *) MBUF_CTOP(bp); 2946059Samurai if (len >= ntohs(chp->length)) { 2956059Samurai if (chp->code < 1 || chp->code > 4) 2966059Samurai chp->code = 0; 29726516Sbrian LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]); 2986059Samurai 2996059Samurai bp->offset += sizeof(struct fsmheader); 3006059Samurai bp->cnt -= sizeof(struct fsmheader); 3016059Samurai 3026059Samurai switch (chp->code) { 3036735Samurai case CHAP_RESPONSE: 3046735Samurai StopAuthTimer(&AuthChapInfo); 3056735Samurai /* Fall into.. */ 3066059Samurai case CHAP_CHALLENGE: 3076059Samurai RecvChapTalk(chp, bp); 3086059Samurai break; 3096059Samurai case CHAP_SUCCESS: 3106059Samurai case CHAP_FAILURE: 3116059Samurai RecvChapResult(chp, bp); 3126059Samurai break; 3136059Samurai } 3146059Samurai } 3156059Samurai } 3166059Samurai pfree(bp); 3176059Samurai} 318