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