1972a1bcfSBrian Somers /* 2972a1bcfSBrian Somers * Copyright 1999 Internet Business Solutions Ltd., Switzerland 3972a1bcfSBrian Somers * All rights reserved. 4972a1bcfSBrian Somers * 5972a1bcfSBrian Somers * Redistribution and use in source and binary forms, with or without 6972a1bcfSBrian Somers * modification, are permitted provided that the following conditions 7972a1bcfSBrian Somers * are met: 8972a1bcfSBrian Somers * 1. Redistributions of source code must retain the above copyright 9972a1bcfSBrian Somers * notice, this list of conditions and the following disclaimer. 10972a1bcfSBrian Somers * 2. Redistributions in binary form must reproduce the above copyright 11972a1bcfSBrian Somers * notice, this list of conditions and the following disclaimer in the 12972a1bcfSBrian Somers * documentation and/or other materials provided with the distribution. 13972a1bcfSBrian Somers * 14972a1bcfSBrian Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15972a1bcfSBrian Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16972a1bcfSBrian Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17972a1bcfSBrian Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18972a1bcfSBrian Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19972a1bcfSBrian Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20972a1bcfSBrian Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21972a1bcfSBrian Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22972a1bcfSBrian Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23972a1bcfSBrian Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24972a1bcfSBrian Somers * SUCH DAMAGE. 25972a1bcfSBrian Somers * 26972a1bcfSBrian Somers * $Id:$ 27972a1bcfSBrian Somers * 28972a1bcfSBrian Somers */ 29972a1bcfSBrian Somers 30972a1bcfSBrian Somers #include <sys/param.h> 31972a1bcfSBrian Somers #include <netinet/in_systm.h> 32972a1bcfSBrian Somers #include <netinet/in.h> 33972a1bcfSBrian Somers #include <netinet/ip.h> 34972a1bcfSBrian Somers #include <arpa/inet.h> 35972a1bcfSBrian Somers #include <sys/un.h> 36972a1bcfSBrian Somers 37972a1bcfSBrian Somers #include <errno.h> 38972a1bcfSBrian Somers #include <radlib.h> 39972a1bcfSBrian Somers #include <signal.h> 40972a1bcfSBrian Somers #include <stdio.h> 41972a1bcfSBrian Somers #include <stdlib.h> 42972a1bcfSBrian Somers #include <string.h> 43972a1bcfSBrian Somers #include <termios.h> 44972a1bcfSBrian Somers 45972a1bcfSBrian Somers #include "defs.h" 46972a1bcfSBrian Somers #include "log.h" 47972a1bcfSBrian Somers #include "descriptor.h" 48972a1bcfSBrian Somers #include "prompt.h" 49972a1bcfSBrian Somers #include "timer.h" 50972a1bcfSBrian Somers #include "fsm.h" 51972a1bcfSBrian Somers #include "iplist.h" 52972a1bcfSBrian Somers #include "slcompress.h" 53972a1bcfSBrian Somers #include "throughput.h" 54972a1bcfSBrian Somers #include "lqr.h" 55972a1bcfSBrian Somers #include "hdlc.h" 56972a1bcfSBrian Somers #include "mbuf.h" 57972a1bcfSBrian Somers #include "ipcp.h" 58972a1bcfSBrian Somers #include "route.h" 59972a1bcfSBrian Somers #include "command.h" 60972a1bcfSBrian Somers #include "filter.h" 61972a1bcfSBrian Somers #include "server.h" 62972a1bcfSBrian Somers #include "lcp.h" 63972a1bcfSBrian Somers #include "ccp.h" 64972a1bcfSBrian Somers #include "link.h" 65972a1bcfSBrian Somers #include "mp.h" 66972a1bcfSBrian Somers #include "radius.h" 67972a1bcfSBrian Somers #include "bundle.h" 68972a1bcfSBrian Somers 69972a1bcfSBrian Somers void 70972a1bcfSBrian Somers radius_Init(struct radius *r) 71972a1bcfSBrian Somers { 72972a1bcfSBrian Somers r->valid = 0; 73972a1bcfSBrian Somers *r->cfg.file = '\0';; 74972a1bcfSBrian Somers } 75972a1bcfSBrian Somers 76972a1bcfSBrian Somers void 77972a1bcfSBrian Somers radius_Destroy(struct radius *r) 78972a1bcfSBrian Somers { 79972a1bcfSBrian Somers r->valid = 0; 80972a1bcfSBrian Somers route_DeleteAll(&r->routes); 81972a1bcfSBrian Somers } 82972a1bcfSBrian Somers 83972a1bcfSBrian Somers int 84972a1bcfSBrian Somers radius_Authenticate(struct radius *r, struct bundle *bundle, const char *name, 85972a1bcfSBrian Somers const char *key, const char *challenge) 86972a1bcfSBrian Somers { 87972a1bcfSBrian Somers struct rad_handle *h; 88972a1bcfSBrian Somers sigset_t alrm, prevset; 89972a1bcfSBrian Somers const void *data; 90972a1bcfSBrian Somers int got, len, argc, addrs; 91972a1bcfSBrian Somers char *argv[MAXARGS], *nuke; 92972a1bcfSBrian Somers struct in_range dest; 93972a1bcfSBrian Somers struct in_addr gw; 94972a1bcfSBrian Somers 95972a1bcfSBrian Somers radius_Destroy(r); 96972a1bcfSBrian Somers 97972a1bcfSBrian Somers if (!*r->cfg.file) 98972a1bcfSBrian Somers return 0; 99972a1bcfSBrian Somers 100972a1bcfSBrian Somers if ((h = rad_open()) == NULL) { 101972a1bcfSBrian Somers log_Printf(LogERROR, "rad_open: %s\n", strerror(errno)); 102972a1bcfSBrian Somers return 0; 103972a1bcfSBrian Somers } 104972a1bcfSBrian Somers 105972a1bcfSBrian Somers if (rad_config(h, r->cfg.file) != 0) { 106972a1bcfSBrian Somers log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(h)); 107972a1bcfSBrian Somers rad_close(h); 108972a1bcfSBrian Somers return 0; 109972a1bcfSBrian Somers } 110972a1bcfSBrian Somers 111972a1bcfSBrian Somers if (rad_create_request(h, RAD_ACCESS_REQUEST) != 0) { 112972a1bcfSBrian Somers log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(h)); 113972a1bcfSBrian Somers rad_close(h); 114972a1bcfSBrian Somers return 0; 115972a1bcfSBrian Somers } 116972a1bcfSBrian Somers 117972a1bcfSBrian Somers if (rad_put_string(h, RAD_USER_NAME, name) != 0 || 118972a1bcfSBrian Somers rad_put_int(h, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 || 119972a1bcfSBrian Somers rad_put_int(h, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) { 120972a1bcfSBrian Somers log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(h)); 121972a1bcfSBrian Somers rad_close(h); 122972a1bcfSBrian Somers return 0; 123972a1bcfSBrian Somers } 124972a1bcfSBrian Somers 125972a1bcfSBrian Somers if (challenge != NULL) { /* CHAP */ 126972a1bcfSBrian Somers if (rad_put_string(h, RAD_CHAP_PASSWORD, key) != 0 || 127972a1bcfSBrian Somers rad_put_string(h, RAD_CHAP_CHALLENGE, challenge) != 0) { 128972a1bcfSBrian Somers log_Printf(LogERROR, "CHAP: rad_put_string: %s\n", rad_strerror(h)); 129972a1bcfSBrian Somers rad_close(h); 130972a1bcfSBrian Somers return 0; 131972a1bcfSBrian Somers } 132972a1bcfSBrian Somers } else if (rad_put_string(h, RAD_USER_PASSWORD, key) != 0) { /* PAP */ 133972a1bcfSBrian Somers /* We're talking PAP */ 134972a1bcfSBrian Somers log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(h)); 135972a1bcfSBrian Somers rad_close(h); 136972a1bcfSBrian Somers return 0; 137972a1bcfSBrian Somers } 138972a1bcfSBrian Somers 139972a1bcfSBrian Somers /* 140972a1bcfSBrian Somers * Having to do this is bad news. The right way is to grab the 141972a1bcfSBrian Somers * descriptor that rad_send_request() selects on and add it to 142972a1bcfSBrian Somers * our own selection list (making a full ``struct descriptor''), 143972a1bcfSBrian Somers * then to ``continue'' the call when the descriptor is ready. 144972a1bcfSBrian Somers * This requires altering libradius.... 145972a1bcfSBrian Somers */ 146972a1bcfSBrian Somers sigemptyset(&alrm); 147972a1bcfSBrian Somers sigaddset(&alrm, SIGALRM); 148972a1bcfSBrian Somers sigprocmask(SIG_BLOCK, &alrm, &prevset); 149972a1bcfSBrian Somers got = rad_send_request(h); 150972a1bcfSBrian Somers sigprocmask(SIG_SETMASK, &prevset, NULL); 151972a1bcfSBrian Somers 152972a1bcfSBrian Somers switch (got) { 153972a1bcfSBrian Somers case RAD_ACCESS_ACCEPT: 154972a1bcfSBrian Somers break; 155972a1bcfSBrian Somers 156972a1bcfSBrian Somers case RAD_ACCESS_CHALLENGE: 157972a1bcfSBrian Somers /* we can't deal with this (for now) ! */ 158972a1bcfSBrian Somers log_Printf(LogPHASE, "Can't handle radius CHALLENGEs !\n"); 159972a1bcfSBrian Somers rad_close(h); 160972a1bcfSBrian Somers return 0; 161972a1bcfSBrian Somers 162972a1bcfSBrian Somers case -1: 163972a1bcfSBrian Somers log_Printf(LogPHASE, "radius: %s\n", rad_strerror(h)); 164972a1bcfSBrian Somers rad_close(h); 165972a1bcfSBrian Somers return 0; 166972a1bcfSBrian Somers 167972a1bcfSBrian Somers default: 168972a1bcfSBrian Somers log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n", 169972a1bcfSBrian Somers got, rad_strerror(h)); 170972a1bcfSBrian Somers rad_close(h); 171972a1bcfSBrian Somers return 0; 172972a1bcfSBrian Somers 173972a1bcfSBrian Somers case RAD_ACCESS_REJECT: 174972a1bcfSBrian Somers log_Printf(LogPHASE, "radius: Rejected !\n"); 175972a1bcfSBrian Somers rad_close(h); 176972a1bcfSBrian Somers return 0; 177972a1bcfSBrian Somers } 178972a1bcfSBrian Somers 179972a1bcfSBrian Somers /* So we've been accepted ! Let's see what we've got in our reply :-I */ 180972a1bcfSBrian Somers r->ip.s_addr = r->mask.s_addr = INADDR_NONE; 181972a1bcfSBrian Somers r->mtu = 0; 182972a1bcfSBrian Somers r->vj = 0; 183972a1bcfSBrian Somers while ((got = rad_get_attr(h, &data, &len)) > 0) { 184972a1bcfSBrian Somers switch (got) { 185972a1bcfSBrian Somers case RAD_FRAMED_IP_ADDRESS: 186972a1bcfSBrian Somers r->ip = rad_cvt_addr(data); 187972a1bcfSBrian Somers log_Printf(LogDEBUG, "radius: Got IP %s\n", inet_ntoa(r->ip)); 188972a1bcfSBrian Somers break; 189972a1bcfSBrian Somers 190972a1bcfSBrian Somers case RAD_FRAMED_IP_NETMASK: 191972a1bcfSBrian Somers r->mask = rad_cvt_addr(data); 192972a1bcfSBrian Somers log_Printf(LogDEBUG, "radius: Got MASK %s\n", inet_ntoa(r->mask)); 193972a1bcfSBrian Somers break; 194972a1bcfSBrian Somers 195972a1bcfSBrian Somers case RAD_FRAMED_MTU: 196972a1bcfSBrian Somers r->mtu = rad_cvt_int(data); 197972a1bcfSBrian Somers log_Printf(LogDEBUG, "radius: Got MTU %lu\n", r->mtu); 198972a1bcfSBrian Somers break; 199972a1bcfSBrian Somers 200972a1bcfSBrian Somers case RAD_FRAMED_ROUTING: 201972a1bcfSBrian Somers /* Disabled for now - should we automatically set up some filters ? */ 202972a1bcfSBrian Somers /* rad_cvt_int(data); */ 203972a1bcfSBrian Somers /* bit 1 = Send routing packets */ 204972a1bcfSBrian Somers /* bit 2 = Receive routing packets */ 205972a1bcfSBrian Somers break; 206972a1bcfSBrian Somers 207972a1bcfSBrian Somers case RAD_FRAMED_COMPRESSION: 208972a1bcfSBrian Somers r->vj = rad_cvt_int(data) == 1 ? 1 : 0; 209972a1bcfSBrian Somers log_Printf(LogDEBUG, "radius: Got VJ %sabled\n", r->vj ? "en" : "dis"); 210972a1bcfSBrian Somers break; 211972a1bcfSBrian Somers 212972a1bcfSBrian Somers case RAD_FRAMED_ROUTE: 213972a1bcfSBrian Somers /* 214972a1bcfSBrian Somers * We expect a string of the format ``dest[/bits] gw [metrics]'' 215972a1bcfSBrian Somers * Any specified metrics are ignored. MYADDR and HISADDR are 216972a1bcfSBrian Somers * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same 217972a1bcfSBrian Somers * as ``HISADDR''. 218972a1bcfSBrian Somers */ 219972a1bcfSBrian Somers 220972a1bcfSBrian Somers if ((nuke = rad_cvt_string(data, len)) == NULL) { 221972a1bcfSBrian Somers log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(h)); 222972a1bcfSBrian Somers rad_close(h); 223972a1bcfSBrian Somers return 0; 224972a1bcfSBrian Somers } 225972a1bcfSBrian Somers 226972a1bcfSBrian Somers dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY; 227972a1bcfSBrian Somers dest.width = 0; 228972a1bcfSBrian Somers argc = command_Interpret(nuke, strlen(nuke), argv); 229972a1bcfSBrian Somers if (argc < 2) 230972a1bcfSBrian Somers log_Printf(LogWARN, "radius: %s: Invalid route\n", 231972a1bcfSBrian Somers argc == 1 ? argv[0] : "\"\""); 232972a1bcfSBrian Somers else if ((strcasecmp(argv[0], "default") != 0 && 233972a1bcfSBrian Somers !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr, 234972a1bcfSBrian Somers &dest.mask, &dest.width)) || 235972a1bcfSBrian Somers !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL)) 236972a1bcfSBrian Somers log_Printf(LogWARN, "radius: %s %s: Invalid route\n", 237972a1bcfSBrian Somers argv[0], argv[1]); 238972a1bcfSBrian Somers else { 239972a1bcfSBrian Somers if (dest.width == 32 && strchr(argv[0], '/') == NULL) 240972a1bcfSBrian Somers /* No mask specified - use the natural mask */ 241972a1bcfSBrian Somers dest.mask.s_addr = addr2mask(dest.ipaddr.s_addr); 242972a1bcfSBrian Somers addrs = 0; 243972a1bcfSBrian Somers 244972a1bcfSBrian Somers if (!strncasecmp(argv[0], "HISADDR", 7)) 245972a1bcfSBrian Somers addrs = ROUTE_DSTHISADDR; 246972a1bcfSBrian Somers else if (!strncasecmp(argv[0], "MYADDR", 6)) 247972a1bcfSBrian Somers addrs = ROUTE_DSTMYADDR; 248972a1bcfSBrian Somers 249972a1bcfSBrian Somers if (gw.s_addr == INADDR_ANY) { 250972a1bcfSBrian Somers addrs |= ROUTE_GWHISADDR; 251972a1bcfSBrian Somers gw = bundle->ncp.ipcp.peer_ip; 252972a1bcfSBrian Somers } else if (strcasecmp(argv[1], "HISADDR") == 0) 253972a1bcfSBrian Somers addrs |= ROUTE_GWHISADDR; 254972a1bcfSBrian Somers 255972a1bcfSBrian Somers route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw); 256972a1bcfSBrian Somers } 257972a1bcfSBrian Somers free(nuke); 258972a1bcfSBrian Somers break; 259972a1bcfSBrian Somers } 260972a1bcfSBrian Somers } 261972a1bcfSBrian Somers 262972a1bcfSBrian Somers if (got == -1) { 263972a1bcfSBrian Somers log_Printf(LogERROR, "rad_get_attr: %s\n", rad_strerror(h)); 264972a1bcfSBrian Somers rad_close(h); 265972a1bcfSBrian Somers return 0; 266972a1bcfSBrian Somers } 267972a1bcfSBrian Somers 268972a1bcfSBrian Somers log_Printf(LogPHASE, "radius: SUCCESS\n"); 269972a1bcfSBrian Somers 270972a1bcfSBrian Somers rad_close(h); 271972a1bcfSBrian Somers return r->valid = 1; 272972a1bcfSBrian Somers } 273972a1bcfSBrian Somers 274972a1bcfSBrian Somers void 275972a1bcfSBrian Somers radius_Show(struct radius *r, struct prompt *p) 276972a1bcfSBrian Somers { 277972a1bcfSBrian Somers prompt_Printf(p, " Radius config: %s", *r->cfg.file ? r->cfg.file : "none"); 278972a1bcfSBrian Somers if (r->valid) { 279972a1bcfSBrian Somers prompt_Printf(p, "\n IP: %s\n", inet_ntoa(r->ip)); 280972a1bcfSBrian Somers prompt_Printf(p, " Netmask: %s\n", inet_ntoa(r->mask)); 281972a1bcfSBrian Somers prompt_Printf(p, " MTU: %lu\n", r->mtu); 282972a1bcfSBrian Somers prompt_Printf(p, " VJ: %sabled\n", r->vj ? "en" : "dis"); 283972a1bcfSBrian Somers if (r->routes) 284972a1bcfSBrian Somers route_ShowSticky(p, r->routes, " Routes", 16); 285972a1bcfSBrian Somers } else 286972a1bcfSBrian Somers prompt_Printf(p, " (not authenticated)\n"); 287972a1bcfSBrian Somers } 288