radius.c revision 54914
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 54914 1999-12-20 20:30:02Z 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 < 0)
180          log_Printf(LogWARN, "radius: %s: Syntax error\n",
181                     argc == 1 ? argv[0] : "\"\"");
182        else if (argc < 2)
183          log_Printf(LogWARN, "radius: %s: Invalid route\n",
184                     argc == 1 ? argv[0] : "\"\"");
185        else if ((strcasecmp(argv[0], "default") != 0 &&
186                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
187                             &dest.mask, &dest.width)) ||
188                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
189          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
190                     argv[0], argv[1]);
191        else {
192          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
193            /* No mask specified - use the natural mask */
194            dest.mask = addr2mask(dest.ipaddr);
195          addrs = 0;
196
197          if (!strncasecmp(argv[0], "HISADDR", 7))
198            addrs = ROUTE_DSTHISADDR;
199          else if (!strncasecmp(argv[0], "MYADDR", 6))
200            addrs = ROUTE_DSTMYADDR;
201
202          if (gw.s_addr == INADDR_ANY) {
203            addrs |= ROUTE_GWHISADDR;
204            gw = bundle->ncp.ipcp.peer_ip;
205          } else if (strcasecmp(argv[1], "HISADDR") == 0)
206            addrs |= ROUTE_GWHISADDR;
207
208          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
209        }
210        free(nuke);
211        break;
212    }
213  }
214
215  if (got == -1) {
216    log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
217               rad_strerror(r->cx.rad));
218    auth_Failure(r->cx.auth);
219    rad_close(r->cx.rad);
220  } else {
221    r->valid = 1;
222    auth_Success(r->cx.auth);
223    rad_close(r->cx.rad);
224  }
225}
226
227/*
228 * We've either timed out or select()ed on the read descriptor
229 */
230static void
231radius_Continue(struct radius *r, int sel)
232{
233  struct timeval tv;
234  int got;
235
236  timer_Stop(&r->cx.timer);
237  if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
238    log_Printf(LogPHASE, "Radius: Request re-sent\n");
239    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
240    timer_Start(&r->cx.timer);
241    return;
242  }
243
244  radius_Process(r, got);
245}
246
247/*
248 * Time to call rad_continue_send_request() - timed out.
249 */
250static void
251radius_Timeout(void *v)
252{
253  radius_Continue((struct radius *)v, 0);
254}
255
256/*
257 * Time to call rad_continue_send_request() - something to read.
258 */
259static void
260radius_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
261{
262  radius_Continue(descriptor2radius(d), 1);
263}
264
265/*
266 * Behave as a struct descriptor (descriptor.h)
267 */
268static int
269radius_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
270{
271  struct radius *rad = descriptor2radius(d);
272
273  if (r && rad->cx.fd != -1) {
274    FD_SET(rad->cx.fd, r);
275    if (*n < rad->cx.fd + 1)
276      *n = rad->cx.fd + 1;
277    log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
278    return 1;
279  }
280
281  return 0;
282}
283
284/*
285 * Behave as a struct descriptor (descriptor.h)
286 */
287static int
288radius_IsSet(struct descriptor *d, const fd_set *fdset)
289{
290  struct radius *r = descriptor2radius(d);
291
292  return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
293}
294
295/*
296 * Behave as a struct descriptor (descriptor.h)
297 */
298static int
299radius_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
300{
301  /* We never want to write here ! */
302  log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
303  return 0;
304}
305
306/*
307 * Initialise ourselves
308 */
309void
310radius_Init(struct radius *r)
311{
312  r->valid = 0;
313  r->cx.fd = -1;
314  *r->cfg.file = '\0';;
315  r->desc.type = RADIUS_DESCRIPTOR;
316  r->desc.UpdateSet = radius_UpdateSet;
317  r->desc.IsSet = radius_IsSet;
318  r->desc.Read = radius_Read;
319  r->desc.Write = radius_Write;
320  memset(&r->cx.timer, '\0', sizeof r->cx.timer);
321}
322
323/*
324 * Forget everything and go back to initialised state.
325 */
326void
327radius_Destroy(struct radius *r)
328{
329  r->valid = 0;
330  timer_Stop(&r->cx.timer);
331  route_DeleteAll(&r->routes);
332  if (r->cx.fd != -1) {
333    r->cx.fd = -1;
334    rad_close(r->cx.rad);
335  }
336}
337
338/*
339 * Start an authentication request to the RADIUS server.
340 */
341void
342radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
343                    const char *key, const char *challenge)
344{
345  struct ttyent *ttyp;
346  struct timeval tv;
347  int got, slot;
348  char hostname[MAXHOSTNAMELEN];
349  struct hostent *hp;
350  struct in_addr hostaddr;
351
352  if (!*r->cfg.file)
353    return;
354
355  if (r->cx.fd != -1)
356    /*
357     * We assume that our name/key/challenge is the same as last time,
358     * and just continue to wait for the RADIUS server(s).
359     */
360    return;
361
362  radius_Destroy(r);
363
364  if ((r->cx.rad = rad_open()) == NULL) {
365    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
366    return;
367  }
368
369  if (rad_config(r->cx.rad, r->cfg.file) != 0) {
370    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
371    rad_close(r->cx.rad);
372    return;
373  }
374
375  if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
376    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
377    rad_close(r->cx.rad);
378    return;
379  }
380
381  if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
382      rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
383      rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
384    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
385    rad_close(r->cx.rad);
386    return;
387  }
388
389  if (challenge != NULL) {
390    /* We're talking CHAP */
391    if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
392        rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
393      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
394                 rad_strerror(r->cx.rad));
395      rad_close(r->cx.rad);
396      return;
397    }
398  } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
399    /* We're talking PAP */
400    log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
401    rad_close(r->cx.rad);
402    return;
403  }
404
405  if (gethostname(hostname, sizeof hostname) != 0)
406    log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
407  else {
408    if ((hp = gethostbyname(hostname)) != NULL) {
409      hostaddr.s_addr = *(u_long *)hp->h_addr;
410      if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
411        log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
412                   rad_strerror(r->cx.rad));
413        rad_close(r->cx.rad);
414        return;
415      }
416    }
417    if (rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
418      log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
419                 rad_strerror(r->cx.rad));
420      rad_close(r->cx.rad);
421      return;
422    }
423  }
424
425  if (authp->physical->handler &&
426      authp->physical->handler->type == TTY_DEVICE) {
427    setttyent();
428    for (slot = 1; (ttyp = getttyent()); ++slot)
429      if (!strcmp(ttyp->ty_name, authp->physical->name.base)) {
430        if(rad_put_int(r->cx.rad, RAD_NAS_PORT, slot) != 0) {
431          log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
432                      rad_strerror(r->cx.rad));
433          rad_close(r->cx.rad);
434          endttyent();
435          return;
436        }
437        break;
438      }
439    endttyent();
440  }
441
442
443  if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
444    radius_Process(r, got);
445  else {
446    log_Printf(LogPHASE, "Radius: Request sent\n");
447    log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
448    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
449    r->cx.timer.func = radius_Timeout;
450    r->cx.timer.name = "radius";
451    r->cx.timer.arg = r;
452    r->cx.auth = authp;
453    timer_Start(&r->cx.timer);
454  }
455}
456
457/*
458 * How do things look at the moment ?
459 */
460void
461radius_Show(struct radius *r, struct prompt *p)
462{
463  prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
464  if (r->valid) {
465    prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
466    prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
467    prompt_Printf(p, "           MTU: %lu\n", r->mtu);
468    prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
469    if (r->routes)
470      route_ShowSticky(p, r->routes, "        Routes", 16);
471  } else
472    prompt_Printf(p, " (not authenticated)\n");
473}
474