radius.c revision 54914
12606Sdfr/*
22606Sdfr * Copyright 1999 Internet Business Solutions Ltd., Switzerland
331603Syokota * All rights reserved.
42606Sdfr *
52606Sdfr * Redistribution and use in source and binary forms, with or without
62606Sdfr * modification, are permitted provided that the following conditions
72606Sdfr * are met:
82606Sdfr * 1. Redistributions of source code must retain the above copyright
92606Sdfr *    notice, this list of conditions and the following disclaimer.
102606Sdfr * 2. Redistributions in binary form must reproduce the above copyright
112606Sdfr *    notice, this list of conditions and the following disclaimer in the
122606Sdfr *    documentation and/or other materials provided with the distribution.
132606Sdfr *
142606Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152606Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162606Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172606Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182606Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192606Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202606Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212606Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222606Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350477Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242606Sdfr * SUCH DAMAGE.
252606Sdfr *
2666860Sphk * $FreeBSD: head/usr.sbin/ppp/radius.c 54914 1999-12-20 20:30:02Z brian $
2766860Sphk *
2819753Ssos */
2919753Ssos
3019753Ssos#include <sys/param.h>
3119753Ssos#include <netinet/in_systm.h>
3231603Syokota#include <netinet/in.h>
3331603Syokota#include <netinet/ip.h>
3431603Syokota#include <arpa/inet.h>
3531603Syokota#include <sys/un.h>
3631603Syokota
3731603Syokota#include <errno.h>
3831603Syokota#include <radlib.h>
3931603Syokota#include <stdio.h>
4031603Syokota#include <stdlib.h>
4131603Syokota#include <string.h>
4231603Syokota#include <sys/time.h>
4319753Ssos#include <termios.h>
44153072Sru#include <ttyent.h>
4531603Syokota#include <unistd.h>
4631603Syokota#include <netdb.h>
4731603Syokota
4831603Syokota#include "layer.h"
4931603Syokota#include "defs.h"
5019753Ssos#include "log.h"
51132865Snjl#include "descriptor.h"
52132865Snjl#include "prompt.h"
5320073Ssos#include "timer.h"
5420073Ssos#include "fsm.h"
5531603Syokota#include "iplist.h"
5620073Ssos#include "slcompress.h"
5720073Ssos#include "throughput.h"
5820073Ssos#include "lqr.h"
5920073Ssos#include "hdlc.h"
6031603Syokota#include "mbuf.h"
6120073Ssos#include "ipcp.h"
6221327Snate#include "route.h"
6320073Ssos#include "command.h"
6420073Ssos#include "filter.h"
6520073Ssos#include "lcp.h"
6620073Ssos#include "ccp.h"
6720073Ssos#include "link.h"
6820073Ssos#include "mp.h"
6920073Ssos#include "radius.h"
7020073Ssos#include "auth.h"
7120073Ssos#include "async.h"
7231603Syokota#include "physical.h"
7331603Syokota#include "chat.h"
7431603Syokota#include "cbcp.h"
7531603Syokota#include "chap.h"
7620073Ssos#include "datalink.h"
7731603Syokota#include "bundle.h"
7831603Syokota
7931603Syokota/*
8031603Syokota * rad_continue_send_request() has given us `got' (non-zero).  Deal with it.
8131603Syokota */
8231603Syokotastatic void
8319753Ssosradius_Process(struct radius *r, int got)
8431603Syokota{
8519753Ssos  char *argv[MAXARGS], *nuke;
8619753Ssos  struct bundle *bundle;
8731603Syokota  int argc, addrs;
8819753Ssos  size_t len;
89281440Srpaulo  struct in_range dest;
9021327Snate  struct in_addr gw;
9119753Ssos  const void *data;
9221327Snate
93132865Snjl  r->cx.fd = -1;		/* Stop select()ing */
94132865Snjl
95132865Snjl  switch (got) {
96132865Snjl    case RAD_ACCESS_ACCEPT:
97132865Snjl      log_Printf(LogPHASE, "Radius: ACCEPT received\n");
98132865Snjl      break;
99132865Snjl
100132865Snjl    case RAD_ACCESS_REJECT:
101132865Snjl      log_Printf(LogPHASE, "Radius: REJECT received\n");
102132865Snjl      auth_Failure(r->cx.auth);
103132865Snjl      rad_close(r->cx.rad);
104132865Snjl      return;
105132865Snjl
106132865Snjl    case RAD_ACCESS_CHALLENGE:
107132865Snjl      /* we can't deal with this (for now) ! */
108132865Snjl      log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
109255154Sdumbbell      auth_Failure(r->cx.auth);
110255154Sdumbbell      rad_close(r->cx.rad);
111281127Srpaulo      return;
112281127Srpaulo
113281127Srpaulo    case -1:
114255154Sdumbbell      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
115255154Sdumbbell      auth_Failure(r->cx.auth);
116281440Srpaulo      rad_close(r->cx.rad);
117281440Srpaulo      return;
118281440Srpaulo
119281440Srpaulo    default:
120281440Srpaulo      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
121281440Srpaulo                 got, rad_strerror(r->cx.rad));
122281440Srpaulo      auth_Failure(r->cx.auth);
123281440Srpaulo      rad_close(r->cx.rad);
124281440Srpaulo      return;
125281440Srpaulo  }
126281440Srpaulo
127281440Srpaulo  /* So we've been accepted !  Let's see what we've got in our reply :-I */
128281440Srpaulo  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
129281440Srpaulo  r->mtu = 0;
130281440Srpaulo  r->vj = 0;
131282734Srpaulo  while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
132282734Srpaulo    switch (got) {
133282734Srpaulo      case RAD_FRAMED_IP_ADDRESS:
134282734Srpaulo        r->ip = rad_cvt_addr(data);
135282734Srpaulo        log_Printf(LogPHASE, "        IP %s\n", inet_ntoa(r->ip));
136282734Srpaulo        break;
137282734Srpaulo
138331170Seadler      case RAD_FRAMED_IP_NETMASK:
139344165Swulf        r->mask = rad_cvt_addr(data);
140132865Snjl        log_Printf(LogPHASE, "        Netmask %s\n", inet_ntoa(r->mask));
141132865Snjl        break;
14219753Ssos
14331603Syokota      case RAD_FRAMED_MTU:
14419753Ssos        r->mtu = rad_cvt_int(data);
14519753Ssos        log_Printf(LogPHASE, "        MTU %lu\n", r->mtu);
14619753Ssos        break;
14719753Ssos
14831603Syokota      case RAD_FRAMED_ROUTING:
14944186Sn_hibma        /* Disabled for now - should we automatically set up some filters ? */
15021327Snate        /* rad_cvt_int(data); */
15119753Ssos        /* bit 1 = Send routing packets */
15219753Ssos        /* bit 2 = Receive routing packets */
15319753Ssos        break;
15419753Ssos
15519753Ssos      case RAD_FRAMED_COMPRESSION:
15619753Ssos        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
1572606Sdfr        log_Printf(LogPHASE, "        VJ %sabled\n", r->vj ? "en" : "dis");
15831603Syokota        break;
15931603Syokota
16031603Syokota      case RAD_FRAMED_ROUTE:
16131603Syokota        /*
16231603Syokota         * We expect a string of the format ``dest[/bits] gw [metrics]''
16331603Syokota         * Any specified metrics are ignored.  MYADDR and HISADDR are
16431603Syokota         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
16531603Syokota         * as ``HISADDR''.
16631603Syokota         */
16731603Syokota
16841271Syokota        if ((nuke = rad_cvt_string(data, len)) == NULL) {
16949964Syokota          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
17058230Syokota          rad_close(r->cx.rad);
17158230Syokota          return;
17258230Syokota        }
173132865Snjl
174248478Sjkim        log_Printf(LogPHASE, "        Route: %s\n", nuke);
175307576Sgonzo        bundle = r->cx.auth->physical->dl->bundle;
17631603Syokota        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
17719753Ssos        dest.width = 0;
17819753Ssos        argc = command_Interpret(nuke, strlen(nuke), argv);
17919753Ssos        if (argc < 0)
18031603Syokota          log_Printf(LogWARN, "radius: %s: Syntax error\n",
18119753Ssos                     argc == 1 ? argv[0] : "\"\"");
18231603Syokota        else if (argc < 2)
18331603Syokota          log_Printf(LogWARN, "radius: %s: Invalid route\n",
18431603Syokota                     argc == 1 ? argv[0] : "\"\"");
18519753Ssos        else if ((strcasecmp(argv[0], "default") != 0 &&
18621327Snate                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
18719753Ssos                             &dest.mask, &dest.width)) ||
18858230Syokota                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
18958230Syokota          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
19058230Syokota                     argv[0], argv[1]);
19158230Syokota        else {
19258230Syokota          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
19358230Syokota            /* No mask specified - use the natural mask */
19458230Syokota            dest.mask = addr2mask(dest.ipaddr);
19558230Syokota          addrs = 0;
19658230Syokota
19758230Syokota          if (!strncasecmp(argv[0], "HISADDR", 7))
19831603Syokota            addrs = ROUTE_DSTHISADDR;
19919753Ssos          else if (!strncasecmp(argv[0], "MYADDR", 6))
20019753Ssos            addrs = ROUTE_DSTMYADDR;
20119753Ssos
20219753Ssos          if (gw.s_addr == INADDR_ANY) {
20319753Ssos            addrs |= ROUTE_GWHISADDR;
20419753Ssos            gw = bundle->ncp.ipcp.peer_ip;
20531603Syokota          } else if (strcasecmp(argv[1], "HISADDR") == 0)
20619753Ssos            addrs |= ROUTE_GWHISADDR;
20731603Syokota
20831603Syokota          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
20931603Syokota        }
210165335Skeramida        free(nuke);
21131603Syokota        break;
21236991Sahasty    }
21341271Syokota  }
21449964Syokota
21593071Swill  if (got == -1) {
216145001Smdodd    log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
21719753Ssos               rad_strerror(r->cx.rad));
21831603Syokota    auth_Failure(r->cx.auth);
21931603Syokota    rad_close(r->cx.rad);
22031603Syokota  } else {
22131603Syokota    r->valid = 1;
22231603Syokota    auth_Success(r->cx.auth);
22331603Syokota    rad_close(r->cx.rad);
22431603Syokota  }
22531603Syokota}
22631603Syokota
22731603Syokota/*
22831603Syokota * We've either timed out or select()ed on the read descriptor
22931603Syokota */
23031603Syokotastatic void
23131603Syokotaradius_Continue(struct radius *r, int sel)
23231603Syokota{
23331603Syokota  struct timeval tv;
23431603Syokota  int got;
23531603Syokota
23631603Syokota  timer_Stop(&r->cx.timer);
23731603Syokota  if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
23831603Syokota    log_Printf(LogPHASE, "Radius: Request re-sent\n");
23931603Syokota    r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
24031603Syokota    timer_Start(&r->cx.timer);
24131603Syokota    return;
24231603Syokota  }
243132865Snjl
244132865Snjl  radius_Process(r, got);
245132865Snjl}
246307576Sgonzo
247307576Sgonzo/*
248307576Sgonzo * Time to call rad_continue_send_request() - timed out.
24919753Ssos */
25019753Ssosstatic void
25119753Ssosradius_Timeout(void *v)
25219753Ssos{
25319753Ssos  radius_Continue((struct radius *)v, 0);
25419753Ssos}
25519753Ssos
25619753Ssos/*
25719753Ssos * Time to call rad_continue_send_request() - something to read.
25820073Ssos */
25931603Syokotastatic void
26020073Ssosradius_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
26179298Sdd{
26231603Syokota  radius_Continue(descriptor2radius(d), 1);
26331603Syokota}
26431603Syokota
26579298Sdd/*
26631603Syokota * Behave as a struct descriptor (descriptor.h)
26731603Syokota */
26831603Syokotastatic int
26931603Syokotaradius_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
27031603Syokota{
27158230Syokota  struct radius *rad = descriptor2radius(d);
27231603Syokota
27319753Ssos  if (r && rad->cx.fd != -1) {
27419753Ssos    FD_SET(rad->cx.fd, r);
27519753Ssos    if (*n < rad->cx.fd + 1)
27619753Ssos      *n = rad->cx.fd + 1;
27719753Ssos    log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
27819753Ssos    return 1;
27919753Ssos  }
28019753Ssos
28144186Sn_hibma  return 0;
28219753Ssos}
28320073Ssos
28420073Ssos/*
28520073Ssos * Behave as a struct descriptor (descriptor.h)
28620073Ssos */
28720073Ssosstatic int
28820073Ssosradius_IsSet(struct descriptor *d, const fd_set *fdset)
28920073Ssos{
29020073Ssos  struct radius *r = descriptor2radius(d);
29120073Ssos
29220073Ssos  return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
29320073Ssos}
29419753Ssos
29519753Ssos/*
29631603Syokota * Behave as a struct descriptor (descriptor.h)
29731603Syokota */
29819753Ssosstatic int
29919753Ssosradius_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
30019753Ssos{
30119753Ssos  /* We never want to write here ! */
30231603Syokota  log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
303281440Srpaulo  return 0;
30431603Syokota}
30531603Syokota
30631603Syokota/*
30719753Ssos * Initialise ourselves
30819753Ssos */
30919753Ssosvoid
31019753Ssosradius_Init(struct radius *r)
31158230Syokota{
31258230Syokota  r->valid = 0;
31348778Syokota  r->cx.fd = -1;
31448778Syokota  *r->cfg.file = '\0';;
31558230Syokota  r->desc.type = RADIUS_DESCRIPTOR;
31658230Syokota  r->desc.UpdateSet = radius_UpdateSet;
31758230Syokota  r->desc.IsSet = radius_IsSet;
31819753Ssos  r->desc.Read = radius_Read;
31958230Syokota  r->desc.Write = radius_Write;
32058230Syokota  memset(&r->cx.timer, '\0', sizeof r->cx.timer);
32158230Syokota}
32258230Syokota
32358230Syokota/*
32458230Syokota * Forget everything and go back to initialised state.
32558230Syokota */
32658230Syokotavoid
32758230Syokotaradius_Destroy(struct radius *r)
32858230Syokota{
32958230Syokota  r->valid = 0;
33058230Syokota  timer_Stop(&r->cx.timer);
33158230Syokota  route_DeleteAll(&r->routes);
33258230Syokota  if (r->cx.fd != -1) {
33358230Syokota    r->cx.fd = -1;
33458230Syokota    rad_close(r->cx.rad);
33549964Syokota  }
33649964Syokota}
33749964Syokota
33849964Syokota/*
33949964Syokota * Start an authentication request to the RADIUS server.
34049964Syokota */
34149964Syokotavoid
34249964Syokotaradius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
34349964Syokota                    const char *key, const char *challenge)
34449964Syokota{
34549964Syokota  struct ttyent *ttyp;
34649964Syokota  struct timeval tv;
34749964Syokota  int got, slot;
34849964Syokota  char hostname[MAXHOSTNAMELEN];
34949964Syokota  struct hostent *hp;
35049964Syokota  struct in_addr hostaddr;
35149964Syokota
35249964Syokota  if (!*r->cfg.file)
35349964Syokota    return;
35449964Syokota
35549964Syokota  if (r->cx.fd != -1)
35649964Syokota    /*
35758230Syokota     * We assume that our name/key/challenge is the same as last time,
358281440Srpaulo     * and just continue to wait for the RADIUS server(s).
35958230Syokota     */
36058230Syokota    return;
36158230Syokota
362281440Srpaulo  radius_Destroy(r);
36358230Syokota
36458230Syokota  if ((r->cx.rad = rad_open()) == NULL) {
36558230Syokota    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
36631603Syokota    return;
36731603Syokota  }
36831603Syokota
36931603Syokota  if (rad_config(r->cx.rad, r->cfg.file) != 0) {
37031603Syokota    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
37131603Syokota    rad_close(r->cx.rad);
37231603Syokota    return;
37331603Syokota  }
374281440Srpaulo
37531603Syokota  if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
37631603Syokota    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
37731603Syokota    rad_close(r->cx.rad);
37831603Syokota    return;
37931603Syokota  }
38031603Syokota
38131603Syokota  if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
38231603Syokota      rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
38331603Syokota      rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
38431603Syokota    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
38531603Syokota    rad_close(r->cx.rad);
38631603Syokota    return;
38731603Syokota  }
38831603Syokota
38931603Syokota  if (challenge != NULL) {
39031603Syokota    /* We're talking CHAP */
39131603Syokota    if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
39231603Syokota        rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
39331603Syokota      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
39436991Sahasty                 rad_strerror(r->cx.rad));
39536991Sahasty      rad_close(r->cx.rad);
39636991Sahasty      return;
39766860Sphk    }
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