chap.c revision 67910
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 67910 2000-10-30 00:15:04Z 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"
7867910Sbrian#include "mppe.h"
7937192Sbrian#endif
8055253Sbrian#include "id.h"
816059Samurai
8255146Sbrianstatic const char * const chapcodes[] = {
8319866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
846059Samurai};
8543693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
866059Samurai
8730715Sbrianstatic void
8836285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
8943693Sbrian	   const u_char *ptr, int count, const char *text)
906059Samurai{
916059Samurai  int plen;
926059Samurai  struct fsmheader lh;
936059Samurai  struct mbuf *bp;
946059Samurai
9528679Sbrian  plen = sizeof(struct fsmheader) + count;
966059Samurai  lh.code = code;
976059Samurai  lh.id = id;
986059Samurai  lh.length = htons(plen);
9954912Sbrian  bp = m_get(plen, MB_CHAPOUT);
10030715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
1016059Samurai  if (count)
10230715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
10336285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
10437926Sbrian  if (text == NULL)
10537926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
10637926Sbrian  else
10737926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
10846686Sbrian  link_PushPacket(&physical->link, bp, physical->dl->bundle,
10950867Sbrian                  LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
1106059Samurai}
1116059Samurai
11243693Sbrianstatic char *
11344123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
11444123Sbrian#ifdef HAVE_DES
11567910Sbrian                 , char *peerchallenge, char *authresponse, int lanman
11644123Sbrian#endif
11744123Sbrian                )
1186059Samurai{
11943693Sbrian  char *result, *digest;
12043693Sbrian  size_t nlen, klen;
12143693Sbrian
12243693Sbrian  nlen = strlen(name);
12343693Sbrian  klen = strlen(key);
12443693Sbrian
12543693Sbrian#ifdef HAVE_DES
12644106Sbrian  if (type == 0x80) {
12743693Sbrian    char expkey[AUTHLEN << 2];
12843693Sbrian    MD4_CTX MD4context;
12943693Sbrian    int f;
13043693Sbrian
13143693Sbrian    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
13243693Sbrian      return result;
13343693Sbrian
13444106Sbrian    digest = result;					/* the response */
13544106Sbrian    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
13644106Sbrian    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
13744106Sbrian    if (lanman) {
13844106Sbrian      memset(digest + 24, '\0', 25);
13944106Sbrian      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
14044106Sbrian    } else {
14144106Sbrian      memset(digest, '\0', 25);
14244106Sbrian      digest += 24;
14343693Sbrian
14444106Sbrian      for (f = 0; f < klen; f++) {
14544106Sbrian        expkey[2*f] = key[f];
14644106Sbrian        expkey[2*f+1] = '\0';
14744106Sbrian      }
14844106Sbrian      /*
14944106Sbrian       *           -----------
15044106Sbrian       * expkey = | k\0e\0y\0 |
15144106Sbrian       *           -----------
15244106Sbrian       */
15344106Sbrian      MD4Init(&MD4context);
15444106Sbrian      MD4Update(&MD4context, expkey, klen << 1);
15544106Sbrian      MD4Final(digest, &MD4context);
15644106Sbrian
15744106Sbrian      /*
15844106Sbrian       *           ---- -------- ---------------- ------- ------
15944106Sbrian       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
16044106Sbrian       *           ---- -------- ---------------- ------- ------
16144106Sbrian       */
16244106Sbrian      mschap_NT(digest, challenge + 1);
16343693Sbrian    }
16443693Sbrian    /*
16544106Sbrian     *           ---- -------- ------------- ----- ------
16644106Sbrian     *          |    |  struct MS_ChapResponse24  |      |
16744106Sbrian     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
16844106Sbrian     *           ---- -------- ------------- ----- ------
16944106Sbrian     * where only one of LANMan & NT digest are set.
17043693Sbrian     */
17167910Sbrian  } else if (type == 0x81) {
17267910Sbrian    char expkey[AUTHLEN << 2];
17367910Sbrian    char pwdhash[CHAP81_HASH_LEN];
17467910Sbrian    char pwdhashhash[CHAP81_HASH_LEN];
17567910Sbrian    char *ntresponse;
17667910Sbrian    int f;
17767910Sbrian
17867910Sbrian    if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL)
17967910Sbrian      return result;
18067910Sbrian
18167910Sbrian    memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN);
18267910Sbrian
18367910Sbrian    digest = result;
18467910Sbrian    *digest++ = CHAP81_RESPONSE_LEN;		/* value size */
18567910Sbrian
18667910Sbrian    /* Copy our challenge */
18767910Sbrian    memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN);
18867910Sbrian
18967910Sbrian    /* Expand password to Unicode XXX */
19067910Sbrian    for (f = 0; f < klen; f++) {
19167910Sbrian      expkey[2*f] = key[f];
19267910Sbrian      expkey[2*f+1] = '\0';
19367910Sbrian    }
19467910Sbrian
19567910Sbrian    ntresponse = digest + CHAP81_NTRESPONSE_OFF;
19667910Sbrian
19767910Sbrian    /* Get some needed hashes */
19867910Sbrian    NtPasswordHash(expkey, klen * 2, pwdhash);
19967910Sbrian    HashNtPasswordHash(pwdhash, pwdhashhash);
20067910Sbrian
20167910Sbrian    /* Generate NTRESPONSE to respond on challenge call */
20267910Sbrian    GenerateNTResponse(challenge + 1, peerchallenge + 1, name, nlen,
20367910Sbrian                       expkey, klen * 2, ntresponse);
20467910Sbrian
20567910Sbrian    /* Generate MPPE MASTERKEY */
20667910Sbrian    GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey);
20767910Sbrian
20867910Sbrian    /* Generate AUTHRESPONSE to verify on auth success */
20967910Sbrian    GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse,
21067910Sbrian                                  peerchallenge + 1, challenge + 1, name, nlen,
21167910Sbrian                                  authresponse);
21267910Sbrian
21367910Sbrian    authresponse[CHAP81_AUTHRESPONSE_LEN] = 0;
21467910Sbrian
21567910Sbrian    memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen);
21643693Sbrian  } else
21743693Sbrian#endif
21843693Sbrian  if ((result = malloc(nlen + 17)) != NULL) {
21943693Sbrian    /* Normal MD5 stuff */
22043693Sbrian    MD5_CTX MD5context;
22143693Sbrian
22243693Sbrian    digest = result;
22343693Sbrian    *digest++ = 16;				/* value size */
22443693Sbrian
22543693Sbrian    MD5Init(&MD5context);
22643693Sbrian    MD5Update(&MD5context, &id, 1);
22743693Sbrian    MD5Update(&MD5context, key, klen);
22843693Sbrian    MD5Update(&MD5context, challenge + 1, *challenge);
22943693Sbrian    MD5Final(digest, &MD5context);
23043693Sbrian
23143693Sbrian    memcpy(digest + 16, name, nlen);
23243693Sbrian    /*
23343693Sbrian     *           ---- -------- ------
23443693Sbrian     * result = | 16 | digest | name |
23543693Sbrian     *           ---- -------- ------
23643693Sbrian     */
23743693Sbrian  }
23843693Sbrian
23943693Sbrian  return result;
24043693Sbrian}
24143693Sbrian
24243693Sbrianstatic void
24343888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name)
24443888Sbrian{
24543888Sbrian  char *argv[MAXARGS], *nargv[MAXARGS];
24643888Sbrian  int argc, fd;
24743888Sbrian  int in[2], out[2];
24847849Sbrian  pid_t pid;
24943888Sbrian
25043888Sbrian  if (chap->child.fd != -1) {
25143888Sbrian    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
25243888Sbrian    return;
25343888Sbrian  }
25443888Sbrian
25543888Sbrian  if (pipe(in) == -1) {
25643888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
25743888Sbrian    return;
25843888Sbrian  }
25943888Sbrian
26043888Sbrian  if (pipe(out) == -1) {
26143888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
26243888Sbrian    close(in[0]);
26343888Sbrian    close(in[1]);
26443888Sbrian    return;
26543888Sbrian  }
26643888Sbrian
26747849Sbrian  pid = getpid();
26843888Sbrian  switch ((chap->child.pid = fork())) {
26943888Sbrian    case -1:
27043888Sbrian      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
27143888Sbrian      close(in[0]);
27243888Sbrian      close(in[1]);
27343888Sbrian      close(out[0]);
27443888Sbrian      close(out[1]);
27543888Sbrian      chap->child.pid = 0;
27643888Sbrian      return;
27743888Sbrian
27843888Sbrian    case 0:
27943888Sbrian      timer_TermService();
28054914Sbrian
28154914Sbrian      if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) {
28254914Sbrian        if (argc < 0) {
28354914Sbrian          log_Printf(LogWARN, "CHAP: Invalid command syntax\n");
28454914Sbrian          _exit(255);
28554914Sbrian        }
28654914Sbrian        _exit(0);
28754914Sbrian      }
28854914Sbrian
28943888Sbrian      close(in[1]);
29043888Sbrian      close(out[0]);
29149976Sbrian      if (out[1] == STDIN_FILENO)
29249976Sbrian        out[1] = dup(out[1]);
29343888Sbrian      dup2(in[0], STDIN_FILENO);
29443888Sbrian      dup2(out[1], STDOUT_FILENO);
29549976Sbrian      close(STDERR_FILENO);
29649976Sbrian      if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
29743888Sbrian        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
29843888Sbrian                  _PATH_DEVNULL, strerror(errno));
29943888Sbrian        exit(1);
30043888Sbrian      }
30149976Sbrian      for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
30249976Sbrian        fcntl(fd, F_SETFD, 1);
30364802Sbrian#ifndef NOSUID
30455252Sbrian      setuid(ID0realuid());
30564802Sbrian#endif
30643888Sbrian      command_Expand(nargv, argc, (char const *const *)argv,
30747849Sbrian                     chap->auth.physical->dl->bundle, 0, pid);
30843888Sbrian      execvp(nargv[0], nargv);
30949976Sbrian      printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
31049976Sbrian      _exit(255);
31143888Sbrian
31243888Sbrian    default:
31343888Sbrian      close(in[0]);
31443888Sbrian      close(out[1]);
31543888Sbrian      chap->child.fd = out[0];
31643888Sbrian      chap->child.buf.len = 0;
31743888Sbrian      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
31843888Sbrian      write(in[1], "\n", 1);
31945907Sbrian      write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
32043888Sbrian      write(in[1], "\n", 1);
32143888Sbrian      write(in[1], name, strlen(name));
32243888Sbrian      write(in[1], "\n", 1);
32343888Sbrian      close(in[1]);
32443888Sbrian      break;
32543888Sbrian  }
32643888Sbrian}
32743888Sbrian
32843888Sbrianstatic void
32943888Sbrianchap_Cleanup(struct chap *chap, int sig)
33043888Sbrian{
33143888Sbrian  if (chap->child.pid) {
33243888Sbrian    int status;
33343888Sbrian
33443888Sbrian    close(chap->child.fd);
33543888Sbrian    chap->child.fd = -1;
33643888Sbrian    if (sig)
33743888Sbrian      kill(chap->child.pid, SIGTERM);
33843888Sbrian    chap->child.pid = 0;
33943888Sbrian    chap->child.buf.len = 0;
34043888Sbrian
34143888Sbrian    if (wait(&status) == -1)
34243888Sbrian      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
34343888Sbrian    else if (WIFSIGNALED(status))
34443888Sbrian      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
34543888Sbrian    else if (WIFEXITED(status) && WEXITSTATUS(status))
34643888Sbrian      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
34743888Sbrian  }
34845907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
34944123Sbrian#ifdef HAVE_DES
35044106Sbrian  chap->peertries = 0;
35144123Sbrian#endif
35243888Sbrian}
35343888Sbrian
35443888Sbrianstatic void
35544123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type
35644123Sbrian#ifdef HAVE_DES
35744123Sbrian             , int lm
35844123Sbrian#endif
35944123Sbrian            )
36043888Sbrian{
36144106Sbrian  u_char *ans;
36243888Sbrian
36345907Sbrian  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
36444123Sbrian#ifdef HAVE_DES
36567910Sbrian                         , chap->challenge.local, chap->authresponse, lm
36644123Sbrian#endif
36744123Sbrian                        );
36843888Sbrian
36943888Sbrian  if (ans) {
37043888Sbrian    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
37143888Sbrian               ans, *ans + 1 + strlen(name), name);
37244123Sbrian#ifdef HAVE_DES
37344106Sbrian    chap->NTRespSent = !lm;
37444123Sbrian#endif
37543888Sbrian    free(ans);
37643888Sbrian  } else
37743888Sbrian    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
37843888Sbrian               "Out of memory!", 14, NULL);
37943888Sbrian}
38043888Sbrian
38143888Sbrianstatic int
38258028Sbrianchap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
38343888Sbrian{
38443888Sbrian  struct chap *chap = descriptor2chap(d);
38543888Sbrian
38643888Sbrian  if (r && chap && chap->child.fd != -1) {
38743888Sbrian    FD_SET(chap->child.fd, r);
38843888Sbrian    if (*n < chap->child.fd + 1)
38943888Sbrian      *n = chap->child.fd + 1;
39043888Sbrian    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
39143888Sbrian    return 1;
39243888Sbrian  }
39343888Sbrian
39443888Sbrian  return 0;
39543888Sbrian}
39643888Sbrian
39743888Sbrianstatic int
39858028Sbrianchap_IsSet(struct fdescriptor *d, const fd_set *fdset)
39943888Sbrian{
40043888Sbrian  struct chap *chap = descriptor2chap(d);
40143888Sbrian
40243888Sbrian  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
40343888Sbrian}
40443888Sbrian
40543888Sbrianstatic void
40658028Sbrianchap_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
40743888Sbrian{
40843888Sbrian  struct chap *chap = descriptor2chap(d);
40943888Sbrian  int got;
41043888Sbrian
41143888Sbrian  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
41243888Sbrian             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
41343888Sbrian  if (got == -1) {
41443888Sbrian    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
41543888Sbrian    chap_Cleanup(chap, SIGTERM);
41643888Sbrian  } else if (got == 0) {
41743888Sbrian    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
41843888Sbrian    chap_Cleanup(chap, SIGTERM);
41943888Sbrian  } else {
42043888Sbrian    char *name, *key, *end;
42143888Sbrian
42243888Sbrian    chap->child.buf.len += got;
42343888Sbrian    chap->child.buf.ptr[chap->child.buf.len] = '\0';
42443888Sbrian    name = chap->child.buf.ptr;
42543888Sbrian    name += strspn(name, " \t");
42643888Sbrian    if ((key = strchr(name, '\n')) == NULL)
42743888Sbrian      end = NULL;
42843888Sbrian    else
42943888Sbrian      end = strchr(++key, '\n');
43043888Sbrian
43143888Sbrian    if (end == NULL) {
43243888Sbrian      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
43343888Sbrian        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
43443888Sbrian        chap_Cleanup(chap, SIGTERM);
43543888Sbrian      }
43643888Sbrian    } else {
43744123Sbrian#ifdef HAVE_DES
43844106Sbrian      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
43944106Sbrian                   ((chap->NTRespSent &&
44044106Sbrian                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
44144106Sbrian                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
44244123Sbrian#endif
44344106Sbrian
44443888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
44543888Sbrian        *end-- = '\0';
44643888Sbrian      end = key - 1;
44743888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
44843888Sbrian        *end-- = '\0';
44943888Sbrian      key += strspn(key, " \t");
45043888Sbrian
45144123Sbrian      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
45244123Sbrian#ifdef HAVE_DES
45344123Sbrian                   , lanman
45444123Sbrian#endif
45544123Sbrian                  );
45643888Sbrian      chap_Cleanup(chap, 0);
45743888Sbrian    }
45843888Sbrian  }
45943888Sbrian}
46043888Sbrian
46143888Sbrianstatic int
46258028Sbrianchap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
46343888Sbrian{
46443888Sbrian  /* We never want to write here ! */
46543888Sbrian  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
46643888Sbrian  return 0;
46743888Sbrian}
46843888Sbrian
46943888Sbrianstatic void
47067910Sbrianchap_ChallengeInit(struct authinfo *authp)
47143693Sbrian{
47243693Sbrian  struct chap *chap = auth2chap(authp);
47313379Sphk  int len, i;
4746059Samurai  char *cp;
4756059Samurai
47643888Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
47743401Sbrian
47845907Sbrian  if (!*chap->challenge.local) {
47943888Sbrian    randinit();
48045907Sbrian    cp = chap->challenge.local;
48143888Sbrian
48243313Sbrian#ifndef NORADIUS
48343888Sbrian    if (*authp->physical->dl->bundle->radius.cfg.file) {
48443888Sbrian      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
48543888Sbrian      *cp++ = 16;
48643888Sbrian      for (i = 0; i < 16; i++)
48743888Sbrian        *cp++ = (random() % 10) + '0';
48843888Sbrian    } else
48943313Sbrian#endif
49043888Sbrian    {
49144106Sbrian#ifdef HAVE_DES
49244106Sbrian      if (authp->physical->link.lcp.want_authtype == 0x80)
49344106Sbrian        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
49467910Sbrian      else if (authp->physical->link.lcp.want_authtype == 0x81)
49567910Sbrian        *cp++ = 16;	/* MS-CHAP-V2 does 16 bytes challenges */
49644106Sbrian      else
49744106Sbrian#endif
49844106Sbrian        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
49945907Sbrian      for (i = 0; i < *chap->challenge.local; i++)
50043888Sbrian        *cp++ = random() & 0xff;
50143888Sbrian    }
50243888Sbrian    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
50343313Sbrian  }
5046059Samurai}
5056059Samurai
50630715Sbrianstatic void
50767910Sbrianchap_Challenge(struct authinfo *authp)
50867910Sbrian{
50967910Sbrian  struct chap *chap = auth2chap(authp);
51067910Sbrian  int len;
51167910Sbrian
51267910Sbrian  log_Printf(LogDEBUG, "CHAP%02X: Challenge\n", authp->physical->link.lcp.want_authtype);
51367910Sbrian
51467910Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
51567910Sbrian
51667910Sbrian  /* Generate new local challenge value */
51767910Sbrian  if (!*chap->challenge.local)
51867910Sbrian    chap_ChallengeInit(authp);
51967910Sbrian
52067910Sbrian#ifdef HAVE_DES
52167910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81)
52267910Sbrian    ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
52367910Sbrian             chap->challenge.local, 1 + *chap->challenge.local, NULL);
52467910Sbrian  else
52567910Sbrian#endif
52667910Sbrian    ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
52767910Sbrian             chap->challenge.local, 1 + *chap->challenge.local + len, NULL);
52867910Sbrian}
52967910Sbrian
53067910Sbrianstatic void
53143693Sbrianchap_Success(struct authinfo *authp)
5326059Samurai{
53367910Sbrian  char *msg;
53443693Sbrian  datalink_GotAuthname(authp->physical->dl, authp->in.name);
53567910Sbrian#ifdef HAVE_DES
53667910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81) {
53767910Sbrian    msg = auth2chap(authp)->authresponse;
53867910Sbrian    MPPE_MasterKeyValid = 1;
53967910Sbrian  } else
54067910Sbrian#endif
54167910Sbrian    msg = "Welcome!!";
54267910Sbrian
54367910Sbrian  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg) + 1,
54467910Sbrian             NULL);
54567910Sbrian
54643693Sbrian  authp->physical->link.lcp.auth_ineed = 0;
54743693Sbrian  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
54843693Sbrian    physical_Login(authp->physical, authp->in.name);
5496059Samurai
55043693Sbrian  if (authp->physical->link.lcp.auth_iwait == 0)
55143693Sbrian    /*
55243693Sbrian     * Either I didn't need to authenticate, or I've already been
55343693Sbrian     * told that I got the answer right.
55443693Sbrian     */
55543693Sbrian    datalink_AuthOk(authp->physical->dl);
55643693Sbrian}
5576059Samurai
55843693Sbrianstatic void
55943693Sbrianchap_Failure(struct authinfo *authp)
56043693Sbrian{
56167910Sbrian#ifdef HAVE_DES
56267910Sbrian  char buf[1024];
56367910Sbrian#endif
56467910Sbrian  char *msg;
56567910Sbrian
56667910Sbrian#ifdef HAVE_DES
56767910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81) {
56867910Sbrian    int i;
56967910Sbrian
57067910Sbrian    msg = buf;
57167910Sbrian    msg += sprintf(buf, "E=691 R=0 C=");
57267910Sbrian    for (i=0; i<16; i++)
57367910Sbrian      msg += sprintf(msg, "%02X", *(auth2chap(authp)->challenge.local+1+i));
57467910Sbrian
57567910Sbrian    sprintf(msg, " V=3 M=Invalid!");
57667910Sbrian    msg = buf;
57767910Sbrian  } else
57867910Sbrian#endif
57967910Sbrian    msg = "Invalid!!";
58067910Sbrian
58167910Sbrian  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1,
58267910Sbrian             NULL);
58343693Sbrian  datalink_AuthNotOk(authp->physical->dl);
58443693Sbrian}
58537926Sbrian
58644106Sbrianstatic int
58744123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
58844123Sbrian#ifdef HAVE_DES
58944123Sbrian         , int lm
59044123Sbrian#endif
59144123Sbrian        )
59244106Sbrian{
59344106Sbrian  if (mylen != hislen)
59444106Sbrian    return 0;
59544123Sbrian#ifdef HAVE_DES
59644106Sbrian  else if (type == 0x80) {
59744106Sbrian    int off = lm ? 0 : 24;
59844106Sbrian
59944106Sbrian    if (memcmp(myans + off, hisans + off, 24))
60044106Sbrian      return 0;
60144123Sbrian  }
60244123Sbrian#endif
60344123Sbrian  else if (memcmp(myans, hisans, mylen))
60444106Sbrian    return 0;
60544106Sbrian
60644106Sbrian  return 1;
60744106Sbrian}
60844106Sbrian
60944123Sbrian#ifdef HAVE_DES
61044106Sbrianstatic int
61144106Sbrianchap_HaveAnotherGo(struct chap *chap)
61244106Sbrian{
61344106Sbrian  if (++chap->peertries < 3) {
61444106Sbrian    /* Give the peer another shot */
61545907Sbrian    *chap->challenge.local = '\0';
61644106Sbrian    chap_Challenge(&chap->auth);
61744106Sbrian    return 1;
61844106Sbrian  }
61944106Sbrian
62044106Sbrian  return 0;
62144106Sbrian}
62244123Sbrian#endif
62344106Sbrian
62443693Sbrianvoid
62543693Sbrianchap_Init(struct chap *chap, struct physical *p)
62643693Sbrian{
62743888Sbrian  chap->desc.type = CHAP_DESCRIPTOR;
62843888Sbrian  chap->desc.UpdateSet = chap_UpdateSet;
62943888Sbrian  chap->desc.IsSet = chap_IsSet;
63043888Sbrian  chap->desc.Read = chap_Read;
63143888Sbrian  chap->desc.Write = chap_Write;
63243888Sbrian  chap->child.pid = 0;
63343888Sbrian  chap->child.fd = -1;
63443693Sbrian  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
63545907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
63644123Sbrian#ifdef HAVE_DES
63744106Sbrian  chap->NTRespSent = 0;
63844106Sbrian  chap->peertries = 0;
63944123Sbrian#endif
64043693Sbrian}
64129840Sbrian
64243693Sbrianvoid
64343888Sbrianchap_ReInit(struct chap *chap)
64443888Sbrian{
64543888Sbrian  chap_Cleanup(chap, SIGTERM);
64643888Sbrian}
64743888Sbrian
64846686Sbrianstruct mbuf *
64946686Sbrianchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
65043693Sbrian{
65146686Sbrian  struct physical *p = link2physical(l);
65243693Sbrian  struct chap *chap = &p->dl->chap;
65344106Sbrian  char *name, *key, *ans;
65444123Sbrian  int len, nlen;
65548817Sbrian  u_char alen;
65644123Sbrian#ifdef HAVE_DES
65744123Sbrian  int lanman;
65844123Sbrian#endif
65929840Sbrian
66046686Sbrian  if (p == NULL) {
66146686Sbrian    log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
66254912Sbrian    m_freem(bp);
66346686Sbrian    return NULL;
66446686Sbrian  }
66546686Sbrian
66646686Sbrian  if (bundle_Phase(bundle) != PHASE_NETWORK &&
66746686Sbrian      bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
66845220Sbrian    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
66954912Sbrian    m_freem(bp);
67046686Sbrian    return NULL;
67145220Sbrian  }
67245220Sbrian
67354912Sbrian  m_settype(bp, MB_CHAPIN);
67444159Sbrian  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
67544159Sbrian      ntohs(chap->auth.in.hdr.length) == 0)
67644159Sbrian    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
67743693Sbrian  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
67843693Sbrian    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
67943693Sbrian               chap->auth.in.hdr.code);
68043693Sbrian  else {
68154912Sbrian    len = m_length(bp);
68243693Sbrian    ans = NULL;
68343693Sbrian
68443693Sbrian    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
68543693Sbrian        chap->auth.id != chap->auth.in.hdr.id &&
68646686Sbrian        Enabled(bundle, OPT_IDCHECK)) {
68743693Sbrian      /* Wrong conversation dude ! */
68843693Sbrian      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
68943693Sbrian                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
69043693Sbrian                 chap->auth.id);
69154912Sbrian      m_freem(bp);
69246686Sbrian      return NULL;
69325630Sbrian    }
69443693Sbrian    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
69529840Sbrian
69644123Sbrian#ifdef HAVE_DES
69744106Sbrian    lanman = 0;
69844123Sbrian#endif
69943693Sbrian    switch (chap->auth.in.hdr.code) {
70043693Sbrian      case CHAP_CHALLENGE:
70144106Sbrian        bp = mbuf_Read(bp, &alen, 1);
70244106Sbrian        len -= alen + 1;
70343693Sbrian        if (len < 0) {
70443693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
70554912Sbrian          m_freem(bp);
70646686Sbrian          return NULL;
70743693Sbrian        }
70845907Sbrian        *chap->challenge.peer = alen;
70945907Sbrian        bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
71043693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
71144123Sbrian#ifdef HAVE_DES
71244106Sbrian        lanman = p->link.lcp.his_authtype == 0x80 &&
71344106Sbrian                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
71444106Sbrian                  !IsAccepted(p->link.lcp.cfg.chap80nt));
71567910Sbrian
71667910Sbrian        /* Generate local challenge value */
71767910Sbrian        chap_ChallengeInit(&chap->auth);
71844123Sbrian#endif
71943693Sbrian        break;
72043313Sbrian
72143693Sbrian      case CHAP_RESPONSE:
72243693Sbrian        auth_StopTimer(&chap->auth);
72343693Sbrian        bp = mbuf_Read(bp, &alen, 1);
72443693Sbrian        len -= alen + 1;
72543693Sbrian        if (len < 0) {
72643693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
72754912Sbrian          m_freem(bp);
72846686Sbrian          return NULL;
72943693Sbrian        }
73043693Sbrian        if ((ans = malloc(alen + 2)) == NULL) {
73143693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
73254912Sbrian          m_freem(bp);
73346686Sbrian          return NULL;
73443693Sbrian        }
73543693Sbrian        *ans = chap->auth.id;
73643693Sbrian        bp = mbuf_Read(bp, ans + 1, alen);
73743693Sbrian        ans[alen+1] = '\0';
73843693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
73944123Sbrian#ifdef HAVE_DES
74067910Sbrian        lanman = p->link.lcp.want_authtype == 0x80 &&
74167910Sbrian                 alen == 49 && ans[alen] == 0;
74244123Sbrian#endif
74343693Sbrian        break;
74443313Sbrian
74543693Sbrian      case CHAP_SUCCESS:
74643693Sbrian      case CHAP_FAILURE:
74743693Sbrian        /* chap->auth.in.name is already set up at CHALLENGE time */
74843693Sbrian        if ((ans = malloc(len + 1)) == NULL) {
74943693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
75054912Sbrian          m_freem(bp);
75146686Sbrian          return NULL;
75243693Sbrian        }
75343693Sbrian        bp = mbuf_Read(bp, ans, len);
75443693Sbrian        ans[len] = '\0';
75543693Sbrian        break;
75643401Sbrian    }
75736285Sbrian
75843693Sbrian    switch (chap->auth.in.hdr.code) {
75943693Sbrian      case CHAP_CHALLENGE:
76043693Sbrian      case CHAP_RESPONSE:
76143693Sbrian        if (*chap->auth.in.name)
76244106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
76344106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
76444106Sbrian                     chap->auth.in.name,
76544123Sbrian#ifdef HAVE_DES
76644106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
76744123Sbrian                     " - lanman" :
76844123Sbrian#endif
76944123Sbrian                     "");
77043693Sbrian        else
77144106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
77244106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
77344123Sbrian#ifdef HAVE_DES
77444106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
77544123Sbrian                     " - lanman" :
77644123Sbrian#endif
77744123Sbrian                     "");
77843693Sbrian        break;
77936285Sbrian
78043693Sbrian      case CHAP_SUCCESS:
78143693Sbrian      case CHAP_FAILURE:
78243693Sbrian        if (*ans)
78343693Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
78443693Sbrian                     chapcodes[chap->auth.in.hdr.code], ans);
78543693Sbrian        else
78643693Sbrian          log_Printf(LogPHASE, "Chap Input: %s\n",
78743693Sbrian                     chapcodes[chap->auth.in.hdr.code]);
78843693Sbrian        break;
7896059Samurai    }
7906059Samurai
79143693Sbrian    switch (chap->auth.in.hdr.code) {
79243693Sbrian      case CHAP_CHALLENGE:
79364465Sbrian        if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!')
79446686Sbrian          chap_StartChild(chap, bundle->cfg.auth.key + 1,
79546686Sbrian                          bundle->cfg.auth.name);
79643888Sbrian        else
79764465Sbrian          chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key +
79864465Sbrian                       (*bundle->cfg.auth.key == '!' ? 1 : 0),
79964465Sbrian                       p->link.lcp.his_authtype
80044123Sbrian#ifdef HAVE_DES
80144123Sbrian                       , lanman
80244123Sbrian#endif
80344123Sbrian                      );
80443693Sbrian        break;
8056059Samurai
80643693Sbrian      case CHAP_RESPONSE:
80743693Sbrian        name = chap->auth.in.name;
80843693Sbrian        nlen = strlen(name);
80943693Sbrian#ifndef NORADIUS
81046686Sbrian        if (*bundle->radius.cfg.file) {
81148817Sbrian          u_char end;
81248817Sbrian
81345907Sbrian          end = chap->challenge.local[*chap->challenge.local+1];
81445907Sbrian          chap->challenge.local[*chap->challenge.local+1] = '\0';
81546686Sbrian          radius_Authenticate(&bundle->radius, &chap->auth,
81645907Sbrian                              chap->auth.in.name, ans,
81745907Sbrian                              chap->challenge.local + 1);
81845907Sbrian          chap->challenge.local[*chap->challenge.local+1] = end;
81943693Sbrian        } else
82043693Sbrian#endif
82143693Sbrian        {
82246686Sbrian          key = auth_GetSecret(bundle, name, nlen, p);
82343693Sbrian          if (key) {
82444106Sbrian            char *myans;
82544123Sbrian#ifdef HAVE_DES
82667910Sbrian            if (p->link.lcp.want_authtype == 0x80 &&
82767910Sbrian                lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
82844106Sbrian              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
82944106Sbrian              if (chap_HaveAnotherGo(chap))
83044106Sbrian                break;
83143693Sbrian              key = NULL;
83267910Sbrian            } else if (p->link.lcp.want_authtype == 0x80 &&
83367910Sbrian                !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) {
83444106Sbrian              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
83544106Sbrian              if (chap_HaveAnotherGo(chap))
83644106Sbrian                break;
83744106Sbrian              key = NULL;
83867910Sbrian            } else if (p->link.lcp.want_authtype == 0x81 &&
83967910Sbrian                !IsEnabled(p->link.lcp.cfg.chap81)) {
84067910Sbrian              log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n");
84167910Sbrian              key = NULL;
84244123Sbrian            } else
84344123Sbrian#endif
84444123Sbrian            {
84567910Sbrian#ifdef HAVE_DES
84667910Sbrian              /* Get peer's challenge */
84767910Sbrian              if (p->link.lcp.want_authtype == 0x81) {
84867910Sbrian                chap->challenge.peer[0] = CHAP81_CHALLENGE_LEN;
84967910Sbrian                memcpy(chap->challenge.peer + 1, ans + 1, CHAP81_CHALLENGE_LEN);
85067910Sbrian              }
85167910Sbrian#endif
85267910Sbrian
85344106Sbrian              myans = chap_BuildAnswer(name, key, chap->auth.id,
85445907Sbrian                                       chap->challenge.local,
85544123Sbrian                                       p->link.lcp.want_authtype
85644123Sbrian#ifdef HAVE_DES
85767910Sbrian                                       , chap->challenge.peer,
85867910Sbrian                                       chap->authresponse, lanman
85944123Sbrian#endif
86044123Sbrian                                      );
86144106Sbrian              if (myans == NULL)
86243693Sbrian                key = NULL;
86344106Sbrian              else {
86444123Sbrian                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
86544123Sbrian                              ans + 1, alen
86644123Sbrian#ifdef HAVE_DES
86744123Sbrian                              , lanman
86844123Sbrian#endif
86944123Sbrian                             ))
87044106Sbrian                  key = NULL;
87144106Sbrian                free(myans);
87244106Sbrian              }
87343693Sbrian            }
87443693Sbrian          }
8756059Samurai
87643693Sbrian          if (key)
87743693Sbrian            chap_Success(&chap->auth);
87843693Sbrian          else
87943693Sbrian            chap_Failure(&chap->auth);
88043693Sbrian        }
8816059Samurai
88243693Sbrian        break;
8836059Samurai
8846059Samurai      case CHAP_SUCCESS:
88543693Sbrian        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
88643693Sbrian          p->link.lcp.auth_iwait = 0;
88767910Sbrian          if (p->link.lcp.auth_ineed == 0) {
88867910Sbrian#ifdef HAVE_DES
88967910Sbrian            if (p->link.lcp.his_authtype == 0x81) {
89067910Sbrian              if (strncmp(ans, chap->authresponse, 42)) {
89167910Sbrian                datalink_AuthNotOk(p->dl);
89267910Sbrian	        log_Printf(LogDEBUG, "CHAP81: AuthenticatorResponse: (%s) != ans: (%s)\n", chap->authresponse, ans);
89367910Sbrian
89467910Sbrian              } else {
89567910Sbrian                /* Successful login */
89667910Sbrian                MPPE_MasterKeyValid = 1;
89767910Sbrian                datalink_AuthOk(p->dl);
89867910Sbrian              }
89967910Sbrian            } else
90067910Sbrian#endif
90143693Sbrian            /*
90243693Sbrian             * We've succeeded in our ``login''
90343693Sbrian             * If we're not expecting  the peer to authenticate (or he already
90443693Sbrian             * has), proceed to network phase.
90543693Sbrian             */
90643693Sbrian            datalink_AuthOk(p->dl);
90767910Sbrian          }
90843693Sbrian        }
90943693Sbrian        break;
91043693Sbrian
9116059Samurai      case CHAP_FAILURE:
91243693Sbrian        datalink_AuthNotOk(p->dl);
91343693Sbrian        break;
9146059Samurai    }
91543693Sbrian    free(ans);
9166059Samurai  }
91743693Sbrian
91854912Sbrian  m_freem(bp);
91946686Sbrian  return NULL;
9206059Samurai}
921