xref: /freebsd/usr.sbin/ppp/auth.c (revision 4f52dfbb)
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 
89 const char *
90 Auth2Nam(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)
107 static int
108 pam_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 
123 static int
124 auth_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 
171 int
172 auth_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) {
181 again:
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 
216 int
217 auth_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) {
242 again:
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 
297 int
298 auth_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);
308 again:
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 
348 char *
349 auth_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 
363 again:
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 
395 static void
396 AuthTimeout(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 
411 void
412 auth_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 
425 void
426 auth_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 
439 void
440 auth_StopTimer(struct authinfo *authp)
441 {
442   timer_Stop(&authp->authtimer);
443 }
444 
445 struct mbuf *
446 auth_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 
468 struct mbuf *
469 auth_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