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: stable/11/usr.sbin/ppp/auth.c 330449 2018-03-05 07:26:05Z eadler $
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;
131    char *cryptpw;
132
133    cryptpw = crypt(key, pw->pw_passwd);
134    result = (pw = getpwnam(name)) &&
135             (cryptpw == NULL || !strcmp(cryptpw, pw->pw_passwd));
136    endpwent();
137    return result;
138#else /* !NOPAM */
139    /* Then consult with PAM. */
140    pam_handle_t *pamh;
141    int status;
142
143    struct pam_conv pamc = {
144#ifdef OPENPAM
145      &openpam_nullconv, NULL
146#else
147      &pam_conv, key
148#endif
149    };
150
151    if (pam_start("ppp", name, &pamc, &pamh) != PAM_SUCCESS)
152      return (0);
153#ifdef OPENPAM
154    if ((status = pam_set_item(pamh, PAM_AUTHTOK, key)) == PAM_SUCCESS)
155#endif
156      status = pam_authenticate(pamh, 0);
157    pam_end(pamh, status);
158    return (status == PAM_SUCCESS);
159#endif /* !NOPAM */
160  }
161
162  return !strcmp(data, key);
163}
164
165int
166auth_SetPhoneList(const char *name, char *phone, int phonelen)
167{
168  FILE *fp;
169  int n, lineno;
170  char *vector[6], buff[LINE_LEN];
171  const char *slash;
172
173  fp = OpenSecret(SECRETFILE);
174  if (fp != NULL) {
175again:
176    lineno = 0;
177    while (fgets(buff, sizeof buff, fp)) {
178      lineno++;
179      if (buff[0] == '#')
180        continue;
181      buff[strlen(buff) - 1] = '\0';
182      memset(vector, '\0', sizeof vector);
183      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
184        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
185      if (n < 5)
186        continue;
187      if (strcmp(vector[0], name) == 0) {
188        CloseSecret(fp);
189        if (*vector[4] == '\0')
190          return 0;
191        strncpy(phone, vector[4], phonelen - 1);
192        phone[phonelen - 1] = '\0';
193        return 1;		/* Valid */
194      }
195    }
196
197    if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
198      /* Look for the name without the leading domain */
199      name = slash + 1;
200      rewind(fp);
201      goto again;
202    }
203
204    CloseSecret(fp);
205  }
206  *phone = '\0';
207  return 0;
208}
209
210int
211auth_Select(struct bundle *bundle, const char *name)
212{
213  FILE *fp;
214  int n, lineno;
215  char *vector[5], buff[LINE_LEN];
216  const char *slash;
217
218  if (*name == '\0') {
219    ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
220    return 1;
221  }
222
223#ifndef NORADIUS
224  if (bundle->radius.valid && bundle->radius.ip.s_addr != INADDR_NONE &&
225	bundle->radius.ip.s_addr != RADIUS_INADDR_POOL) {
226    /* We've got a radius IP - it overrides everything */
227    if (!ipcp_UseHisIPaddr(bundle, bundle->radius.ip))
228      return 0;
229    ipcp_Setup(&bundle->ncp.ipcp, bundle->radius.mask.s_addr);
230    /* Continue with ppp.secret in case we've got a new label */
231  }
232#endif
233
234  fp = OpenSecret(SECRETFILE);
235  if (fp != NULL) {
236again:
237    lineno = 0;
238    while (fgets(buff, sizeof buff, fp)) {
239      lineno++;
240      if (buff[0] == '#')
241        continue;
242      buff[strlen(buff) - 1] = '\0';
243      memset(vector, '\0', sizeof vector);
244      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
245        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
246      if (n < 2)
247        continue;
248      if (strcmp(vector[0], name) == 0) {
249        CloseSecret(fp);
250#ifndef NORADIUS
251        if (!bundle->radius.valid || bundle->radius.ip.s_addr == INADDR_NONE) {
252#endif
253          if (n > 2 && *vector[2] && strcmp(vector[2], "*") &&
254              !ipcp_UseHisaddr(bundle, vector[2], 1))
255            return 0;
256          ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
257#ifndef NORADIUS
258        }
259#endif
260        if (n > 3 && *vector[3] && strcmp(vector[3], "*"))
261          bundle_SetLabel(bundle, vector[3]);
262        return 1;		/* Valid */
263      }
264    }
265
266    if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
267      /* Look for the name without the leading domain */
268      name = slash + 1;
269      rewind(fp);
270      goto again;
271    }
272
273    CloseSecret(fp);
274  }
275
276#ifndef NOPASSWDAUTH
277  /* Let 'em in anyway - they must have been in the passwd file */
278  ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
279  return 1;
280#else
281#ifndef NORADIUS
282  if (bundle->radius.valid)
283    return 1;
284#endif
285
286  /* Disappeared from ppp.secret ??? */
287  return 0;
288#endif
289}
290
291int
292auth_Validate(struct bundle *bundle, const char *name, const char *key)
293{
294  /* Used by PAP routines */
295
296  FILE *fp;
297  int n, lineno;
298  char *vector[5], buff[LINE_LEN];
299  const char *slash;
300
301  fp = OpenSecret(SECRETFILE);
302again:
303  lineno = 0;
304  if (fp != NULL) {
305    while (fgets(buff, sizeof buff, fp)) {
306      lineno++;
307      if (buff[0] == '#')
308        continue;
309      buff[strlen(buff) - 1] = 0;
310      memset(vector, '\0', sizeof vector);
311      if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
312        log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
313      if (n < 2)
314        continue;
315      if (strcmp(vector[0], name) == 0) {
316        CloseSecret(fp);
317        return auth_CheckPasswd(name, vector[1], key);
318      }
319    }
320  }
321
322  if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
323    /* Look for the name without the leading domain */
324    name = slash + 1;
325    if (fp != NULL) {
326      rewind(fp);
327      goto again;
328    }
329  }
330
331  if (fp != NULL)
332    CloseSecret(fp);
333
334#ifndef NOPASSWDAUTH
335  if (Enabled(bundle, OPT_PASSWDAUTH))
336    return auth_CheckPasswd(name, "*", key);
337#endif
338
339  return 0;			/* Invalid */
340}
341
342char *
343auth_GetSecret(const char *name, size_t len)
344{
345  /* Used by CHAP routines */
346
347  FILE *fp;
348  int n, lineno;
349  char *vector[5];
350  const char *slash;
351  static char buff[LINE_LEN];	/* vector[] will point here when returned */
352
353  fp = OpenSecret(SECRETFILE);
354  if (fp == NULL)
355    return (NULL);
356
357again:
358  lineno = 0;
359  while (fgets(buff, sizeof buff, fp)) {
360    lineno++;
361    if (buff[0] == '#')
362      continue;
363    n = strlen(buff) - 1;
364    if (buff[n] == '\n')
365      buff[n] = '\0';	/* Trim the '\n' */
366    memset(vector, '\0', sizeof vector);
367    if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
368      log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
369    if (n < 2)
370      continue;
371    if (strlen(vector[0]) == len && strncmp(vector[0], name, len) == 0) {
372      CloseSecret(fp);
373      return vector[1];
374    }
375  }
376
377  if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
378    /* Go back and look for the name without the leading domain */
379    len -= slash - name + 1;
380    name = slash + 1;
381    rewind(fp);
382    goto again;
383  }
384
385  CloseSecret(fp);
386  return (NULL);		/* Invalid */
387}
388
389static void
390AuthTimeout(void *vauthp)
391{
392  struct authinfo *authp = (struct authinfo *)vauthp;
393
394  timer_Stop(&authp->authtimer);
395  if (--authp->retry > 0) {
396    authp->id++;
397    (*authp->fn.req)(authp);
398    timer_Start(&authp->authtimer);
399  } else {
400    log_Printf(LogPHASE, "Auth: No response from server\n");
401    datalink_AuthNotOk(authp->physical->dl);
402  }
403}
404
405void
406auth_Init(struct authinfo *authp, struct physical *p, auth_func req,
407          auth_func success, auth_func failure)
408{
409  memset(authp, '\0', sizeof(struct authinfo));
410  authp->cfg.fsm.timeout = DEF_FSMRETRY;
411  authp->cfg.fsm.maxreq = DEF_FSMAUTHTRIES;
412  authp->cfg.fsm.maxtrm = 0;	/* not used */
413  authp->fn.req = req;
414  authp->fn.success = success;
415  authp->fn.failure = failure;
416  authp->physical = p;
417}
418
419void
420auth_StartReq(struct authinfo *authp)
421{
422  timer_Stop(&authp->authtimer);
423  authp->authtimer.func = AuthTimeout;
424  authp->authtimer.name = "auth";
425  authp->authtimer.load = authp->cfg.fsm.timeout * SECTICKS;
426  authp->authtimer.arg = (void *)authp;
427  authp->retry = authp->cfg.fsm.maxreq;
428  authp->id = 1;
429  (*authp->fn.req)(authp);
430  timer_Start(&authp->authtimer);
431}
432
433void
434auth_StopTimer(struct authinfo *authp)
435{
436  timer_Stop(&authp->authtimer);
437}
438
439struct mbuf *
440auth_ReadHeader(struct authinfo *authp, struct mbuf *bp)
441{
442  size_t len;
443
444  len = m_length(bp);
445  if (len >= sizeof authp->in.hdr) {
446    bp = mbuf_Read(bp, (u_char *)&authp->in.hdr, sizeof authp->in.hdr);
447    if (len >= ntohs(authp->in.hdr.length))
448      return bp;
449    authp->in.hdr.length = htons(0);
450    log_Printf(LogWARN, "auth_ReadHeader: Short packet (%u > %zu) !\n",
451               ntohs(authp->in.hdr.length), len);
452  } else {
453    authp->in.hdr.length = htons(0);
454    log_Printf(LogWARN, "auth_ReadHeader: Short packet header (%u > %zu) !\n",
455               (int)(sizeof authp->in.hdr), len);
456  }
457
458  m_freem(bp);
459  return NULL;
460}
461
462struct mbuf *
463auth_ReadName(struct authinfo *authp, struct mbuf *bp, size_t len)
464{
465  if (len > sizeof authp->in.name - 1)
466    log_Printf(LogWARN, "auth_ReadName: Name too long (%zu) !\n", len);
467  else {
468    size_t mlen = m_length(bp);
469
470    if (len > mlen)
471      log_Printf(LogWARN, "auth_ReadName: Short packet (%zu > %zu) !\n",
472                 len, mlen);
473    else {
474      bp = mbuf_Read(bp, (u_char *)authp->in.name, len);
475      authp->in.name[len] = '\0';
476      return bp;
477    }
478  }
479
480  *authp->in.name = '\0';
481  m_freem(bp);
482  return NULL;
483}
484