xref: /freebsd/usr.sbin/ppp/auth.c (revision fe267a559009cbf34f9341666fe4d88a92c02d5e)
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 = 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 
169 int
170 auth_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) {
179 again:
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 
214 int
215 auth_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) {
240 again:
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 
295 int
296 auth_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);
306 again:
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 
346 char *
347 auth_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 
361 again:
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 
393 static void
394 AuthTimeout(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 
409 void
410 auth_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 
423 void
424 auth_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 
437 void
438 auth_StopTimer(struct authinfo *authp)
439 {
440   timer_Stop(&authp->authtimer);
441 }
442 
443 struct mbuf *
444 auth_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 
466 struct mbuf *
467 auth_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