radius.c revision 43313
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:$
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 <signal.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.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 "server.h"
62#include "lcp.h"
63#include "ccp.h"
64#include "link.h"
65#include "mp.h"
66#include "radius.h"
67#include "bundle.h"
68
69void
70radius_Init(struct radius *r)
71{
72  r->valid = 0;
73  *r->cfg.file = '\0';;
74}
75
76void
77radius_Destroy(struct radius *r)
78{
79  r->valid = 0;
80  route_DeleteAll(&r->routes);
81}
82
83int
84radius_Authenticate(struct radius *r, struct bundle *bundle, const char *name,
85                    const char *key, const char *challenge)
86{
87  struct rad_handle *h;
88  sigset_t alrm, prevset;
89  const void *data;
90  int got, len, argc, addrs;
91  char *argv[MAXARGS], *nuke;
92  struct in_range dest;
93  struct in_addr gw;
94
95  radius_Destroy(r);
96
97  if (!*r->cfg.file)
98    return 0;
99
100  if ((h = rad_open()) == NULL) {
101    log_Printf(LogERROR, "rad_open: %s\n", strerror(errno));
102    return 0;
103  }
104
105  if (rad_config(h, r->cfg.file) != 0) {
106    log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(h));
107    rad_close(h);
108    return 0;
109  }
110
111  if (rad_create_request(h, RAD_ACCESS_REQUEST) != 0) {
112    log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(h));
113    rad_close(h);
114    return 0;
115  }
116
117  if (rad_put_string(h, RAD_USER_NAME, name) != 0 ||
118      rad_put_int(h, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
119      rad_put_int(h, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
120    log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(h));
121    rad_close(h);
122    return 0;
123  }
124
125  if (challenge != NULL) {					/* CHAP */
126    if (rad_put_string(h, RAD_CHAP_PASSWORD, key) != 0 ||
127        rad_put_string(h, RAD_CHAP_CHALLENGE, challenge) != 0) {
128      log_Printf(LogERROR, "CHAP: rad_put_string: %s\n", rad_strerror(h));
129      rad_close(h);
130      return 0;
131    }
132  } else if (rad_put_string(h, RAD_USER_PASSWORD, key) != 0) {	/* PAP */
133    /* We're talking PAP */
134    log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(h));
135    rad_close(h);
136    return 0;
137  }
138
139  /*
140   * Having to do this is bad news.  The right way is to grab the
141   * descriptor that rad_send_request() selects on and add it to
142   * our own selection list (making a full ``struct descriptor''),
143   * then to ``continue'' the call when the descriptor is ready.
144   * This requires altering libradius....
145   */
146  sigemptyset(&alrm);
147  sigaddset(&alrm, SIGALRM);
148  sigprocmask(SIG_BLOCK, &alrm, &prevset);
149  got = rad_send_request(h);
150  sigprocmask(SIG_SETMASK, &prevset, NULL);
151
152  switch (got) {
153    case RAD_ACCESS_ACCEPT:
154      break;
155
156    case RAD_ACCESS_CHALLENGE:
157      /* we can't deal with this (for now) ! */
158      log_Printf(LogPHASE, "Can't handle radius CHALLENGEs !\n");
159      rad_close(h);
160      return 0;
161
162    case -1:
163      log_Printf(LogPHASE, "radius: %s\n", rad_strerror(h));
164      rad_close(h);
165      return 0;
166
167    default:
168      log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
169                 got, rad_strerror(h));
170      rad_close(h);
171      return 0;
172
173    case RAD_ACCESS_REJECT:
174      log_Printf(LogPHASE, "radius: Rejected !\n");
175      rad_close(h);
176      return 0;
177  }
178
179  /* So we've been accepted !  Let's see what we've got in our reply :-I */
180  r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
181  r->mtu = 0;
182  r->vj = 0;
183  while ((got = rad_get_attr(h, &data, &len)) > 0) {
184    switch (got) {
185      case RAD_FRAMED_IP_ADDRESS:
186        r->ip = rad_cvt_addr(data);
187        log_Printf(LogDEBUG, "radius: Got IP %s\n", inet_ntoa(r->ip));
188        break;
189
190      case RAD_FRAMED_IP_NETMASK:
191        r->mask = rad_cvt_addr(data);
192        log_Printf(LogDEBUG, "radius: Got MASK %s\n", inet_ntoa(r->mask));
193        break;
194
195      case RAD_FRAMED_MTU:
196        r->mtu = rad_cvt_int(data);
197        log_Printf(LogDEBUG, "radius: Got MTU %lu\n", r->mtu);
198        break;
199
200      case RAD_FRAMED_ROUTING:
201        /* Disabled for now - should we automatically set up some filters ? */
202        /* rad_cvt_int(data); */
203        /* bit 1 = Send routing packets */
204        /* bit 2 = Receive routing packets */
205        break;
206
207      case RAD_FRAMED_COMPRESSION:
208        r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
209        log_Printf(LogDEBUG, "radius: Got VJ %sabled\n", r->vj ? "en" : "dis");
210        break;
211
212      case RAD_FRAMED_ROUTE:
213        /*
214         * We expect a string of the format ``dest[/bits] gw [metrics]''
215         * Any specified metrics are ignored.  MYADDR and HISADDR are
216         * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
217         * as ``HISADDR''.
218         */
219
220        if ((nuke = rad_cvt_string(data, len)) == NULL) {
221          log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(h));
222          rad_close(h);
223          return 0;
224        }
225
226        dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
227        dest.width = 0;
228        argc = command_Interpret(nuke, strlen(nuke), argv);
229        if (argc < 2)
230          log_Printf(LogWARN, "radius: %s: Invalid route\n",
231                     argc == 1 ? argv[0] : "\"\"");
232        else if ((strcasecmp(argv[0], "default") != 0 &&
233                  !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
234                             &dest.mask, &dest.width)) ||
235                 !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
236          log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
237                     argv[0], argv[1]);
238        else {
239          if (dest.width == 32 && strchr(argv[0], '/') == NULL)
240            /* No mask specified - use the natural mask */
241            dest.mask.s_addr = addr2mask(dest.ipaddr.s_addr);
242          addrs = 0;
243
244          if (!strncasecmp(argv[0], "HISADDR", 7))
245            addrs = ROUTE_DSTHISADDR;
246          else if (!strncasecmp(argv[0], "MYADDR", 6))
247            addrs = ROUTE_DSTMYADDR;
248
249          if (gw.s_addr == INADDR_ANY) {
250            addrs |= ROUTE_GWHISADDR;
251            gw = bundle->ncp.ipcp.peer_ip;
252          } else if (strcasecmp(argv[1], "HISADDR") == 0)
253            addrs |= ROUTE_GWHISADDR;
254
255          route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
256        }
257        free(nuke);
258        break;
259    }
260  }
261
262  if (got == -1) {
263    log_Printf(LogERROR, "rad_get_attr: %s\n", rad_strerror(h));
264    rad_close(h);
265    return 0;
266  }
267
268  log_Printf(LogPHASE, "radius: SUCCESS\n");
269
270  rad_close(h);
271  return r->valid = 1;
272}
273
274void
275radius_Show(struct radius *r, struct prompt *p)
276{
277  prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none");
278  if (r->valid) {
279    prompt_Printf(p, "\n            IP: %s\n", inet_ntoa(r->ip));
280    prompt_Printf(p, "       Netmask: %s\n", inet_ntoa(r->mask));
281    prompt_Printf(p, "           MTU: %lu\n", r->mtu);
282    prompt_Printf(p, "            VJ: %sabled\n", r->vj ? "en" : "dis");
283    if (r->routes)
284      route_ShowSticky(p, r->routes, "        Routes", 16);
285  } else
286    prompt_Printf(p, " (not authenticated)\n");
287}
288