chap.c revision 29840
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 *
2029840Sbrian * $Id: chap.c,v 1.22 1997/09/22 23:59:13 brian Exp $
218857Srgrimes *
226059Samurai *	TODO:
236059Samurai */
2413379Sphk#include <sys/types.h>
2513379Sphk#include <time.h>
2629729Sbrian#include <utmp.h>
2729840Sbrian#include <ctype.h>
2829840Sbrian
296059Samurai#include "fsm.h"
306059Samurai#include "chap.h"
3129840Sbrian#include "chap_ms.h"
326059Samurai#include "lcpproto.h"
336059Samurai#include "lcp.h"
346059Samurai#include "hdlc.h"
356059Samurai#include "phase.h"
3626142Sbrian#include "loadalias.h"
376059Samurai#include "vars.h"
386735Samurai#include "auth.h"
3929729Sbrian#ifdef __OpenBSD__
4029729Sbrian#include "util.h"
4129729Sbrian#else
4229729Sbrian#include "libutil.h"
4329729Sbrian#endif
446059Samurai
456059Samuraistatic char *chapcodes[] = {
4619866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
476059Samurai};
486059Samurai
4928679Sbrianstruct authinfo AuthChapInfo = {
506735Samurai  SendChapChallenge,
516735Samurai};
526735Samurai
536059Samuraiextern char *AuthGetSecret();
5423603Sacheextern int randinit;
556059Samurai
566059Samuraivoid
5728679SbrianChapOutput(u_int code, u_int id, u_char * ptr, int count)
586059Samurai{
596059Samurai  int plen;
606059Samurai  struct fsmheader lh;
616059Samurai  struct mbuf *bp;
626059Samurai
6328679Sbrian  plen = sizeof(struct fsmheader) + count;
646059Samurai  lh.code = code;
656059Samurai  lh.id = id;
666059Samurai  lh.length = htons(plen);
676059Samurai  bp = mballoc(plen, MB_FSM);
686059Samurai  bcopy(&lh, MBUF_CTOP(bp), sizeof(struct fsmheader));
696059Samurai  if (count)
706059Samurai    bcopy(ptr, MBUF_CTOP(bp) + sizeof(struct fsmheader), count);
7126516Sbrian  LogDumpBp(LogDEBUG, "ChapOutput", bp);
7226516Sbrian  LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
7313733Sdfr  HdlcOutput(PRI_LINK, PROTO_CHAP, bp);
746059Samurai}
756059Samurai
766059Samurai
776059Samuraistatic char challenge_data[80];
7828679Sbrianstatic int challenge_len;
796059Samurai
806059Samuraivoid
8128679SbrianSendChapChallenge(int chapid)
826059Samurai{
8313379Sphk  int len, i;
846059Samurai  char *cp;
856059Samurai
8623603Sache  if (!randinit) {
8723603Sache    randinit = 1;
8826626Sache    srandomdev();
8923603Sache  }
906059Samurai  cp = challenge_data;
916059Samurai  *cp++ = challenge_len = random() % 32 + 16;
926059Samurai  for (i = 0; i < challenge_len; i++)
936059Samurai    *cp++ = random() & 0xff;
946059Samurai  len = strlen(VarAuthName);
956059Samurai  bcopy(VarAuthName, cp, len);
966059Samurai  cp += len;
976059Samurai  ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data);
986059Samurai}
996059Samurai
1006059Samuraivoid
10128679SbrianRecvChapTalk(struct fsmheader * chp, struct mbuf * bp)
1026059Samurai{
1036059Samurai  int valsize, len;
1046059Samurai  int arglen, keylen, namelen;
1056059Samurai  char *cp, *argp, *ap, *name, *digest;
1066059Samurai  char *keyp;
10729549Sbrian  MD5_CTX MD5context;		/* context for MD5 */
1086059Samurai  char answer[100];
1096059Samurai  char cdigest[16];
11029840Sbrian#ifdef HAVE_DES
11129840Sbrian  int ix;
11229840Sbrian  MD4_CTX MD4context;		/* context for MD4 */
11329840Sbrian#endif
1146059Samurai
1156059Samurai  len = ntohs(chp->length);
11626516Sbrian  LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
1176059Samurai  arglen = len - sizeof(struct fsmheader);
11828679Sbrian  cp = (char *) MBUF_CTOP(bp);
1196059Samurai  valsize = *cp++ & 255;
1206059Samurai  name = cp + valsize;
1216059Samurai  namelen = arglen - valsize - 1;
1226059Samurai  name[namelen] = 0;
12326516Sbrian  LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name);
1246059Samurai
1256059Samurai  /*
1266059Samurai   * Get a secret key corresponds to the peer
1276059Samurai   */
1286059Samurai  keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE);
1296059Samurai
1306059Samurai  switch (chp->code) {
1316059Samurai  case CHAP_CHALLENGE:
1326059Samurai    if (keyp) {
1336059Samurai      keylen = strlen(keyp);
1346059Samurai    } else {
1356059Samurai      keylen = strlen(VarAuthKey);
1366059Samurai      keyp = VarAuthKey;
1376059Samurai    }
1386059Samurai    name = VarAuthName;
1396059Samurai    namelen = strlen(VarAuthName);
14029840Sbrian
14129840Sbrian#ifdef HAVE_DES
14229840Sbrian    if (VarMSChap)
14329840Sbrian      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
14429840Sbrian    else
14529840Sbrian#endif
14629840Sbrian      argp = malloc(1 + valsize + namelen + 16);
14729840Sbrian
14825630Sbrian    if (argp == NULL) {
14925630Sbrian      ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14);
15025630Sbrian      return;
15125630Sbrian    }
15229840Sbrian#ifdef HAVE_DES
15329840Sbrian    if (VarMSChap) {
15429840Sbrian      digest = argp;     /* this is the response */
15529840Sbrian      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
15629840Sbrian      bzero(digest, 24); digest += 24;
15729840Sbrian
15829840Sbrian      ap = answer;       /* this is the challenge */
15929840Sbrian      bcopy(keyp, ap, keylen);
16029840Sbrian      ap += 2 * keylen;
16129840Sbrian      bcopy(cp, ap, valsize);
16229840Sbrian      LogDumpBuff(LogDEBUG, "recv", ap, valsize);
16329840Sbrian      ap += valsize;
16429840Sbrian      for (ix = keylen; ix > 0 ; ix--) {
16529840Sbrian          answer[2*ix-2] = answer[ix-1];
16629840Sbrian          answer[2*ix-1] = 0;
16729840Sbrian      }
16829549Sbrian      MD4Init(&MD4context);
16929840Sbrian      MD4Update(&MD4context, answer, 2 * keylen);
17029549Sbrian      MD4Final(digest, &MD4context);
17129840Sbrian      bcopy(name, digest + 25, namelen);
17229840Sbrian      ap += 2 * keylen;
17329840Sbrian      ChapMS(digest, answer + 2 * keylen, valsize);
17429840Sbrian      LogDumpBuff(LogDEBUG, "answer", digest, 24);
17529840Sbrian      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1);
17629549Sbrian    } else {
17729840Sbrian#endif
17829840Sbrian      digest = argp;
17929840Sbrian      *digest++ = 16;		/* value size */
18029840Sbrian      ap = answer;
18129840Sbrian      *ap++ = chp->id;
18229840Sbrian      bcopy(keyp, ap, keylen);
18329840Sbrian      ap += keylen;
18429840Sbrian      bcopy(cp, ap, valsize);
18529840Sbrian      LogDumpBuff(LogDEBUG, "recv", ap, valsize);
18629840Sbrian      ap += valsize;
18729549Sbrian      MD5Init(&MD5context);
18829549Sbrian      MD5Update(&MD5context, answer, ap - answer);
18929549Sbrian      MD5Final(digest, &MD5context);
19029840Sbrian      LogDumpBuff(LogDEBUG, "answer", digest, 16);
19129840Sbrian      bcopy(name, digest + 16, namelen);
19229840Sbrian      ap += namelen;
19329840Sbrian      /* Send answer to the peer */
19429840Sbrian      ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17);
19529840Sbrian#ifdef HAVE_DES
19629549Sbrian    }
19729840Sbrian#endif
19818885Sjkh    free(argp);
1996059Samurai    break;
2006059Samurai  case CHAP_RESPONSE:
2016059Samurai    if (keyp) {
20228679Sbrian
2036059Samurai      /*
2046059Samurai       * Compute correct digest value
2056059Samurai       */
2066059Samurai      keylen = strlen(keyp);
2076059Samurai      ap = answer;
2086059Samurai      *ap++ = chp->id;
2096059Samurai      bcopy(keyp, ap, keylen);
2106059Samurai      ap += keylen;
21129840Sbrian      MD5Init(&MD5context);
21229840Sbrian      MD5Update(&MD5context, answer, ap - answer);
21329840Sbrian      MD5Update(&MD5context, challenge_data + 1, challenge_len);
21429840Sbrian      MD5Final(cdigest, &MD5context);
21526516Sbrian      LogDumpBuff(LogDEBUG, "got", cp, 16);
21626516Sbrian      LogDumpBuff(LogDEBUG, "expect", cdigest, 16);
21728679Sbrian
2186059Samurai      /*
2196059Samurai       * Compare with the response
2206059Samurai       */
2216059Samurai      if (bcmp(cp, cdigest, 16) == 0) {
22229729Sbrian	ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10);
22329729Sbrian        if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
22429729Sbrian	  if (Utmp)
22529729Sbrian	    LogPrintf(LogERROR, "Oops, already logged in on %s\n",
22629729Sbrian		      VarBaseDevice);
22729729Sbrian	  else {
22829729Sbrian	    struct utmp ut;
22929729Sbrian	    memset(&ut, 0, sizeof(ut));
23029729Sbrian	    time(&ut.ut_time);
23129729Sbrian	    strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1);
23229729Sbrian	    strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
23329729Sbrian	    if (logout(ut.ut_line))
23429729Sbrian	      logwtmp(ut.ut_line, "", "");
23529729Sbrian	    login(&ut);
23629729Sbrian	    Utmp = 1;
23729729Sbrian	  }
2386059Samurai	NewPhase(PHASE_NETWORK);
2396059Samurai	break;
2406059Samurai      }
2416059Samurai    }
24228679Sbrian
2436059Samurai    /*
2446059Samurai     * Peer is not registerd, or response digest is wrong.
2456059Samurai     */
2466059Samurai    ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9);
24726098Sbrian    reconnect(RECON_FALSE);
2486059Samurai    LcpClose();
2496059Samurai    break;
2506059Samurai  }
2516059Samurai}
2526059Samurai
2536059Samuraivoid
25428679SbrianRecvChapResult(struct fsmheader * chp, struct mbuf * bp)
2556059Samurai{
2566059Samurai  int len;
2576059Samurai  struct lcpstate *lcp = &LcpInfo;
2586059Samurai
2596059Samurai  len = ntohs(chp->length);
26026516Sbrian  LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len);
2616059Samurai  if (chp->code == CHAP_SUCCESS) {
2626059Samurai    if (lcp->auth_iwait == PROTO_CHAP) {
2636059Samurai      lcp->auth_iwait = 0;
2646059Samurai      if (lcp->auth_ineed == 0)
2656059Samurai	NewPhase(PHASE_NETWORK);
2666059Samurai    }
2676059Samurai  } else {
26828679Sbrian
2696059Samurai    /*
2706059Samurai     * Maybe, we shoud close LCP. Of cause, peer may take close action, too.
2716059Samurai     */
2726059Samurai    ;
2736059Samurai  }
2746059Samurai}
2756059Samurai
2766059Samuraivoid
27728679SbrianChapInput(struct mbuf * bp)
2786059Samurai{
2796059Samurai  int len = plength(bp);
2806059Samurai  struct fsmheader *chp;
2816059Samurai
2826059Samurai  if (len >= sizeof(struct fsmheader)) {
28328679Sbrian    chp = (struct fsmheader *) MBUF_CTOP(bp);
2846059Samurai    if (len >= ntohs(chp->length)) {
2856059Samurai      if (chp->code < 1 || chp->code > 4)
2866059Samurai	chp->code = 0;
28726516Sbrian      LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]);
2886059Samurai
2896059Samurai      bp->offset += sizeof(struct fsmheader);
2906059Samurai      bp->cnt -= sizeof(struct fsmheader);
2916059Samurai
2926059Samurai      switch (chp->code) {
2936735Samurai      case CHAP_RESPONSE:
2946735Samurai	StopAuthTimer(&AuthChapInfo);
2956735Samurai	/* Fall into.. */
2966059Samurai      case CHAP_CHALLENGE:
2976059Samurai	RecvChapTalk(chp, bp);
2986059Samurai	break;
2996059Samurai      case CHAP_SUCCESS:
3006059Samurai      case CHAP_FAILURE:
3016059Samurai	RecvChapResult(chp, bp);
3026059Samurai	break;
3036059Samurai      }
3046059Samurai    }
3056059Samurai  }
3066059Samurai  pfree(bp);
3076059Samurai}
308