chap.c revision 43313
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 * 2043313Sbrian * $Id: chap.c,v 1.37 1998/08/26 18:07:56 brian Exp $ 218857Srgrimes * 226059Samurai * TODO: 236059Samurai */ 2443313Sbrian#include <sys/param.h> 2530715Sbrian#include <netinet/in.h> 2636285Sbrian#include <netinet/in_systm.h> 2736285Sbrian#include <netinet/ip.h> 2836285Sbrian#include <sys/un.h> 2930715Sbrian 3037192Sbrian#ifdef HAVE_DES 3136287Sbrian#include <md4.h> 3237192Sbrian#endif 3330715Sbrian#include <md5.h> 3430715Sbrian#include <stdlib.h> 3538559Sbrian#include <string.h> 3636285Sbrian#include <termios.h> 3729840Sbrian 3830715Sbrian#include "mbuf.h" 3930715Sbrian#include "log.h" 4030715Sbrian#include "defs.h" 4130715Sbrian#include "timer.h" 426059Samurai#include "fsm.h" 436059Samurai#include "lcpproto.h" 446059Samurai#include "lcp.h" 4536285Sbrian#include "lqr.h" 466059Samurai#include "hdlc.h" 476735Samurai#include "auth.h" 4836285Sbrian#include "chap.h" 4936285Sbrian#include "async.h" 5036285Sbrian#include "throughput.h" 5136285Sbrian#include "descriptor.h" 5236285Sbrian#include "iplist.h" 5336285Sbrian#include "slcompress.h" 5436285Sbrian#include "ipcp.h" 5536285Sbrian#include "filter.h" 5636285Sbrian#include "ccp.h" 5736285Sbrian#include "link.h" 5836285Sbrian#include "physical.h" 5936285Sbrian#include "mp.h" 6043313Sbrian#ifndef NORADIUS 6143313Sbrian#include "radius.h" 6243313Sbrian#endif 6336285Sbrian#include "bundle.h" 6436285Sbrian#include "chat.h" 6538174Sbrian#include "cbcp.h" 6636285Sbrian#include "datalink.h" 6737192Sbrian#ifdef HAVE_DES 6836287Sbrian#include "chap_ms.h" 6937192Sbrian#endif 706059Samurai 7131343Sbrianstatic const char *chapcodes[] = { 7219866Sphk "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 736059Samurai}; 746059Samurai 7530715Sbrianstatic void 7636285SbrianChapOutput(struct physical *physical, u_int code, u_int id, 7737926Sbrian const u_char * ptr, int count, const char *text) 786059Samurai{ 796059Samurai int plen; 806059Samurai struct fsmheader lh; 816059Samurai struct mbuf *bp; 826059Samurai 8328679Sbrian plen = sizeof(struct fsmheader) + count; 846059Samurai lh.code = code; 856059Samurai lh.id = id; 866059Samurai lh.length = htons(plen); 8736285Sbrian bp = mbuf_Alloc(plen, MB_FSM); 8830715Sbrian memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 896059Samurai if (count) 9030715Sbrian memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 9136285Sbrian log_DumpBp(LogDEBUG, "ChapOutput", bp); 9237926Sbrian if (text == NULL) 9337926Sbrian log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); 9437926Sbrian else 9537926Sbrian log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); 9636285Sbrian hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp); 976059Samurai} 986059Samurai 9936285Sbrianvoid 10036285Sbrianchap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical) 1016059Samurai{ 10236285Sbrian struct chap *chap = auth2chap(auth); 10313379Sphk int len, i; 1046059Samurai char *cp; 1056059Samurai 10630715Sbrian randinit(); 10736285Sbrian cp = chap->challenge_data; 10843313Sbrian#ifndef NORADIUS 10943313Sbrian if (*physical->dl->bundle->radius.cfg.file) { 11043313Sbrian /* For radius, our challenge is 16 readable NUL terminated bytes :*/ 11143313Sbrian *cp++ = chap->challenge_len = 16; 11243313Sbrian for (i = 0; i < chap->challenge_len; i++) 11343313Sbrian *cp++ = (random() & (0x7f - 0x20)) + 0x20; 11443313Sbrian *cp = '\0'; 11543313Sbrian } else { 11643313Sbrian#endif 11743313Sbrian *cp++ = chap->challenge_len = random() % (CHAPCHALLENGELEN-16) + 16; 11843313Sbrian for (i = 0; i < chap->challenge_len; i++) 11943313Sbrian *cp++ = random() & 0xff; 12043313Sbrian len = strlen(physical->dl->bundle->cfg.auth.name); 12143313Sbrian memcpy(cp, physical->dl->bundle->cfg.auth.name, len); 12243313Sbrian cp += len; 12343313Sbrian#ifndef NORADIUS 12443313Sbrian } 12543313Sbrian#endif 12636285Sbrian ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data, 12737926Sbrian cp - chap->challenge_data, NULL); 1286059Samurai} 1296059Samurai 13030715Sbrianstatic void 13136285SbrianRecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp, 13236285Sbrian struct physical *physical) 1336059Samurai{ 1346059Samurai int valsize, len; 1356059Samurai int arglen, keylen, namelen; 1366059Samurai char *cp, *argp, *ap, *name, *digest; 1376059Samurai char *keyp; 13829549Sbrian MD5_CTX MD5context; /* context for MD5 */ 13943313Sbrian char answer[CHAPDIGESTLEN]; 1406059Samurai char cdigest[16]; 14129840Sbrian#ifdef HAVE_DES 14229840Sbrian int ix; 14329840Sbrian MD4_CTX MD4context; /* context for MD4 */ 14429840Sbrian#endif 1456059Samurai 1466059Samurai len = ntohs(chp->length); 14736285Sbrian log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len); 1486059Samurai arglen = len - sizeof(struct fsmheader); 14928679Sbrian cp = (char *) MBUF_CTOP(bp); 1506059Samurai valsize = *cp++ & 255; 1516059Samurai name = cp + valsize; 1526059Samurai namelen = arglen - valsize - 1; 1536059Samurai name[namelen] = 0; 1546059Samurai 15537926Sbrian log_Printf(LogPHASE, "Chap Input: %s (from %s)\n", 15637926Sbrian chapcodes[chp->code], name); 15737926Sbrian 1586059Samurai switch (chp->code) { 1596059Samurai case CHAP_CHALLENGE: 16036285Sbrian keyp = bundle->cfg.auth.key; 16136285Sbrian keylen = strlen(bundle->cfg.auth.key); 16236285Sbrian name = bundle->cfg.auth.name; 16336285Sbrian namelen = strlen(bundle->cfg.auth.name); 16429840Sbrian 16529840Sbrian#ifdef HAVE_DES 16636285Sbrian if (physical->dl->chap.using_MSChap) 16729840Sbrian argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN); 16829840Sbrian else 16929840Sbrian#endif 17029840Sbrian argp = malloc(1 + valsize + namelen + 16); 17129840Sbrian 17225630Sbrian if (argp == NULL) { 17337926Sbrian ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL); 17425630Sbrian return; 17525630Sbrian } 17629840Sbrian#ifdef HAVE_DES 17736285Sbrian if (physical->dl->chap.using_MSChap) { 17829840Sbrian digest = argp; /* this is the response */ 17929840Sbrian *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 18030715Sbrian memset(digest, '\0', 24); 18130715Sbrian digest += 24; 18229840Sbrian 18329840Sbrian ap = answer; /* this is the challenge */ 18430715Sbrian memcpy(ap, keyp, keylen); 18529840Sbrian ap += 2 * keylen; 18630715Sbrian memcpy(ap, cp, valsize); 18736285Sbrian log_DumpBuff(LogDEBUG, "recv", ap, valsize); 18829840Sbrian ap += valsize; 18929840Sbrian for (ix = keylen; ix > 0 ; ix--) { 19029840Sbrian answer[2*ix-2] = answer[ix-1]; 19129840Sbrian answer[2*ix-1] = 0; 19229840Sbrian } 19329549Sbrian MD4Init(&MD4context); 19429840Sbrian MD4Update(&MD4context, answer, 2 * keylen); 19529549Sbrian MD4Final(digest, &MD4context); 19630715Sbrian memcpy(digest + 25, name, namelen); 19729840Sbrian ap += 2 * keylen; 19836285Sbrian chap_MS(digest, answer + 2 * keylen, valsize); 19936285Sbrian log_DumpBuff(LogDEBUG, "answer", digest, 24); 20036285Sbrian ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, 20137926Sbrian namelen + MS_CHAP_RESPONSE_LEN + 1, name); 20229549Sbrian } else { 20329840Sbrian#endif 20429840Sbrian digest = argp; 20529840Sbrian *digest++ = 16; /* value size */ 20629840Sbrian ap = answer; 20729840Sbrian *ap++ = chp->id; 20830715Sbrian memcpy(ap, keyp, keylen); 20929840Sbrian ap += keylen; 21030715Sbrian memcpy(ap, cp, valsize); 21136285Sbrian log_DumpBuff(LogDEBUG, "recv", ap, valsize); 21229840Sbrian ap += valsize; 21329549Sbrian MD5Init(&MD5context); 21429549Sbrian MD5Update(&MD5context, answer, ap - answer); 21529549Sbrian MD5Final(digest, &MD5context); 21636285Sbrian log_DumpBuff(LogDEBUG, "answer", digest, 16); 21730715Sbrian memcpy(digest + 16, name, namelen); 21829840Sbrian ap += namelen; 21929840Sbrian /* Send answer to the peer */ 22037926Sbrian ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name); 22129840Sbrian#ifdef HAVE_DES 22229549Sbrian } 22329840Sbrian#endif 22418885Sjkh free(argp); 22537926Sbrian if (*name == '\0') 22637926Sbrian log_Printf(LogWARN, "Sending empty CHAP authname!\n"); 2276059Samurai break; 2286059Samurai case CHAP_RESPONSE: 22931051Sbrian /* 23031051Sbrian * Get a secret key corresponds to the peer 23131051Sbrian */ 23243313Sbrian#ifndef NORADIUS 23343313Sbrian if (*bundle->radius.cfg.file) { 23443313Sbrian char chapname[AUTHLEN]; 23543313Sbrian 23643313Sbrian if (namelen > AUTHLEN - 1) 23743313Sbrian namelen = AUTHLEN - 1; 23843313Sbrian strncpy(chapname, name, namelen); 23943313Sbrian chapname[namelen] = '\0'; 24043313Sbrian strncpy(answer, cp-1, 17); 24143313Sbrian answer[17] = '\0'; 24243313Sbrian 24343313Sbrian if (radius_Authenticate(&bundle->radius, bundle, chapname, answer, 24443313Sbrian physical->dl->chap.challenge_data + 1)) 24543313Sbrian break; /* And there was much rejoicing ! */ 24643313Sbrian 24743313Sbrian } else 24843313Sbrian#endif 24943313Sbrian if ((keyp = auth_GetSecret(bundle, name, namelen, physical))) { 25043313Sbrian /* Compute correct digest value */ 2516059Samurai keylen = strlen(keyp); 2526059Samurai ap = answer; 2536059Samurai *ap++ = chp->id; 25430715Sbrian memcpy(ap, keyp, keylen); 2556059Samurai ap += keylen; 25629840Sbrian MD5Init(&MD5context); 25729840Sbrian MD5Update(&MD5context, answer, ap - answer); 25836285Sbrian MD5Update(&MD5context, physical->dl->chap.challenge_data + 1, 25936285Sbrian physical->dl->chap.challenge_len); 26029840Sbrian MD5Final(cdigest, &MD5context); 26136285Sbrian log_DumpBuff(LogDEBUG, "got", cp, 16); 26236285Sbrian log_DumpBuff(LogDEBUG, "expect", cdigest, 16); 26328679Sbrian 2646059Samurai /* 2656059Samurai * Compare with the response 2666059Samurai */ 26730715Sbrian if (memcmp(cp, cdigest, 16) == 0) { 26836285Sbrian datalink_GotAuthname(physical->dl, name, namelen); 26937926Sbrian ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL); 27037926Sbrian physical->link.lcp.auth_ineed = 0; 27136285Sbrian if (Enabled(bundle, OPT_UTMP)) 27236285Sbrian physical_Login(physical, name); 27336285Sbrian 27436285Sbrian if (physical->link.lcp.auth_iwait == 0) 27536285Sbrian /* 27636285Sbrian * Either I didn't need to authenticate, or I've already been 27736285Sbrian * told that I got the answer right. 27836285Sbrian */ 27936285Sbrian datalink_AuthOk(physical->dl); 28036285Sbrian 2816059Samurai break; 2826059Samurai } 2836059Samurai } 28428679Sbrian 2856059Samurai /* 2866059Samurai * Peer is not registerd, or response digest is wrong. 2876059Samurai */ 28837926Sbrian ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL); 28936285Sbrian datalink_AuthNotOk(physical->dl); 2906059Samurai break; 2916059Samurai } 2926059Samurai} 2936059Samurai 29430715Sbrianstatic void 29536285SbrianRecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp, 29636285Sbrian struct physical *physical) 2976059Samurai{ 2986059Samurai int len; 2996059Samurai 3006059Samurai len = ntohs(chp->length); 30136285Sbrian log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len); 3026059Samurai if (chp->code == CHAP_SUCCESS) { 30336285Sbrian if (physical->link.lcp.auth_iwait == PROTO_CHAP) { 30436285Sbrian physical->link.lcp.auth_iwait = 0; 30536285Sbrian if (physical->link.lcp.auth_ineed == 0) 30636285Sbrian /* 30736285Sbrian * We've succeeded in our ``login'' 30836285Sbrian * If we're not expecting the peer to authenticate (or he already 30936285Sbrian * has), proceed to network phase. 31036285Sbrian */ 31136285Sbrian datalink_AuthOk(physical->dl); 3126059Samurai } 3136059Samurai } else { 31436285Sbrian /* CHAP failed - it's not going to get any better */ 31537926Sbrian log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n"); 31636285Sbrian datalink_AuthNotOk(physical->dl); 3176059Samurai } 3186059Samurai} 3196059Samurai 3206059Samuraivoid 32136285Sbrianchap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical) 3226059Samurai{ 32336285Sbrian int len = mbuf_Length(bp); 3246059Samurai struct fsmheader *chp; 3256059Samurai 3266059Samurai if (len >= sizeof(struct fsmheader)) { 32728679Sbrian chp = (struct fsmheader *) MBUF_CTOP(bp); 3286059Samurai if (len >= ntohs(chp->length)) { 3296059Samurai if (chp->code < 1 || chp->code > 4) 3306059Samurai chp->code = 0; 3316059Samurai bp->offset += sizeof(struct fsmheader); 3326059Samurai bp->cnt -= sizeof(struct fsmheader); 3336059Samurai 3346059Samurai switch (chp->code) { 3356735Samurai case CHAP_RESPONSE: 33636285Sbrian auth_StopTimer(&physical->dl->chap.auth); 3376735Samurai /* Fall into.. */ 3386059Samurai case CHAP_CHALLENGE: 33936285Sbrian RecvChapTalk(bundle, chp, bp, physical); 3406059Samurai break; 3416059Samurai case CHAP_SUCCESS: 3426059Samurai case CHAP_FAILURE: 34337926Sbrian log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]); 34436285Sbrian RecvChapResult(bundle, chp, bp, physical); 3456059Samurai break; 3466059Samurai } 3476059Samurai } 3486059Samurai } 34936285Sbrian mbuf_Free(bp); 3506059Samurai} 351