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