chap.c revision 37192
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 *
2037192Sbrian * $Id: chap.c,v 1.32 1998/05/21 22:55:02 brian Exp $
218857Srgrimes *
226059Samurai *	TODO:
236059Samurai */
2436285Sbrian#include <sys/types.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>
3536285Sbrian#include <termios.h>
3629840Sbrian
3730715Sbrian#include "mbuf.h"
3830715Sbrian#include "log.h"
3930715Sbrian#include "defs.h"
4030715Sbrian#include "timer.h"
416059Samurai#include "fsm.h"
426059Samurai#include "lcpproto.h"
436059Samurai#include "lcp.h"
4436285Sbrian#include "lqr.h"
456059Samurai#include "hdlc.h"
466735Samurai#include "auth.h"
4736285Sbrian#include "chap.h"
4836285Sbrian#include "async.h"
4936285Sbrian#include "throughput.h"
5036285Sbrian#include "descriptor.h"
5136285Sbrian#include "iplist.h"
5236285Sbrian#include "slcompress.h"
5336285Sbrian#include "ipcp.h"
5436285Sbrian#include "filter.h"
5536285Sbrian#include "ccp.h"
5636285Sbrian#include "link.h"
5736285Sbrian#include "physical.h"
5836285Sbrian#include "mp.h"
5936285Sbrian#include "bundle.h"
6036285Sbrian#include "chat.h"
6136285Sbrian#include "datalink.h"
6237192Sbrian#ifdef HAVE_DES
6336287Sbrian#include "chap_ms.h"
6437192Sbrian#endif
656059Samurai
6631343Sbrianstatic const char *chapcodes[] = {
6719866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
686059Samurai};
696059Samurai
7030715Sbrianstatic void
7136285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
7236285Sbrian	   const u_char * ptr, int count)
736059Samurai{
746059Samurai  int plen;
756059Samurai  struct fsmheader lh;
766059Samurai  struct mbuf *bp;
776059Samurai
7828679Sbrian  plen = sizeof(struct fsmheader) + count;
796059Samurai  lh.code = code;
806059Samurai  lh.id = id;
816059Samurai  lh.length = htons(plen);
8236285Sbrian  bp = mbuf_Alloc(plen, MB_FSM);
8330715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
846059Samurai  if (count)
8530715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
8636285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
8736285Sbrian  log_Printf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
8836285Sbrian  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
896059Samurai}
906059Samurai
9136285Sbrianvoid
9236285Sbrianchap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
936059Samurai{
9436285Sbrian  struct chap *chap = auth2chap(auth);
9513379Sphk  int len, i;
966059Samurai  char *cp;
976059Samurai
9830715Sbrian  randinit();
9936285Sbrian  cp = chap->challenge_data;
10036285Sbrian  *cp++ = chap->challenge_len = random() % 32 + 16;
10136285Sbrian  for (i = 0; i < chap->challenge_len; i++)
1026059Samurai    *cp++ = random() & 0xff;
10336285Sbrian  len = strlen(physical->dl->bundle->cfg.auth.name);
10436285Sbrian  memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
1056059Samurai  cp += len;
10636285Sbrian  ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
10736285Sbrian	     cp - chap->challenge_data);
1086059Samurai}
1096059Samurai
11030715Sbrianstatic void
11136285SbrianRecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
11236285Sbrian             struct physical *physical)
1136059Samurai{
1146059Samurai  int valsize, len;
1156059Samurai  int arglen, keylen, namelen;
1166059Samurai  char *cp, *argp, *ap, *name, *digest;
1176059Samurai  char *keyp;
11829549Sbrian  MD5_CTX MD5context;		/* context for MD5 */
1196059Samurai  char answer[100];
1206059Samurai  char cdigest[16];
12129840Sbrian#ifdef HAVE_DES
12229840Sbrian  int ix;
12329840Sbrian  MD4_CTX MD4context;		/* context for MD4 */
12429840Sbrian#endif
1256059Samurai
1266059Samurai  len = ntohs(chp->length);
12736285Sbrian  log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
1286059Samurai  arglen = len - sizeof(struct fsmheader);
12928679Sbrian  cp = (char *) MBUF_CTOP(bp);
1306059Samurai  valsize = *cp++ & 255;
1316059Samurai  name = cp + valsize;
1326059Samurai  namelen = arglen - valsize - 1;
1336059Samurai  name[namelen] = 0;
13436285Sbrian  log_Printf(LogLCP, " Valsize = %d, Name = \"%s\"\n", valsize, name);
1356059Samurai
1366059Samurai  switch (chp->code) {
1376059Samurai  case CHAP_CHALLENGE:
13836285Sbrian    keyp = bundle->cfg.auth.key;
13936285Sbrian    keylen = strlen(bundle->cfg.auth.key);
14036285Sbrian    name = bundle->cfg.auth.name;
14136285Sbrian    namelen = strlen(bundle->cfg.auth.name);
14229840Sbrian
14329840Sbrian#ifdef HAVE_DES
14436285Sbrian    if (physical->dl->chap.using_MSChap)
14529840Sbrian      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
14629840Sbrian    else
14729840Sbrian#endif
14829840Sbrian      argp = malloc(1 + valsize + namelen + 16);
14929840Sbrian
15025630Sbrian    if (argp == NULL) {
15136285Sbrian      ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14);
15225630Sbrian      return;
15325630Sbrian    }
15429840Sbrian#ifdef HAVE_DES
15536285Sbrian    if (physical->dl->chap.using_MSChap) {
15629840Sbrian      digest = argp;     /* this is the response */
15729840Sbrian      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
15830715Sbrian      memset(digest, '\0', 24);
15930715Sbrian      digest += 24;
16029840Sbrian
16129840Sbrian      ap = answer;       /* this is the challenge */
16230715Sbrian      memcpy(ap, keyp, keylen);
16329840Sbrian      ap += 2 * keylen;
16430715Sbrian      memcpy(ap, cp, valsize);
16536285Sbrian      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
16629840Sbrian      ap += valsize;
16729840Sbrian      for (ix = keylen; ix > 0 ; ix--) {
16829840Sbrian          answer[2*ix-2] = answer[ix-1];
16929840Sbrian          answer[2*ix-1] = 0;
17029840Sbrian      }
17129549Sbrian      MD4Init(&MD4context);
17229840Sbrian      MD4Update(&MD4context, answer, 2 * keylen);
17329549Sbrian      MD4Final(digest, &MD4context);
17430715Sbrian      memcpy(digest + 25, name, namelen);
17529840Sbrian      ap += 2 * keylen;
17636285Sbrian      chap_MS(digest, answer + 2 * keylen, valsize);
17736285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 24);
17836285Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
17936285Sbrian		 namelen + MS_CHAP_RESPONSE_LEN + 1);
18029549Sbrian    } else {
18129840Sbrian#endif
18229840Sbrian      digest = argp;
18329840Sbrian      *digest++ = 16;		/* value size */
18429840Sbrian      ap = answer;
18529840Sbrian      *ap++ = chp->id;
18630715Sbrian      memcpy(ap, keyp, keylen);
18729840Sbrian      ap += keylen;
18830715Sbrian      memcpy(ap, cp, valsize);
18936285Sbrian      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
19029840Sbrian      ap += valsize;
19129549Sbrian      MD5Init(&MD5context);
19229549Sbrian      MD5Update(&MD5context, answer, ap - answer);
19329549Sbrian      MD5Final(digest, &MD5context);
19436285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 16);
19530715Sbrian      memcpy(digest + 16, name, namelen);
19629840Sbrian      ap += namelen;
19729840Sbrian      /* Send answer to the peer */
19836285Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17);
19929840Sbrian#ifdef HAVE_DES
20029549Sbrian    }
20129840Sbrian#endif
20218885Sjkh    free(argp);
2036059Samurai    break;
2046059Samurai  case CHAP_RESPONSE:
20531051Sbrian    /*
20631051Sbrian     * Get a secret key corresponds to the peer
20731051Sbrian     */
20836285Sbrian    keyp = auth_GetSecret(bundle, name, namelen, physical);
2096059Samurai    if (keyp) {
2106059Samurai      /*
2116059Samurai       * Compute correct digest value
2126059Samurai       */
2136059Samurai      keylen = strlen(keyp);
2146059Samurai      ap = answer;
2156059Samurai      *ap++ = chp->id;
21630715Sbrian      memcpy(ap, keyp, keylen);
2176059Samurai      ap += keylen;
21829840Sbrian      MD5Init(&MD5context);
21929840Sbrian      MD5Update(&MD5context, answer, ap - answer);
22036285Sbrian      MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
22136285Sbrian                physical->dl->chap.challenge_len);
22229840Sbrian      MD5Final(cdigest, &MD5context);
22336285Sbrian      log_DumpBuff(LogDEBUG, "got", cp, 16);
22436285Sbrian      log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
22528679Sbrian
2266059Samurai      /*
2276059Samurai       * Compare with the response
2286059Samurai       */
22930715Sbrian      if (memcmp(cp, cdigest, 16) == 0) {
23036285Sbrian        datalink_GotAuthname(physical->dl, name, namelen);
23136285Sbrian	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10);
23236285Sbrian        if (Enabled(bundle, OPT_UTMP))
23336285Sbrian          physical_Login(physical, name);
23436285Sbrian
23536285Sbrian        if (physical->link.lcp.auth_iwait == 0)
23636285Sbrian          /*
23736285Sbrian           * Either I didn't need to authenticate, or I've already been
23836285Sbrian           * told that I got the answer right.
23936285Sbrian           */
24036285Sbrian          datalink_AuthOk(physical->dl);
24136285Sbrian
2426059Samurai	break;
2436059Samurai      }
2446059Samurai    }
24528679Sbrian
2466059Samurai    /*
2476059Samurai     * Peer is not registerd, or response digest is wrong.
2486059Samurai     */
24936285Sbrian    ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9);
25036285Sbrian    datalink_AuthNotOk(physical->dl);
2516059Samurai    break;
2526059Samurai  }
2536059Samurai}
2546059Samurai
25530715Sbrianstatic void
25636285SbrianRecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
25736285Sbrian	       struct physical *physical)
2586059Samurai{
2596059Samurai  int len;
2606059Samurai
2616059Samurai  len = ntohs(chp->length);
26236285Sbrian  log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
2636059Samurai  if (chp->code == CHAP_SUCCESS) {
26436285Sbrian    if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
26536285Sbrian      physical->link.lcp.auth_iwait = 0;
26636285Sbrian      if (physical->link.lcp.auth_ineed == 0)
26736285Sbrian        /*
26836285Sbrian         * We've succeeded in our ``login''
26936285Sbrian         * If we're not expecting  the peer to authenticate (or he already
27036285Sbrian         * has), proceed to network phase.
27136285Sbrian         */
27236285Sbrian        datalink_AuthOk(physical->dl);
2736059Samurai    }
2746059Samurai  } else {
27536285Sbrian    /* CHAP failed - it's not going to get any better */
27636285Sbrian    log_Printf(LogPHASE, "Received CHAP_FAILURE\n");
27736285Sbrian    datalink_AuthNotOk(physical->dl);
2786059Samurai  }
2796059Samurai}
2806059Samurai
2816059Samuraivoid
28236285Sbrianchap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
2836059Samurai{
28436285Sbrian  int len = mbuf_Length(bp);
2856059Samurai  struct fsmheader *chp;
2866059Samurai
2876059Samurai  if (len >= sizeof(struct fsmheader)) {
28828679Sbrian    chp = (struct fsmheader *) MBUF_CTOP(bp);
2896059Samurai    if (len >= ntohs(chp->length)) {
2906059Samurai      if (chp->code < 1 || chp->code > 4)
2916059Samurai	chp->code = 0;
29236285Sbrian      log_Printf(LogLCP, "chap_Input: %s\n", chapcodes[chp->code]);
2936059Samurai
2946059Samurai      bp->offset += sizeof(struct fsmheader);
2956059Samurai      bp->cnt -= sizeof(struct fsmheader);
2966059Samurai
2976059Samurai      switch (chp->code) {
2986735Samurai      case CHAP_RESPONSE:
29936285Sbrian	auth_StopTimer(&physical->dl->chap.auth);
3006735Samurai	/* Fall into.. */
3016059Samurai      case CHAP_CHALLENGE:
30236285Sbrian	RecvChapTalk(bundle, chp, bp, physical);
3036059Samurai	break;
3046059Samurai      case CHAP_SUCCESS:
3056059Samurai      case CHAP_FAILURE:
30636285Sbrian	RecvChapResult(bundle, chp, bp, physical);
3076059Samurai	break;
3086059Samurai      }
3096059Samurai    }
3106059Samurai  }
31136285Sbrian  mbuf_Free(bp);
3126059Samurai}
313