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