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