chap.c revision 54912
137446Srnordier/*
237446Srnordier *			PPP CHAP Module
337446Srnordier *
437446Srnordier *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
537446Srnordier *
637446Srnordier *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
737446Srnordier *
837446Srnordier * Redistribution and use in source and binary forms are permitted
937446Srnordier * provided that the above copyright notice and this paragraph are
1037446Srnordier * duplicated in all such forms and that any documentation,
1137446Srnordier * advertising materials, and other materials related to such
1237446Srnordier * distribution and use acknowledge that the software was developed
1337446Srnordier * by the Internet Initiative Japan, Inc.  The name of the
1437446Srnordier * IIJ may not be used to endorse or promote products derived
1537446Srnordier * from this software without specific prior written permission.
1637446Srnordier * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1737446Srnordier * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1837446Srnordier * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1937446Srnordier *
2037446Srnordier * $FreeBSD: head/usr.sbin/ppp/chap.c 54912 1999-12-20 20:29:47Z brian $
2137446Srnordier *
2237446Srnordier *	TODO:
2337446Srnordier */
2437446Srnordier#include <sys/param.h>
2537446Srnordier#include <netinet/in.h>
2637446Srnordier#include <netinet/in_systm.h>
2737446Srnordier#include <netinet/ip.h>
2837446Srnordier#include <sys/un.h>
2937446Srnordier
3050476Speter#include <errno.h>
3137446Srnordier#include <fcntl.h>
3237446Srnordier#ifdef HAVE_DES
3337446Srnordier#include <md4.h>
34106372Sscottl#endif
35106372Sscottl#include <md5.h>
3637446Srnordier#include <paths.h>
3737446Srnordier#include <signal.h>
3866907Swollman#include <stdio.h>
3966907Swollman#include <stdlib.h>
4037446Srnordier#include <string.h>
4137446Srnordier#include <sys/wait.h>
4237446Srnordier#include <termios.h>
4337446Srnordier#include <unistd.h>
4437446Srnordier
45185594Smlaier#include "layer.h"
4637446Srnordier#include "mbuf.h"
47223945Sae#include "log.h"
4837446Srnordier#include "defs.h"
4937446Srnordier#include "timer.h"
5037446Srnordier#include "fsm.h"
5166907Swollman#include "proto.h"
5237446Srnordier#include "lcp.h"
5337446Srnordier#include "lqr.h"
5437446Srnordier#include "hdlc.h"
5537446Srnordier#include "auth.h"
5637446Srnordier#include "async.h"
5737446Srnordier#include "throughput.h"
5837446Srnordier#include "descriptor.h"
59170166Strhodes#include "chap.h"
6037446Srnordier#include "iplist.h"
6137446Srnordier#include "slcompress.h"
6237446Srnordier#include "ipcp.h"
6337446Srnordier#include "filter.h"
6437446Srnordier#include "ccp.h"
6537446Srnordier#include "link.h"
66203868Skib#include "physical.h"
67203868Skib#include "mp.h"
68203868Skib#ifndef NORADIUS
69229550Spfg#include "radius.h"
70203868Skib#endif
71203868Skib#include "bundle.h"
7237446Srnordier#include "chat.h"
7337446Srnordier#include "cbcp.h"
7437446Srnordier#include "command.h"
7537446Srnordier#include "datalink.h"
7637446Srnordier#ifdef HAVE_DES
7737446Srnordier#include "chap_ms.h"
7837446Srnordier#endif
7937446Srnordier
8037446Srnordierstatic const char *chapcodes[] = {
8137446Srnordier  "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
8237446Srnordier};
8337446Srnordier#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
8437446Srnordier
8537446Srnordierstatic void
8637446SrnordierChapOutput(struct physical *physical, u_int code, u_int id,
8737446Srnordier	   const u_char *ptr, int count, const char *text)
8837446Srnordier{
8937446Srnordier  int plen;
9037446Srnordier  struct fsmheader lh;
9137446Srnordier  struct mbuf *bp;
9237446Srnordier
9337446Srnordier  plen = sizeof(struct fsmheader) + count;
9437446Srnordier  lh.code = code;
9537446Srnordier  lh.id = id;
9637446Srnordier  lh.length = htons(plen);
9737446Srnordier  bp = m_get(plen, MB_CHAPOUT);
9837446Srnordier  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
9937446Srnordier  if (count)
100229550Spfg    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
101203869Skib  log_DumpBp(LogDEBUG, "ChapOutput", bp);
102231162Sdelphij  if (text == NULL)
10337446Srnordier    log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
10437446Srnordier  else
105203869Skib    log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
106203869Skib  link_PushPacket(&physical->link, bp, physical->dl->bundle,
107203869Skib                  LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
108203869Skib}
109203869Skib
110203869Skibstatic char *
111203869Skibchap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
112203869Skib#ifdef HAVE_DES
113203869Skib                 , int lanman
114203869Skib#endif
115203869Skib                )
116203869Skib{
117231162Sdelphij  char *result, *digest;
11837446Srnordier  size_t nlen, klen;
11937446Srnordier
120203869Skib  nlen = strlen(name);
121203869Skib  klen = strlen(key);
122203869Skib
123203869Skib#ifdef HAVE_DES
124203869Skib  if (type == 0x80) {
125203869Skib    char expkey[AUTHLEN << 2];
126229550Spfg    MD4_CTX MD4context;
127231162Sdelphij    int f;
12837446Srnordier
12937446Srnordier    if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
130203869Skib      return result;
131203869Skib
132203869Skib    digest = result;					/* the response */
133203869Skib    *digest++ = MS_CHAP_RESPONSE_LEN;			/* 49 */
134203869Skib    memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
135203869Skib    if (lanman) {
136231162Sdelphij      memset(digest + 24, '\0', 25);
13737446Srnordier      mschap_LANMan(digest, challenge + 1, key);	/* LANMan response */
13837446Srnordier    } else {
139229550Spfg      memset(digest, '\0', 25);
140229550Spfg      digest += 24;
141229550Spfg
142229550Spfg      for (f = 0; f < klen; f++) {
143229550Spfg        expkey[2*f] = key[f];
144229550Spfg        expkey[2*f+1] = '\0';
145229550Spfg      }
146231162Sdelphij      /*
14737446Srnordier       *           -----------
14837446Srnordier       * expkey = | k\0e\0y\0 |
149203869Skib       *           -----------
150203869Skib       */
151203869Skib      MD4Init(&MD4context);
152203869Skib      MD4Update(&MD4context, expkey, klen << 1);
153203869Skib      MD4Final(digest, &MD4context);
154203869Skib
155203869Skib      /*
156203869Skib       *           ---- -------- ---------------- ------- ------
157203869Skib       * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
158203869Skib       *           ---- -------- ---------------- ------- ------
159203869Skib       */
160203869Skib      mschap_NT(digest, challenge + 1);
161203869Skib    }
162203869Skib    /*
163229550Spfg     *           ---- -------- ------------- ----- ------
164229550Spfg     *          |    |  struct MS_ChapResponse24  |      |
16537446Srnordier     * result = | 49 | LANMan  |  NT digest | 0/1 | name |
16637446Srnordier     *           ---- -------- ------------- ----- ------
167140384Sdelphij     * where only one of LANMan & NT digest are set.
168140384Sdelphij     */
16937446Srnordier  } else
17037446Srnordier#endif
17137446Srnordier  if ((result = malloc(nlen + 17)) != NULL) {
172190927Sed    /* Normal MD5 stuff */
173229550Spfg    MD5_CTX MD5context;
174229550Spfg
175229550Spfg    digest = result;
176229550Spfg    *digest++ = 16;				/* value size */
177229550Spfg
178229550Spfg    MD5Init(&MD5context);
179229550Spfg    MD5Update(&MD5context, &id, 1);
180229550Spfg    MD5Update(&MD5context, key, klen);
181229550Spfg    MD5Update(&MD5context, challenge + 1, *challenge);
182229550Spfg    MD5Final(digest, &MD5context);
18337446Srnordier
18437446Srnordier    memcpy(digest + 16, name, nlen);
185190927Sed    /*
18637446Srnordier     *           ---- -------- ------
18737446Srnordier     * result = | 16 | digest | name |
18837446Srnordier     *           ---- -------- ------
18937446Srnordier     */
19037446Srnordier  }
19137446Srnordier
19237446Srnordier  return result;
19337446Srnordier}
19437446Srnordier
19537446Srnordierstatic void
19637446Srnordierchap_StartChild(struct chap *chap, char *prog, const char *name)
19737446Srnordier{
19837446Srnordier  char *argv[MAXARGS], *nargv[MAXARGS];
19937446Srnordier  int argc, fd;
20037446Srnordier  int in[2], out[2];
20137446Srnordier  pid_t pid;
20237446Srnordier
20337446Srnordier  if (chap->child.fd != -1) {
20437446Srnordier    log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
20537446Srnordier    return;
20637446Srnordier  }
20737446Srnordier
20837446Srnordier  if (pipe(in) == -1) {
20937446Srnordier    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21037446Srnordier    return;
21137446Srnordier  }
21237446Srnordier
21337446Srnordier  if (pipe(out) == -1) {
21437446Srnordier    log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
21537446Srnordier    close(in[0]);
21637446Srnordier    close(in[1]);
217223945Sae    return;
218223945Sae  }
219223945Sae
22037446Srnordier  pid = getpid();
22137446Srnordier  switch ((chap->child.pid = fork())) {
22237446Srnordier    case -1:
22337446Srnordier      log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
22437446Srnordier      close(in[0]);
22537446Srnordier      close(in[1]);
22637446Srnordier      close(out[0]);
227185587Sluigi      close(out[1]);
22837446Srnordier      chap->child.pid = 0;
22937446Srnordier      return;
23037446Srnordier
23137446Srnordier    case 0:
23237446Srnordier      timer_TermService();
23337446Srnordier      close(in[1]);
234102231Strhodes      close(out[0]);
23537446Srnordier      if (out[1] == STDIN_FILENO)
23637446Srnordier        out[1] = dup(out[1]);
23737446Srnordier      dup2(in[0], STDIN_FILENO);
23837446Srnordier      dup2(out[1], STDOUT_FILENO);
239190927Sed      close(STDERR_FILENO);
240190927Sed      if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
241190927Sed        log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
242190927Sed                  _PATH_DEVNULL, strerror(errno));
243190927Sed        exit(1);
244190927Sed      }
245190927Sed      for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
24637446Srnordier        fcntl(fd, F_SETFD, 1);
247223945Sae      setuid(geteuid());
24837446Srnordier      argc = command_Interpret(prog, strlen(prog), argv);
24937446Srnordier      command_Expand(nargv, argc, (char const *const *)argv,
25037446Srnordier                     chap->auth.physical->dl->bundle, 0, pid);
25137446Srnordier      execvp(nargv[0], nargv);
25237446Srnordier      printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
25337446Srnordier      _exit(255);
25437446Srnordier
25537446Srnordier    default:
25637446Srnordier      close(in[0]);
25737446Srnordier      close(out[1]);
25837446Srnordier      chap->child.fd = out[0];
25937446Srnordier      chap->child.buf.len = 0;
26040487Sbde      write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
26137446Srnordier      write(in[1], "\n", 1);
26237446Srnordier      write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
263190927Sed      write(in[1], "\n", 1);
26437446Srnordier      write(in[1], name, strlen(name));
26537446Srnordier      write(in[1], "\n", 1);
26637446Srnordier      close(in[1]);
267185587Sluigi      break;
268185587Sluigi  }
269185587Sluigi}
27037446Srnordier
27137446Srnordierstatic void
27237446Srnordierchap_Cleanup(struct chap *chap, int sig)
27337446Srnordier{
27437446Srnordier  if (chap->child.pid) {
27537446Srnordier    int status;
276185587Sluigi
277185587Sluigi    close(chap->child.fd);
278185587Sluigi    chap->child.fd = -1;
27937446Srnordier    if (sig)
28037446Srnordier      kill(chap->child.pid, SIGTERM);
28137446Srnordier    chap->child.pid = 0;
28237446Srnordier    chap->child.buf.len = 0;
28337446Srnordier
28437446Srnordier    if (wait(&status) == -1)
28537446Srnordier      log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
28637446Srnordier    else if (WIFSIGNALED(status))
28737446Srnordier      log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
28837446Srnordier    else if (WIFEXITED(status) && WEXITSTATUS(status))
28937446Srnordier      log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
29037446Srnordier  }
29137446Srnordier  *chap->challenge.local = *chap->challenge.peer = '\0';
29237446Srnordier#ifdef HAVE_DES
29337446Srnordier  chap->peertries = 0;
29437446Srnordier#endif
29537446Srnordier}
29637446Srnordier
29737446Srnordierstatic void
29837446Srnordierchap_Respond(struct chap *chap, char *name, char *key, u_char type
29937446Srnordier#ifdef HAVE_DES
30037446Srnordier             , int lm
30137446Srnordier#endif
30237446Srnordier            )
30337446Srnordier{
30437446Srnordier  u_char *ans;
30537446Srnordier
30637446Srnordier  ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
30737446Srnordier#ifdef HAVE_DES
30837446Srnordier                         , lm
30937446Srnordier#endif
31037446Srnordier                        );
31137446Srnordier
31237446Srnordier  if (ans) {
31337446Srnordier    ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
31437446Srnordier               ans, *ans + 1 + strlen(name), name);
31537446Srnordier#ifdef HAVE_DES
31637446Srnordier    chap->NTRespSent = !lm;
31737446Srnordier#endif
31837446Srnordier    free(ans);
31937446Srnordier  } else
32037446Srnordier    ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
32137446Srnordier               "Out of memory!", 14, NULL);
32237446Srnordier}
32337446Srnordier
32437446Srnordierstatic int
32537446Srnordierchap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
32637446Srnordier{
32737446Srnordier  struct chap *chap = descriptor2chap(d);
32837446Srnordier
32937446Srnordier  if (r && chap && chap->child.fd != -1) {
33037446Srnordier    FD_SET(chap->child.fd, r);
33137446Srnordier    if (*n < chap->child.fd + 1)
33237446Srnordier      *n = chap->child.fd + 1;
33337446Srnordier    log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
33437446Srnordier    return 1;
33537446Srnordier  }
33637446Srnordier
33737446Srnordier  return 0;
33837446Srnordier}
33937446Srnordier
34037446Srnordierstatic int
34137446Srnordierchap_IsSet(struct descriptor *d, const fd_set *fdset)
34237446Srnordier{
34337446Srnordier  struct chap *chap = descriptor2chap(d);
344102231Strhodes
34537446Srnordier  return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
34637446Srnordier}
34737446Srnordier
34837446Srnordierstatic void
34937446Srnordierchap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
35037446Srnordier{
35137446Srnordier  struct chap *chap = descriptor2chap(d);
35237446Srnordier  int got;
35337446Srnordier
35437446Srnordier  got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
35537446Srnordier             sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
35637446Srnordier  if (got == -1) {
357190932Sed    log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
35863892Sasmodai    chap_Cleanup(chap, SIGTERM);
35937446Srnordier  } else if (got == 0) {
36037446Srnordier    log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
36137446Srnordier    chap_Cleanup(chap, SIGTERM);
36237446Srnordier  } else {
363185587Sluigi    char *name, *key, *end;
364185587Sluigi
365185587Sluigi    chap->child.buf.len += got;
366185587Sluigi    chap->child.buf.ptr[chap->child.buf.len] = '\0';
367185587Sluigi    name = chap->child.buf.ptr;
368185587Sluigi    name += strspn(name, " \t");
369190929Sed    if ((key = strchr(name, '\n')) == NULL)
370185594Smlaier      end = NULL;
371190930Sed    else
37237446Srnordier      end = strchr(++key, '\n');
373190930Sed
374190930Sed    if (end == NULL) {
375190931Sed      if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
376190931Sed        log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
377190931Sed        chap_Cleanup(chap, SIGTERM);
378190931Sed      }
379190931Sed    } else {
380190931Sed#ifdef HAVE_DES
381190931Sed      int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
38237446Srnordier                   ((chap->NTRespSent &&
38337446Srnordier                     IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
384185587Sluigi                    !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
385185594Smlaier#endif
38637446Srnordier
38737446Srnordier      while (end >= name && strchr(" \t\r\n", *end))
38837446Srnordier        *end-- = '\0';
389203869Skib      end = key - 1;
390203869Skib      while (end >= name && strchr(" \t\r\n", *end))
391203869Skib        *end-- = '\0';
392203869Skib      key += strspn(key, " \t");
39337446Srnordier
39437446Srnordier      chap_Respond(chap, name, key, chap->auth.physical->link.lcp.his_authtype
395203869Skib#ifdef HAVE_DES
39637446Srnordier                   , lanman
397203869Skib#endif
39837446Srnordier                  );
399203869Skib      chap_Cleanup(chap, 0);
40037446Srnordier    }
401203869Skib  }
40237446Srnordier}
403203869Skib
404185587Sluigistatic int
405185587Sluigichap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
40637446Srnordier{
407203869Skib  /* We never want to write here ! */
408203869Skib  log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
409185587Sluigi  return 0;
410185587Sluigi}
411203869Skib
412203869Skibstatic void
413185587Sluigichap_Challenge(struct authinfo *authp)
414203869Skib{
415203869Skib  struct chap *chap = auth2chap(authp);
416203869Skib  int len, i;
417203869Skib  char *cp;
418203869Skib
419203869Skib  len = strlen(authp->physical->dl->bundle->cfg.auth.name);
420203869Skib
421203869Skib  if (!*chap->challenge.local) {
422203869Skib    randinit();
423185587Sluigi    cp = chap->challenge.local;
424203869Skib
425185587Sluigi#ifndef NORADIUS
426185587Sluigi    if (*authp->physical->dl->bundle->radius.cfg.file) {
427203869Skib      /* For radius, our challenge is 16 readable NUL terminated bytes :*/
428203869Skib      *cp++ = 16;
429203869Skib      for (i = 0; i < 16; i++)
43037446Srnordier        *cp++ = (random() % 10) + '0';
431203869Skib    } else
43248954Sbillf#endif
43337446Srnordier    {
43437446Srnordier#ifdef HAVE_DES
43537446Srnordier      if (authp->physical->link.lcp.want_authtype == 0x80)
43637446Srnordier        *cp++ = 8;	/* MS does 8 byte callenges :-/ */
43748954Sbillf      else
43837446Srnordier#endif
43937446Srnordier        *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
44037446Srnordier      for (i = 0; i < *chap->challenge.local; i++)
44137446Srnordier        *cp++ = random() & 0xff;
44237446Srnordier    }
443203869Skib    memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
44437446Srnordier  }
44537446Srnordier  ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge.local,
44637446Srnordier	     1 + *chap->challenge.local + len, NULL);
447203869Skib}
44837446Srnordier
449203869Skibstatic void
450203869Skibchap_Success(struct authinfo *authp)
45137446Srnordier{
452203869Skib  datalink_GotAuthname(authp->physical->dl, authp->in.name);
453203869Skib  ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
45437446Srnordier  authp->physical->link.lcp.auth_ineed = 0;
45537446Srnordier  if (Enabled(authp->physical->dl->bundle, OPT_UTMP))
45637446Srnordier    physical_Login(authp->physical, authp->in.name);
45737446Srnordier
458203869Skib  if (authp->physical->link.lcp.auth_iwait == 0)
45937446Srnordier    /*
46037446Srnordier     * Either I didn't need to authenticate, or I've already been
461203869Skib     * told that I got the answer right.
46237446Srnordier     */
46337446Srnordier    datalink_AuthOk(authp->physical->dl);
46437446Srnordier}
46537446Srnordier
466203869Skibstatic void
46737446Srnordierchap_Failure(struct authinfo *authp)
46837446Srnordier{
469203869Skib  ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
47037446Srnordier  datalink_AuthNotOk(authp->physical->dl);
47137446Srnordier}
47241586Srnordier
473203869Skibstatic int
47437446Srnordierchap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
47537446Srnordier#ifdef HAVE_DES
476203869Skib         , int lm
47737446Srnordier#endif
478203869Skib        )
47937446Srnordier{
480203869Skib  if (mylen != hislen)
48137446Srnordier    return 0;
48237446Srnordier#ifdef HAVE_DES
48337446Srnordier  else if (type == 0x80) {
48437446Srnordier    int off = lm ? 0 : 24;
48537446Srnordier
48637446Srnordier    if (memcmp(myans + off, hisans + off, 24))
48742261Sjkh      return 0;
48837446Srnordier  }
48937446Srnordier#endif
49037446Srnordier  else if (memcmp(myans, hisans, mylen))
49137446Srnordier    return 0;
49237446Srnordier
493203869Skib  return 1;
494229550Spfg}
495229550Spfg
49637446Srnordier#ifdef HAVE_DES
497203869Skibstatic int
49837446Srnordierchap_HaveAnotherGo(struct chap *chap)
499203869Skib{
500203869Skib  if (++chap->peertries < 3) {
50148954Sbillf    /* Give the peer another shot */
502203869Skib    *chap->challenge.local = '\0';
503203869Skib    chap_Challenge(&chap->auth);
504229550Spfg    return 1;
505229550Spfg  }
506203869Skib
507203869Skib  return 0;
508203869Skib}
509203869Skib#endif
510229550Spfg
511229550Spfgvoid
51237446Srnordierchap_Init(struct chap *chap, struct physical *p)
513203869Skib{
514203869Skib  chap->desc.type = CHAP_DESCRIPTOR;
515229550Spfg  chap->desc.UpdateSet = chap_UpdateSet;
516229550Spfg  chap->desc.IsSet = chap_IsSet;
517203869Skib  chap->desc.Read = chap_Read;
51837446Srnordier  chap->desc.Write = chap_Write;
519229550Spfg  chap->child.pid = 0;
520229550Spfg  chap->child.fd = -1;
52137446Srnordier  auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
52237446Srnordier  *chap->challenge.local = *chap->challenge.peer = '\0';
52337446Srnordier#ifdef HAVE_DES
52448954Sbillf  chap->NTRespSent = 0;
52537446Srnordier  chap->peertries = 0;
52637446Srnordier#endif
527203869Skib}
528203869Skib
52937446Srnordiervoid
530203869Skibchap_ReInit(struct chap *chap)
53137446Srnordier{
532203869Skib  chap_Cleanup(chap, SIGTERM);
533203869Skib}
534203869Skib
53537446Srnordierstruct mbuf *
53637446Srnordierchap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
537203869Skib{
538203869Skib  struct physical *p = link2physical(l);
53937446Srnordier  struct chap *chap = &p->dl->chap;
540203869Skib  char *name, *key, *ans;
541203869Skib  int len, nlen;
54237446Srnordier  u_char alen;
543203869Skib#ifdef HAVE_DES
544229550Spfg  int lanman;
545229550Spfg#endif
546203869Skib
547229550Spfg  if (p == NULL) {
548229550Spfg    log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
549203869Skib    m_freem(bp);
550203869Skib    return NULL;
551203869Skib  }
552203869Skib
553229550Spfg  if (bundle_Phase(bundle) != PHASE_NETWORK &&
554229550Spfg      bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
555203869Skib    log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
556203869Skib    m_freem(bp);
55737446Srnordier    return NULL;
558229550Spfg  }
559229550Spfg
56037446Srnordier  m_settype(bp, MB_CHAPIN);
561229550Spfg  if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
562229550Spfg      ntohs(chap->auth.in.hdr.length) == 0)
563229550Spfg    log_Printf(LogWARN, "Chap Input: Truncated header !\n");
564229550Spfg  else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
565203869Skib    log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
56637446Srnordier               chap->auth.in.hdr.code);
567203869Skib  else {
568203869Skib    len = m_length(bp);
569203869Skib    ans = NULL;
570102231Strhodes
571203869Skib    if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
572203869Skib        chap->auth.id != chap->auth.in.hdr.id &&
573229550Spfg        Enabled(bundle, OPT_IDCHECK)) {
574229550Spfg      /* Wrong conversation dude ! */
57537446Srnordier      log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
576203869Skib                 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
577203869Skib                 chap->auth.id);
578203869Skib      m_freem(bp);
579203869Skib      return NULL;
58037446Srnordier    }
581203869Skib    chap->auth.id = chap->auth.in.hdr.id;	/* We respond with this id */
582229550Spfg
583229550Spfg#ifdef HAVE_DES
58437446Srnordier    lanman = 0;
58537446Srnordier#endif
586203869Skib    switch (chap->auth.in.hdr.code) {
587102231Strhodes      case CHAP_CHALLENGE:
58837446Srnordier        bp = mbuf_Read(bp, &alen, 1);
58937446Srnordier        len -= alen + 1;
59059215Simp        if (len < 0) {
59159215Simp          log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
59237446Srnordier          m_freem(bp);
59337446Srnordier          return NULL;
594203869Skib        }
595102231Strhodes        *chap->challenge.peer = alen;
596203869Skib        bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
59737446Srnordier        bp = auth_ReadName(&chap->auth, bp, len);
59837446Srnordier#ifdef HAVE_DES
599203869Skib        lanman = p->link.lcp.his_authtype == 0x80 &&
600203869Skib                 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
601203869Skib                  !IsAccepted(p->link.lcp.cfg.chap80nt));
602203869Skib#endif
603203869Skib        break;
60437446Srnordier
605203869Skib      case CHAP_RESPONSE:
606203869Skib        auth_StopTimer(&chap->auth);
607203869Skib        bp = mbuf_Read(bp, &alen, 1);
608203869Skib        len -= alen + 1;
60937446Srnordier        if (len < 0) {
61037446Srnordier          log_Printf(LogERROR, "Chap Input: Truncated response !\n");
611203869Skib          m_freem(bp);
612203869Skib          return NULL;
61337446Srnordier        }
61437446Srnordier        if ((ans = malloc(alen + 2)) == NULL) {
61537446Srnordier          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
61637446Srnordier          m_freem(bp);
61740487Sbde          return NULL;
61840487Sbde        }
619203869Skib        *ans = chap->auth.id;
62037446Srnordier        bp = mbuf_Read(bp, ans + 1, alen);
621229550Spfg        ans[alen+1] = '\0';
622229550Spfg        bp = auth_ReadName(&chap->auth, bp, len);
623223945Sae#ifdef HAVE_DES
624223945Sae        lanman = alen == 49 && ans[alen] == 0;
625223946Sae#endif
626223945Sae        break;
627203869Skib
628223945Sae      case CHAP_SUCCESS:
629223945Sae      case CHAP_FAILURE:
630223945Sae        /* chap->auth.in.name is already set up at CHALLENGE time */
631223945Sae        if ((ans = malloc(len + 1)) == NULL) {
632223945Sae          log_Printf(LogERROR, "Chap Input: Out of memory !\n");
633223945Sae          m_freem(bp);
634223945Sae          return NULL;
635223945Sae        }
63637446Srnordier        bp = mbuf_Read(bp, ans, len);
63737446Srnordier        ans[len] = '\0';
638203869Skib        break;
639203869Skib    }
640203869Skib
641185587Sluigi    switch (chap->auth.in.hdr.code) {
64237446Srnordier      case CHAP_CHALLENGE:
64337446Srnordier      case CHAP_RESPONSE:
64437446Srnordier        if (*chap->auth.in.name)
645203869Skib          log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
64637446Srnordier                     chapcodes[chap->auth.in.hdr.code], alen,
647203869Skib                     chap->auth.in.name,
64837446Srnordier#ifdef HAVE_DES
64937446Srnordier                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
650203869Skib                     " - lanman" :
65137446Srnordier#endif
652229550Spfg                     "");
653229550Spfg        else
65437446Srnordier          log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
65537446Srnordier                     chapcodes[chap->auth.in.hdr.code], alen,
656203869Skib#ifdef HAVE_DES
657203869Skib                     lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
658203869Skib                     " - lanman" :
659203869Skib#endif
660203869Skib                     "");
661203869Skib        break;
662203869Skib
663203869Skib      case CHAP_SUCCESS:
664203869Skib      case CHAP_FAILURE:
665203869Skib        if (*ans)
666203869Skib          log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
667203869Skib                     chapcodes[chap->auth.in.hdr.code], ans);
66837446Srnordier        else
66937446Srnordier          log_Printf(LogPHASE, "Chap Input: %s\n",
67037446Srnordier                     chapcodes[chap->auth.in.hdr.code]);
671203869Skib        break;
672203869Skib    }
673203869Skib
674203869Skib    switch (chap->auth.in.hdr.code) {
675203869Skib      case CHAP_CHALLENGE:
676203869Skib        if (*bundle->cfg.auth.key == '!')
67737446Srnordier          chap_StartChild(chap, bundle->cfg.auth.key + 1,
67837446Srnordier                          bundle->cfg.auth.name);
67937446Srnordier        else
680203869Skib          chap_Respond(chap, bundle->cfg.auth.name,
68137446Srnordier                       bundle->cfg.auth.key, p->link.lcp.his_authtype
68237446Srnordier#ifdef HAVE_DES
68337446Srnordier                       , lanman
68437446Srnordier#endif
68537446Srnordier                      );
68637446Srnordier        break;
68737446Srnordier
68837446Srnordier      case CHAP_RESPONSE:
68937446Srnordier        name = chap->auth.in.name;
69037446Srnordier        nlen = strlen(name);
691203869Skib#ifndef NORADIUS
692203869Skib        if (*bundle->radius.cfg.file) {
69337446Srnordier          u_char end;
694203869Skib
69537446Srnordier          end = chap->challenge.local[*chap->challenge.local+1];
69637446Srnordier          chap->challenge.local[*chap->challenge.local+1] = '\0';
69737446Srnordier          radius_Authenticate(&bundle->radius, &chap->auth,
698203869Skib                              chap->auth.in.name, ans,
699203869Skib                              chap->challenge.local + 1);
700203869Skib          chap->challenge.local[*chap->challenge.local+1] = end;
701203869Skib        } else
702203869Skib#endif
70337446Srnordier        {
704170166Strhodes          key = auth_GetSecret(bundle, name, nlen, p);
70537446Srnordier          if (key) {
706203869Skib            char *myans;
707203869Skib#ifdef HAVE_DES
708203869Skib            if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
709203869Skib              log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
71037446Srnordier              if (chap_HaveAnotherGo(chap))
711170166Strhodes                break;
712170166Strhodes              key = NULL;
713203869Skib            } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
714170166Strhodes                       p->link.lcp.want_authtype == 0x80) {
715203869Skib              log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
716203869Skib              if (chap_HaveAnotherGo(chap))
717229550Spfg                break;
718229550Spfg              key = NULL;
719203869Skib            } else
72037446Srnordier#endif
72137446Srnordier            {
72237446Srnordier              myans = chap_BuildAnswer(name, key, chap->auth.id,
72337759Srnordier                                       chap->challenge.local,
724203869Skib                                       p->link.lcp.want_authtype
725203869Skib#ifdef HAVE_DES
72637446Srnordier                                       , lanman
72737446Srnordier#endif
72837446Srnordier                                      );
729203869Skib              if (myans == NULL)
73037446Srnordier                key = NULL;
73137446Srnordier              else {
73237446Srnordier                if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
733203869Skib                              ans + 1, alen
73437446Srnordier#ifdef HAVE_DES
735203869Skib                              , lanman
73637446Srnordier#endif
737203869Skib                             ))
73837446Srnordier                  key = NULL;
73937446Srnordier                free(myans);
74037446Srnordier              }
74137446Srnordier            }
74237446Srnordier          }
74337446Srnordier
74437446Srnordier          if (key)
745102231Strhodes            chap_Success(&chap->auth);
74637446Srnordier          else
74737446Srnordier            chap_Failure(&chap->auth);
74837446Srnordier        }
74937446Srnordier
75037446Srnordier        break;
75137446Srnordier
75237446Srnordier      case CHAP_SUCCESS:
75337446Srnordier        if (p->link.lcp.auth_iwait == PROTO_CHAP) {
75437446Srnordier          p->link.lcp.auth_iwait = 0;
75537446Srnordier          if (p->link.lcp.auth_ineed == 0)
75637446Srnordier            /*
75737446Srnordier             * We've succeeded in our ``login''
75837446Srnordier             * If we're not expecting  the peer to authenticate (or he already
75937446Srnordier             * has), proceed to network phase.
76037446Srnordier             */
76137446Srnordier            datalink_AuthOk(p->dl);
76237446Srnordier        }
76337446Srnordier        break;
76437446Srnordier
76537446Srnordier      case CHAP_FAILURE:
76637446Srnordier        datalink_AuthNotOk(p->dl);
76737446Srnordier        break;
76837446Srnordier    }
76937446Srnordier    free(ans);
77037446Srnordier  }
77137446Srnordier
77237446Srnordier  m_freem(bp);
77337446Srnordier  return NULL;
77437446Srnordier}
77537446Srnordier