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