chap.c revision 64802
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 *
2050479Speter * $FreeBSD: head/usr.sbin/ppp/chap.c 64802 2000-08-18 00:01:44Z brian $
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>
3849976Sbrian#include <stdio.h>
3930715Sbrian#include <stdlib.h>
4044106Sbrian#include <string.h>
4143888Sbrian#include <sys/wait.h>
4236285Sbrian#include <termios.h>
4343888Sbrian#include <unistd.h>
4429840Sbrian
4546686Sbrian#include "layer.h"
4630715Sbrian#include "mbuf.h"
4730715Sbrian#include "log.h"
4830715Sbrian#include "defs.h"
4930715Sbrian#include "timer.h"
506059Samurai#include "fsm.h"
5146686Sbrian#include "proto.h"
5236285Sbrian#include "lqr.h"
536059Samurai#include "hdlc.h"
5463484Sbrian#include "lcp.h"
556735Samurai#include "auth.h"
5636285Sbrian#include "async.h"
5736285Sbrian#include "throughput.h"
5836285Sbrian#include "descriptor.h"
5943888Sbrian#include "chap.h"
6036285Sbrian#include "iplist.h"
6136285Sbrian#include "slcompress.h"
6236285Sbrian#include "ipcp.h"
6336285Sbrian#include "filter.h"
6436285Sbrian#include "ccp.h"
6536285Sbrian#include "link.h"
6636285Sbrian#include "physical.h"
6736285Sbrian#include "mp.h"
6843313Sbrian#ifndef NORADIUS
6943313Sbrian#include "radius.h"
7043313Sbrian#endif
7136285Sbrian#include "bundle.h"
7236285Sbrian#include "chat.h"
7338174Sbrian#include "cbcp.h"
7443888Sbrian#include "command.h"
7536285Sbrian#include "datalink.h"
7637192Sbrian#ifdef HAVE_DES
7736287Sbrian#include "chap_ms.h"
7837192Sbrian#endif
7955253Sbrian#include "id.h"
806059Samurai
8155146Sbrianstatic const char * const chapcodes[] = {
8219866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
836059Samurai};
8443693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
856059Samurai
8630715Sbrianstatic void
8736285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
8843693Sbrian	   const u_char *ptr, int count, const char *text)
896059Samurai{
906059Samurai  int plen;
916059Samurai  struct fsmheader lh;
926059Samurai  struct mbuf *bp;
936059Samurai
9428679Sbrian  plen = sizeof(struct fsmheader) + count;
956059Samurai  lh.code = code;
966059Samurai  lh.id = id;
976059Samurai  lh.length = htons(plen);
9854912Sbrian  bp = m_get(plen, MB_CHAPOUT);
9930715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
1006059Samurai  if (count)
10130715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
10236285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
10337926Sbrian  if (text == NULL)
10437926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
10537926Sbrian  else
10637926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
10746686Sbrian  link_PushPacket(&physical->link, bp, physical->dl->bundle,
10850867Sbrian                  LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
1096059Samurai}
1106059Samurai
11143693Sbrianstatic char *
11244123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
11344123Sbrian#ifdef HAVE_DES
11444123Sbrian                 , int lanman
11544123Sbrian#endif
11644123Sbrian                )
1176059Samurai{
11843693Sbrian  char *result, *digest;
11943693Sbrian  size_t nlen, klen;
12043693Sbrian
12143693Sbrian  nlen = strlen(name);
12243693Sbrian  klen = strlen(key);
12343693Sbrian
12443693Sbrian#ifdef HAVE_DES
12544106Sbrian  if (type == 0x80) {
12643693Sbrian    char expkey[AUTHLEN << 2];
12743693Sbrian    MD4_CTX MD4context;
12843693Sbrian    int f;
12943693Sbrian
13043693Sbrian    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
13143693Sbrian      return result;
13243693Sbrian
13344106Sbrian    digest = result;					/* the response */
13444106Sbrian    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
13544106Sbrian    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
13644106Sbrian    if (lanman) {
13744106Sbrian      memset(digest + 24, '\0', 25);
13844106Sbrian      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
13944106Sbrian    } else {
14044106Sbrian      memset(digest, '\0', 25);
14144106Sbrian      digest += 24;
14243693Sbrian
14344106Sbrian      for (f = 0; f < klen; f++) {
14444106Sbrian        expkey[2*f] = key[f];
14544106Sbrian        expkey[2*f+1] = '\0';
14644106Sbrian      }
14744106Sbrian      /*
14844106Sbrian       *           -----------
14944106Sbrian       * expkey = | k\0e\0y\0 |
15044106Sbrian       *           -----------
15144106Sbrian       */
15244106Sbrian      MD4Init(&MD4context);
15344106Sbrian      MD4Update(&MD4context, expkey, klen << 1);
15444106Sbrian      MD4Final(digest, &MD4context);
15544106Sbrian
15644106Sbrian      /*
15744106Sbrian       *           ---- -------- ---------------- ------- ------
15844106Sbrian       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
15944106Sbrian       *           ---- -------- ---------------- ------- ------
16044106Sbrian       */
16144106Sbrian      mschap_NT(digest, challenge + 1);
16243693Sbrian    }
16343693Sbrian    /*
16444106Sbrian     *           ---- -------- ------------- ----- ------
16544106Sbrian     *          |    |  struct MS_ChapResponse24  |      |
16644106Sbrian     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
16744106Sbrian     *           ---- -------- ------------- ----- ------
16844106Sbrian     * where only one of LANMan & NT digest are set.
16943693Sbrian     */
17043693Sbrian  } else
17143693Sbrian#endif
17243693Sbrian  if ((result = malloc(nlen + 17)) != NULL) {
17343693Sbrian    /* Normal MD5 stuff */
17443693Sbrian    MD5_CTX MD5context;
17543693Sbrian
17643693Sbrian    digest = result;
17743693Sbrian    *digest++ = 16;				/* value size */
17843693Sbrian
17943693Sbrian    MD5Init(&MD5context);
18043693Sbrian    MD5Update(&MD5context, &id, 1);
18143693Sbrian    MD5Update(&MD5context, key, klen);
18243693Sbrian    MD5Update(&MD5context, challenge + 1, *challenge);
18343693Sbrian    MD5Final(digest, &MD5context);
18443693Sbrian
18543693Sbrian    memcpy(digest + 16, name, nlen);
18643693Sbrian    /*
18743693Sbrian     *           ---- -------- ------
18843693Sbrian     * result = | 16 | digest | name |
18943693Sbrian     *           ---- -------- ------
19043693Sbrian     */
19143693Sbrian  }
19243693Sbrian
19343693Sbrian  return result;
19443693Sbrian}
19543693Sbrian
19643693Sbrianstatic void
19743888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name)
19843888Sbrian{
19943888Sbrian  char *argv[MAXARGS], *nargv[MAXARGS];
20043888Sbrian  int argc, fd;
20143888Sbrian  int in[2], out[2];
20247849Sbrian  pid_t pid;
20343888Sbrian
20443888Sbrian  if (chap->child.fd != -1) {
20543888Sbrian    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
20643888Sbrian    return;
20743888Sbrian  }
20843888Sbrian
20943888Sbrian  if (pipe(in) == -1) {
21043888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21143888Sbrian    return;
21243888Sbrian  }
21343888Sbrian
21443888Sbrian  if (pipe(out) == -1) {
21543888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21643888Sbrian    close(in[0]);
21743888Sbrian    close(in[1]);
21843888Sbrian    return;
21943888Sbrian  }
22043888Sbrian
22147849Sbrian  pid = getpid();
22243888Sbrian  switch ((chap->child.pid = fork())) {
22343888Sbrian    case -1:
22443888Sbrian      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
22543888Sbrian      close(in[0]);
22643888Sbrian      close(in[1]);
22743888Sbrian      close(out[0]);
22843888Sbrian      close(out[1]);
22943888Sbrian      chap->child.pid = 0;
23043888Sbrian      return;
23143888Sbrian
23243888Sbrian    case 0:
23343888Sbrian      timer_TermService();
23454914Sbrian
23554914Sbrian      if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) {
23654914Sbrian        if (argc < 0) {
23754914Sbrian          log_Printf(LogWARN, "CHAP: Invalid command syntax\n");
23854914Sbrian          _exit(255);
23954914Sbrian        }
24054914Sbrian        _exit(0);
24154914Sbrian      }
24254914Sbrian
24343888Sbrian      close(in[1]);
24443888Sbrian      close(out[0]);
24549976Sbrian      if (out[1] == STDIN_FILENO)
24649976Sbrian        out[1] = dup(out[1]);
24743888Sbrian      dup2(in[0], STDIN_FILENO);
24843888Sbrian      dup2(out[1], STDOUT_FILENO);
24949976Sbrian      close(STDERR_FILENO);
25049976Sbrian      if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
25143888Sbrian        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
25243888Sbrian                  _PATH_DEVNULL, strerror(errno));
25343888Sbrian        exit(1);
25443888Sbrian      }
25549976Sbrian      for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
25649976Sbrian        fcntl(fd, F_SETFD, 1);
25764802Sbrian#ifndef NOSUID
25855252Sbrian      setuid(ID0realuid());
25964802Sbrian#endif
26043888Sbrian      command_Expand(nargv, argc, (char const *const *)argv,
26147849Sbrian                     chap->auth.physical->dl->bundle, 0, pid);
26243888Sbrian      execvp(nargv[0], nargv);
26349976Sbrian      printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
26449976Sbrian      _exit(255);
26543888Sbrian
26643888Sbrian    default:
26743888Sbrian      close(in[0]);
26843888Sbrian      close(out[1]);
26943888Sbrian      chap->child.fd = out[0];
27043888Sbrian      chap->child.buf.len = 0;
27143888Sbrian      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
27243888Sbrian      write(in[1], "\n", 1);
27345907Sbrian      write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
27443888Sbrian      write(in[1], "\n", 1);
27543888Sbrian      write(in[1], name, strlen(name));
27643888Sbrian      write(in[1], "\n", 1);
27743888Sbrian      close(in[1]);
27843888Sbrian      break;
27943888Sbrian  }
28043888Sbrian}
28143888Sbrian
28243888Sbrianstatic void
28343888Sbrianchap_Cleanup(struct chap *chap, int sig)
28443888Sbrian{
28543888Sbrian  if (chap->child.pid) {
28643888Sbrian    int status;
28743888Sbrian
28843888Sbrian    close(chap->child.fd);
28943888Sbrian    chap->child.fd = -1;
29043888Sbrian    if (sig)
29143888Sbrian      kill(chap->child.pid, SIGTERM);
29243888Sbrian    chap->child.pid = 0;
29343888Sbrian    chap->child.buf.len = 0;
29443888Sbrian
29543888Sbrian    if (wait(&status) == -1)
29643888Sbrian      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
29743888Sbrian    else if (WIFSIGNALED(status))
29843888Sbrian      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
29943888Sbrian    else if (WIFEXITED(status) && WEXITSTATUS(status))
30043888Sbrian      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
30143888Sbrian  }
30245907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
30344123Sbrian#ifdef HAVE_DES
30444106Sbrian  chap->peertries = 0;
30544123Sbrian#endif
30643888Sbrian}
30743888Sbrian
30843888Sbrianstatic void
30944123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type
31044123Sbrian#ifdef HAVE_DES
31144123Sbrian             , int lm
31244123Sbrian#endif
31344123Sbrian            )
31443888Sbrian{
31544106Sbrian  u_char *ans;
31643888Sbrian
31745907Sbrian  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
31844123Sbrian#ifdef HAVE_DES
31944123Sbrian                         , lm
32044123Sbrian#endif
32144123Sbrian                        );
32243888Sbrian
32343888Sbrian  if (ans) {
32443888Sbrian    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
32543888Sbrian               ans, *ans + 1 + strlen(name), name);
32644123Sbrian#ifdef HAVE_DES
32744106Sbrian    chap->NTRespSent = !lm;
32844123Sbrian#endif
32943888Sbrian    free(ans);
33043888Sbrian  } else
33143888Sbrian    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
33243888Sbrian               "Out of memory!", 14, NULL);
33343888Sbrian}
33443888Sbrian
33543888Sbrianstatic int
33658028Sbrianchap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
33743888Sbrian{
33843888Sbrian  struct chap *chap = descriptor2chap(d);
33943888Sbrian
34043888Sbrian  if (r && chap && chap->child.fd != -1) {
34143888Sbrian    FD_SET(chap->child.fd, r);
34243888Sbrian    if (*n < chap->child.fd + 1)
34343888Sbrian      *n = chap->child.fd + 1;
34443888Sbrian    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
34543888Sbrian    return 1;
34643888Sbrian  }
34743888Sbrian
34843888Sbrian  return 0;
34943888Sbrian}
35043888Sbrian
35143888Sbrianstatic int
35258028Sbrianchap_IsSet(struct fdescriptor *d, const fd_set *fdset)
35343888Sbrian{
35443888Sbrian  struct chap *chap = descriptor2chap(d);
35543888Sbrian
35643888Sbrian  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
35743888Sbrian}
35843888Sbrian
35943888Sbrianstatic void
36058028Sbrianchap_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
36143888Sbrian{
36243888Sbrian  struct chap *chap = descriptor2chap(d);
36343888Sbrian  int got;
36443888Sbrian
36543888Sbrian  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
36643888Sbrian             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
36743888Sbrian  if (got == -1) {
36843888Sbrian    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
36943888Sbrian    chap_Cleanup(chap, SIGTERM);
37043888Sbrian  } else if (got == 0) {
37143888Sbrian    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
37243888Sbrian    chap_Cleanup(chap, SIGTERM);
37343888Sbrian  } else {
37443888Sbrian    char *name, *key, *end;
37543888Sbrian
37643888Sbrian    chap->child.buf.len += got;
37743888Sbrian    chap->child.buf.ptr[chap->child.buf.len] = '\0';
37843888Sbrian    name = chap->child.buf.ptr;
37943888Sbrian    name += strspn(name, " \t");
38043888Sbrian    if ((key = strchr(name, '\n')) == NULL)
38143888Sbrian      end = NULL;
38243888Sbrian    else
38343888Sbrian      end = strchr(++key, '\n');
38443888Sbrian
38543888Sbrian    if (end == NULL) {
38643888Sbrian      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
38743888Sbrian        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
38843888Sbrian        chap_Cleanup(chap, SIGTERM);
38943888Sbrian      }
39043888Sbrian    } else {
39144123Sbrian#ifdef HAVE_DES
39244106Sbrian      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
39344106Sbrian                   ((chap->NTRespSent &&
39444106Sbrian                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
39544106Sbrian                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
39644123Sbrian#endif
39744106Sbrian
39843888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
39943888Sbrian        *end-- = '\0';
40043888Sbrian      end = key - 1;
40143888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
40243888Sbrian        *end-- = '\0';
40343888Sbrian      key += strspn(key, " \t");
40443888Sbrian
40544123Sbrian      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
40644123Sbrian#ifdef HAVE_DES
40744123Sbrian                   , lanman
40844123Sbrian#endif
40944123Sbrian                  );
41043888Sbrian      chap_Cleanup(chap, 0);
41143888Sbrian    }
41243888Sbrian  }
41343888Sbrian}
41443888Sbrian
41543888Sbrianstatic int
41658028Sbrianchap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
41743888Sbrian{
41843888Sbrian  /* We never want to write here ! */
41943888Sbrian  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
42043888Sbrian  return 0;
42143888Sbrian}
42243888Sbrian
42343888Sbrianstatic void
42443693Sbrianchap_Challenge(struct authinfo *authp)
42543693Sbrian{
42643693Sbrian  struct chap *chap = auth2chap(authp);
42713379Sphk  int len, i;
4286059Samurai  char *cp;
4296059Samurai
43043888Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
43143401Sbrian
43245907Sbrian  if (!*chap->challenge.local) {
43343888Sbrian    randinit();
43445907Sbrian    cp = chap->challenge.local;
43543888Sbrian
43643313Sbrian#ifndef NORADIUS
43743888Sbrian    if (*authp->physical->dl->bundle->radius.cfg.file) {
43843888Sbrian      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
43943888Sbrian      *cp++ = 16;
44043888Sbrian      for (i = 0; i < 16; i++)
44143888Sbrian        *cp++ = (random() % 10) + '0';
44243888Sbrian    } else
44343313Sbrian#endif
44443888Sbrian    {
44544106Sbrian#ifdef HAVE_DES
44644106Sbrian      if (authp->physical->link.lcp.want_authtype == 0x80)
44744106Sbrian        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
44844106Sbrian      else
44944106Sbrian#endif
45044106Sbrian        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
45145907Sbrian      for (i = 0; i < *chap->challenge.local; i++)
45243888Sbrian        *cp++ = random() & 0xff;
45343888Sbrian    }
45443888Sbrian    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
45543313Sbrian  }
45645907Sbrian  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge.local,
45745907Sbrian	     1 + *chap->challenge.local + len, NULL);
4586059Samurai}
4596059Samurai
46030715Sbrianstatic void
46143693Sbrianchap_Success(struct authinfo *authp)
4626059Samurai{
46343693Sbrian  datalink_GotAuthname(authp->physical->dl, authp->in.name);
46443693Sbrian  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
46543693Sbrian  authp->physical->link.lcp.auth_ineed = 0;
46643693Sbrian  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
46743693Sbrian    physical_Login(authp->physical, authp->in.name);
4686059Samurai
46943693Sbrian  if (authp->physical->link.lcp.auth_iwait == 0)
47043693Sbrian    /*
47143693Sbrian     * Either I didn't need to authenticate, or I've already been
47243693Sbrian     * told that I got the answer right.
47343693Sbrian     */
47443693Sbrian    datalink_AuthOk(authp->physical->dl);
47543693Sbrian}
4766059Samurai
47743693Sbrianstatic void
47843693Sbrianchap_Failure(struct authinfo *authp)
47943693Sbrian{
48043693Sbrian  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
48143693Sbrian  datalink_AuthNotOk(authp->physical->dl);
48243693Sbrian}
48337926Sbrian
48444106Sbrianstatic int
48544123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
48644123Sbrian#ifdef HAVE_DES
48744123Sbrian         , int lm
48844123Sbrian#endif
48944123Sbrian        )
49044106Sbrian{
49144106Sbrian  if (mylen != hislen)
49244106Sbrian    return 0;
49344123Sbrian#ifdef HAVE_DES
49444106Sbrian  else if (type == 0x80) {
49544106Sbrian    int off = lm ? 0 : 24;
49644106Sbrian
49744106Sbrian    if (memcmp(myans + off, hisans + off, 24))
49844106Sbrian      return 0;
49944123Sbrian  }
50044123Sbrian#endif
50144123Sbrian  else if (memcmp(myans, hisans, mylen))
50244106Sbrian    return 0;
50344106Sbrian
50444106Sbrian  return 1;
50544106Sbrian}
50644106Sbrian
50744123Sbrian#ifdef HAVE_DES
50844106Sbrianstatic int
50944106Sbrianchap_HaveAnotherGo(struct chap *chap)
51044106Sbrian{
51144106Sbrian  if (++chap->peertries < 3) {
51244106Sbrian    /* Give the peer another shot */
51345907Sbrian    *chap->challenge.local = '\0';
51444106Sbrian    chap_Challenge(&chap->auth);
51544106Sbrian    return 1;
51644106Sbrian  }
51744106Sbrian
51844106Sbrian  return 0;
51944106Sbrian}
52044123Sbrian#endif
52144106Sbrian
52243693Sbrianvoid
52343693Sbrianchap_Init(struct chap *chap, struct physical *p)
52443693Sbrian{
52543888Sbrian  chap->desc.type = CHAP_DESCRIPTOR;
52643888Sbrian  chap->desc.UpdateSet = chap_UpdateSet;
52743888Sbrian  chap->desc.IsSet = chap_IsSet;
52843888Sbrian  chap->desc.Read = chap_Read;
52943888Sbrian  chap->desc.Write = chap_Write;
53043888Sbrian  chap->child.pid = 0;
53143888Sbrian  chap->child.fd = -1;
53243693Sbrian  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
53345907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
53444123Sbrian#ifdef HAVE_DES
53544106Sbrian  chap->NTRespSent = 0;
53644106Sbrian  chap->peertries = 0;
53744123Sbrian#endif
53843693Sbrian}
53929840Sbrian
54043693Sbrianvoid
54143888Sbrianchap_ReInit(struct chap *chap)
54243888Sbrian{
54343888Sbrian  chap_Cleanup(chap, SIGTERM);
54443888Sbrian}
54543888Sbrian
54646686Sbrianstruct mbuf *
54746686Sbrianchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
54843693Sbrian{
54946686Sbrian  struct physical *p = link2physical(l);
55043693Sbrian  struct chap *chap = &p->dl->chap;
55144106Sbrian  char *name, *key, *ans;
55244123Sbrian  int len, nlen;
55348817Sbrian  u_char alen;
55444123Sbrian#ifdef HAVE_DES
55544123Sbrian  int lanman;
55644123Sbrian#endif
55729840Sbrian
55846686Sbrian  if (p == NULL) {
55946686Sbrian    log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
56054912Sbrian    m_freem(bp);
56146686Sbrian    return NULL;
56246686Sbrian  }
56346686Sbrian
56446686Sbrian  if (bundle_Phase(bundle) != PHASE_NETWORK &&
56546686Sbrian      bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
56645220Sbrian    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
56754912Sbrian    m_freem(bp);
56846686Sbrian    return NULL;
56945220Sbrian  }
57045220Sbrian
57154912Sbrian  m_settype(bp, MB_CHAPIN);
57244159Sbrian  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
57344159Sbrian      ntohs(chap->auth.in.hdr.length) == 0)
57444159Sbrian    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
57543693Sbrian  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
57643693Sbrian    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
57743693Sbrian               chap->auth.in.hdr.code);
57843693Sbrian  else {
57954912Sbrian    len = m_length(bp);
58043693Sbrian    ans = NULL;
58143693Sbrian
58243693Sbrian    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
58343693Sbrian        chap->auth.id != chap->auth.in.hdr.id &&
58446686Sbrian        Enabled(bundle, OPT_IDCHECK)) {
58543693Sbrian      /* Wrong conversation dude ! */
58643693Sbrian      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
58743693Sbrian                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
58843693Sbrian                 chap->auth.id);
58954912Sbrian      m_freem(bp);
59046686Sbrian      return NULL;
59125630Sbrian    }
59243693Sbrian    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
59329840Sbrian
59444123Sbrian#ifdef HAVE_DES
59544106Sbrian    lanman = 0;
59644123Sbrian#endif
59743693Sbrian    switch (chap->auth.in.hdr.code) {
59843693Sbrian      case CHAP_CHALLENGE:
59944106Sbrian        bp = mbuf_Read(bp, &alen, 1);
60044106Sbrian        len -= alen + 1;
60143693Sbrian        if (len < 0) {
60243693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
60354912Sbrian          m_freem(bp);
60446686Sbrian          return NULL;
60543693Sbrian        }
60645907Sbrian        *chap->challenge.peer = alen;
60745907Sbrian        bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
60843693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
60944123Sbrian#ifdef HAVE_DES
61044106Sbrian        lanman = p->link.lcp.his_authtype == 0x80 &&
61144106Sbrian                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
61244106Sbrian                  !IsAccepted(p->link.lcp.cfg.chap80nt));
61344123Sbrian#endif
61443693Sbrian        break;
61543313Sbrian
61643693Sbrian      case CHAP_RESPONSE:
61743693Sbrian        auth_StopTimer(&chap->auth);
61843693Sbrian        bp = mbuf_Read(bp, &alen, 1);
61943693Sbrian        len -= alen + 1;
62043693Sbrian        if (len < 0) {
62143693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
62254912Sbrian          m_freem(bp);
62346686Sbrian          return NULL;
62443693Sbrian        }
62543693Sbrian        if ((ans = malloc(alen + 2)) == NULL) {
62643693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
62754912Sbrian          m_freem(bp);
62846686Sbrian          return NULL;
62943693Sbrian        }
63043693Sbrian        *ans = chap->auth.id;
63143693Sbrian        bp = mbuf_Read(bp, ans + 1, alen);
63243693Sbrian        ans[alen+1] = '\0';
63343693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
63444123Sbrian#ifdef HAVE_DES
63544106Sbrian        lanman = alen == 49 && ans[alen] == 0;
63644123Sbrian#endif
63743693Sbrian        break;
63843313Sbrian
63943693Sbrian      case CHAP_SUCCESS:
64043693Sbrian      case CHAP_FAILURE:
64143693Sbrian        /* chap->auth.in.name is already set up at CHALLENGE time */
64243693Sbrian        if ((ans = malloc(len + 1)) == NULL) {
64343693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
64454912Sbrian          m_freem(bp);
64546686Sbrian          return NULL;
64643693Sbrian        }
64743693Sbrian        bp = mbuf_Read(bp, ans, len);
64843693Sbrian        ans[len] = '\0';
64943693Sbrian        break;
65043401Sbrian    }
65136285Sbrian
65243693Sbrian    switch (chap->auth.in.hdr.code) {
65343693Sbrian      case CHAP_CHALLENGE:
65443693Sbrian      case CHAP_RESPONSE:
65543693Sbrian        if (*chap->auth.in.name)
65644106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
65744106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
65844106Sbrian                     chap->auth.in.name,
65944123Sbrian#ifdef HAVE_DES
66044106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
66144123Sbrian                     " - lanman" :
66244123Sbrian#endif
66344123Sbrian                     "");
66443693Sbrian        else
66544106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
66644106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
66744123Sbrian#ifdef HAVE_DES
66844106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
66944123Sbrian                     " - lanman" :
67044123Sbrian#endif
67144123Sbrian                     "");
67243693Sbrian        break;
67336285Sbrian
67443693Sbrian      case CHAP_SUCCESS:
67543693Sbrian      case CHAP_FAILURE:
67643693Sbrian        if (*ans)
67743693Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
67843693Sbrian                     chapcodes[chap->auth.in.hdr.code], ans);
67943693Sbrian        else
68043693Sbrian          log_Printf(LogPHASE, "Chap Input: %s\n",
68143693Sbrian                     chapcodes[chap->auth.in.hdr.code]);
68243693Sbrian        break;
6836059Samurai    }
6846059Samurai
68543693Sbrian    switch (chap->auth.in.hdr.code) {
68643693Sbrian      case CHAP_CHALLENGE:
68764465Sbrian        if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!')
68846686Sbrian          chap_StartChild(chap, bundle->cfg.auth.key + 1,
68946686Sbrian                          bundle->cfg.auth.name);
69043888Sbrian        else
69164465Sbrian          chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key +
69264465Sbrian                       (*bundle->cfg.auth.key == '!' ? 1 : 0),
69364465Sbrian                       p->link.lcp.his_authtype
69444123Sbrian#ifdef HAVE_DES
69544123Sbrian                       , lanman
69644123Sbrian#endif
69744123Sbrian                      );
69843693Sbrian        break;
6996059Samurai
70043693Sbrian      case CHAP_RESPONSE:
70143693Sbrian        name = chap->auth.in.name;
70243693Sbrian        nlen = strlen(name);
70343693Sbrian#ifndef NORADIUS
70446686Sbrian        if (*bundle->radius.cfg.file) {
70548817Sbrian          u_char end;
70648817Sbrian
70745907Sbrian          end = chap->challenge.local[*chap->challenge.local+1];
70845907Sbrian          chap->challenge.local[*chap->challenge.local+1] = '\0';
70946686Sbrian          radius_Authenticate(&bundle->radius, &chap->auth,
71045907Sbrian                              chap->auth.in.name, ans,
71145907Sbrian                              chap->challenge.local + 1);
71245907Sbrian          chap->challenge.local[*chap->challenge.local+1] = end;
71343693Sbrian        } else
71443693Sbrian#endif
71543693Sbrian        {
71646686Sbrian          key = auth_GetSecret(bundle, name, nlen, p);
71743693Sbrian          if (key) {
71844106Sbrian            char *myans;
71944123Sbrian#ifdef HAVE_DES
72044106Sbrian            if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
72144106Sbrian              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
72244106Sbrian              if (chap_HaveAnotherGo(chap))
72344106Sbrian                break;
72443693Sbrian              key = NULL;
72544122Sbrian            } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
72644122Sbrian                       p->link.lcp.want_authtype == 0x80) {
72744106Sbrian              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
72844106Sbrian              if (chap_HaveAnotherGo(chap))
72944106Sbrian                break;
73044106Sbrian              key = NULL;
73144123Sbrian            } else
73244123Sbrian#endif
73344123Sbrian            {
73444106Sbrian              myans = chap_BuildAnswer(name, key, chap->auth.id,
73545907Sbrian                                       chap->challenge.local,
73644123Sbrian                                       p->link.lcp.want_authtype
73744123Sbrian#ifdef HAVE_DES
73844123Sbrian                                       , lanman
73944123Sbrian#endif
74044123Sbrian                                      );
74144106Sbrian              if (myans == NULL)
74243693Sbrian                key = NULL;
74344106Sbrian              else {
74444123Sbrian                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
74544123Sbrian                              ans + 1, alen
74644123Sbrian#ifdef HAVE_DES
74744123Sbrian                              , lanman
74844123Sbrian#endif
74944123Sbrian                             ))
75044106Sbrian                  key = NULL;
75144106Sbrian                free(myans);
75244106Sbrian              }
75343693Sbrian            }
75443693Sbrian          }
7556059Samurai
75643693Sbrian          if (key)
75743693Sbrian            chap_Success(&chap->auth);
75843693Sbrian          else
75943693Sbrian            chap_Failure(&chap->auth);
76043693Sbrian        }
7616059Samurai
76243693Sbrian        break;
7636059Samurai
7646059Samurai      case CHAP_SUCCESS:
76543693Sbrian        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
76643693Sbrian          p->link.lcp.auth_iwait = 0;
76743693Sbrian          if (p->link.lcp.auth_ineed == 0)
76843693Sbrian            /*
76943693Sbrian             * We've succeeded in our ``login''
77043693Sbrian             * If we're not expecting  the peer to authenticate (or he already
77143693Sbrian             * has), proceed to network phase.
77243693Sbrian             */
77343693Sbrian            datalink_AuthOk(p->dl);
77443693Sbrian        }
77543693Sbrian        break;
77643693Sbrian
7776059Samurai      case CHAP_FAILURE:
77843693Sbrian        datalink_AuthNotOk(p->dl);
77943693Sbrian        break;
7806059Samurai    }
78143693Sbrian    free(ans);
7826059Samurai  }
78343693Sbrian
78454912Sbrian  m_freem(bp);
78546686Sbrian  return NULL;
7866059Samurai}
787