chap.c revision 36287
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 *
2036287Sbrian * $Id: chap.c,v 1.31 1998/05/21 21:44:25 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
3036287Sbrian#include <md4.h>
3130715Sbrian#include <md5.h>
3230715Sbrian#include <stdlib.h>
3336287Sbrian#include <string.h>
3436285Sbrian#include <termios.h>
3529840Sbrian
3630715Sbrian#include "mbuf.h"
3730715Sbrian#include "log.h"
3830715Sbrian#include "defs.h"
3930715Sbrian#include "timer.h"
406059Samurai#include "fsm.h"
416059Samurai#include "lcpproto.h"
426059Samurai#include "lcp.h"
4336285Sbrian#include "lqr.h"
446059Samurai#include "hdlc.h"
456735Samurai#include "auth.h"
4636285Sbrian#include "chap.h"
4736285Sbrian#include "async.h"
4836285Sbrian#include "throughput.h"
4936285Sbrian#include "descriptor.h"
5036285Sbrian#include "iplist.h"
5136285Sbrian#include "slcompress.h"
5236285Sbrian#include "ipcp.h"
5336285Sbrian#include "filter.h"
5436285Sbrian#include "ccp.h"
5536285Sbrian#include "link.h"
5636285Sbrian#include "physical.h"
5736285Sbrian#include "mp.h"
5836285Sbrian#include "bundle.h"
5936285Sbrian#include "chat.h"
6036285Sbrian#include "datalink.h"
6136287Sbrian#include "chap_ms.h"
626059Samurai
6331343Sbrianstatic const char *chapcodes[] = {
6419866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
656059Samurai};
666059Samurai
6730715Sbrianstatic void
6836285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
6936285Sbrian	   const u_char * ptr, int count)
706059Samurai{
716059Samurai  int plen;
726059Samurai  struct fsmheader lh;
736059Samurai  struct mbuf *bp;
746059Samurai
7528679Sbrian  plen = sizeof(struct fsmheader) + count;
766059Samurai  lh.code = code;
776059Samurai  lh.id = id;
786059Samurai  lh.length = htons(plen);
7936285Sbrian  bp = mbuf_Alloc(plen, MB_FSM);
8030715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
816059Samurai  if (count)
8230715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
8336285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
8436285Sbrian  log_Printf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
8536285Sbrian  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
866059Samurai}
876059Samurai
8836285Sbrianvoid
8936285Sbrianchap_SendChallenge(struct authinfo *auth, int chapid, struct physical *physical)
906059Samurai{
9136285Sbrian  struct chap *chap = auth2chap(auth);
9213379Sphk  int len, i;
936059Samurai  char *cp;
946059Samurai
9530715Sbrian  randinit();
9636285Sbrian  cp = chap->challenge_data;
9736285Sbrian  *cp++ = chap->challenge_len = random() % 32 + 16;
9836285Sbrian  for (i = 0; i < chap->challenge_len; i++)
996059Samurai    *cp++ = random() & 0xff;
10036285Sbrian  len = strlen(physical->dl->bundle->cfg.auth.name);
10136285Sbrian  memcpy(cp, physical->dl->bundle->cfg.auth.name, len);
1026059Samurai  cp += len;
10336285Sbrian  ChapOutput(physical, CHAP_CHALLENGE, chapid, chap->challenge_data,
10436285Sbrian	     cp - chap->challenge_data);
1056059Samurai}
1066059Samurai
10730715Sbrianstatic void
10836285SbrianRecvChapTalk(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
10936285Sbrian             struct physical *physical)
1106059Samurai{
1116059Samurai  int valsize, len;
1126059Samurai  int arglen, keylen, namelen;
1136059Samurai  char *cp, *argp, *ap, *name, *digest;
1146059Samurai  char *keyp;
11529549Sbrian  MD5_CTX MD5context;		/* context for MD5 */
1166059Samurai  char answer[100];
1176059Samurai  char cdigest[16];
11829840Sbrian#ifdef HAVE_DES
11929840Sbrian  int ix;
12029840Sbrian  MD4_CTX MD4context;		/* context for MD4 */
12129840Sbrian#endif
1226059Samurai
1236059Samurai  len = ntohs(chp->length);
12436285Sbrian  log_Printf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
1256059Samurai  arglen = len - sizeof(struct fsmheader);
12628679Sbrian  cp = (char *) MBUF_CTOP(bp);
1276059Samurai  valsize = *cp++ & 255;
1286059Samurai  name = cp + valsize;
1296059Samurai  namelen = arglen - valsize - 1;
1306059Samurai  name[namelen] = 0;
13136285Sbrian  log_Printf(LogLCP, " Valsize = %d, Name = \"%s\"\n", valsize, name);
1326059Samurai
1336059Samurai  switch (chp->code) {
1346059Samurai  case CHAP_CHALLENGE:
13536285Sbrian    keyp = bundle->cfg.auth.key;
13636285Sbrian    keylen = strlen(bundle->cfg.auth.key);
13736285Sbrian    name = bundle->cfg.auth.name;
13836285Sbrian    namelen = strlen(bundle->cfg.auth.name);
13929840Sbrian
14029840Sbrian#ifdef HAVE_DES
14136285Sbrian    if (physical->dl->chap.using_MSChap)
14229840Sbrian      argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
14329840Sbrian    else
14429840Sbrian#endif
14529840Sbrian      argp = malloc(1 + valsize + namelen + 16);
14629840Sbrian
14725630Sbrian    if (argp == NULL) {
14836285Sbrian      ChapOutput(physical, CHAP_FAILURE, chp->id, "Out of memory!", 14);
14925630Sbrian      return;
15025630Sbrian    }
15129840Sbrian#ifdef HAVE_DES
15236285Sbrian    if (physical->dl->chap.using_MSChap) {
15329840Sbrian      digest = argp;     /* this is the response */
15429840Sbrian      *digest++ = MS_CHAP_RESPONSE_LEN;   /* 49 */
15530715Sbrian      memset(digest, '\0', 24);
15630715Sbrian      digest += 24;
15729840Sbrian
15829840Sbrian      ap = answer;       /* this is the challenge */
15930715Sbrian      memcpy(ap, keyp, keylen);
16029840Sbrian      ap += 2 * keylen;
16130715Sbrian      memcpy(ap, cp, valsize);
16236285Sbrian      log_DumpBuff(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);
17130715Sbrian      memcpy(digest + 25, name, namelen);
17229840Sbrian      ap += 2 * keylen;
17336285Sbrian      chap_MS(digest, answer + 2 * keylen, valsize);
17436285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 24);
17536285Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp,
17636285Sbrian		 namelen + MS_CHAP_RESPONSE_LEN + 1);
17729549Sbrian    } else {
17829840Sbrian#endif
17929840Sbrian      digest = argp;
18029840Sbrian      *digest++ = 16;		/* value size */
18129840Sbrian      ap = answer;
18229840Sbrian      *ap++ = chp->id;
18330715Sbrian      memcpy(ap, keyp, keylen);
18429840Sbrian      ap += keylen;
18530715Sbrian      memcpy(ap, cp, valsize);
18636285Sbrian      log_DumpBuff(LogDEBUG, "recv", ap, valsize);
18729840Sbrian      ap += valsize;
18829549Sbrian      MD5Init(&MD5context);
18929549Sbrian      MD5Update(&MD5context, answer, ap - answer);
19029549Sbrian      MD5Final(digest, &MD5context);
19136285Sbrian      log_DumpBuff(LogDEBUG, "answer", digest, 16);
19230715Sbrian      memcpy(digest + 16, name, namelen);
19329840Sbrian      ap += namelen;
19429840Sbrian      /* Send answer to the peer */
19536285Sbrian      ChapOutput(physical, CHAP_RESPONSE, chp->id, argp, namelen + 17);
19629840Sbrian#ifdef HAVE_DES
19729549Sbrian    }
19829840Sbrian#endif
19918885Sjkh    free(argp);
2006059Samurai    break;
2016059Samurai  case CHAP_RESPONSE:
20231051Sbrian    /*
20331051Sbrian     * Get a secret key corresponds to the peer
20431051Sbrian     */
20536285Sbrian    keyp = auth_GetSecret(bundle, name, namelen, physical);
2066059Samurai    if (keyp) {
2076059Samurai      /*
2086059Samurai       * Compute correct digest value
2096059Samurai       */
2106059Samurai      keylen = strlen(keyp);
2116059Samurai      ap = answer;
2126059Samurai      *ap++ = chp->id;
21330715Sbrian      memcpy(ap, keyp, keylen);
2146059Samurai      ap += keylen;
21529840Sbrian      MD5Init(&MD5context);
21629840Sbrian      MD5Update(&MD5context, answer, ap - answer);
21736285Sbrian      MD5Update(&MD5context, physical->dl->chap.challenge_data + 1,
21836285Sbrian                physical->dl->chap.challenge_len);
21929840Sbrian      MD5Final(cdigest, &MD5context);
22036285Sbrian      log_DumpBuff(LogDEBUG, "got", cp, 16);
22136285Sbrian      log_DumpBuff(LogDEBUG, "expect", cdigest, 16);
22228679Sbrian
2236059Samurai      /*
2246059Samurai       * Compare with the response
2256059Samurai       */
22630715Sbrian      if (memcmp(cp, cdigest, 16) == 0) {
22736285Sbrian        datalink_GotAuthname(physical->dl, name, namelen);
22836285Sbrian	ChapOutput(physical, CHAP_SUCCESS, chp->id, "Welcome!!", 10);
22936285Sbrian        if (Enabled(bundle, OPT_UTMP))
23036285Sbrian          physical_Login(physical, name);
23136285Sbrian
23236285Sbrian        if (physical->link.lcp.auth_iwait == 0)
23336285Sbrian          /*
23436285Sbrian           * Either I didn't need to authenticate, or I've already been
23536285Sbrian           * told that I got the answer right.
23636285Sbrian           */
23736285Sbrian          datalink_AuthOk(physical->dl);
23836285Sbrian
2396059Samurai	break;
2406059Samurai      }
2416059Samurai    }
24228679Sbrian
2436059Samurai    /*
2446059Samurai     * Peer is not registerd, or response digest is wrong.
2456059Samurai     */
24636285Sbrian    ChapOutput(physical, CHAP_FAILURE, chp->id, "Invalid!!", 9);
24736285Sbrian    datalink_AuthNotOk(physical->dl);
2486059Samurai    break;
2496059Samurai  }
2506059Samurai}
2516059Samurai
25230715Sbrianstatic void
25336285SbrianRecvChapResult(struct bundle *bundle, struct fsmheader *chp, struct mbuf *bp,
25436285Sbrian	       struct physical *physical)
2556059Samurai{
2566059Samurai  int len;
2576059Samurai
2586059Samurai  len = ntohs(chp->length);
25936285Sbrian  log_Printf(LogDEBUG, "RecvChapResult: length: %d\n", len);
2606059Samurai  if (chp->code == CHAP_SUCCESS) {
26136285Sbrian    if (physical->link.lcp.auth_iwait == PROTO_CHAP) {
26236285Sbrian      physical->link.lcp.auth_iwait = 0;
26336285Sbrian      if (physical->link.lcp.auth_ineed == 0)
26436285Sbrian        /*
26536285Sbrian         * We've succeeded in our ``login''
26636285Sbrian         * If we're not expecting  the peer to authenticate (or he already
26736285Sbrian         * has), proceed to network phase.
26836285Sbrian         */
26936285Sbrian        datalink_AuthOk(physical->dl);
2706059Samurai    }
2716059Samurai  } else {
27236285Sbrian    /* CHAP failed - it's not going to get any better */
27336285Sbrian    log_Printf(LogPHASE, "Received CHAP_FAILURE\n");
27436285Sbrian    datalink_AuthNotOk(physical->dl);
2756059Samurai  }
2766059Samurai}
2776059Samurai
2786059Samuraivoid
27936285Sbrianchap_Input(struct bundle *bundle, struct mbuf *bp, struct physical *physical)
2806059Samurai{
28136285Sbrian  int len = mbuf_Length(bp);
2826059Samurai  struct fsmheader *chp;
2836059Samurai
2846059Samurai  if (len >= sizeof(struct fsmheader)) {
28528679Sbrian    chp = (struct fsmheader *) MBUF_CTOP(bp);
2866059Samurai    if (len >= ntohs(chp->length)) {
2876059Samurai      if (chp->code < 1 || chp->code > 4)
2886059Samurai	chp->code = 0;
28936285Sbrian      log_Printf(LogLCP, "chap_Input: %s\n", chapcodes[chp->code]);
2906059Samurai
2916059Samurai      bp->offset += sizeof(struct fsmheader);
2926059Samurai      bp->cnt -= sizeof(struct fsmheader);
2936059Samurai
2946059Samurai      switch (chp->code) {
2956735Samurai      case CHAP_RESPONSE:
29636285Sbrian	auth_StopTimer(&physical->dl->chap.auth);
2976735Samurai	/* Fall into.. */
2986059Samurai      case CHAP_CHALLENGE:
29936285Sbrian	RecvChapTalk(bundle, chp, bp, physical);
3006059Samurai	break;
3016059Samurai      case CHAP_SUCCESS:
3026059Samurai      case CHAP_FAILURE:
30336285Sbrian	RecvChapResult(bundle, chp, bp, physical);
3046059Samurai	break;
3056059Samurai      }
3066059Samurai    }
3076059Samurai  }
30836285Sbrian  mbuf_Free(bp);
3096059Samurai}
310