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