radius.c revision 51449
119304Speter/*
219304Speter * Copyright 1999 Internet Business Solutions Ltd., Switzerland
319304Speter * All rights reserved.
419304Speter *
519304Speter * Redistribution and use in source and binary forms, with or without
619304Speter * modification, are permitted provided that the following conditions
719304Speter * are met:
819304Speter * 1. Redistributions of source code must retain the above copyright
919304Speter *    notice, this list of conditions and the following disclaimer.
1019304Speter * 2. Redistributions in binary form must reproduce the above copyright
1119304Speter *    notice, this list of conditions and the following disclaimer in the
1219304Speter *    documentation and/or other materials provided with the distribution.
1319304Speter *
1419304Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1519304Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1619304Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1719304Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1819304Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1919304Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2019304Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2119304Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2219304Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2319304Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2419304Speter * SUCH DAMAGE.
2519304Speter *
2619304Speter * $FreeBSD: head/usr.sbin/ppp/radius.c 51449 1999-09-20 07:36:46Z brian $
2719304Speter *
2819304Speter */
2919304Speter
3019304Speter#include <sys/param.h>
3119304Speter#include <netinet/in_systm.h>
3219304Speter#include <netinet/in.h>
3319304Speter#include <netinet/ip.h>
3419304Speter#include <arpa/inet.h>
3519304Speter#include <sys/un.h>
3619304Speter
3719304Speter#include <errno.h>
3819304Speter#include <radlib.h>
3919304Speter#ifdef __NetBSD__
4019304Speter#include <signal.h>	/* for `errno' ?!? */
4119304Speter#endif
4219304Speter#include <stdio.h>
4319304Speter#include <stdlib.h>
4419304Speter#include <string.h>
4519304Speter#include <sys/time.h>
4619304Speter#include <termios.h>
4719304Speter#include <ttyent.h>
4819304Speter#include <unistd.h>
4919304Speter#include <netdb.h>
5019304Speter
5119304Speter#include "layer.h"
5219304Speter#include "defs.h"
5319304Speter#include "log.h"
5419304Speter#include "descriptor.h"
5519304Speter#include "prompt.h"
5619304Speter#include "timer.h"
5719304Speter#include "fsm.h"
5819304Speter#include "iplist.h"
5919304Speter#include "slcompress.h"
6019304Speter#include "throughput.h"
6119304Speter#include "lqr.h"
6219304Speter#include "hdlc.h"
6319304Speter#include "mbuf.h"
6419304Speter#include "ipcp.h"
6519304Speter#include "route.h"
6619304Speter#include "command.h"
6719304Speter#include "filter.h"
6819304Speter#include "lcp.h"
6919304Speter#include "ccp.h"
7019304Speter#include "link.h"
7119304Speter#include "mp.h"
7219304Speter#include "radius.h"
7319304Speter#include "auth.h"
7419304Speter#include "async.h"
7519304Speter#include "physical.h"
7619304Speter#include "chat.h"
7719304Speter#include "cbcp.h"
7819304Speter#include "chap.h"
7919304Speter#include "datalink.h"
8019304Speter#include "bundle.h"
8119304Speter
8219304Speter/*
8319304Speter * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
8419304Speter */
8519304Speterstatic void
8619304Speterradius_Process(struct radius *r, int got)
8719304Speter{
8819304Speter  char *argv[MAXARGS], *nuke;
8919304Speter  struct bundle *bundle;
9019304Speter  int argc, addrs;
9119304Speter  size_t len;
9219304Speter  struct in_range dest;
9319304Speter  struct in_addr gw;
9419304Speter  const void *data;
9519304Speter
9619304Speter  r->cx.fd = -1;		/* Stop select()ing */
9719304Speter
9819304Speter  switch (got) {
9919304Speter    case RAD_ACCESS_ACCEPT:
10019304Speter      log_Printf(LogPHASE, "Radius: ACCEPT received\n");
10119304Speter      break;
10219304Speter
10319304Speter    case RAD_ACCESS_REJECT:
10419304Speter      log_Printf(LogPHASE, "Radius: REJECT received\n");
10519304Speter      auth_Failure(r->cx.auth);
10619304Speter      rad_close(r->cx.rad);
10719304Speter      return;
10819304Speter
10919304Speter    case RAD_ACCESS_CHALLENGE:
11019304Speter      /* we can't deal with this (for now) ! */
11119304Speter      log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
11219304Speter      auth_Failure(r->cx.auth);
11319304Speter      rad_close(r->cx.rad);
11419304Speter      return;
11519304Speter
11619304Speter    case -1:
11719304Speter      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
11819304Speter      auth_Failure(r->cx.auth);
11919304Speter      rad_close(r->cx.rad);
12019304Speter      return;
12119304Speter
12219304Speter    default:
12319304Speter      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
12419304Speter                 got, rad_strerror(r->cx.rad));
12519304Speter      auth_Failure(r->cx.auth);
12619304Speter      rad_close(r->cx.rad);
12719304Speter      return;
12819304Speter  }
12919304Speter
13019304Speter  /* So we've been accepted !  Let's see what we've got in our reply :-I */
13119304Speter  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
13219304Speter  r->mtu = 0;
13319304Speter  r->vj = 0;
13419304Speter  while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
13519304Speter    switch (got) {
13619304Speter      case RAD_FRAMED_IP_ADDRESS:
13719304Speter        r->ip = rad_cvt_addr(data);
13819304Speter        log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
13919304Speter        break;
14019304Speter
14119304Speter      case RAD_FRAMED_IP_NETMASK:
14219304Speter        r->mask = rad_cvt_addr(data);
14319304Speter        log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
14419304Speter        break;
14519304Speter
14619304Speter      case RAD_FRAMED_MTU:
14719304Speter        r->mtu = rad_cvt_int(data);
148        log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
149        break;
150
151      case RAD_FRAMED_ROUTING:
152        /* Disabled for now - should we automatically set up some filters ? */
153        /* rad_cvt_int(data); */
154        /* bit 1 = Send routing packets */
155        /* bit 2 = Receive routing packets */
156        break;
157
158      case RAD_FRAMED_COMPRESSION:
159        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
160        log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
161        break;
162
163      case RAD_FRAMED_ROUTE:
164        /*
165         * We expect a string of the format ``dest[/bits] gw [metrics]''
166         * Any specified metrics are ignored.  MYADDR and HISADDR are
167         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
168         * as ``HISADDR''.
169         */
170
171        if ((nuke = rad_cvt_string(data, len)) == NULL) {
172          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
173          rad_close(r->cx.rad);
174          return;
175        }
176
177        log_Printf(LogPHASE, "        Route: %s\n", nuke);
178        bundle = r->cx.auth->physical->dl->bundle;
179        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
180        dest.width = 0;
181        argc = command_Interpret(nuke, strlen(nuke), argv);
182        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