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