chap.c revision 80763
178189Sbrian/*-
278189Sbrian * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
378189Sbrian *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
478189Sbrian *                           Internet Initiative Japan, Inc (IIJ)
578189Sbrian * All rights reserved.
66059Samurai *
778189Sbrian * Redistribution and use in source and binary forms, with or without
878189Sbrian * modification, are permitted provided that the following conditions
978189Sbrian * are met:
1078189Sbrian * 1. Redistributions of source code must retain the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer.
1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer in the
1478189Sbrian *    documentation and/or other materials provided with the distribution.
156059Samurai *
1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1978189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2678189Sbrian * SUCH DAMAGE.
276059Samurai *
2850479Speter * $FreeBSD: head/usr.sbin/ppp/chap.c 80763 2001-07-31 21:36:00Z brian $
296059Samurai */
3078189Sbrian
3143313Sbrian#include <sys/param.h>
3230715Sbrian#include <netinet/in.h>
3336285Sbrian#include <netinet/in_systm.h>
3436285Sbrian#include <netinet/ip.h>
3536285Sbrian#include <sys/un.h>
3630715Sbrian
3743888Sbrian#include <errno.h>
3843888Sbrian#include <fcntl.h>
3937192Sbrian#ifdef HAVE_DES
4036287Sbrian#include <md4.h>
4137192Sbrian#endif
4230715Sbrian#include <md5.h>
4343888Sbrian#include <paths.h>
4443888Sbrian#include <signal.h>
4549976Sbrian#include <stdio.h>
4630715Sbrian#include <stdlib.h>
4744106Sbrian#include <string.h>
4843888Sbrian#include <sys/wait.h>
4936285Sbrian#include <termios.h>
5043888Sbrian#include <unistd.h>
5129840Sbrian
5246686Sbrian#include "layer.h"
5330715Sbrian#include "mbuf.h"
5430715Sbrian#include "log.h"
5530715Sbrian#include "defs.h"
5630715Sbrian#include "timer.h"
576059Samurai#include "fsm.h"
5846686Sbrian#include "proto.h"
5936285Sbrian#include "lqr.h"
606059Samurai#include "hdlc.h"
6163484Sbrian#include "lcp.h"
626735Samurai#include "auth.h"
6336285Sbrian#include "async.h"
6436285Sbrian#include "throughput.h"
6536285Sbrian#include "descriptor.h"
6643888Sbrian#include "chap.h"
6736285Sbrian#include "iplist.h"
6836285Sbrian#include "slcompress.h"
6936285Sbrian#include "ipcp.h"
7036285Sbrian#include "filter.h"
7136285Sbrian#include "ccp.h"
7236285Sbrian#include "link.h"
7336285Sbrian#include "physical.h"
7436285Sbrian#include "mp.h"
7543313Sbrian#ifndef NORADIUS
7643313Sbrian#include "radius.h"
7743313Sbrian#endif
7836285Sbrian#include "bundle.h"
7936285Sbrian#include "chat.h"
8038174Sbrian#include "cbcp.h"
8143888Sbrian#include "command.h"
8236285Sbrian#include "datalink.h"
8337192Sbrian#ifdef HAVE_DES
8436287Sbrian#include "chap_ms.h"
8567910Sbrian#include "mppe.h"
8637192Sbrian#endif
8755253Sbrian#include "id.h"
886059Samurai
8955146Sbrianstatic const char * const chapcodes[] = {
9019866Sphk  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
916059Samurai};
9243693Sbrian#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
936059Samurai
9430715Sbrianstatic void
9536285SbrianChapOutput(struct physical *physical, u_int code, u_int id,
9643693Sbrian	   const u_char *ptr, int count, const char *text)
976059Samurai{
986059Samurai  int plen;
996059Samurai  struct fsmheader lh;
1006059Samurai  struct mbuf *bp;
1016059Samurai
10228679Sbrian  plen = sizeof(struct fsmheader) + count;
1036059Samurai  lh.code = code;
1046059Samurai  lh.id = id;
1056059Samurai  lh.length = htons(plen);
10654912Sbrian  bp = m_get(plen, MB_CHAPOUT);
10730715Sbrian  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
1086059Samurai  if (count)
10930715Sbrian    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
11036285Sbrian  log_DumpBp(LogDEBUG, "ChapOutput", bp);
11137926Sbrian  if (text == NULL)
11237926Sbrian    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
11337926Sbrian  else
11437926Sbrian    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
11546686Sbrian  link_PushPacket(&physical->link, bp, physical->dl->bundle,
11650867Sbrian                  LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
1176059Samurai}
1186059Samurai
11943693Sbrianstatic char *
12044123Sbrianchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
12144123Sbrian#ifdef HAVE_DES
12267910Sbrian                 , char *peerchallenge, char *authresponse, int lanman
12344123Sbrian#endif
12444123Sbrian                )
1256059Samurai{
12643693Sbrian  char *result, *digest;
12743693Sbrian  size_t nlen, klen;
12843693Sbrian
12943693Sbrian  nlen = strlen(name);
13043693Sbrian  klen = strlen(key);
13143693Sbrian
13243693Sbrian#ifdef HAVE_DES
13344106Sbrian  if (type == 0x80) {
13443693Sbrian    char expkey[AUTHLEN << 2];
13543693Sbrian    MD4_CTX MD4context;
13643693Sbrian    int f;
13743693Sbrian
13843693Sbrian    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
13943693Sbrian      return result;
14043693Sbrian
14144106Sbrian    digest = result;					/* the response */
14244106Sbrian    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
14344106Sbrian    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
14444106Sbrian    if (lanman) {
14544106Sbrian      memset(digest + 24, '\0', 25);
14644106Sbrian      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
14744106Sbrian    } else {
14844106Sbrian      memset(digest, '\0', 25);
14944106Sbrian      digest += 24;
15043693Sbrian
15144106Sbrian      for (f = 0; f < klen; f++) {
15244106Sbrian        expkey[2*f] = key[f];
15344106Sbrian        expkey[2*f+1] = '\0';
15444106Sbrian      }
15544106Sbrian      /*
15644106Sbrian       *           -----------
15744106Sbrian       * expkey = | k\0e\0y\0 |
15844106Sbrian       *           -----------
15944106Sbrian       */
16044106Sbrian      MD4Init(&MD4context);
16144106Sbrian      MD4Update(&MD4context, expkey, klen << 1);
16244106Sbrian      MD4Final(digest, &MD4context);
16344106Sbrian
16444106Sbrian      /*
16544106Sbrian       *           ---- -------- ---------------- ------- ------
16644106Sbrian       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
16744106Sbrian       *           ---- -------- ---------------- ------- ------
16844106Sbrian       */
16944106Sbrian      mschap_NT(digest, challenge + 1);
17043693Sbrian    }
17143693Sbrian    /*
17244106Sbrian     *           ---- -------- ------------- ----- ------
17344106Sbrian     *          |    |  struct MS_ChapResponse24  |      |
17444106Sbrian     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
17544106Sbrian     *           ---- -------- ------------- ----- ------
17644106Sbrian     * where only one of LANMan & NT digest are set.
17743693Sbrian     */
17867910Sbrian  } else if (type == 0x81) {
17967910Sbrian    char expkey[AUTHLEN << 2];
18067910Sbrian    char pwdhash[CHAP81_HASH_LEN];
18167910Sbrian    char pwdhashhash[CHAP81_HASH_LEN];
18267910Sbrian    char *ntresponse;
18367910Sbrian    int f;
18467910Sbrian
18567910Sbrian    if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL)
18667910Sbrian      return result;
18767910Sbrian
18867910Sbrian    memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN);
18967910Sbrian
19067910Sbrian    digest = result;
19167910Sbrian    *digest++ = CHAP81_RESPONSE_LEN;		/* value size */
19267910Sbrian
19367910Sbrian    /* Copy our challenge */
19467910Sbrian    memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN);
19567910Sbrian
19667910Sbrian    /* Expand password to Unicode XXX */
19767910Sbrian    for (f = 0; f < klen; f++) {
19867910Sbrian      expkey[2*f] = key[f];
19967910Sbrian      expkey[2*f+1] = '\0';
20067910Sbrian    }
20167910Sbrian
20267910Sbrian    ntresponse = digest + CHAP81_NTRESPONSE_OFF;
20367910Sbrian
20467910Sbrian    /* Get some needed hashes */
20567910Sbrian    NtPasswordHash(expkey, klen * 2, pwdhash);
20667910Sbrian    HashNtPasswordHash(pwdhash, pwdhashhash);
20767910Sbrian
20867910Sbrian    /* Generate NTRESPONSE to respond on challenge call */
20967910Sbrian    GenerateNTResponse(challenge + 1, peerchallenge + 1, name, nlen,
21067910Sbrian                       expkey, klen * 2, ntresponse);
21167910Sbrian
21267910Sbrian    /* Generate MPPE MASTERKEY */
21368461Sbrian    GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey);    /* XXX Global ! */
21467910Sbrian
21567910Sbrian    /* Generate AUTHRESPONSE to verify on auth success */
21667910Sbrian    GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse,
21767910Sbrian                                  peerchallenge + 1, challenge + 1, name, nlen,
21867910Sbrian                                  authresponse);
21967910Sbrian
22067910Sbrian    authresponse[CHAP81_AUTHRESPONSE_LEN] = 0;
22167910Sbrian
22267910Sbrian    memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen);
22343693Sbrian  } else
22443693Sbrian#endif
22543693Sbrian  if ((result = malloc(nlen + 17)) != NULL) {
22643693Sbrian    /* Normal MD5 stuff */
22743693Sbrian    MD5_CTX MD5context;
22843693Sbrian
22943693Sbrian    digest = result;
23043693Sbrian    *digest++ = 16;				/* value size */
23143693Sbrian
23243693Sbrian    MD5Init(&MD5context);
23343693Sbrian    MD5Update(&MD5context, &id, 1);
23443693Sbrian    MD5Update(&MD5context, key, klen);
23543693Sbrian    MD5Update(&MD5context, challenge + 1, *challenge);
23643693Sbrian    MD5Final(digest, &MD5context);
23743693Sbrian
23843693Sbrian    memcpy(digest + 16, name, nlen);
23943693Sbrian    /*
24043693Sbrian     *           ---- -------- ------
24143693Sbrian     * result = | 16 | digest | name |
24243693Sbrian     *           ---- -------- ------
24343693Sbrian     */
24443693Sbrian  }
24543693Sbrian
24643693Sbrian  return result;
24743693Sbrian}
24843693Sbrian
24943693Sbrianstatic void
25043888Sbrianchap_StartChild(struct chap *chap, char *prog, const char *name)
25143888Sbrian{
25243888Sbrian  char *argv[MAXARGS], *nargv[MAXARGS];
25343888Sbrian  int argc, fd;
25443888Sbrian  int in[2], out[2];
25547849Sbrian  pid_t pid;
25643888Sbrian
25743888Sbrian  if (chap->child.fd != -1) {
25843888Sbrian    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
25943888Sbrian    return;
26043888Sbrian  }
26143888Sbrian
26243888Sbrian  if (pipe(in) == -1) {
26343888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
26443888Sbrian    return;
26543888Sbrian  }
26643888Sbrian
26743888Sbrian  if (pipe(out) == -1) {
26843888Sbrian    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
26943888Sbrian    close(in[0]);
27043888Sbrian    close(in[1]);
27143888Sbrian    return;
27243888Sbrian  }
27343888Sbrian
27447849Sbrian  pid = getpid();
27543888Sbrian  switch ((chap->child.pid = fork())) {
27643888Sbrian    case -1:
27743888Sbrian      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
27843888Sbrian      close(in[0]);
27943888Sbrian      close(in[1]);
28043888Sbrian      close(out[0]);
28143888Sbrian      close(out[1]);
28243888Sbrian      chap->child.pid = 0;
28343888Sbrian      return;
28443888Sbrian
28543888Sbrian    case 0:
28643888Sbrian      timer_TermService();
28754914Sbrian
28854914Sbrian      if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) {
28954914Sbrian        if (argc < 0) {
29054914Sbrian          log_Printf(LogWARN, "CHAP: Invalid command syntax\n");
29154914Sbrian          _exit(255);
29254914Sbrian        }
29354914Sbrian        _exit(0);
29454914Sbrian      }
29554914Sbrian
29643888Sbrian      close(in[1]);
29743888Sbrian      close(out[0]);
29849976Sbrian      if (out[1] == STDIN_FILENO)
29949976Sbrian        out[1] = dup(out[1]);
30043888Sbrian      dup2(in[0], STDIN_FILENO);
30143888Sbrian      dup2(out[1], STDOUT_FILENO);
30249976Sbrian      close(STDERR_FILENO);
30349976Sbrian      if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
30443888Sbrian        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
30543888Sbrian                  _PATH_DEVNULL, strerror(errno));
30643888Sbrian        exit(1);
30743888Sbrian      }
30849976Sbrian      for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
30949976Sbrian        fcntl(fd, F_SETFD, 1);
31064802Sbrian#ifndef NOSUID
31155252Sbrian      setuid(ID0realuid());
31264802Sbrian#endif
31343888Sbrian      command_Expand(nargv, argc, (char const *const *)argv,
31447849Sbrian                     chap->auth.physical->dl->bundle, 0, pid);
31543888Sbrian      execvp(nargv[0], nargv);
31649976Sbrian      printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
31749976Sbrian      _exit(255);
31843888Sbrian
31943888Sbrian    default:
32043888Sbrian      close(in[0]);
32143888Sbrian      close(out[1]);
32243888Sbrian      chap->child.fd = out[0];
32343888Sbrian      chap->child.buf.len = 0;
32443888Sbrian      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
32543888Sbrian      write(in[1], "\n", 1);
32645907Sbrian      write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
32743888Sbrian      write(in[1], "\n", 1);
32843888Sbrian      write(in[1], name, strlen(name));
32943888Sbrian      write(in[1], "\n", 1);
33043888Sbrian      close(in[1]);
33143888Sbrian      break;
33243888Sbrian  }
33343888Sbrian}
33443888Sbrian
33543888Sbrianstatic void
33643888Sbrianchap_Cleanup(struct chap *chap, int sig)
33743888Sbrian{
33843888Sbrian  if (chap->child.pid) {
33943888Sbrian    int status;
34043888Sbrian
34143888Sbrian    close(chap->child.fd);
34243888Sbrian    chap->child.fd = -1;
34343888Sbrian    if (sig)
34443888Sbrian      kill(chap->child.pid, SIGTERM);
34543888Sbrian    chap->child.pid = 0;
34643888Sbrian    chap->child.buf.len = 0;
34743888Sbrian
34843888Sbrian    if (wait(&status) == -1)
34943888Sbrian      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
35043888Sbrian    else if (WIFSIGNALED(status))
35143888Sbrian      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
35243888Sbrian    else if (WIFEXITED(status) && WEXITSTATUS(status))
35343888Sbrian      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
35443888Sbrian  }
35545907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
35644123Sbrian#ifdef HAVE_DES
35744106Sbrian  chap->peertries = 0;
35844123Sbrian#endif
35943888Sbrian}
36043888Sbrian
36143888Sbrianstatic void
36244123Sbrianchap_Respond(struct chap *chap, char *name, char *key, u_char type
36344123Sbrian#ifdef HAVE_DES
36444123Sbrian             , int lm
36544123Sbrian#endif
36644123Sbrian            )
36743888Sbrian{
36844106Sbrian  u_char *ans;
36943888Sbrian
37045907Sbrian  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
37144123Sbrian#ifdef HAVE_DES
37267910Sbrian                         , chap->challenge.local, chap->authresponse, lm
37344123Sbrian#endif
37444123Sbrian                        );
37543888Sbrian
37643888Sbrian  if (ans) {
37743888Sbrian    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
37843888Sbrian               ans, *ans + 1 + strlen(name), name);
37944123Sbrian#ifdef HAVE_DES
38044106Sbrian    chap->NTRespSent = !lm;
38168461Sbrian    MPPE_IsServer = 0;		/* XXX Global ! */
38244123Sbrian#endif
38343888Sbrian    free(ans);
38443888Sbrian  } else
38543888Sbrian    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
38643888Sbrian               "Out of memory!", 14, NULL);
38743888Sbrian}
38843888Sbrian
38943888Sbrianstatic int
39058028Sbrianchap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
39143888Sbrian{
39243888Sbrian  struct chap *chap = descriptor2chap(d);
39343888Sbrian
39443888Sbrian  if (r && chap && chap->child.fd != -1) {
39543888Sbrian    FD_SET(chap->child.fd, r);
39643888Sbrian    if (*n < chap->child.fd + 1)
39743888Sbrian      *n = chap->child.fd + 1;
39843888Sbrian    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
39943888Sbrian    return 1;
40043888Sbrian  }
40143888Sbrian
40243888Sbrian  return 0;
40343888Sbrian}
40443888Sbrian
40543888Sbrianstatic int
40658028Sbrianchap_IsSet(struct fdescriptor *d, const fd_set *fdset)
40743888Sbrian{
40843888Sbrian  struct chap *chap = descriptor2chap(d);
40943888Sbrian
41043888Sbrian  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
41143888Sbrian}
41243888Sbrian
41343888Sbrianstatic void
41458028Sbrianchap_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
41543888Sbrian{
41643888Sbrian  struct chap *chap = descriptor2chap(d);
41743888Sbrian  int got;
41843888Sbrian
41943888Sbrian  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
42043888Sbrian             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
42143888Sbrian  if (got == -1) {
42243888Sbrian    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
42343888Sbrian    chap_Cleanup(chap, SIGTERM);
42443888Sbrian  } else if (got == 0) {
42543888Sbrian    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
42643888Sbrian    chap_Cleanup(chap, SIGTERM);
42743888Sbrian  } else {
42843888Sbrian    char *name, *key, *end;
42943888Sbrian
43043888Sbrian    chap->child.buf.len += got;
43143888Sbrian    chap->child.buf.ptr[chap->child.buf.len] = '\0';
43243888Sbrian    name = chap->child.buf.ptr;
43343888Sbrian    name += strspn(name, " \t");
43443888Sbrian    if ((key = strchr(name, '\n')) == NULL)
43543888Sbrian      end = NULL;
43643888Sbrian    else
43743888Sbrian      end = strchr(++key, '\n');
43843888Sbrian
43943888Sbrian    if (end == NULL) {
44043888Sbrian      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
44143888Sbrian        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
44243888Sbrian        chap_Cleanup(chap, SIGTERM);
44343888Sbrian      }
44443888Sbrian    } else {
44544123Sbrian#ifdef HAVE_DES
44644106Sbrian      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
44744106Sbrian                   ((chap->NTRespSent &&
44844106Sbrian                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
44944106Sbrian                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
45044123Sbrian#endif
45144106Sbrian
45243888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
45343888Sbrian        *end-- = '\0';
45443888Sbrian      end = key - 1;
45543888Sbrian      while (end >= name && strchr(" \t\r\n", *end))
45643888Sbrian        *end-- = '\0';
45743888Sbrian      key += strspn(key, " \t");
45843888Sbrian
45944123Sbrian      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
46044123Sbrian#ifdef HAVE_DES
46144123Sbrian                   , lanman
46244123Sbrian#endif
46344123Sbrian                  );
46443888Sbrian      chap_Cleanup(chap, 0);
46543888Sbrian    }
46643888Sbrian  }
46743888Sbrian}
46843888Sbrian
46943888Sbrianstatic int
47058028Sbrianchap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
47143888Sbrian{
47243888Sbrian  /* We never want to write here ! */
47343888Sbrian  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
47443888Sbrian  return 0;
47543888Sbrian}
47643888Sbrian
47743888Sbrianstatic void
47867910Sbrianchap_ChallengeInit(struct authinfo *authp)
47943693Sbrian{
48043693Sbrian  struct chap *chap = auth2chap(authp);
48113379Sphk  int len, i;
4826059Samurai  char *cp;
4836059Samurai
48443888Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
48543401Sbrian
48645907Sbrian  if (!*chap->challenge.local) {
48743888Sbrian    randinit();
48845907Sbrian    cp = chap->challenge.local;
48943888Sbrian
49043313Sbrian#ifndef NORADIUS
49143888Sbrian    if (*authp->physical->dl->bundle->radius.cfg.file) {
49243888Sbrian      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
49343888Sbrian      *cp++ = 16;
49443888Sbrian      for (i = 0; i < 16; i++)
49543888Sbrian        *cp++ = (random() % 10) + '0';
49643888Sbrian    } else
49743313Sbrian#endif
49843888Sbrian    {
49944106Sbrian#ifdef HAVE_DES
50044106Sbrian      if (authp->physical->link.lcp.want_authtype == 0x80)
50144106Sbrian        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
50267910Sbrian      else if (authp->physical->link.lcp.want_authtype == 0x81)
50367910Sbrian        *cp++ = 16;	/* MS-CHAP-V2 does 16 bytes challenges */
50444106Sbrian      else
50544106Sbrian#endif
50644106Sbrian        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
50745907Sbrian      for (i = 0; i < *chap->challenge.local; i++)
50843888Sbrian        *cp++ = random() & 0xff;
50943888Sbrian    }
51043888Sbrian    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
51143313Sbrian  }
5126059Samurai}
5136059Samurai
51430715Sbrianstatic void
51567910Sbrianchap_Challenge(struct authinfo *authp)
51667910Sbrian{
51767910Sbrian  struct chap *chap = auth2chap(authp);
51867910Sbrian  int len;
51967910Sbrian
52067912Sbrian  log_Printf(LogDEBUG, "CHAP%02X: Challenge\n",
52167912Sbrian             authp->physical->link.lcp.want_authtype);
52267910Sbrian
52367910Sbrian  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
52467910Sbrian
52567910Sbrian  /* Generate new local challenge value */
52667910Sbrian  if (!*chap->challenge.local)
52767910Sbrian    chap_ChallengeInit(authp);
52867910Sbrian
52967910Sbrian#ifdef HAVE_DES
53067910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81)
53167910Sbrian    ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
53267910Sbrian             chap->challenge.local, 1 + *chap->challenge.local, NULL);
53367910Sbrian  else
53467910Sbrian#endif
53567910Sbrian    ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
53667910Sbrian             chap->challenge.local, 1 + *chap->challenge.local + len, NULL);
53767910Sbrian}
53867910Sbrian
53967910Sbrianstatic void
54043693Sbrianchap_Success(struct authinfo *authp)
5416059Samurai{
54267912Sbrian  const char *msg;
54343693Sbrian  datalink_GotAuthname(authp->physical->dl, authp->in.name);
54467910Sbrian#ifdef HAVE_DES
54567910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81) {
54667910Sbrian    msg = auth2chap(authp)->authresponse;
54768461Sbrian    MPPE_MasterKeyValid = 1;		/* XXX Global ! */
54867910Sbrian  } else
54967910Sbrian#endif
55067910Sbrian    msg = "Welcome!!";
55167910Sbrian
55280763Sbrian  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg),
55367910Sbrian             NULL);
55467910Sbrian
55543693Sbrian  authp->physical->link.lcp.auth_ineed = 0;
55643693Sbrian  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
55743693Sbrian    physical_Login(authp->physical, authp->in.name);
5586059Samurai
55943693Sbrian  if (authp->physical->link.lcp.auth_iwait == 0)
56043693Sbrian    /*
56143693Sbrian     * Either I didn't need to authenticate, or I've already been
56243693Sbrian     * told that I got the answer right.
56343693Sbrian     */
56443693Sbrian    datalink_AuthOk(authp->physical->dl);
56543693Sbrian}
5666059Samurai
56743693Sbrianstatic void
56843693Sbrianchap_Failure(struct authinfo *authp)
56943693Sbrian{
57067910Sbrian#ifdef HAVE_DES
57167910Sbrian  char buf[1024];
57267910Sbrian#endif
57367912Sbrian  const char *msg;
57467910Sbrian
57567910Sbrian#ifdef HAVE_DES
57667910Sbrian  if (authp->physical->link.lcp.want_authtype == 0x81) {
57767912Sbrian    char *ptr;
57867910Sbrian    int i;
57967910Sbrian
58067912Sbrian    ptr = buf;
58167912Sbrian    ptr += sprintf(buf, "E=691 R=0 C=");
58267910Sbrian    for (i=0; i<16; i++)
58367912Sbrian      ptr += sprintf(ptr, "%02X", *(auth2chap(authp)->challenge.local+1+i));
58467910Sbrian
58567912Sbrian    sprintf(ptr, " V=3 M=Invalid!");
58667910Sbrian    msg = buf;
58767910Sbrian  } else
58867910Sbrian#endif
58967910Sbrian    msg = "Invalid!!";
59067910Sbrian
59167910Sbrian  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1,
59267910Sbrian             NULL);
59343693Sbrian  datalink_AuthNotOk(authp->physical->dl);
59443693Sbrian}
59537926Sbrian
59644106Sbrianstatic int
59744123Sbrianchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
59844123Sbrian#ifdef HAVE_DES
59944123Sbrian         , int lm
60044123Sbrian#endif
60144123Sbrian        )
60244106Sbrian{
60344106Sbrian  if (mylen != hislen)
60444106Sbrian    return 0;
60544123Sbrian#ifdef HAVE_DES
60644106Sbrian  else if (type == 0x80) {
60744106Sbrian    int off = lm ? 0 : 24;
60844106Sbrian
60944106Sbrian    if (memcmp(myans + off, hisans + off, 24))
61044106Sbrian      return 0;
61144123Sbrian  }
61244123Sbrian#endif
61344123Sbrian  else if (memcmp(myans, hisans, mylen))
61444106Sbrian    return 0;
61544106Sbrian
61644106Sbrian  return 1;
61744106Sbrian}
61844106Sbrian
61944123Sbrian#ifdef HAVE_DES
62044106Sbrianstatic int
62144106Sbrianchap_HaveAnotherGo(struct chap *chap)
62244106Sbrian{
62344106Sbrian  if (++chap->peertries < 3) {
62444106Sbrian    /* Give the peer another shot */
62545907Sbrian    *chap->challenge.local = '\0';
62644106Sbrian    chap_Challenge(&chap->auth);
62744106Sbrian    return 1;
62844106Sbrian  }
62944106Sbrian
63044106Sbrian  return 0;
63144106Sbrian}
63244123Sbrian#endif
63344106Sbrian
63443693Sbrianvoid
63543693Sbrianchap_Init(struct chap *chap, struct physical *p)
63643693Sbrian{
63743888Sbrian  chap->desc.type = CHAP_DESCRIPTOR;
63843888Sbrian  chap->desc.UpdateSet = chap_UpdateSet;
63943888Sbrian  chap->desc.IsSet = chap_IsSet;
64043888Sbrian  chap->desc.Read = chap_Read;
64143888Sbrian  chap->desc.Write = chap_Write;
64243888Sbrian  chap->child.pid = 0;
64343888Sbrian  chap->child.fd = -1;
64443693Sbrian  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
64545907Sbrian  *chap->challenge.local = *chap->challenge.peer = '\0';
64644123Sbrian#ifdef HAVE_DES
64744106Sbrian  chap->NTRespSent = 0;
64844106Sbrian  chap->peertries = 0;
64944123Sbrian#endif
65043693Sbrian}
65129840Sbrian
65243693Sbrianvoid
65343888Sbrianchap_ReInit(struct chap *chap)
65443888Sbrian{
65543888Sbrian  chap_Cleanup(chap, SIGTERM);
65643888Sbrian}
65743888Sbrian
65846686Sbrianstruct mbuf *
65946686Sbrianchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
66043693Sbrian{
66146686Sbrian  struct physical *p = link2physical(l);
66243693Sbrian  struct chap *chap = &p->dl->chap;
66344106Sbrian  char *name, *key, *ans;
66444123Sbrian  int len, nlen;
66548817Sbrian  u_char alen;
66644123Sbrian#ifdef HAVE_DES
66744123Sbrian  int lanman;
66844123Sbrian#endif
66929840Sbrian
67046686Sbrian  if (p == NULL) {
67146686Sbrian    log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
67254912Sbrian    m_freem(bp);
67346686Sbrian    return NULL;
67446686Sbrian  }
67546686Sbrian
67646686Sbrian  if (bundle_Phase(bundle) != PHASE_NETWORK &&
67746686Sbrian      bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
67845220Sbrian    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
67954912Sbrian    m_freem(bp);
68046686Sbrian    return NULL;
68145220Sbrian  }
68245220Sbrian
68354912Sbrian  m_settype(bp, MB_CHAPIN);
68444159Sbrian  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
68544159Sbrian      ntohs(chap->auth.in.hdr.length) == 0)
68644159Sbrian    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
68743693Sbrian  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
68843693Sbrian    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
68943693Sbrian               chap->auth.in.hdr.code);
69043693Sbrian  else {
69154912Sbrian    len = m_length(bp);
69243693Sbrian    ans = NULL;
69343693Sbrian
69443693Sbrian    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
69543693Sbrian        chap->auth.id != chap->auth.in.hdr.id &&
69646686Sbrian        Enabled(bundle, OPT_IDCHECK)) {
69743693Sbrian      /* Wrong conversation dude ! */
69843693Sbrian      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
69943693Sbrian                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
70043693Sbrian                 chap->auth.id);
70154912Sbrian      m_freem(bp);
70246686Sbrian      return NULL;
70325630Sbrian    }
70443693Sbrian    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
70529840Sbrian
70644123Sbrian#ifdef HAVE_DES
70744106Sbrian    lanman = 0;
70844123Sbrian#endif
70943693Sbrian    switch (chap->auth.in.hdr.code) {
71043693Sbrian      case CHAP_CHALLENGE:
71144106Sbrian        bp = mbuf_Read(bp, &alen, 1);
71244106Sbrian        len -= alen + 1;
71343693Sbrian        if (len < 0) {
71443693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
71554912Sbrian          m_freem(bp);
71646686Sbrian          return NULL;
71743693Sbrian        }
71845907Sbrian        *chap->challenge.peer = alen;
71945907Sbrian        bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
72043693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
72144123Sbrian#ifdef HAVE_DES
72244106Sbrian        lanman = p->link.lcp.his_authtype == 0x80 &&
72344106Sbrian                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
72444106Sbrian                  !IsAccepted(p->link.lcp.cfg.chap80nt));
72567910Sbrian
72667910Sbrian        /* Generate local challenge value */
72767910Sbrian        chap_ChallengeInit(&chap->auth);
72844123Sbrian#endif
72943693Sbrian        break;
73043313Sbrian
73143693Sbrian      case CHAP_RESPONSE:
73243693Sbrian        auth_StopTimer(&chap->auth);
73343693Sbrian        bp = mbuf_Read(bp, &alen, 1);
73443693Sbrian        len -= alen + 1;
73543693Sbrian        if (len < 0) {
73643693Sbrian          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
73754912Sbrian          m_freem(bp);
73846686Sbrian          return NULL;
73943693Sbrian        }
74043693Sbrian        if ((ans = malloc(alen + 2)) == NULL) {
74143693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
74254912Sbrian          m_freem(bp);
74346686Sbrian          return NULL;
74443693Sbrian        }
74543693Sbrian        *ans = chap->auth.id;
74643693Sbrian        bp = mbuf_Read(bp, ans + 1, alen);
74780721Sbrian        if (p->link.lcp.want_authtype == 0x81 && ans[alen] != '\0') {
74880721Sbrian          log_Printf(LogWARN, "%s: Compensating for corrupt (Win98?) "
74980721Sbrian                     "CHAP81 RESPONSE\n", l->name);
75080721Sbrian          ans[alen] = '\0';
75180721Sbrian        }
75243693Sbrian        ans[alen+1] = '\0';
75343693Sbrian        bp = auth_ReadName(&chap->auth, bp, len);
75444123Sbrian#ifdef HAVE_DES
75567910Sbrian        lanman = p->link.lcp.want_authtype == 0x80 &&
75667910Sbrian                 alen == 49 && ans[alen] == 0;
75744123Sbrian#endif
75843693Sbrian        break;
75943313Sbrian
76043693Sbrian      case CHAP_SUCCESS:
76143693Sbrian      case CHAP_FAILURE:
76243693Sbrian        /* chap->auth.in.name is already set up at CHALLENGE time */
76343693Sbrian        if ((ans = malloc(len + 1)) == NULL) {
76443693Sbrian          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
76554912Sbrian          m_freem(bp);
76646686Sbrian          return NULL;
76743693Sbrian        }
76843693Sbrian        bp = mbuf_Read(bp, ans, len);
76943693Sbrian        ans[len] = '\0';
77043693Sbrian        break;
77143401Sbrian    }
77236285Sbrian
77343693Sbrian    switch (chap->auth.in.hdr.code) {
77443693Sbrian      case CHAP_CHALLENGE:
77543693Sbrian      case CHAP_RESPONSE:
77643693Sbrian        if (*chap->auth.in.name)
77744106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
77844106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
77944106Sbrian                     chap->auth.in.name,
78044123Sbrian#ifdef HAVE_DES
78144106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
78244123Sbrian                     " - lanman" :
78344123Sbrian#endif
78444123Sbrian                     "");
78543693Sbrian        else
78644106Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
78744106Sbrian                     chapcodes[chap->auth.in.hdr.code], alen,
78844123Sbrian#ifdef HAVE_DES
78944106Sbrian                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
79044123Sbrian                     " - lanman" :
79144123Sbrian#endif
79244123Sbrian                     "");
79343693Sbrian        break;
79436285Sbrian
79543693Sbrian      case CHAP_SUCCESS:
79643693Sbrian      case CHAP_FAILURE:
79743693Sbrian        if (*ans)
79843693Sbrian          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
79943693Sbrian                     chapcodes[chap->auth.in.hdr.code], ans);
80043693Sbrian        else
80143693Sbrian          log_Printf(LogPHASE, "Chap Input: %s\n",
80243693Sbrian                     chapcodes[chap->auth.in.hdr.code]);
80343693Sbrian        break;
8046059Samurai    }
8056059Samurai
80643693Sbrian    switch (chap->auth.in.hdr.code) {
80743693Sbrian      case CHAP_CHALLENGE:
80864465Sbrian        if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!')
80946686Sbrian          chap_StartChild(chap, bundle->cfg.auth.key + 1,
81046686Sbrian                          bundle->cfg.auth.name);
81143888Sbrian        else
81264465Sbrian          chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key +
81364465Sbrian                       (*bundle->cfg.auth.key == '!' ? 1 : 0),
81464465Sbrian                       p->link.lcp.his_authtype
81544123Sbrian#ifdef HAVE_DES
81644123Sbrian                       , lanman
81744123Sbrian#endif
81844123Sbrian                      );
81943693Sbrian        break;
8206059Samurai
82143693Sbrian      case CHAP_RESPONSE:
82243693Sbrian        name = chap->auth.in.name;
82343693Sbrian        nlen = strlen(name);
82443693Sbrian#ifndef NORADIUS
82575071Sbrian        if (*bundle->radius.cfg.file)
82646686Sbrian          radius_Authenticate(&bundle->radius, &chap->auth,
82775071Sbrian                              chap->auth.in.name, ans, alen + 1,
82875071Sbrian                              chap->challenge.local + 1,
82975071Sbrian                              *chap->challenge.local);
83075071Sbrian        else
83143693Sbrian#endif
83243693Sbrian        {
83346686Sbrian          key = auth_GetSecret(bundle, name, nlen, p);
83443693Sbrian          if (key) {
83544106Sbrian            char *myans;
83644123Sbrian#ifdef HAVE_DES
83767910Sbrian            if (p->link.lcp.want_authtype == 0x80 &&
83867910Sbrian                lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
83944106Sbrian              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
84044106Sbrian              if (chap_HaveAnotherGo(chap))
84144106Sbrian                break;
84243693Sbrian              key = NULL;
84367910Sbrian            } else if (p->link.lcp.want_authtype == 0x80 &&
84467910Sbrian                !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) {
84544106Sbrian              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
84644106Sbrian              if (chap_HaveAnotherGo(chap))
84744106Sbrian                break;
84844106Sbrian              key = NULL;
84967910Sbrian            } else if (p->link.lcp.want_authtype == 0x81 &&
85067910Sbrian                !IsEnabled(p->link.lcp.cfg.chap81)) {
85167910Sbrian              log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n");
85267910Sbrian              key = NULL;
85344123Sbrian            } else
85444123Sbrian#endif
85544123Sbrian            {
85667910Sbrian#ifdef HAVE_DES
85767910Sbrian              /* Get peer's challenge */
85867910Sbrian              if (p->link.lcp.want_authtype == 0x81) {
85967910Sbrian                chap->challenge.peer[0] = CHAP81_CHALLENGE_LEN;
86067910Sbrian                memcpy(chap->challenge.peer + 1, ans + 1, CHAP81_CHALLENGE_LEN);
86167910Sbrian              }
86267910Sbrian#endif
86367910Sbrian
86444106Sbrian              myans = chap_BuildAnswer(name, key, chap->auth.id,
86545907Sbrian                                       chap->challenge.local,
86644123Sbrian                                       p->link.lcp.want_authtype
86744123Sbrian#ifdef HAVE_DES
86867910Sbrian                                       , chap->challenge.peer,
86968461Sbrian                                       chap->authresponse, lanman);
87068461Sbrian              MPPE_IsServer = 1;		/* XXX Global ! */
87168461Sbrian#else
87268461Sbrian                                      );
87344123Sbrian#endif
87444106Sbrian              if (myans == NULL)
87543693Sbrian                key = NULL;
87644106Sbrian              else {
87744123Sbrian                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
87844123Sbrian                              ans + 1, alen
87944123Sbrian#ifdef HAVE_DES
88044123Sbrian                              , lanman
88144123Sbrian#endif
88244123Sbrian                             ))
88344106Sbrian                  key = NULL;
88444106Sbrian                free(myans);
88544106Sbrian              }
88643693Sbrian            }
88743693Sbrian          }
8886059Samurai
88943693Sbrian          if (key)
89043693Sbrian            chap_Success(&chap->auth);
89143693Sbrian          else
89243693Sbrian            chap_Failure(&chap->auth);
89343693Sbrian        }
8946059Samurai
89543693Sbrian        break;
8966059Samurai
8976059Samurai      case CHAP_SUCCESS:
89843693Sbrian        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
89943693Sbrian          p->link.lcp.auth_iwait = 0;
90067910Sbrian          if (p->link.lcp.auth_ineed == 0) {
90167910Sbrian#ifdef HAVE_DES
90267910Sbrian            if (p->link.lcp.his_authtype == 0x81) {
90367910Sbrian              if (strncmp(ans, chap->authresponse, 42)) {
90467910Sbrian                datalink_AuthNotOk(p->dl);
90567912Sbrian	        log_Printf(LogDEBUG, "CHAP81: AuthenticatorResponse: (%s)"
90667912Sbrian                           " != ans: (%s)\n", chap->authresponse, ans);
90767910Sbrian
90867910Sbrian              } else {
90967910Sbrian                /* Successful login */
91068461Sbrian                MPPE_MasterKeyValid = 1;		/* XXX Global ! */
91167910Sbrian                datalink_AuthOk(p->dl);
91267910Sbrian              }
91367910Sbrian            } else
91467910Sbrian#endif
91543693Sbrian            /*
91643693Sbrian             * We've succeeded in our ``login''
91743693Sbrian             * If we're not expecting  the peer to authenticate (or he already
91843693Sbrian             * has), proceed to network phase.
91943693Sbrian             */
92043693Sbrian            datalink_AuthOk(p->dl);
92167910Sbrian          }
92243693Sbrian        }
92343693Sbrian        break;
92443693Sbrian
9256059Samurai      case CHAP_FAILURE:
92643693Sbrian        datalink_AuthNotOk(p->dl);
92743693Sbrian        break;
9286059Samurai    }
92943693Sbrian    free(ans);
9306059Samurai  }
93143693Sbrian
93254912Sbrian  m_freem(bp);
93346686Sbrian  return NULL;
9346059Samurai}
935