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