chap.c revision 45220
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 *
2045220Sbrian * $Id: chap.c,v 1.47 1999/02/20 01:12:45 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
4430715Sbrian#include "mbuf.h"
4530715Sbrian#include "log.h"
4630715Sbrian#include "defs.h"
4730715Sbrian#include "timer.h"
486059Samurai#include "fsm.h"
496059Samurai#include "lcpproto.h"
506059Samurai#include "lcp.h"
5136285Sbrian#include "lqr.h"
526059Samurai#include "hdlc.h"
536735Samurai#include "auth.h"
5436285Sbrian#include "async.h"
5536285Sbrian#include "throughput.h"
5636285Sbrian#include "descriptor.h"
5743888Sbrian#include "chap.h"
5836285Sbrian#include "iplist.h"
5936285Sbrian#include "slcompress.h"
6036285Sbrian#include "ipcp.h"
6136285Sbrian#include "filter.h"
6236285Sbrian#include "ccp.h"
6336285Sbrian#include "link.h"
6436285Sbrian#include "physical.h"
6536285Sbrian#include "mp.h"
6643313Sbrian#ifndef NORADIUS
6743313Sbrian#include "radius.h"
6843313Sbrian#endif
6936285Sbrian#include "bundle.h"
7036285Sbrian#include "chat.h"
7138174Sbrian#include "cbcp.h"
7243888Sbrian#include "command.h"
7336285Sbrian#include "datalink.h"
7437192Sbrian#ifdef HAVE_DES
7536287Sbrian#include "chap_ms.h"
7637192Sbrian#endif
776059Samurai
7831343Sbrianstatic const char *chapcodes[] = {
7919866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
806059Samurai};
8143693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
826059Samurai
8330715Sbrianstatic void
8436285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
8543693Sbrian	   const u_char *ptr, int count, const char *text)
866059Samurai{
876059Samurai  int plen;
886059Samurai  struct fsmheader lh;
896059Samurai  struct mbuf *bp;
906059Samurai
9128679Sbrian  plen = sizeof(struct fsmheader) + count;
926059Samurai  lh.code = code;
936059Samurai  lh.id = id;
946059Samurai  lh.length = htons(plen);
9536285Sbrian  bp = mbuf_Alloc(plen, MB_FSM);
9630715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
976059Samurai  if (count)
9830715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
9936285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
10037926Sbrian  if (text == NULL)
10137926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
10237926Sbrian  else
10337926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
10436285Sbrian  hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
1056059Samurai}
1066059Samurai
10743693Sbrianstatic char *
10844123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
10944123Sbrian#ifdef HAVE_DES
11044123Sbrian                 , int lanman
11144123Sbrian#endif
11244123Sbrian                )
1136059Samurai{
11443693Sbrian  char *result, *digest;
11543693Sbrian  size_t nlen, klen;
11643693Sbrian
11743693Sbrian  nlen = strlen(name);
11843693Sbrian  klen = strlen(key);
11943693Sbrian
12043693Sbrian#ifdef HAVE_DES
12144106Sbrian  if (type == 0x80) {
12243693Sbrian    char expkey[AUTHLEN << 2];
12343693Sbrian    MD4_CTX MD4context;
12443693Sbrian    int f;
12543693Sbrian
12643693Sbrian    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
12743693Sbrian      return result;
12843693Sbrian
12944106Sbrian    digest = result;					/* the response */
13044106Sbrian    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
13144106Sbrian    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
13244106Sbrian    if (lanman) {
13344106Sbrian      memset(digest + 24, '\0', 25);
13444106Sbrian      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
13544106Sbrian    } else {
13644106Sbrian      memset(digest, '\0', 25);
13744106Sbrian      digest += 24;
13843693Sbrian
13944106Sbrian      for (f = 0; f < klen; f++) {
14044106Sbrian        expkey[2*f] = key[f];
14144106Sbrian        expkey[2*f+1] = '\0';
14244106Sbrian      }
14344106Sbrian      /*
14444106Sbrian       *           -----------
14544106Sbrian       * expkey = | k\0e\0y\0 |
14644106Sbrian       *           -----------
14744106Sbrian       */
14844106Sbrian      MD4Init(&MD4context);
14944106Sbrian      MD4Update(&MD4context, expkey, klen << 1);
15044106Sbrian      MD4Final(digest, &MD4context);
15144106Sbrian
15244106Sbrian      /*
15344106Sbrian       *           ---- -------- ---------------- ------- ------
15444106Sbrian       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
15544106Sbrian       *           ---- -------- ---------------- ------- ------
15644106Sbrian       */
15744106Sbrian      mschap_NT(digest, challenge + 1);
15843693Sbrian    }
15943693Sbrian    /*
16044106Sbrian     *           ---- -------- ------------- ----- ------
16144106Sbrian     *          |    |  struct MS_ChapResponse24  |      |
16244106Sbrian     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
16344106Sbrian     *           ---- -------- ------------- ----- ------
16444106Sbrian     * where only one of LANMan & NT digest are set.
16543693Sbrian     */
16643693Sbrian  } else
16743693Sbrian#endif
16843693Sbrian  if ((result = malloc(nlen + 17)) != NULL) {
16943693Sbrian    /* Normal MD5 stuff */
17043693Sbrian    MD5_CTX MD5context;
17143693Sbrian
17243693Sbrian    digest = result;
17343693Sbrian    *digest++ = 16;				/* value size */
17443693Sbrian
17543693Sbrian    MD5Init(&MD5context);
17643693Sbrian    MD5Update(&MD5context, &id, 1);
17743693Sbrian    MD5Update(&MD5context, key, klen);
17843693Sbrian    MD5Update(&MD5context, challenge + 1, *challenge);
17943693Sbrian    MD5Final(digest, &MD5context);
18043693Sbrian
18143693Sbrian    memcpy(digest + 16, name, nlen);
18243693Sbrian    /*
18343693Sbrian     *           ---- -------- ------
18443693Sbrian     * result = | 16 | digest | name |
18543693Sbrian     *           ---- -------- ------
18643693Sbrian     */
18743693Sbrian  }
18843693Sbrian
18943693Sbrian  return result;
19043693Sbrian}
19143693Sbrian
19243693Sbrianstatic void
19343888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name)
19443888Sbrian{
19543888Sbrian  char *argv[MAXARGS], *nargv[MAXARGS];
19643888Sbrian  int argc, fd;
19743888Sbrian  int in[2], out[2];
19843888Sbrian
19943888Sbrian  if (chap->child.fd != -1) {
20043888Sbrian    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
20143888Sbrian    return;
20243888Sbrian  }
20343888Sbrian
20443888Sbrian  if (pipe(in) == -1) {
20543888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
20643888Sbrian    return;
20743888Sbrian  }
20843888Sbrian
20943888Sbrian  if (pipe(out) == -1) {
21043888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21143888Sbrian    close(in[0]);
21243888Sbrian    close(in[1]);
21343888Sbrian    return;
21443888Sbrian  }
21543888Sbrian
21643888Sbrian  switch ((chap->child.pid = fork())) {
21743888Sbrian    case -1:
21843888Sbrian      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
21943888Sbrian      close(in[0]);
22043888Sbrian      close(in[1]);
22143888Sbrian      close(out[0]);
22243888Sbrian      close(out[1]);
22343888Sbrian      chap->child.pid = 0;
22443888Sbrian      return;
22543888Sbrian
22643888Sbrian    case 0:
22743888Sbrian      timer_TermService();
22843888Sbrian      close(in[1]);
22943888Sbrian      close(out[0]);
23043888Sbrian      if (out[1] == STDIN_FILENO) {
23143888Sbrian        fd = dup(out[1]);
23243888Sbrian        close(out[1]);
23343888Sbrian        out[1] = fd;
23443888Sbrian      }
23543888Sbrian      dup2(in[0], STDIN_FILENO);
23643888Sbrian      dup2(out[1], STDOUT_FILENO);
23743888Sbrian      if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
23843888Sbrian        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
23943888Sbrian                  _PATH_DEVNULL, strerror(errno));
24043888Sbrian        exit(1);
24143888Sbrian      }
24243888Sbrian      dup2(fd, STDERR_FILENO);
24343888Sbrian      fcntl(3, F_SETFD, 1);		/* Set close-on-exec flag */
24443888Sbrian
24543888Sbrian      setuid(geteuid());
24643888Sbrian      argc = command_Interpret(prog, strlen(prog), argv);
24743888Sbrian      command_Expand(nargv, argc, (char const *const *)argv,
24843888Sbrian                     chap->auth.physical->dl->bundle, 0);
24943888Sbrian      execvp(nargv[0], nargv);
25043888Sbrian
25143888Sbrian      log_Printf(LogWARN, "exec() of %s failed: %s\n",
25243888Sbrian                nargv[0], strerror(errno));
25343888Sbrian      exit(255);
25443888Sbrian
25543888Sbrian    default:
25643888Sbrian      close(in[0]);
25743888Sbrian      close(out[1]);
25843888Sbrian      chap->child.fd = out[0];
25943888Sbrian      chap->child.buf.len = 0;
26043888Sbrian      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
26143888Sbrian      write(in[1], "\n", 1);
26243888Sbrian      write(in[1], chap->challenge + 1, *chap->challenge);
26343888Sbrian      write(in[1], "\n", 1);
26443888Sbrian      write(in[1], name, strlen(name));
26543888Sbrian      write(in[1], "\n", 1);
26643888Sbrian      close(in[1]);
26743888Sbrian      break;
26843888Sbrian  }
26943888Sbrian}
27043888Sbrian
27143888Sbrianstatic void
27243888Sbrianchap_Cleanup(struct chap *chap, int sig)
27343888Sbrian{
27443888Sbrian  if (chap->child.pid) {
27543888Sbrian    int status;
27643888Sbrian
27743888Sbrian    close(chap->child.fd);
27843888Sbrian    chap->child.fd = -1;
27943888Sbrian    if (sig)
28043888Sbrian      kill(chap->child.pid, SIGTERM);
28143888Sbrian    chap->child.pid = 0;
28243888Sbrian    chap->child.buf.len = 0;
28343888Sbrian
28443888Sbrian    if (wait(&status) == -1)
28543888Sbrian      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
28643888Sbrian    else if (WIFSIGNALED(status))
28743888Sbrian      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
28843888Sbrian    else if (WIFEXITED(status) && WEXITSTATUS(status))
28943888Sbrian      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
29043888Sbrian  }
29143888Sbrian  *chap->challenge = 0;
29244123Sbrian#ifdef HAVE_DES
29344106Sbrian  chap->peertries = 0;
29444123Sbrian#endif
29543888Sbrian}
29643888Sbrian
29743888Sbrianstatic void
29844123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type
29944123Sbrian#ifdef HAVE_DES
30044123Sbrian             , int lm
30144123Sbrian#endif
30244123Sbrian            )
30343888Sbrian{
30444106Sbrian  u_char *ans;
30543888Sbrian
30644123Sbrian  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, type
30744123Sbrian#ifdef HAVE_DES
30844123Sbrian                         , lm
30944123Sbrian#endif
31044123Sbrian                        );
31143888Sbrian
31243888Sbrian  if (ans) {
31343888Sbrian    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
31443888Sbrian               ans, *ans + 1 + strlen(name), name);
31544123Sbrian#ifdef HAVE_DES
31644106Sbrian    chap->NTRespSent = !lm;
31744123Sbrian#endif
31843888Sbrian    free(ans);
31943888Sbrian  } else
32043888Sbrian    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
32143888Sbrian               "Out of memory!", 14, NULL);
32243888Sbrian}
32343888Sbrian
32443888Sbrianstatic int
32543888Sbrianchap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
32643888Sbrian{
32743888Sbrian  struct chap *chap = descriptor2chap(d);
32843888Sbrian
32943888Sbrian  if (r && chap && chap->child.fd != -1) {
33043888Sbrian    FD_SET(chap->child.fd, r);
33143888Sbrian    if (*n < chap->child.fd + 1)
33243888Sbrian      *n = chap->child.fd + 1;
33343888Sbrian    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
33443888Sbrian    return 1;
33543888Sbrian  }
33643888Sbrian
33743888Sbrian  return 0;
33843888Sbrian}
33943888Sbrian
34043888Sbrianstatic int
34143888Sbrianchap_IsSet(struct descriptor *d, const fd_set *fdset)
34243888Sbrian{
34343888Sbrian  struct chap *chap = descriptor2chap(d);
34443888Sbrian
34543888Sbrian  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
34643888Sbrian}
34743888Sbrian
34843888Sbrianstatic void
34943888Sbrianchap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
35043888Sbrian{
35143888Sbrian  struct chap *chap = descriptor2chap(d);
35243888Sbrian  int got;
35343888Sbrian
35443888Sbrian  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
35543888Sbrian             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
35643888Sbrian  if (got == -1) {
35743888Sbrian    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
35843888Sbrian    chap_Cleanup(chap, SIGTERM);
35943888Sbrian  } else if (got == 0) {
36043888Sbrian    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
36143888Sbrian    chap_Cleanup(chap, SIGTERM);
36243888Sbrian  } else {
36343888Sbrian    char *name, *key, *end;
36443888Sbrian
36543888Sbrian    chap->child.buf.len += got;
36643888Sbrian    chap->child.buf.ptr[chap->child.buf.len] = '\0';
36743888Sbrian    name = chap->child.buf.ptr;
36843888Sbrian    name += strspn(name, " \t");
36943888Sbrian    if ((key = strchr(name, '\n')) == NULL)
37043888Sbrian      end = NULL;
37143888Sbrian    else
37243888Sbrian      end = strchr(++key, '\n');
37343888Sbrian
37443888Sbrian    if (end == NULL) {
37543888Sbrian      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
37643888Sbrian        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
37743888Sbrian        chap_Cleanup(chap, SIGTERM);
37843888Sbrian      }
37943888Sbrian    } else {
38044123Sbrian#ifdef HAVE_DES
38144106Sbrian      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
38244106Sbrian                   ((chap->NTRespSent &&
38344106Sbrian                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
38444106Sbrian                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
38544123Sbrian#endif
38644106Sbrian
38743888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
38843888Sbrian        *end-- = '\0';
38943888Sbrian      end = key - 1;
39043888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
39143888Sbrian        *end-- = '\0';
39243888Sbrian      key += strspn(key, " \t");
39343888Sbrian
39444123Sbrian      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
39544123Sbrian#ifdef HAVE_DES
39644123Sbrian                   , lanman
39744123Sbrian#endif
39844123Sbrian                  );
39943888Sbrian      chap_Cleanup(chap, 0);
40043888Sbrian    }
40143888Sbrian  }
40243888Sbrian}
40343888Sbrian
40443888Sbrianstatic int
40543888Sbrianchap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
40643888Sbrian{
40743888Sbrian  /* We never want to write here ! */
40843888Sbrian  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
40943888Sbrian  return 0;
41043888Sbrian}
41143888Sbrian
41243888Sbrianstatic void
41343693Sbrianchap_Challenge(struct authinfo *authp)
41443693Sbrian{
41543693Sbrian  struct chap *chap = auth2chap(authp);
41613379Sphk  int len, i;
4176059Samurai  char *cp;
4186059Samurai
41943888Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
42043401Sbrian
42143888Sbrian  if (!*chap->challenge) {
42243888Sbrian    randinit();
42343888Sbrian    cp = chap->challenge;
42443888Sbrian
42543313Sbrian#ifndef NORADIUS
42643888Sbrian    if (*authp->physical->dl->bundle->radius.cfg.file) {
42743888Sbrian      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
42843888Sbrian      *cp++ = 16;
42943888Sbrian      for (i = 0; i < 16; i++)
43043888Sbrian        *cp++ = (random() % 10) + '0';
43143888Sbrian    } else
43243313Sbrian#endif
43343888Sbrian    {
43444106Sbrian#ifdef HAVE_DES
43544106Sbrian      if (authp->physical->link.lcp.want_authtype == 0x80)
43644106Sbrian        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
43744106Sbrian      else
43844106Sbrian#endif
43944106Sbrian        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
44043888Sbrian      for (i = 0; i < *chap->challenge; i++)
44143888Sbrian        *cp++ = random() & 0xff;
44243888Sbrian    }
44343888Sbrian    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
44443313Sbrian  }
44543693Sbrian  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
44643888Sbrian	     1 + *chap->challenge + len, NULL);
4476059Samurai}
4486059Samurai
44930715Sbrianstatic void
45043693Sbrianchap_Success(struct authinfo *authp)
4516059Samurai{
45243693Sbrian  datalink_GotAuthname(authp->physical->dl, authp->in.name);
45343693Sbrian  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
45443693Sbrian  authp->physical->link.lcp.auth_ineed = 0;
45543693Sbrian  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
45643693Sbrian    physical_Login(authp->physical, authp->in.name);
4576059Samurai
45843693Sbrian  if (authp->physical->link.lcp.auth_iwait == 0)
45943693Sbrian    /*
46043693Sbrian     * Either I didn't need to authenticate, or I've already been
46143693Sbrian     * told that I got the answer right.
46243693Sbrian     */
46343693Sbrian    datalink_AuthOk(authp->physical->dl);
46443693Sbrian}
4656059Samurai
46643693Sbrianstatic void
46743693Sbrianchap_Failure(struct authinfo *authp)
46843693Sbrian{
46943693Sbrian  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
47043693Sbrian  datalink_AuthNotOk(authp->physical->dl);
47143693Sbrian}
47237926Sbrian
47344106Sbrianstatic int
47444123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
47544123Sbrian#ifdef HAVE_DES
47644123Sbrian         , int lm
47744123Sbrian#endif
47844123Sbrian        )
47944106Sbrian{
48044106Sbrian  if (mylen != hislen)
48144106Sbrian    return 0;
48244123Sbrian#ifdef HAVE_DES
48344106Sbrian  else if (type == 0x80) {
48444106Sbrian    int off = lm ? 0 : 24;
48544106Sbrian
48644106Sbrian    if (memcmp(myans + off, hisans + off, 24))
48744106Sbrian      return 0;
48844123Sbrian  }
48944123Sbrian#endif
49044123Sbrian  else if (memcmp(myans, hisans, mylen))
49144106Sbrian    return 0;
49244106Sbrian
49344106Sbrian  return 1;
49444106Sbrian}
49544106Sbrian
49644123Sbrian#ifdef HAVE_DES
49744106Sbrianstatic int
49844106Sbrianchap_HaveAnotherGo(struct chap *chap)
49944106Sbrian{
50044106Sbrian  if (++chap->peertries < 3) {
50144106Sbrian    /* Give the peer another shot */
50244106Sbrian    *chap->challenge = '\0';
50344106Sbrian    chap_Challenge(&chap->auth);
50444106Sbrian    return 1;
50544106Sbrian  }
50644106Sbrian
50744106Sbrian  return 0;
50844106Sbrian}
50944123Sbrian#endif
51044106Sbrian
51143693Sbrianvoid
51243693Sbrianchap_Init(struct chap *chap, struct physical *p)
51343693Sbrian{
51443888Sbrian  chap->desc.type = CHAP_DESCRIPTOR;
51543888Sbrian  chap->desc.UpdateSet = chap_UpdateSet;
51643888Sbrian  chap->desc.IsSet = chap_IsSet;
51743888Sbrian  chap->desc.Read = chap_Read;
51843888Sbrian  chap->desc.Write = chap_Write;
51943888Sbrian  chap->child.pid = 0;
52043888Sbrian  chap->child.fd = -1;
52143693Sbrian  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
52243693Sbrian  *chap->challenge = 0;
52344123Sbrian#ifdef HAVE_DES
52444106Sbrian  chap->NTRespSent = 0;
52544106Sbrian  chap->peertries = 0;
52644123Sbrian#endif
52743693Sbrian}
52829840Sbrian
52943693Sbrianvoid
53043888Sbrianchap_ReInit(struct chap *chap)
53143888Sbrian{
53243888Sbrian  chap_Cleanup(chap, SIGTERM);
53343888Sbrian}
53443888Sbrian
53543888Sbrianvoid
53643693Sbrianchap_Input(struct physical *p, struct mbuf *bp)
53743693Sbrian{
53843693Sbrian  struct chap *chap = &p->dl->chap;
53944106Sbrian  char *name, *key, *ans;
54044123Sbrian  int len, nlen;
54143693Sbrian  u_char alen;
54244123Sbrian#ifdef HAVE_DES
54344123Sbrian  int lanman;
54444123Sbrian#endif
54529840Sbrian
54645220Sbrian  if (bundle_Phase(p->dl->bundle) != PHASE_NETWORK &&
54745220Sbrian      bundle_Phase(p->dl->bundle) != PHASE_AUTHENTICATE) {
54845220Sbrian    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
54945220Sbrian    mbuf_Free(bp);
55045220Sbrian    return;
55145220Sbrian  }
55245220Sbrian
55344159Sbrian  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
55444159Sbrian      ntohs(chap->auth.in.hdr.length) == 0)
55544159Sbrian    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
55643693Sbrian  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
55743693Sbrian    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
55843693Sbrian               chap->auth.in.hdr.code);
55943693Sbrian  else {
56043693Sbrian    len = mbuf_Length(bp);
56143693Sbrian    ans = NULL;
56243693Sbrian
56343693Sbrian    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
56443693Sbrian        chap->auth.id != chap->auth.in.hdr.id &&
56543693Sbrian        Enabled(p->dl->bundle, OPT_IDCHECK)) {
56643693Sbrian      /* Wrong conversation dude ! */
56743693Sbrian      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
56843693Sbrian                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
56943693Sbrian                 chap->auth.id);
57043693Sbrian      mbuf_Free(bp);
57125630Sbrian      return;
57225630Sbrian    }
57343693Sbrian    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
57429840Sbrian
57544123Sbrian#ifdef HAVE_DES
57644106Sbrian    lanman = 0;
57744123Sbrian#endif
57843693Sbrian    switch (chap->auth.in.hdr.code) {
57943693Sbrian      case CHAP_CHALLENGE:
58044106Sbrian        bp = mbuf_Read(bp, &alen, 1);
58144106Sbrian        len -= alen + 1;
58243693Sbrian        if (len < 0) {
58343693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
58443693Sbrian          mbuf_Free(bp);
58543693Sbrian          return;
58643693Sbrian        }
58744106Sbrian        *chap->challenge = alen;
58844106Sbrian        bp = mbuf_Read(bp, chap->challenge + 1, alen);
58943693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
59044123Sbrian#ifdef HAVE_DES
59144106Sbrian        lanman = p->link.lcp.his_authtype == 0x80 &&
59244106Sbrian                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
59344106Sbrian                  !IsAccepted(p->link.lcp.cfg.chap80nt));
59444123Sbrian#endif
59543693Sbrian        break;
59643313Sbrian
59743693Sbrian      case CHAP_RESPONSE:
59843693Sbrian        auth_StopTimer(&chap->auth);
59943693Sbrian        bp = mbuf_Read(bp, &alen, 1);
60043693Sbrian        len -= alen + 1;
60143693Sbrian        if (len < 0) {
60243693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
60343693Sbrian          mbuf_Free(bp);
60443693Sbrian          return;
60543693Sbrian        }
60643693Sbrian        if ((ans = malloc(alen + 2)) == NULL) {
60743693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
60843693Sbrian          mbuf_Free(bp);
60943693Sbrian          return;
61043693Sbrian        }
61143693Sbrian        *ans = chap->auth.id;
61243693Sbrian        bp = mbuf_Read(bp, ans + 1, alen);
61343693Sbrian        ans[alen+1] = '\0';
61443693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
61544123Sbrian#ifdef HAVE_DES
61644106Sbrian        lanman = alen == 49 && ans[alen] == 0;
61744123Sbrian#endif
61843693Sbrian        break;
61943313Sbrian
62043693Sbrian      case CHAP_SUCCESS:
62143693Sbrian      case CHAP_FAILURE:
62243693Sbrian        /* chap->auth.in.name is already set up at CHALLENGE time */
62343693Sbrian        if ((ans = malloc(len + 1)) == NULL) {
62443693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
62543693Sbrian          mbuf_Free(bp);
62643693Sbrian          return;
62743693Sbrian        }
62843693Sbrian        bp = mbuf_Read(bp, ans, len);
62943693Sbrian        ans[len] = '\0';
63043693Sbrian        break;
63143401Sbrian    }
63236285Sbrian
63343693Sbrian    switch (chap->auth.in.hdr.code) {
63443693Sbrian      case CHAP_CHALLENGE:
63543693Sbrian      case CHAP_RESPONSE:
63643693Sbrian        if (*chap->auth.in.name)
63744106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
63844106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
63944106Sbrian                     chap->auth.in.name,
64044123Sbrian#ifdef HAVE_DES
64144106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
64244123Sbrian                     " - lanman" :
64344123Sbrian#endif
64444123Sbrian                     "");
64543693Sbrian        else
64644106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
64744106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
64844123Sbrian#ifdef HAVE_DES
64944106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
65044123Sbrian                     " - lanman" :
65144123Sbrian#endif
65244123Sbrian                     "");
65343693Sbrian        break;
65436285Sbrian
65543693Sbrian      case CHAP_SUCCESS:
65643693Sbrian      case CHAP_FAILURE:
65743693Sbrian        if (*ans)
65843693Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
65943693Sbrian                     chapcodes[chap->auth.in.hdr.code], ans);
66043693Sbrian        else
66143693Sbrian          log_Printf(LogPHASE, "Chap Input: %s\n",
66243693Sbrian                     chapcodes[chap->auth.in.hdr.code]);
66343693Sbrian        break;
6646059Samurai    }
6656059Samurai
66643693Sbrian    switch (chap->auth.in.hdr.code) {
66743693Sbrian      case CHAP_CHALLENGE:
66843888Sbrian        if (*p->dl->bundle->cfg.auth.key == '!')
66943888Sbrian          chap_StartChild(chap, p->dl->bundle->cfg.auth.key + 1,
67043888Sbrian                          p->dl->bundle->cfg.auth.name);
67143888Sbrian        else
67244106Sbrian          chap_Respond(chap, p->dl->bundle->cfg.auth.name,
67344123Sbrian                       p->dl->bundle->cfg.auth.key, p->link.lcp.his_authtype
67444123Sbrian#ifdef HAVE_DES
67544123Sbrian                       , lanman
67644123Sbrian#endif
67744123Sbrian                      );
67843693Sbrian        break;
6796059Samurai
68043693Sbrian      case CHAP_RESPONSE:
68143693Sbrian        name = chap->auth.in.name;
68243693Sbrian        nlen = strlen(name);
68343693Sbrian#ifndef NORADIUS
68443693Sbrian        if (*p->dl->bundle->radius.cfg.file) {
68543693Sbrian          chap->challenge[*chap->challenge+1] = '\0';
68643693Sbrian          radius_Authenticate(&p->dl->bundle->radius, &chap->auth,
68743693Sbrian                              chap->auth.in.name, ans, chap->challenge + 1);
68843693Sbrian        } else
68943693Sbrian#endif
69043693Sbrian        {
69143693Sbrian          key = auth_GetSecret(p->dl->bundle, name, nlen, p);
69243693Sbrian          if (key) {
69344106Sbrian            char *myans;
69444123Sbrian#ifdef HAVE_DES
69544106Sbrian            if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
69644106Sbrian              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
69744106Sbrian              if (chap_HaveAnotherGo(chap))
69844106Sbrian                break;
69943693Sbrian              key = NULL;
70044122Sbrian            } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
70144122Sbrian                       p->link.lcp.want_authtype == 0x80) {
70244106Sbrian              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
70344106Sbrian              if (chap_HaveAnotherGo(chap))
70444106Sbrian                break;
70544106Sbrian              key = NULL;
70644123Sbrian            } else
70744123Sbrian#endif
70844123Sbrian            {
70944106Sbrian              myans = chap_BuildAnswer(name, key, chap->auth.id,
71044106Sbrian                                       chap->challenge,
71144123Sbrian                                       p->link.lcp.want_authtype
71244123Sbrian#ifdef HAVE_DES
71344123Sbrian                                       , lanman
71444123Sbrian#endif
71544123Sbrian                                      );
71644106Sbrian              if (myans == NULL)
71743693Sbrian                key = NULL;
71844106Sbrian              else {
71944123Sbrian                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
72044123Sbrian                              ans + 1, alen
72144123Sbrian#ifdef HAVE_DES
72244123Sbrian                              , lanman
72344123Sbrian#endif
72444123Sbrian                             ))
72544106Sbrian                  key = NULL;
72644106Sbrian                free(myans);
72744106Sbrian              }
72843693Sbrian            }
72943693Sbrian          }
7306059Samurai
73143693Sbrian          if (key)
73243693Sbrian            chap_Success(&chap->auth);
73343693Sbrian          else
73443693Sbrian            chap_Failure(&chap->auth);
73543693Sbrian        }
7366059Samurai
73743693Sbrian        break;
7386059Samurai
7396059Samurai      case CHAP_SUCCESS:
74043693Sbrian        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
74143693Sbrian          p->link.lcp.auth_iwait = 0;
74243693Sbrian          if (p->link.lcp.auth_ineed == 0)
74343693Sbrian            /*
74443693Sbrian             * We've succeeded in our ``login''
74543693Sbrian             * If we're not expecting  the peer to authenticate (or he already
74643693Sbrian             * has), proceed to network phase.
74743693Sbrian             */
74843693Sbrian            datalink_AuthOk(p->dl);
74943693Sbrian        }
75043693Sbrian        break;
75143693Sbrian
7526059Samurai      case CHAP_FAILURE:
75343693Sbrian        datalink_AuthNotOk(p->dl);
75443693Sbrian        break;
7556059Samurai    }
75643693Sbrian    free(ans);
7576059Samurai  }
75843693Sbrian
75936285Sbrian  mbuf_Free(bp);
7606059Samurai}
761