chap.c revision 46686
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 *
2046686Sbrian * $Id: chap.c,v 1.49 1999/04/21 08:03:51 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
3043888Sbrian#include <errno.h>
3143888Sbrian#include <fcntl.h>
3237192Sbrian#ifdef HAVE_DES
3336287Sbrian#include <md4.h>
3437192Sbrian#endif
3530715Sbrian#include <md5.h>
3643888Sbrian#include <paths.h>
3743888Sbrian#include <signal.h>
3830715Sbrian#include <stdlib.h>
3944106Sbrian#include <string.h>
4043888Sbrian#include <sys/wait.h>
4136285Sbrian#include <termios.h>
4243888Sbrian#include <unistd.h>
4329840Sbrian
4446686Sbrian#include "layer.h"
4530715Sbrian#include "mbuf.h"
4630715Sbrian#include "log.h"
4730715Sbrian#include "defs.h"
4830715Sbrian#include "timer.h"
496059Samurai#include "fsm.h"
5046686Sbrian#include "proto.h"
516059Samurai#include "lcp.h"
5236285Sbrian#include "lqr.h"
536059Samurai#include "hdlc.h"
546735Samurai#include "auth.h"
5536285Sbrian#include "async.h"
5636285Sbrian#include "throughput.h"
5736285Sbrian#include "descriptor.h"
5843888Sbrian#include "chap.h"
5936285Sbrian#include "iplist.h"
6036285Sbrian#include "slcompress.h"
6136285Sbrian#include "ipcp.h"
6236285Sbrian#include "filter.h"
6336285Sbrian#include "ccp.h"
6436285Sbrian#include "link.h"
6536285Sbrian#include "physical.h"
6636285Sbrian#include "mp.h"
6743313Sbrian#ifndef NORADIUS
6843313Sbrian#include "radius.h"
6943313Sbrian#endif
7036285Sbrian#include "bundle.h"
7136285Sbrian#include "chat.h"
7238174Sbrian#include "cbcp.h"
7343888Sbrian#include "command.h"
7436285Sbrian#include "datalink.h"
7537192Sbrian#ifdef HAVE_DES
7636287Sbrian#include "chap_ms.h"
7737192Sbrian#endif
786059Samurai
7931343Sbrianstatic const char *chapcodes[] = {
8019866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
816059Samurai};
8243693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
836059Samurai
8430715Sbrianstatic void
8536285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
8643693Sbrian	   const u_char *ptr, int count, const char *text)
876059Samurai{
886059Samurai  int plen;
896059Samurai  struct fsmheader lh;
906059Samurai  struct mbuf *bp;
916059Samurai
9228679Sbrian  plen = sizeof(struct fsmheader) + count;
936059Samurai  lh.code = code;
946059Samurai  lh.id = id;
956059Samurai  lh.length = htons(plen);
9636285Sbrian  bp = mbuf_Alloc(plen, MB_FSM);
9730715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
986059Samurai  if (count)
9930715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
10036285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
10137926Sbrian  if (text == NULL)
10237926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
10337926Sbrian  else
10437926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
10546686Sbrian  link_PushPacket(&physical->link, bp, physical->dl->bundle,
10646686Sbrian                  PRI_LINK, PROTO_CHAP);
1076059Samurai}
1086059Samurai
10943693Sbrianstatic char *
11044123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
11144123Sbrian#ifdef HAVE_DES
11244123Sbrian                 , int lanman
11344123Sbrian#endif
11444123Sbrian                )
1156059Samurai{
11643693Sbrian  char *result, *digest;
11743693Sbrian  size_t nlen, klen;
11843693Sbrian
11943693Sbrian  nlen = strlen(name);
12043693Sbrian  klen = strlen(key);
12143693Sbrian
12243693Sbrian#ifdef HAVE_DES
12344106Sbrian  if (type == 0x80) {
12443693Sbrian    char expkey[AUTHLEN << 2];
12543693Sbrian    MD4_CTX MD4context;
12643693Sbrian    int f;
12743693Sbrian
12843693Sbrian    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
12943693Sbrian      return result;
13043693Sbrian
13144106Sbrian    digest = result;					/* the response */
13244106Sbrian    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
13344106Sbrian    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
13444106Sbrian    if (lanman) {
13544106Sbrian      memset(digest + 24, '\0', 25);
13644106Sbrian      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
13744106Sbrian    } else {
13844106Sbrian      memset(digest, '\0', 25);
13944106Sbrian      digest += 24;
14043693Sbrian
14144106Sbrian      for (f = 0; f < klen; f++) {
14244106Sbrian        expkey[2*f] = key[f];
14344106Sbrian        expkey[2*f+1] = '\0';
14444106Sbrian      }
14544106Sbrian      /*
14644106Sbrian       *           -----------
14744106Sbrian       * expkey = | k\0e\0y\0 |
14844106Sbrian       *           -----------
14944106Sbrian       */
15044106Sbrian      MD4Init(&MD4context);
15144106Sbrian      MD4Update(&MD4context, expkey, klen << 1);
15244106Sbrian      MD4Final(digest, &MD4context);
15344106Sbrian
15444106Sbrian      /*
15544106Sbrian       *           ---- -------- ---------------- ------- ------
15644106Sbrian       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
15744106Sbrian       *           ---- -------- ---------------- ------- ------
15844106Sbrian       */
15944106Sbrian      mschap_NT(digest, challenge + 1);
16043693Sbrian    }
16143693Sbrian    /*
16244106Sbrian     *           ---- -------- ------------- ----- ------
16344106Sbrian     *          |    |  struct MS_ChapResponse24  |      |
16444106Sbrian     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
16544106Sbrian     *           ---- -------- ------------- ----- ------
16644106Sbrian     * where only one of LANMan & NT digest are set.
16743693Sbrian     */
16843693Sbrian  } else
16943693Sbrian#endif
17043693Sbrian  if ((result = malloc(nlen + 17)) != NULL) {
17143693Sbrian    /* Normal MD5 stuff */
17243693Sbrian    MD5_CTX MD5context;
17343693Sbrian
17443693Sbrian    digest = result;
17543693Sbrian    *digest++ = 16;				/* value size */
17643693Sbrian
17743693Sbrian    MD5Init(&MD5context);
17843693Sbrian    MD5Update(&MD5context, &id, 1);
17943693Sbrian    MD5Update(&MD5context, key, klen);
18043693Sbrian    MD5Update(&MD5context, challenge + 1, *challenge);
18143693Sbrian    MD5Final(digest, &MD5context);
18243693Sbrian
18343693Sbrian    memcpy(digest + 16, name, nlen);
18443693Sbrian    /*
18543693Sbrian     *           ---- -------- ------
18643693Sbrian     * result = | 16 | digest | name |
18743693Sbrian     *           ---- -------- ------
18843693Sbrian     */
18943693Sbrian  }
19043693Sbrian
19143693Sbrian  return result;
19243693Sbrian}
19343693Sbrian
19443693Sbrianstatic void
19543888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name)
19643888Sbrian{
19743888Sbrian  char *argv[MAXARGS], *nargv[MAXARGS];
19843888Sbrian  int argc, fd;
19943888Sbrian  int in[2], out[2];
20043888Sbrian
20143888Sbrian  if (chap->child.fd != -1) {
20243888Sbrian    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
20343888Sbrian    return;
20443888Sbrian  }
20543888Sbrian
20643888Sbrian  if (pipe(in) == -1) {
20743888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
20843888Sbrian    return;
20943888Sbrian  }
21043888Sbrian
21143888Sbrian  if (pipe(out) == -1) {
21243888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21343888Sbrian    close(in[0]);
21443888Sbrian    close(in[1]);
21543888Sbrian    return;
21643888Sbrian  }
21743888Sbrian
21843888Sbrian  switch ((chap->child.pid = fork())) {
21943888Sbrian    case -1:
22043888Sbrian      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
22143888Sbrian      close(in[0]);
22243888Sbrian      close(in[1]);
22343888Sbrian      close(out[0]);
22443888Sbrian      close(out[1]);
22543888Sbrian      chap->child.pid = 0;
22643888Sbrian      return;
22743888Sbrian
22843888Sbrian    case 0:
22943888Sbrian      timer_TermService();
23043888Sbrian      close(in[1]);
23143888Sbrian      close(out[0]);
23243888Sbrian      if (out[1] == STDIN_FILENO) {
23343888Sbrian        fd = dup(out[1]);
23443888Sbrian        close(out[1]);
23543888Sbrian        out[1] = fd;
23643888Sbrian      }
23743888Sbrian      dup2(in[0], STDIN_FILENO);
23843888Sbrian      dup2(out[1], STDOUT_FILENO);
23943888Sbrian      if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
24043888Sbrian        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
24143888Sbrian                  _PATH_DEVNULL, strerror(errno));
24243888Sbrian        exit(1);
24343888Sbrian      }
24443888Sbrian      dup2(fd, STDERR_FILENO);
24543888Sbrian      fcntl(3, F_SETFD, 1);		/* Set close-on-exec flag */
24643888Sbrian
24743888Sbrian      setuid(geteuid());
24843888Sbrian      argc = command_Interpret(prog, strlen(prog), argv);
24943888Sbrian      command_Expand(nargv, argc, (char const *const *)argv,
25043888Sbrian                     chap->auth.physical->dl->bundle, 0);
25143888Sbrian      execvp(nargv[0], nargv);
25243888Sbrian
25343888Sbrian      log_Printf(LogWARN, "exec() of %s failed: %s\n",
25443888Sbrian                nargv[0], strerror(errno));
25543888Sbrian      exit(255);
25643888Sbrian
25743888Sbrian    default:
25843888Sbrian      close(in[0]);
25943888Sbrian      close(out[1]);
26043888Sbrian      chap->child.fd = out[0];
26143888Sbrian      chap->child.buf.len = 0;
26243888Sbrian      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
26343888Sbrian      write(in[1], "\n", 1);
26445907Sbrian      write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
26543888Sbrian      write(in[1], "\n", 1);
26643888Sbrian      write(in[1], name, strlen(name));
26743888Sbrian      write(in[1], "\n", 1);
26843888Sbrian      close(in[1]);
26943888Sbrian      break;
27043888Sbrian  }
27143888Sbrian}
27243888Sbrian
27343888Sbrianstatic void
27443888Sbrianchap_Cleanup(struct chap *chap, int sig)
27543888Sbrian{
27643888Sbrian  if (chap->child.pid) {
27743888Sbrian    int status;
27843888Sbrian
27943888Sbrian    close(chap->child.fd);
28043888Sbrian    chap->child.fd = -1;
28143888Sbrian    if (sig)
28243888Sbrian      kill(chap->child.pid, SIGTERM);
28343888Sbrian    chap->child.pid = 0;
28443888Sbrian    chap->child.buf.len = 0;
28543888Sbrian
28643888Sbrian    if (wait(&status) == -1)
28743888Sbrian      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
28843888Sbrian    else if (WIFSIGNALED(status))
28943888Sbrian      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
29043888Sbrian    else if (WIFEXITED(status) && WEXITSTATUS(status))
29143888Sbrian      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
29243888Sbrian  }
29345907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
29444123Sbrian#ifdef HAVE_DES
29544106Sbrian  chap->peertries = 0;
29644123Sbrian#endif
29743888Sbrian}
29843888Sbrian
29943888Sbrianstatic void
30044123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type
30144123Sbrian#ifdef HAVE_DES
30244123Sbrian             , int lm
30344123Sbrian#endif
30444123Sbrian            )
30543888Sbrian{
30644106Sbrian  u_char *ans;
30743888Sbrian
30845907Sbrian  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
30944123Sbrian#ifdef HAVE_DES
31044123Sbrian                         , lm
31144123Sbrian#endif
31244123Sbrian                        );
31343888Sbrian
31443888Sbrian  if (ans) {
31543888Sbrian    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
31643888Sbrian               ans, *ans + 1 + strlen(name), name);
31744123Sbrian#ifdef HAVE_DES
31844106Sbrian    chap->NTRespSent = !lm;
31944123Sbrian#endif
32043888Sbrian    free(ans);
32143888Sbrian  } else
32243888Sbrian    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
32343888Sbrian               "Out of memory!", 14, NULL);
32443888Sbrian}
32543888Sbrian
32643888Sbrianstatic int
32743888Sbrianchap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
32843888Sbrian{
32943888Sbrian  struct chap *chap = descriptor2chap(d);
33043888Sbrian
33143888Sbrian  if (r && chap && chap->child.fd != -1) {
33243888Sbrian    FD_SET(chap->child.fd, r);
33343888Sbrian    if (*n < chap->child.fd + 1)
33443888Sbrian      *n = chap->child.fd + 1;
33543888Sbrian    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
33643888Sbrian    return 1;
33743888Sbrian  }
33843888Sbrian
33943888Sbrian  return 0;
34043888Sbrian}
34143888Sbrian
34243888Sbrianstatic int
34343888Sbrianchap_IsSet(struct descriptor *d, const fd_set *fdset)
34443888Sbrian{
34543888Sbrian  struct chap *chap = descriptor2chap(d);
34643888Sbrian
34743888Sbrian  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
34843888Sbrian}
34943888Sbrian
35043888Sbrianstatic void
35143888Sbrianchap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
35243888Sbrian{
35343888Sbrian  struct chap *chap = descriptor2chap(d);
35443888Sbrian  int got;
35543888Sbrian
35643888Sbrian  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
35743888Sbrian             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
35843888Sbrian  if (got == -1) {
35943888Sbrian    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
36043888Sbrian    chap_Cleanup(chap, SIGTERM);
36143888Sbrian  } else if (got == 0) {
36243888Sbrian    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
36343888Sbrian    chap_Cleanup(chap, SIGTERM);
36443888Sbrian  } else {
36543888Sbrian    char *name, *key, *end;
36643888Sbrian
36743888Sbrian    chap->child.buf.len += got;
36843888Sbrian    chap->child.buf.ptr[chap->child.buf.len] = '\0';
36943888Sbrian    name = chap->child.buf.ptr;
37043888Sbrian    name += strspn(name, " \t");
37143888Sbrian    if ((key = strchr(name, '\n')) == NULL)
37243888Sbrian      end = NULL;
37343888Sbrian    else
37443888Sbrian      end = strchr(++key, '\n');
37543888Sbrian
37643888Sbrian    if (end == NULL) {
37743888Sbrian      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
37843888Sbrian        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
37943888Sbrian        chap_Cleanup(chap, SIGTERM);
38043888Sbrian      }
38143888Sbrian    } else {
38244123Sbrian#ifdef HAVE_DES
38344106Sbrian      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
38444106Sbrian                   ((chap->NTRespSent &&
38544106Sbrian                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
38644106Sbrian                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
38744123Sbrian#endif
38844106Sbrian
38943888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
39043888Sbrian        *end-- = '\0';
39143888Sbrian      end = key - 1;
39243888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
39343888Sbrian        *end-- = '\0';
39443888Sbrian      key += strspn(key, " \t");
39543888Sbrian
39644123Sbrian      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
39744123Sbrian#ifdef HAVE_DES
39844123Sbrian                   , lanman
39944123Sbrian#endif
40044123Sbrian                  );
40143888Sbrian      chap_Cleanup(chap, 0);
40243888Sbrian    }
40343888Sbrian  }
40443888Sbrian}
40543888Sbrian
40643888Sbrianstatic int
40743888Sbrianchap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
40843888Sbrian{
40943888Sbrian  /* We never want to write here ! */
41043888Sbrian  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
41143888Sbrian  return 0;
41243888Sbrian}
41343888Sbrian
41443888Sbrianstatic void
41543693Sbrianchap_Challenge(struct authinfo *authp)
41643693Sbrian{
41743693Sbrian  struct chap *chap = auth2chap(authp);
41813379Sphk  int len, i;
4196059Samurai  char *cp;
4206059Samurai
42143888Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
42243401Sbrian
42345907Sbrian  if (!*chap->challenge.local) {
42443888Sbrian    randinit();
42545907Sbrian    cp = chap->challenge.local;
42643888Sbrian
42743313Sbrian#ifndef NORADIUS
42843888Sbrian    if (*authp->physical->dl->bundle->radius.cfg.file) {
42943888Sbrian      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
43043888Sbrian      *cp++ = 16;
43143888Sbrian      for (i = 0; i < 16; i++)
43243888Sbrian        *cp++ = (random() % 10) + '0';
43343888Sbrian    } else
43443313Sbrian#endif
43543888Sbrian    {
43644106Sbrian#ifdef HAVE_DES
43744106Sbrian      if (authp->physical->link.lcp.want_authtype == 0x80)
43844106Sbrian        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
43944106Sbrian      else
44044106Sbrian#endif
44144106Sbrian        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
44245907Sbrian      for (i = 0; i < *chap->challenge.local; i++)
44343888Sbrian        *cp++ = random() & 0xff;
44443888Sbrian    }
44543888Sbrian    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
44643313Sbrian  }
44745907Sbrian  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge.local,
44845907Sbrian	     1 + *chap->challenge.local + len, NULL);
4496059Samurai}
4506059Samurai
45130715Sbrianstatic void
45243693Sbrianchap_Success(struct authinfo *authp)
4536059Samurai{
45443693Sbrian  datalink_GotAuthname(authp->physical->dl, authp->in.name);
45543693Sbrian  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
45643693Sbrian  authp->physical->link.lcp.auth_ineed = 0;
45743693Sbrian  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
45843693Sbrian    physical_Login(authp->physical, authp->in.name);
4596059Samurai
46043693Sbrian  if (authp->physical->link.lcp.auth_iwait == 0)
46143693Sbrian    /*
46243693Sbrian     * Either I didn't need to authenticate, or I've already been
46343693Sbrian     * told that I got the answer right.
46443693Sbrian     */
46543693Sbrian    datalink_AuthOk(authp->physical->dl);
46643693Sbrian}
4676059Samurai
46843693Sbrianstatic void
46943693Sbrianchap_Failure(struct authinfo *authp)
47043693Sbrian{
47143693Sbrian  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
47243693Sbrian  datalink_AuthNotOk(authp->physical->dl);
47343693Sbrian}
47437926Sbrian
47544106Sbrianstatic int
47644123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
47744123Sbrian#ifdef HAVE_DES
47844123Sbrian         , int lm
47944123Sbrian#endif
48044123Sbrian        )
48144106Sbrian{
48244106Sbrian  if (mylen != hislen)
48344106Sbrian    return 0;
48444123Sbrian#ifdef HAVE_DES
48544106Sbrian  else if (type == 0x80) {
48644106Sbrian    int off = lm ? 0 : 24;
48744106Sbrian
48844106Sbrian    if (memcmp(myans + off, hisans + off, 24))
48944106Sbrian      return 0;
49044123Sbrian  }
49144123Sbrian#endif
49244123Sbrian  else if (memcmp(myans, hisans, mylen))
49344106Sbrian    return 0;
49444106Sbrian
49544106Sbrian  return 1;
49644106Sbrian}
49744106Sbrian
49844123Sbrian#ifdef HAVE_DES
49944106Sbrianstatic int
50044106Sbrianchap_HaveAnotherGo(struct chap *chap)
50144106Sbrian{
50244106Sbrian  if (++chap->peertries < 3) {
50344106Sbrian    /* Give the peer another shot */
50445907Sbrian    *chap->challenge.local = '\0';
50544106Sbrian    chap_Challenge(&chap->auth);
50644106Sbrian    return 1;
50744106Sbrian  }
50844106Sbrian
50944106Sbrian  return 0;
51044106Sbrian}
51144123Sbrian#endif
51244106Sbrian
51343693Sbrianvoid
51443693Sbrianchap_Init(struct chap *chap, struct physical *p)
51543693Sbrian{
51643888Sbrian  chap->desc.type = CHAP_DESCRIPTOR;
51743888Sbrian  chap->desc.UpdateSet = chap_UpdateSet;
51843888Sbrian  chap->desc.IsSet = chap_IsSet;
51943888Sbrian  chap->desc.Read = chap_Read;
52043888Sbrian  chap->desc.Write = chap_Write;
52143888Sbrian  chap->child.pid = 0;
52243888Sbrian  chap->child.fd = -1;
52343693Sbrian  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
52445907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
52544123Sbrian#ifdef HAVE_DES
52644106Sbrian  chap->NTRespSent = 0;
52744106Sbrian  chap->peertries = 0;
52844123Sbrian#endif
52943693Sbrian}
53029840Sbrian
53143693Sbrianvoid
53243888Sbrianchap_ReInit(struct chap *chap)
53343888Sbrian{
53443888Sbrian  chap_Cleanup(chap, SIGTERM);
53543888Sbrian}
53643888Sbrian
53746686Sbrianstruct mbuf *
53846686Sbrianchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
53943693Sbrian{
54046686Sbrian  struct physical *p = link2physical(l);
54143693Sbrian  struct chap *chap = &p->dl->chap;
54244106Sbrian  char *name, *key, *ans;
54344123Sbrian  int len, nlen;
54445907Sbrian  u_char alen, end;
54544123Sbrian#ifdef HAVE_DES
54644123Sbrian  int lanman;
54744123Sbrian#endif
54829840Sbrian
54946686Sbrian  if (p == NULL) {
55046686Sbrian    log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
55146686Sbrian    mbuf_Free(bp);
55246686Sbrian    return NULL;
55346686Sbrian  }
55446686Sbrian
55546686Sbrian  if (bundle_Phase(bundle) != PHASE_NETWORK &&
55646686Sbrian      bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
55745220Sbrian    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
55845220Sbrian    mbuf_Free(bp);
55946686Sbrian    return NULL;
56045220Sbrian  }
56145220Sbrian
56244159Sbrian  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
56344159Sbrian      ntohs(chap->auth.in.hdr.length) == 0)
56444159Sbrian    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
56543693Sbrian  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
56643693Sbrian    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
56743693Sbrian               chap->auth.in.hdr.code);
56843693Sbrian  else {
56943693Sbrian    len = mbuf_Length(bp);
57043693Sbrian    ans = NULL;
57143693Sbrian
57243693Sbrian    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
57343693Sbrian        chap->auth.id != chap->auth.in.hdr.id &&
57446686Sbrian        Enabled(bundle, OPT_IDCHECK)) {
57543693Sbrian      /* Wrong conversation dude ! */
57643693Sbrian      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
57743693Sbrian                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
57843693Sbrian                 chap->auth.id);
57943693Sbrian      mbuf_Free(bp);
58046686Sbrian      return NULL;
58125630Sbrian    }
58243693Sbrian    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
58329840Sbrian
58444123Sbrian#ifdef HAVE_DES
58544106Sbrian    lanman = 0;
58644123Sbrian#endif
58743693Sbrian    switch (chap->auth.in.hdr.code) {
58843693Sbrian      case CHAP_CHALLENGE:
58944106Sbrian        bp = mbuf_Read(bp, &alen, 1);
59044106Sbrian        len -= alen + 1;
59143693Sbrian        if (len < 0) {
59243693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
59343693Sbrian          mbuf_Free(bp);
59446686Sbrian          return NULL;
59543693Sbrian        }
59645907Sbrian        *chap->challenge.peer = alen;
59745907Sbrian        bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
59843693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
59944123Sbrian#ifdef HAVE_DES
60044106Sbrian        lanman = p->link.lcp.his_authtype == 0x80 &&
60144106Sbrian                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
60244106Sbrian                  !IsAccepted(p->link.lcp.cfg.chap80nt));
60344123Sbrian#endif
60443693Sbrian        break;
60543313Sbrian
60643693Sbrian      case CHAP_RESPONSE:
60743693Sbrian        auth_StopTimer(&chap->auth);
60843693Sbrian        bp = mbuf_Read(bp, &alen, 1);
60943693Sbrian        len -= alen + 1;
61043693Sbrian        if (len < 0) {
61143693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
61243693Sbrian          mbuf_Free(bp);
61346686Sbrian          return NULL;
61443693Sbrian        }
61543693Sbrian        if ((ans = malloc(alen + 2)) == NULL) {
61643693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
61743693Sbrian          mbuf_Free(bp);
61846686Sbrian          return NULL;
61943693Sbrian        }
62043693Sbrian        *ans = chap->auth.id;
62143693Sbrian        bp = mbuf_Read(bp, ans + 1, alen);
62243693Sbrian        ans[alen+1] = '\0';
62343693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
62444123Sbrian#ifdef HAVE_DES
62544106Sbrian        lanman = alen == 49 && ans[alen] == 0;
62644123Sbrian#endif
62743693Sbrian        break;
62843313Sbrian
62943693Sbrian      case CHAP_SUCCESS:
63043693Sbrian      case CHAP_FAILURE:
63143693Sbrian        /* chap->auth.in.name is already set up at CHALLENGE time */
63243693Sbrian        if ((ans = malloc(len + 1)) == NULL) {
63343693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
63443693Sbrian          mbuf_Free(bp);
63546686Sbrian          return NULL;
63643693Sbrian        }
63743693Sbrian        bp = mbuf_Read(bp, ans, len);
63843693Sbrian        ans[len] = '\0';
63943693Sbrian        break;
64043401Sbrian    }
64136285Sbrian
64243693Sbrian    switch (chap->auth.in.hdr.code) {
64343693Sbrian      case CHAP_CHALLENGE:
64443693Sbrian      case CHAP_RESPONSE:
64543693Sbrian        if (*chap->auth.in.name)
64644106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
64744106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
64844106Sbrian                     chap->auth.in.name,
64944123Sbrian#ifdef HAVE_DES
65044106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
65144123Sbrian                     " - lanman" :
65244123Sbrian#endif
65344123Sbrian                     "");
65443693Sbrian        else
65544106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
65644106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
65744123Sbrian#ifdef HAVE_DES
65844106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
65944123Sbrian                     " - lanman" :
66044123Sbrian#endif
66144123Sbrian                     "");
66243693Sbrian        break;
66336285Sbrian
66443693Sbrian      case CHAP_SUCCESS:
66543693Sbrian      case CHAP_FAILURE:
66643693Sbrian        if (*ans)
66743693Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
66843693Sbrian                     chapcodes[chap->auth.in.hdr.code], ans);
66943693Sbrian        else
67043693Sbrian          log_Printf(LogPHASE, "Chap Input: %s\n",
67143693Sbrian                     chapcodes[chap->auth.in.hdr.code]);
67243693Sbrian        break;
6736059Samurai    }
6746059Samurai
67543693Sbrian    switch (chap->auth.in.hdr.code) {
67643693Sbrian      case CHAP_CHALLENGE:
67746686Sbrian        if (*bundle->cfg.auth.key == '!')
67846686Sbrian          chap_StartChild(chap, bundle->cfg.auth.key + 1,
67946686Sbrian                          bundle->cfg.auth.name);
68043888Sbrian        else
68146686Sbrian          chap_Respond(chap, bundle->cfg.auth.name,
68246686Sbrian                       bundle->cfg.auth.key, p->link.lcp.his_authtype
68344123Sbrian#ifdef HAVE_DES
68444123Sbrian                       , lanman
68544123Sbrian#endif
68644123Sbrian                      );
68743693Sbrian        break;
6886059Samurai
68943693Sbrian      case CHAP_RESPONSE:
69043693Sbrian        name = chap->auth.in.name;
69143693Sbrian        nlen = strlen(name);
69243693Sbrian#ifndef NORADIUS
69346686Sbrian        if (*bundle->radius.cfg.file) {
69445907Sbrian          end = chap->challenge.local[*chap->challenge.local+1];
69545907Sbrian          chap->challenge.local[*chap->challenge.local+1] = '\0';
69646686Sbrian          radius_Authenticate(&bundle->radius, &chap->auth,
69745907Sbrian                              chap->auth.in.name, ans,
69845907Sbrian                              chap->challenge.local + 1);
69945907Sbrian          chap->challenge.local[*chap->challenge.local+1] = end;
70043693Sbrian        } else
70143693Sbrian#endif
70243693Sbrian        {
70346686Sbrian          key = auth_GetSecret(bundle, name, nlen, p);
70443693Sbrian          if (key) {
70544106Sbrian            char *myans;
70644123Sbrian#ifdef HAVE_DES
70744106Sbrian            if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
70844106Sbrian              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
70944106Sbrian              if (chap_HaveAnotherGo(chap))
71044106Sbrian                break;
71143693Sbrian              key = NULL;
71244122Sbrian            } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
71344122Sbrian                       p->link.lcp.want_authtype == 0x80) {
71444106Sbrian              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
71544106Sbrian              if (chap_HaveAnotherGo(chap))
71644106Sbrian                break;
71744106Sbrian              key = NULL;
71844123Sbrian            } else
71944123Sbrian#endif
72044123Sbrian            {
72144106Sbrian              myans = chap_BuildAnswer(name, key, chap->auth.id,
72245907Sbrian                                       chap->challenge.local,
72344123Sbrian                                       p->link.lcp.want_authtype
72444123Sbrian#ifdef HAVE_DES
72544123Sbrian                                       , lanman
72644123Sbrian#endif
72744123Sbrian                                      );
72844106Sbrian              if (myans == NULL)
72943693Sbrian                key = NULL;
73044106Sbrian              else {
73144123Sbrian                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
73244123Sbrian                              ans + 1, alen
73344123Sbrian#ifdef HAVE_DES
73444123Sbrian                              , lanman
73544123Sbrian#endif
73644123Sbrian                             ))
73744106Sbrian                  key = NULL;
73844106Sbrian                free(myans);
73944106Sbrian              }
74043693Sbrian            }
74143693Sbrian          }
7426059Samurai
74343693Sbrian          if (key)
74443693Sbrian            chap_Success(&chap->auth);
74543693Sbrian          else
74643693Sbrian            chap_Failure(&chap->auth);
74743693Sbrian        }
7486059Samurai
74943693Sbrian        break;
7506059Samurai
7516059Samurai      case CHAP_SUCCESS:
75243693Sbrian        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
75343693Sbrian          p->link.lcp.auth_iwait = 0;
75443693Sbrian          if (p->link.lcp.auth_ineed == 0)
75543693Sbrian            /*
75643693Sbrian             * We've succeeded in our ``login''
75743693Sbrian             * If we're not expecting  the peer to authenticate (or he already
75843693Sbrian             * has), proceed to network phase.
75943693Sbrian             */
76043693Sbrian            datalink_AuthOk(p->dl);
76143693Sbrian        }
76243693Sbrian        break;
76343693Sbrian
7646059Samurai      case CHAP_FAILURE:
76543693Sbrian        datalink_AuthNotOk(p->dl);
76643693Sbrian        break;
7676059Samurai    }
76843693Sbrian    free(ans);
7696059Samurai  }
77043693Sbrian
77136285Sbrian  mbuf_Free(bp);
77246686Sbrian  return NULL;
7736059Samurai}
774