1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
5 *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
6 *                           Internet Initiative Japan, Inc (IIJ)
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD$
31 */
32
33#include <sys/param.h>
34#include <netinet/in.h>
35#include <netinet/in_systm.h>
36#include <netinet/ip.h>
37#include <sys/socket.h>
38#include <sys/un.h>
39
40#include <pwd.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <termios.h>
45#include <unistd.h>
46
47#ifndef NOPAM
48#include <security/pam_appl.h>
49#ifdef OPENPAM
50#include <security/openpam.h>
51#endif
52#endif /* !NOPAM */
53
54#include "layer.h"
55#include "mbuf.h"
56#include "defs.h"
57#include "log.h"
58#include "timer.h"
59#include "fsm.h"
60#include "iplist.h"
61#include "throughput.h"
62#include "slcompress.h"
63#include "lqr.h"
64#include "hdlc.h"
65#include "ncpaddr.h"
66#include "ipcp.h"
67#include "auth.h"
68#include "systems.h"
69#include "lcp.h"
70#include "ccp.h"
71#include "link.h"
72#include "descriptor.h"
73#include "chat.h"
74#include "proto.h"
75#include "filter.h"
76#include "mp.h"
77#ifndef NORADIUS
78#include "radius.h"
79#endif
80#include "cbcp.h"
81#include "chap.h"
82#include "async.h"
83#include "physical.h"
84#include "datalink.h"
85#include "ipv6cp.h"
86#include "ncp.h"
87#include "bundle.h"
88
89const char *
90Auth2Nam(u_short auth, u_char type)
91{
92  static char chap[10];
93
94  switch (auth) {
95  case PROTO_PAP:
96    return "PAP";
97  case PROTO_CHAP:
98    snprintf(chap, sizeof chap, "CHAP 0x%02x", type);
99    return chap;
100  case 0:
101    return "none";
102  }
103  return "unknown";
104}
105
106#if !defined(NOPAM) && !defined(OPENPAM)
107static int
108pam_conv(int n, const struct pam_message **msg, struct pam_response **resp,
109  void *data)
110{
111
112  if (n != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
113    return (PAM_CONV_ERR);
114  if ((*resp = malloc(sizeof(struct pam_response))) == NULL)
115    return (PAM_CONV_ERR);
116  (*resp)[0].resp = strdup((const char *)data);
117  (*resp)[0].resp_retcode = 0;
118
119  return ((*resp)[0].resp != NULL ? PAM_SUCCESS : PAM_CONV_ERR);
120}
121#endif /* !defined(NOPAM) && !defined(OPENPAM) */
122
123static int
124auth_CheckPasswd(const char *name, const char *data, const char *key)
125{
126  if (!strcmp(data, "*")) {
127#ifdef NOPAM
128    /* Then look up the real password database */
129    struct passwd *pw;
130    int result = 0;
131    char *cryptpw;
132
133    pw = getpwnam(name);
134
135    if (pw) {
136      cryptpw = crypt(key, pw->pw_passwd);
137
138      result = (cryptpw != NULL) && !strcmp(cryptpw, pw->pw_passwd);
139    }
140
141    endpwent();
142
143    return result;
144#else /* !NOPAM */
145    /* Then consult with PAM. */
146    pam_handle_t *pamh;
147    int status;
148
149    struct pam_conv pamc = {
150#ifdef OPENPAM
151      &openpam_nullconv, NULL
152#else
153      &pam_conv, key
154#endif
155    };
156
157    if (pam_start("ppp", name, &pamc, &pamh) != PAM_SUCCESS)
158      return (0);
159#ifdef OPENPAM
160    if ((status = pam_set_item(pamh, PAM_AUTHTOK, key)) == PAM_SUCCESS)
161#endif
162      status = pam_authenticate(pamh, 0);
163    pam_end(pamh, status);
164    return (status == PAM_SUCCESS);
165#endif /* !NOPAM */
166  }
167
168  return !strcmp(data, key);
169}
170
171int
172auth_SetPhoneList(const char *name, char *phone, int phonelen)
173{
174  FILE *fp;
175  int n, lineno;
176  char *vector[6], buff[LINE_LEN];
177  const char *slash;
178
179  fp = OpenSecret(SECRETFILE);
180  if (fp != NULL) {
181again:
182    lineno = 0;
183    while (fgets(buff, sizeof buff, fp)) {
184      lineno++;
185      if (buff[0] == '#')
186        continue;
187      buff[strlen(buff) - 1] = '\0';
188      memset(vector, '\0', sizeof vector);
189      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
190        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
191      if (n < 5)
192        continue;
193      if (strcmp(vector[0], name) == 0) {
194        CloseSecret(fp);
195        if (*vector[4] == '\0')
196          return 0;
197        strncpy(phone, vector[4], phonelen - 1);
198        phone[phonelen - 1] = '\0';
199        return 1;		/* Valid */
200      }
201    }
202
203    if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
204      /* Look for the name without the leading domain */
205      name = slash + 1;
206      rewind(fp);
207      goto again;
208    }
209
210    CloseSecret(fp);
211  }
212  *phone = '\0';
213  return 0;
214}
215
216int
217auth_Select(struct bundle *bundle, const char *name)
218{
219  FILE *fp;
220  int n, lineno;
221  char *vector[5], buff[LINE_LEN];
222  const char *slash;
223
224  if (*name == '\0') {
225    ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
226    return 1;
227  }
228
229#ifndef NORADIUS
230  if (bundle->radius.valid && bundle->radius.ip.s_addr != INADDR_NONE &&
231	bundle->radius.ip.s_addr != RADIUS_INADDR_POOL) {
232    /* We've got a radius IP - it overrides everything */
233    if (!ipcp_UseHisIPaddr(bundle, bundle->radius.ip))
234      return 0;
235    ipcp_Setup(&bundle->ncp.ipcp, bundle->radius.mask.s_addr);
236    /* Continue with ppp.secret in case we've got a new label */
237  }
238#endif
239
240  fp = OpenSecret(SECRETFILE);
241  if (fp != NULL) {
242again:
243    lineno = 0;
244    while (fgets(buff, sizeof buff, fp)) {
245      lineno++;
246      if (buff[0] == '#')
247        continue;
248      buff[strlen(buff) - 1] = '\0';
249      memset(vector, '\0', sizeof vector);
250      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
251        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
252      if (n < 2)
253        continue;
254      if (strcmp(vector[0], name) == 0) {
255        CloseSecret(fp);
256#ifndef NORADIUS
257        if (!bundle->radius.valid || bundle->radius.ip.s_addr == INADDR_NONE) {
258#endif
259          if (n > 2 && *vector[2] && strcmp(vector[2], "*") &&
260              !ipcp_UseHisaddr(bundle, vector[2], 1))
261            return 0;
262          ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
263#ifndef NORADIUS
264        }
265#endif
266        if (n > 3 && *vector[3] && strcmp(vector[3], "*"))
267          bundle_SetLabel(bundle, vector[3]);
268        return 1;		/* Valid */
269      }
270    }
271
272    if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
273      /* Look for the name without the leading domain */
274      name = slash + 1;
275      rewind(fp);
276      goto again;
277    }
278
279    CloseSecret(fp);
280  }
281
282#ifndef NOPASSWDAUTH
283  /* Let 'em in anyway - they must have been in the passwd file */
284  ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
285  return 1;
286#else
287#ifndef NORADIUS
288  if (bundle->radius.valid)
289    return 1;
290#endif
291
292  /* Disappeared from ppp.secret ??? */
293  return 0;
294#endif
295}
296
297int
298auth_Validate(struct bundle *bundle, const char *name, const char *key)
299{
300  /* Used by PAP routines */
301
302  FILE *fp;
303  int n, lineno;
304  char *vector[5], buff[LINE_LEN];
305  const char *slash;
306
307  fp = OpenSecret(SECRETFILE);
308again:
309  lineno = 0;
310  if (fp != NULL) {
311    while (fgets(buff, sizeof buff, fp)) {
312      lineno++;
313      if (buff[0] == '#')
314        continue;
315      buff[strlen(buff) - 1] = 0;
316      memset(vector, '\0', sizeof vector);
317      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
318        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
319      if (n < 2)
320        continue;
321      if (strcmp(vector[0], name) == 0) {
322        CloseSecret(fp);
323        return auth_CheckPasswd(name, vector[1], key);
324      }
325    }
326  }
327
328  if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
329    /* Look for the name without the leading domain */
330    name = slash + 1;
331    if (fp != NULL) {
332      rewind(fp);
333      goto again;
334    }
335  }
336
337  if (fp != NULL)
338    CloseSecret(fp);
339
340#ifndef NOPASSWDAUTH
341  if (Enabled(bundle, OPT_PASSWDAUTH))
342    return auth_CheckPasswd(name, "*", key);
343#endif
344
345  return 0;			/* Invalid */
346}
347
348char *
349auth_GetSecret(const char *name, size_t len)
350{
351  /* Used by CHAP routines */
352
353  FILE *fp;
354  int n, lineno;
355  char *vector[5];
356  const char *slash;
357  static char buff[LINE_LEN];	/* vector[] will point here when returned */
358
359  fp = OpenSecret(SECRETFILE);
360  if (fp == NULL)
361    return (NULL);
362
363again:
364  lineno = 0;
365  while (fgets(buff, sizeof buff, fp)) {
366    lineno++;
367    if (buff[0] == '#')
368      continue;
369    n = strlen(buff) - 1;
370    if (buff[n] == '\n')
371      buff[n] = '\0';	/* Trim the '\n' */
372    memset(vector, '\0', sizeof vector);
373    if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
374      log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
375    if (n < 2)
376      continue;
377    if (strlen(vector[0]) == len && strncmp(vector[0], name, len) == 0) {
378      CloseSecret(fp);
379      return vector[1];
380    }
381  }
382
383  if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
384    /* Go back and look for the name without the leading domain */
385    len -= slash - name + 1;
386    name = slash + 1;
387    rewind(fp);
388    goto again;
389  }
390
391  CloseSecret(fp);
392  return (NULL);		/* Invalid */
393}
394
395static void
396AuthTimeout(void *vauthp)
397{
398  struct authinfo *authp = (struct authinfo *)vauthp;
399
400  timer_Stop(&authp->authtimer);
401  if (--authp->retry > 0) {
402    authp->id++;
403    (*authp->fn.req)(authp);
404    timer_Start(&authp->authtimer);
405  } else {
406    log_Printf(LogPHASE, "Auth: No response from server\n");
407    datalink_AuthNotOk(authp->physical->dl);
408  }
409}
410
411void
412auth_Init(struct authinfo *authp, struct physical *p, auth_func req,
413          auth_func success, auth_func failure)
414{
415  memset(authp, '\0', sizeof(struct authinfo));
416  authp->cfg.fsm.timeout = DEF_FSMRETRY;
417  authp->cfg.fsm.maxreq = DEF_FSMAUTHTRIES;
418  authp->cfg.fsm.maxtrm = 0;	/* not used */
419  authp->fn.req = req;
420  authp->fn.success = success;
421  authp->fn.failure = failure;
422  authp->physical = p;
423}
424
425void
426auth_StartReq(struct authinfo *authp)
427{
428  timer_Stop(&authp->authtimer);
429  authp->authtimer.func = AuthTimeout;
430  authp->authtimer.name = "auth";
431  authp->authtimer.load = authp->cfg.fsm.timeout * SECTICKS;
432  authp->authtimer.arg = (void *)authp;
433  authp->retry = authp->cfg.fsm.maxreq;
434  authp->id = 1;
435  (*authp->fn.req)(authp);
436  timer_Start(&authp->authtimer);
437}
438
439void
440auth_StopTimer(struct authinfo *authp)
441{
442  timer_Stop(&authp->authtimer);
443}
444
445struct mbuf *
446auth_ReadHeader(struct authinfo *authp, struct mbuf *bp)
447{
448  size_t len;
449
450  len = m_length(bp);
451  if (len >= sizeof authp->in.hdr) {
452    bp = mbuf_Read(bp, (u_char *)&authp->in.hdr, sizeof authp->in.hdr);
453    if (len >= ntohs(authp->in.hdr.length))
454      return bp;
455    authp->in.hdr.length = htons(0);
456    log_Printf(LogWARN, "auth_ReadHeader: Short packet (%u > %zu) !\n",
457               ntohs(authp->in.hdr.length), len);
458  } else {
459    authp->in.hdr.length = htons(0);
460    log_Printf(LogWARN, "auth_ReadHeader: Short packet header (%u > %zu) !\n",
461               (int)(sizeof authp->in.hdr), len);
462  }
463
464  m_freem(bp);
465  return NULL;
466}
467
468struct mbuf *
469auth_ReadName(struct authinfo *authp, struct mbuf *bp, size_t len)
470{
471  if (len > sizeof authp->in.name - 1)
472    log_Printf(LogWARN, "auth_ReadName: Name too long (%zu) !\n", len);
473  else {
474    size_t mlen = m_length(bp);
475
476    if (len > mlen)
477      log_Printf(LogWARN, "auth_ReadName: Short packet (%zu > %zu) !\n",
478                 len, mlen);
479    else {
480      bp = mbuf_Read(bp, (u_char *)authp->in.name, len);
481      authp->in.name[len] = '\0';
482      return bp;
483    }
484  }
485
486  *authp->in.name = '\0';
487  m_freem(bp);
488  return NULL;
489}
490