xref: /freebsd/usr.sbin/ppp/radius.c (revision c3aac50f284c6cca5b4f2eb46aaa13812cb8b630)
1 /*
2  * Copyright 1999 Internet Business Solutions Ltd., Switzerland
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$Id: radius.c,v 1.5 1999/04/21 08:13:09 brian Exp $
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <netinet/in_systm.h>
32 #include <netinet/in.h>
33 #include <netinet/ip.h>
34 #include <arpa/inet.h>
35 #include <sys/un.h>
36 
37 #include <errno.h>
38 #include <radlib.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/time.h>
43 #include <termios.h>
44 
45 #include "layer.h"
46 #include "defs.h"
47 #include "log.h"
48 #include "descriptor.h"
49 #include "prompt.h"
50 #include "timer.h"
51 #include "fsm.h"
52 #include "iplist.h"
53 #include "slcompress.h"
54 #include "throughput.h"
55 #include "lqr.h"
56 #include "hdlc.h"
57 #include "mbuf.h"
58 #include "ipcp.h"
59 #include "route.h"
60 #include "command.h"
61 #include "filter.h"
62 #include "lcp.h"
63 #include "ccp.h"
64 #include "link.h"
65 #include "mp.h"
66 #include "radius.h"
67 #include "auth.h"
68 #include "async.h"
69 #include "physical.h"
70 #include "chat.h"
71 #include "cbcp.h"
72 #include "chap.h"
73 #include "datalink.h"
74 #include "bundle.h"
75 
76 /*
77  * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
78  */
79 static void
80 radius_Process(struct radius *r, int got)
81 {
82   char *argv[MAXARGS], *nuke;
83   struct bundle *bundle;
84   int argc, addrs;
85   size_t len;
86   struct in_range dest;
87   struct in_addr gw;
88   const void *data;
89 
90   r->cx.fd = -1;		/* Stop select()ing */
91 
92   switch (got) {
93     case RAD_ACCESS_ACCEPT:
94       log_Printf(LogPHASE, "Radius: ACCEPT received\n");
95       break;
96 
97     case RAD_ACCESS_REJECT:
98       log_Printf(LogPHASE, "Radius: REJECT received\n");
99       auth_Failure(r->cx.auth);
100       rad_close(r->cx.rad);
101       return;
102 
103     case RAD_ACCESS_CHALLENGE:
104       /* we can't deal with this (for now) ! */
105       log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
106       auth_Failure(r->cx.auth);
107       rad_close(r->cx.rad);
108       return;
109 
110     case -1:
111       log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
112       auth_Failure(r->cx.auth);
113       rad_close(r->cx.rad);
114       return;
115 
116     default:
117       log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
118                  got, rad_strerror(r->cx.rad));
119       auth_Failure(r->cx.auth);
120       rad_close(r->cx.rad);
121       return;
122   }
123 
124   /* So we've been accepted !  Let's see what we've got in our reply :-I */
125   r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
126   r->mtu = 0;
127   r->vj = 0;
128   while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
129     switch (got) {
130       case RAD_FRAMED_IP_ADDRESS:
131         r->ip = rad_cvt_addr(data);
132         log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
133         break;
134 
135       case RAD_FRAMED_IP_NETMASK:
136         r->mask = rad_cvt_addr(data);
137         log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
138         break;
139 
140       case RAD_FRAMED_MTU:
141         r->mtu = rad_cvt_int(data);
142         log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
143         break;
144 
145       case RAD_FRAMED_ROUTING:
146         /* Disabled for now - should we automatically set up some filters ? */
147         /* rad_cvt_int(data); */
148         /* bit 1 = Send routing packets */
149         /* bit 2 = Receive routing packets */
150         break;
151 
152       case RAD_FRAMED_COMPRESSION:
153         r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
154         log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
155         break;
156 
157       case RAD_FRAMED_ROUTE:
158         /*
159          * We expect a string of the format ``dest[/bits] gw [metrics]''
160          * Any specified metrics are ignored.  MYADDR and HISADDR are
161          * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
162          * as ``HISADDR''.
163          */
164 
165         if ((nuke = rad_cvt_string(data, len)) == NULL) {
166           log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
167           rad_close(r->cx.rad);
168           return;
169         }
170 
171         log_Printf(LogPHASE, "        Route: %s\n", nuke);
172         bundle = r->cx.auth->physical->dl->bundle;
173         dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
174         dest.width = 0;
175         argc = command_Interpret(nuke, strlen(nuke), argv);
176         if (argc < 2)
177           log_Printf(LogWARN, "radius: %s: Invalid route\n",
178                      argc == 1 ? argv[0] : "\"\"");
179         else if ((strcasecmp(argv[0], "default") != 0 &&
180                   !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
181                              &dest.mask, &dest.width)) ||
182                  !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
183           log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
184                      argv[0], argv[1]);
185         else {
186           if (dest.width == 32 && strchr(argv[0], '/') == NULL)
187             /* No mask specified - use the natural mask */
188             dest.mask = addr2mask(dest.ipaddr);
189           addrs = 0;
190 
191           if (!strncasecmp(argv[0], "HISADDR", 7))
192             addrs = ROUTE_DSTHISADDR;
193           else if (!strncasecmp(argv[0], "MYADDR", 6))
194             addrs = ROUTE_DSTMYADDR;
195 
196           if (gw.s_addr == INADDR_ANY) {
197             addrs |= ROUTE_GWHISADDR;
198             gw = bundle->ncp.ipcp.peer_ip;
199           } else if (strcasecmp(argv[1], "HISADDR") == 0)
200             addrs |= ROUTE_GWHISADDR;
201 
202           route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
203         }
204         free(nuke);
205         break;
206     }
207   }
208 
209   if (got == -1) {
210     log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
211                rad_strerror(r->cx.rad));
212     auth_Failure(r->cx.auth);
213     rad_close(r->cx.rad);
214   } else {
215     r->valid = 1;
216     auth_Success(r->cx.auth);
217     rad_close(r->cx.rad);
218   }
219 }
220 
221 /*
222  * We've either timed out or select()ed on the read descriptor
223  */
224 static void
225 radius_Continue(struct radius *r, int sel)
226 {
227   struct timeval tv;
228   int got;
229 
230   timer_Stop(&r->cx.timer);
231   if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
232     log_Printf(LogPHASE, "Radius: Request re-sent\n");
233     r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
234     timer_Start(&r->cx.timer);
235     return;
236   }
237 
238   radius_Process(r, got);
239 }
240 
241 /*
242  * Time to call rad_continue_send_request() - timed out.
243  */
244 static void
245 radius_Timeout(void *v)
246 {
247   radius_Continue((struct radius *)v, 0);
248 }
249 
250 /*
251  * Time to call rad_continue_send_request() - something to read.
252  */
253 static void
254 radius_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
255 {
256   radius_Continue(descriptor2radius(d), 1);
257 }
258 
259 /*
260  * Behave as a struct descriptor (descriptor.h)
261  */
262 static int
263 radius_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
264 {
265   struct radius *rad = descriptor2radius(d);
266 
267   if (r && rad->cx.fd != -1) {
268     FD_SET(rad->cx.fd, r);
269     if (*n < rad->cx.fd + 1)
270       *n = rad->cx.fd + 1;
271     log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
272     return 1;
273   }
274 
275   return 0;
276 }
277 
278 /*
279  * Behave as a struct descriptor (descriptor.h)
280  */
281 static int
282 radius_IsSet(struct descriptor *d, const fd_set *fdset)
283 {
284   struct radius *r = descriptor2radius(d);
285 
286   return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
287 }
288 
289 /*
290  * Behave as a struct descriptor (descriptor.h)
291  */
292 static int
293 radius_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
294 {
295   /* We never want to write here ! */
296   log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
297   return 0;
298 }
299 
300 /*
301  * Initialise ourselves
302  */
303 void
304 radius_Init(struct radius *r)
305 {
306   r->valid = 0;
307   r->cx.fd = -1;
308   *r->cfg.file = '\0';;
309   r->desc.type = RADIUS_DESCRIPTOR;
310   r->desc.UpdateSet = radius_UpdateSet;
311   r->desc.IsSet = radius_IsSet;
312   r->desc.Read = radius_Read;
313   r->desc.Write = radius_Write;
314   memset(&r->cx.timer, '\0', sizeof r->cx.timer);
315 }
316 
317 /*
318  * Forget everything and go back to initialised state.
319  */
320 void
321 radius_Destroy(struct radius *r)
322 {
323   r->valid = 0;
324   timer_Stop(&r->cx.timer);
325   route_DeleteAll(&r->routes);
326   if (r->cx.fd != -1) {
327     r->cx.fd = -1;
328     rad_close(r->cx.rad);
329   }
330 }
331 
332 /*
333  * Start an authentication request to the RADIUS server.
334  */
335 void
336 radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
337                     const char *key, const char *challenge)
338 {
339   struct timeval tv;
340   int got;
341 
342   if (!*r->cfg.file)
343     return;
344 
345   if (r->cx.fd != -1)
346     /*
347      * We assume that our name/key/challenge is the same as last time,
348      * and just continue to wait for the RADIUS server(s).
349      */
350     return;
351 
352   radius_Destroy(r);
353 
354   if ((r->cx.rad = rad_open()) == NULL) {
355     log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
356     return;
357   }
358 
359   if (rad_config(r->cx.rad, r->cfg.file) != 0) {
360     log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
361     rad_close(r->cx.rad);
362     return;
363   }
364 
365   if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
366     log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
367     rad_close(r->cx.rad);
368     return;
369   }
370 
371   if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
372       rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
373       rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
374     log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
375     rad_close(r->cx.rad);
376     return;
377   }
378 
379   if (challenge != NULL) {
380     /* We're talking CHAP */
381     if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
382         rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
383       log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
384                  rad_strerror(r->cx.rad));
385       rad_close(r->cx.rad);
386       return;
387     }
388   } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
389     /* We're talking PAP */
390     log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
391     rad_close(r->cx.rad);
392     return;
393   }
394 
395   if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
396     radius_Process(r, got);
397   else {
398     log_Printf(LogPHASE, "Radius: Request sent\n");
399     log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
400     r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
401     r->cx.timer.func = radius_Timeout;
402     r->cx.timer.name = "radius";
403     r->cx.timer.arg = r;
404     r->cx.auth = authp;
405     timer_Start(&r->cx.timer);
406   }
407 }
408 
409 /*
410  * How do things look at the moment ?
411  */
412 void
413 radius_Show(struct radius *r, struct prompt *p)
414 {
415   prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
416   if (r->valid) {
417     prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
418     prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
419     prompt_Printf(p, "           MTU: %lu\n", r->mtu);
420     prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
421     if (r->routes)
422       route_ShowSticky(p, r->routes, "        Routes", 16);
423   } else
424     prompt_Printf(p, " (not authenticated)\n");
425 }
426