chap.c revision 38174
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 *
2038174Sbrian * $Id: chap.c,v 1.35 1998/07/28 21:54:29 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>
3238174Sbrian#include <string.h>
3337192Sbrian#endif
3430715Sbrian#include <md5.h>
3530715Sbrian#include <stdlib.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"
6036285Sbrian#include "bundle.h"
6136285Sbrian#include "chat.h"
6238174Sbrian#include "cbcp.h"
6336285Sbrian#include "datalink.h"
6437192Sbrian#ifdef HAVE_DES
6536287Sbrian#include "chap_ms.h"
6637192Sbrian#endif
676059Samurai
6831343Sbrianstatic const char *chapcodes[] = {
6919866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
706059Samurai};
716059Samurai
7230715Sbrianstatic void
7336285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
7437926Sbrian	   const u_char * ptr, int count, const char *text)
756059Samurai{
766059Samurai  int plen;
776059Samurai  struct fsmheader lh;
786059Samurai  struct mbuf *bp;
796059Samurai
8028679Sbrian  plen = sizeof(struct fsmheader) + count;
816059Samurai  lh.code = code;
826059Samurai  lh.id = id;
836059Samurai  lh.length = htons(plen);
8436285Sbrian  bp = mbuf_Alloc(plen, MB_FSM);
8530715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
866059Samurai  if (count)
8730715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
8836285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
8937926Sbrian  if (text == NULL)
9037926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
9137926Sbrian  else
9237926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
9336285Sbrian  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
946059Samurai}
956059Samurai
9636285Sbrianvoid
9736285Sbrianchap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
986059Samurai{
9936285Sbrian  struct chap *chap = auth2chap(auth);
10013379Sphk  int len, i;
1016059Samurai  char *cp;
1026059Samurai
10330715Sbrian  randinit();
10436285Sbrian  cp = chap->challenge_data;
10536285Sbrian  *cp++ = chap->challenge_len = random() % 32 + 16;
10636285Sbrian  for (i = 0; i < chap->challenge_len; i++)
1076059Samurai    *cp++ = random() & 0xff;
10836285Sbrian  len = strlen(physical->dl->bundle->cfg.auth.name);
10936285Sbrian  memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
1106059Samurai  cp += len;
11136285Sbrian  ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
11237926Sbrian	     cp - chap->challenge_data, NULL);
1136059Samurai}
1146059Samurai
11530715Sbrianstatic void
11636285SbrianRecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
11736285Sbrian             struct physical *physical)
1186059Samurai{
1196059Samurai  int valsize, len;
1206059Samurai  int arglen, keylen, namelen;
1216059Samurai  char *cp, *argp, *ap, *name, *digest;
1226059Samurai  char *keyp;
12329549Sbrian  MD5_CTX MD5context;		/* context for MD5 */
1246059Samurai  char answer[100];
1256059Samurai  char cdigest[16];
12629840Sbrian#ifdef HAVE_DES
12729840Sbrian  int ix;
12829840Sbrian  MD4_CTX MD4context;		/* context for MD4 */
12929840Sbrian#endif
1306059Samurai
1316059Samurai  len = ntohs(chp->length);
13236285Sbrian  log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
1336059Samurai  arglen = len - sizeof(struct fsmheader);
13428679Sbrian  cp = (char *) MBUF_CTOP(bp);
1356059Samurai  valsize = *cp++ & 255;
1366059Samurai  name = cp + valsize;
1376059Samurai  namelen = arglen - valsize - 1;
1386059Samurai  name[namelen] = 0;
1396059Samurai
14037926Sbrian  log_Printf(LogPHASE, "Chap Input: %s (from %s)\n",
14137926Sbrian             chapcodes[chp->code], name);
14237926Sbrian
1436059Samurai  switch (chp->code) {
1446059Samurai  case CHAP_CHALLENGE:
14536285Sbrian    keyp = bundle->cfg.auth.key;
14636285Sbrian    keylen = strlen(bundle->cfg.auth.key);
14736285Sbrian    name = bundle->cfg.auth.name;
14836285Sbrian    namelen = strlen(bundle->cfg.auth.name);
14929840Sbrian
15029840Sbrian#ifdef HAVE_DES
15136285Sbrian    if (physical->dl->chap.using_MSChap)
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) {
15837926Sbrian      ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14, NULL);
15925630Sbrian      return;
16025630Sbrian    }
16129840Sbrian#ifdef HAVE_DES
16236285Sbrian    if (physical->dl->chap.using_MSChap) {
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);
17236285Sbrian      log_DumpBuff(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;
18336285Sbrian      chap_MS(digest, answer + 2 * keylen, valsize);
18436285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 24);
18536285Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
18637926Sbrian		 namelen + MS_CHAP_RESPONSE_LEN + 1, name);
18729549Sbrian    } else {
18829840Sbrian#endif
18929840Sbrian      digest = argp;
19029840Sbrian      *digest++ = 16;		/* value size */
19129840Sbrian      ap = answer;
19229840Sbrian      *ap++ = chp->id;
19330715Sbrian      memcpy(ap, keyp, keylen);
19429840Sbrian      ap += keylen;
19530715Sbrian      memcpy(ap, cp, valsize);
19636285Sbrian      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
19729840Sbrian      ap += valsize;
19829549Sbrian      MD5Init(&MD5context);
19929549Sbrian      MD5Update(&MD5context, answer, ap - answer);
20029549Sbrian      MD5Final(digest, &MD5context);
20136285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 16);
20230715Sbrian      memcpy(digest + 16, name, namelen);
20329840Sbrian      ap += namelen;
20429840Sbrian      /* Send answer to the peer */
20537926Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17, name);
20629840Sbrian#ifdef HAVE_DES
20729549Sbrian    }
20829840Sbrian#endif
20918885Sjkh    free(argp);
21037926Sbrian    if (*name == '\0')
21137926Sbrian      log_Printf(LogWARN, "Sending empty CHAP authname!\n");
2126059Samurai    break;
2136059Samurai  case CHAP_RESPONSE:
21431051Sbrian    /*
21531051Sbrian     * Get a secret key corresponds to the peer
21631051Sbrian     */
21736285Sbrian    keyp = auth_GetSecret(bundle, name, namelen, physical);
2186059Samurai    if (keyp) {
2196059Samurai      /*
2206059Samurai       * Compute correct digest value
2216059Samurai       */
2226059Samurai      keylen = strlen(keyp);
2236059Samurai      ap = answer;
2246059Samurai      *ap++ = chp->id;
22530715Sbrian      memcpy(ap, keyp, keylen);
2266059Samurai      ap += keylen;
22729840Sbrian      MD5Init(&MD5context);
22829840Sbrian      MD5Update(&MD5context, answer, ap - answer);
22936285Sbrian      MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
23036285Sbrian                physical->dl->chap.challenge_len);
23129840Sbrian      MD5Final(cdigest, &MD5context);
23236285Sbrian      log_DumpBuff(LogDEBUG, "got", cp, 16);
23336285Sbrian      log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
23428679Sbrian
2356059Samurai      /*
2366059Samurai       * Compare with the response
2376059Samurai       */
23830715Sbrian      if (memcmp(cp, cdigest, 16) == 0) {
23936285Sbrian        datalink_GotAuthname(physical->dl, name, namelen);
24037926Sbrian	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10, NULL);
24137926Sbrian	physical->link.lcp.auth_ineed = 0;
24236285Sbrian        if (Enabled(bundle, OPT_UTMP))
24336285Sbrian          physical_Login(physical, name);
24436285Sbrian
24536285Sbrian        if (physical->link.lcp.auth_iwait == 0)
24636285Sbrian          /*
24736285Sbrian           * Either I didn't need to authenticate, or I've already been
24836285Sbrian           * told that I got the answer right.
24936285Sbrian           */
25036285Sbrian          datalink_AuthOk(physical->dl);
25136285Sbrian
2526059Samurai	break;
2536059Samurai      }
2546059Samurai    }
25528679Sbrian
2566059Samurai    /*
2576059Samurai     * Peer is not registerd, or response digest is wrong.
2586059Samurai     */
25937926Sbrian    ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9, NULL);
26036285Sbrian    datalink_AuthNotOk(physical->dl);
2616059Samurai    break;
2626059Samurai  }
2636059Samurai}
2646059Samurai
26530715Sbrianstatic void
26636285SbrianRecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
26736285Sbrian	       struct physical *physical)
2686059Samurai{
2696059Samurai  int len;
2706059Samurai
2716059Samurai  len = ntohs(chp->length);
27236285Sbrian  log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
2736059Samurai  if (chp->code == CHAP_SUCCESS) {
27436285Sbrian    if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
27536285Sbrian      physical->link.lcp.auth_iwait = 0;
27636285Sbrian      if (physical->link.lcp.auth_ineed == 0)
27736285Sbrian        /*
27836285Sbrian         * We've succeeded in our ``login''
27936285Sbrian         * If we're not expecting  the peer to authenticate (or he already
28036285Sbrian         * has), proceed to network phase.
28136285Sbrian         */
28236285Sbrian        datalink_AuthOk(physical->dl);
2836059Samurai    }
2846059Samurai  } else {
28536285Sbrian    /* CHAP failed - it's not going to get any better */
28637926Sbrian    log_Printf(LogPHASE, "Chap Input: Giving up after name/key FAILURE\n");
28736285Sbrian    datalink_AuthNotOk(physical->dl);
2886059Samurai  }
2896059Samurai}
2906059Samurai
2916059Samuraivoid
29236285Sbrianchap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
2936059Samurai{
29436285Sbrian  int len = mbuf_Length(bp);
2956059Samurai  struct fsmheader *chp;
2966059Samurai
2976059Samurai  if (len >= sizeof(struct fsmheader)) {
29828679Sbrian    chp = (struct fsmheader *) MBUF_CTOP(bp);
2996059Samurai    if (len >= ntohs(chp->length)) {
3006059Samurai      if (chp->code < 1 || chp->code > 4)
3016059Samurai	chp->code = 0;
3026059Samurai      bp->offset += sizeof(struct fsmheader);
3036059Samurai      bp->cnt -= sizeof(struct fsmheader);
3046059Samurai
3056059Samurai      switch (chp->code) {
3066735Samurai      case CHAP_RESPONSE:
30736285Sbrian	auth_StopTimer(&physical->dl->chap.auth);
3086735Samurai	/* Fall into.. */
3096059Samurai      case CHAP_CHALLENGE:
31036285Sbrian	RecvChapTalk(bundle, chp, bp, physical);
3116059Samurai	break;
3126059Samurai      case CHAP_SUCCESS:
3136059Samurai      case CHAP_FAILURE:
31437926Sbrian        log_Printf(LogPHASE, "Chap Input: %s\n", chapcodes[chp->code]);
31536285Sbrian	RecvChapResult(bundle, chp, bp, physical);
3166059Samurai	break;
3176059Samurai      }
3186059Samurai    }
3196059Samurai  }
32036285Sbrian  mbuf_Free(bp);
3216059Samurai}
322