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