141118Sjdp/*- 241118Sjdp * Copyright 1998 Juniper Networks, Inc. 341118Sjdp * All rights reserved. 441118Sjdp * 541118Sjdp * Redistribution and use in source and binary forms, with or without 641118Sjdp * modification, are permitted provided that the following conditions 741118Sjdp * are met: 841118Sjdp * 1. Redistributions of source code must retain the above copyright 941118Sjdp * notice, this list of conditions and the following disclaimer. 1041118Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1141118Sjdp * notice, this list of conditions and the following disclaimer in the 1241118Sjdp * documentation and/or other materials provided with the distribution. 1341118Sjdp * 1441118Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1541118Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1641118Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1741118Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1841118Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1941118Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2041118Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2141118Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2241118Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2341118Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2441118Sjdp * SUCH DAMAGE. 2541118Sjdp */ 2641118Sjdp 2784219Sdillon#include <sys/cdefs.h> 2884219Sdillon__FBSDID("$FreeBSD$"); 2984219Sdillon 3041118Sjdp#include <sys/types.h> 3141118Sjdp#include <sys/socket.h> 3241118Sjdp#include <sys/time.h> 3341118Sjdp#include <netinet/in.h> 3441118Sjdp#include <arpa/inet.h> 35128684Sru#ifdef WITH_SSL 36128684Sru#include <openssl/hmac.h> 37128684Sru#include <openssl/md5.h> 38128684Sru#define MD5Init MD5_Init 39128684Sru#define MD5Update MD5_Update 40128684Sru#define MD5Final MD5_Final 41128684Sru#else 42128684Sru#define MD5_DIGEST_LENGTH 16 43128684Sru#include <md5.h> 44128684Sru#endif 4541118Sjdp 46243956Ssem#define MAX_FIELDS 7 47243956Ssem 48128684Sru/* We need the MPPE_KEY_LEN define */ 49128684Sru#include <netgraph/ng_mppc.h> 50128684Sru 5141118Sjdp#include <errno.h> 5241118Sjdp#include <netdb.h> 5341118Sjdp#include <stdarg.h> 5441118Sjdp#include <stddef.h> 5541118Sjdp#include <stdio.h> 5641118Sjdp#include <stdlib.h> 5741118Sjdp#include <string.h> 5841118Sjdp#include <unistd.h> 5941118Sjdp 6041118Sjdp#include "radlib_private.h" 6141118Sjdp 6241118Sjdpstatic void clear_password(struct rad_handle *); 6341118Sjdpstatic void generr(struct rad_handle *, const char *, ...) 6441118Sjdp __printflike(2, 3); 6541118Sjdpstatic void insert_scrambled_password(struct rad_handle *, int); 6652709Sjdpstatic void insert_request_authenticator(struct rad_handle *, int); 67128684Srustatic void insert_message_authenticator(struct rad_handle *, int); 6841118Sjdpstatic int is_valid_response(struct rad_handle *, int, 6941118Sjdp const struct sockaddr_in *); 7041118Sjdpstatic int put_password_attr(struct rad_handle *, int, 7141118Sjdp const void *, size_t); 7241118Sjdpstatic int put_raw_attr(struct rad_handle *, int, 7341118Sjdp const void *, size_t); 7441118Sjdpstatic int split(char *, char *[], int, char *, size_t); 7541118Sjdp 7641118Sjdpstatic void 7741118Sjdpclear_password(struct rad_handle *h) 7841118Sjdp{ 7941118Sjdp if (h->pass_len != 0) { 8041118Sjdp memset(h->pass, 0, h->pass_len); 8141118Sjdp h->pass_len = 0; 8241118Sjdp } 8368499Seivind h->pass_pos = 0; 8441118Sjdp} 8541118Sjdp 8641118Sjdpstatic void 8741118Sjdpgenerr(struct rad_handle *h, const char *format, ...) 8841118Sjdp{ 8941118Sjdp va_list ap; 9041118Sjdp 9141118Sjdp va_start(ap, format); 9241118Sjdp vsnprintf(h->errmsg, ERRSIZE, format, ap); 9341118Sjdp va_end(ap); 9441118Sjdp} 9541118Sjdp 9641118Sjdpstatic void 9741118Sjdpinsert_scrambled_password(struct rad_handle *h, int srv) 9841118Sjdp{ 9941118Sjdp MD5_CTX ctx; 100128684Sru unsigned char md5[MD5_DIGEST_LENGTH]; 10141118Sjdp const struct rad_server *srvp; 10241118Sjdp int padded_len; 10341118Sjdp int pos; 10441118Sjdp 10541118Sjdp srvp = &h->servers[srv]; 10641118Sjdp padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf; 10741118Sjdp 108197086Smav memcpy(md5, &h->out[POS_AUTH], LEN_AUTH); 10941118Sjdp for (pos = 0; pos < padded_len; pos += 16) { 11041118Sjdp int i; 11141118Sjdp 11241118Sjdp /* Calculate the new scrambler */ 11341118Sjdp MD5Init(&ctx); 11441118Sjdp MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 11541118Sjdp MD5Update(&ctx, md5, 16); 11641118Sjdp MD5Final(md5, &ctx); 11741118Sjdp 11841118Sjdp /* 11941118Sjdp * Mix in the current chunk of the password, and copy 12041118Sjdp * the result into the right place in the request. Also 12141118Sjdp * modify the scrambler in place, since we will use this 12241118Sjdp * in calculating the scrambler for next time. 12341118Sjdp */ 12441118Sjdp for (i = 0; i < 16; i++) 125197086Smav h->out[h->pass_pos + pos + i] = 12641118Sjdp md5[i] ^= h->pass[pos + i]; 12741118Sjdp } 12841118Sjdp} 12941118Sjdp 13052709Sjdpstatic void 131197086Smavinsert_request_authenticator(struct rad_handle *h, int resp) 13252709Sjdp{ 13352709Sjdp MD5_CTX ctx; 13452709Sjdp const struct rad_server *srvp; 13552709Sjdp 136197086Smav srvp = &h->servers[h->srv]; 13752709Sjdp 13852709Sjdp /* Create the request authenticator */ 13952709Sjdp MD5Init(&ctx); 140197086Smav MD5Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE); 141197086Smav if (resp) 142197086Smav MD5Update(&ctx, &h->in[POS_AUTH], LEN_AUTH); 143197086Smav else 144197086Smav MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH); 145197086Smav MD5Update(&ctx, &h->out[POS_ATTRS], h->out_len - POS_ATTRS); 14652709Sjdp MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 147197086Smav MD5Final(&h->out[POS_AUTH], &ctx); 14852709Sjdp} 14952709Sjdp 150128684Srustatic void 151197086Smavinsert_message_authenticator(struct rad_handle *h, int resp) 152128684Sru{ 153128684Sru#ifdef WITH_SSL 154128684Sru u_char md[EVP_MAX_MD_SIZE]; 155128684Sru u_int md_len; 156128684Sru const struct rad_server *srvp; 157128684Sru HMAC_CTX ctx; 158197086Smav srvp = &h->servers[h->srv]; 159128684Sru 160128684Sru if (h->authentic_pos != 0) { 161128684Sru HMAC_CTX_init(&ctx); 162128684Sru HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5()); 163197086Smav HMAC_Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE); 164197086Smav if (resp) 165197086Smav HMAC_Update(&ctx, &h->in[POS_AUTH], LEN_AUTH); 166197086Smav else 167197086Smav HMAC_Update(&ctx, &h->out[POS_AUTH], LEN_AUTH); 168197086Smav HMAC_Update(&ctx, &h->out[POS_ATTRS], 169197086Smav h->out_len - POS_ATTRS); 170128684Sru HMAC_Final(&ctx, md, &md_len); 171128684Sru HMAC_CTX_cleanup(&ctx); 172128684Sru HMAC_cleanup(&ctx); 173197086Smav memcpy(&h->out[h->authentic_pos + 2], md, md_len); 174128684Sru } 175128684Sru#endif 176128684Sru} 177128684Sru 17841118Sjdp/* 17941118Sjdp * Return true if the current response is valid for a request to the 18041118Sjdp * specified server. 18141118Sjdp */ 18241118Sjdpstatic int 18341118Sjdpis_valid_response(struct rad_handle *h, int srv, 18441118Sjdp const struct sockaddr_in *from) 18541118Sjdp{ 18641118Sjdp MD5_CTX ctx; 187128684Sru unsigned char md5[MD5_DIGEST_LENGTH]; 18841118Sjdp const struct rad_server *srvp; 18941118Sjdp int len; 190128684Sru#ifdef WITH_SSL 191128684Sru HMAC_CTX hctx; 192128684Sru u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE]; 193168341Skan u_int md_len; 194168341Skan int pos; 195128684Sru#endif 19641118Sjdp 19741118Sjdp srvp = &h->servers[srv]; 19841118Sjdp 19941118Sjdp /* Check the source address */ 20041118Sjdp if (from->sin_family != srvp->addr.sin_family || 20141118Sjdp from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr || 20241118Sjdp from->sin_port != srvp->addr.sin_port) 20341118Sjdp return 0; 20441118Sjdp 20541118Sjdp /* Check the message length */ 206197086Smav if (h->in_len < POS_ATTRS) 20741118Sjdp return 0; 208197086Smav len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1]; 209197086Smav if (len > h->in_len) 21041118Sjdp return 0; 21141118Sjdp 21241118Sjdp /* Check the response authenticator */ 21341118Sjdp MD5Init(&ctx); 214197086Smav MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE); 215197086Smav MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH); 216197086Smav MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS); 21741118Sjdp MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 21841118Sjdp MD5Final(md5, &ctx); 219197086Smav if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0) 22041118Sjdp return 0; 22141118Sjdp 222128684Sru#ifdef WITH_SSL 223128684Sru /* 224128684Sru * For non accounting responses check the message authenticator, 225128684Sru * if any. 226128684Sru */ 227197086Smav if (h->in[POS_CODE] != RAD_ACCOUNTING_RESPONSE) { 228128684Sru 229197086Smav memcpy(resp, h->in, MSGSIZE); 230128684Sru pos = POS_ATTRS; 231128684Sru 232128684Sru /* Search and verify the Message-Authenticator */ 233128684Sru while (pos < len - 2) { 234128684Sru 235197086Smav if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) { 236128684Sru /* zero fill the Message-Authenticator */ 237128684Sru memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH); 238128684Sru 239128684Sru HMAC_CTX_init(&hctx); 240128684Sru HMAC_Init(&hctx, srvp->secret, 241128684Sru strlen(srvp->secret), EVP_md5()); 242197086Smav HMAC_Update(&hctx, &h->in[POS_CODE], 243128684Sru POS_AUTH - POS_CODE); 244197086Smav HMAC_Update(&hctx, &h->out[POS_AUTH], 245128684Sru LEN_AUTH); 246128684Sru HMAC_Update(&hctx, &resp[POS_ATTRS], 247197086Smav h->in_len - POS_ATTRS); 248128684Sru HMAC_Final(&hctx, md, &md_len); 249128684Sru HMAC_CTX_cleanup(&hctx); 250128684Sru HMAC_cleanup(&hctx); 251197086Smav if (memcmp(md, &h->in[pos + 2], 252128684Sru MD5_DIGEST_LENGTH) != 0) 253128684Sru return 0; 254128684Sru break; 255128684Sru } 256197086Smav pos += h->in[pos + 1]; 257128684Sru } 258128684Sru } 259128684Sru#endif 26041118Sjdp return 1; 26141118Sjdp} 26241118Sjdp 263197086Smav/* 264197086Smav * Return true if the current request is valid for the specified server. 265197086Smav */ 26641118Sjdpstatic int 267197086Smavis_valid_request(struct rad_handle *h) 268197086Smav{ 269197086Smav MD5_CTX ctx; 270197086Smav unsigned char md5[MD5_DIGEST_LENGTH]; 271197086Smav const struct rad_server *srvp; 272197086Smav int len; 273197086Smav#ifdef WITH_SSL 274197086Smav HMAC_CTX hctx; 275197086Smav u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE]; 276197086Smav u_int md_len; 277197086Smav int pos; 278197086Smav#endif 279197086Smav 280197086Smav srvp = &h->servers[h->srv]; 281197086Smav 282197086Smav /* Check the message length */ 283197086Smav if (h->in_len < POS_ATTRS) 284197086Smav return (0); 285197086Smav len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1]; 286197086Smav if (len > h->in_len) 287197086Smav return (0); 288197086Smav 289197086Smav if (h->in[POS_CODE] != RAD_ACCESS_REQUEST) { 290197086Smav uint32_t zeroes[4] = { 0, 0, 0, 0 }; 291197086Smav /* Check the request authenticator */ 292197086Smav MD5Init(&ctx); 293197086Smav MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE); 294197086Smav MD5Update(&ctx, zeroes, LEN_AUTH); 295197086Smav MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS); 296197086Smav MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 297197086Smav MD5Final(md5, &ctx); 298197086Smav if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0) 299197086Smav return (0); 300197086Smav } 301197086Smav 302197086Smav#ifdef WITH_SSL 303197086Smav /* Search and verify the Message-Authenticator */ 304197086Smav pos = POS_ATTRS; 305197086Smav while (pos < len - 2) { 306197086Smav if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) { 307197086Smav memcpy(resp, h->in, MSGSIZE); 308197086Smav /* zero fill the Request-Authenticator */ 309197086Smav if (h->in[POS_CODE] != RAD_ACCESS_REQUEST) 310197086Smav memset(&resp[POS_AUTH], 0, LEN_AUTH); 311197086Smav /* zero fill the Message-Authenticator */ 312197086Smav memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH); 313197086Smav 314197086Smav HMAC_CTX_init(&hctx); 315197086Smav HMAC_Init(&hctx, srvp->secret, 316197086Smav strlen(srvp->secret), EVP_md5()); 317197086Smav HMAC_Update(&hctx, resp, h->in_len); 318197086Smav HMAC_Final(&hctx, md, &md_len); 319197086Smav HMAC_CTX_cleanup(&hctx); 320197086Smav HMAC_cleanup(&hctx); 321197086Smav if (memcmp(md, &h->in[pos + 2], 322197086Smav MD5_DIGEST_LENGTH) != 0) 323197086Smav return (0); 324197086Smav break; 325197086Smav } 326197086Smav pos += h->in[pos + 1]; 327197086Smav } 328197086Smav#endif 329197086Smav return (1); 330197086Smav} 331197086Smav 332197086Smavstatic int 33341118Sjdpput_password_attr(struct rad_handle *h, int type, const void *value, size_t len) 33441118Sjdp{ 33541118Sjdp int padded_len; 33641118Sjdp int pad_len; 33741118Sjdp 33841118Sjdp if (h->pass_pos != 0) { 33941118Sjdp generr(h, "Multiple User-Password attributes specified"); 34041118Sjdp return -1; 34141118Sjdp } 34241118Sjdp if (len > PASSSIZE) 34341118Sjdp len = PASSSIZE; 34441118Sjdp padded_len = len == 0 ? 16 : (len+15) & ~0xf; 34541118Sjdp pad_len = padded_len - len; 34641118Sjdp 34741118Sjdp /* 34841118Sjdp * Put in a place-holder attribute containing all zeros, and 34941118Sjdp * remember where it is so we can fill it in later. 35041118Sjdp */ 35141118Sjdp clear_password(h); 35241118Sjdp put_raw_attr(h, type, h->pass, padded_len); 353197086Smav h->pass_pos = h->out_len - padded_len; 35441118Sjdp 35541118Sjdp /* Save the cleartext password, padded as necessary */ 35641118Sjdp memcpy(h->pass, value, len); 35741118Sjdp h->pass_len = len; 35841118Sjdp memset(h->pass + len, 0, pad_len); 35941118Sjdp return 0; 36041118Sjdp} 36141118Sjdp 36241118Sjdpstatic int 36341118Sjdpput_raw_attr(struct rad_handle *h, int type, const void *value, size_t len) 36441118Sjdp{ 36541118Sjdp if (len > 253) { 36641118Sjdp generr(h, "Attribute too long"); 36741118Sjdp return -1; 36841118Sjdp } 369197086Smav if (h->out_len + 2 + len > MSGSIZE) { 37041118Sjdp generr(h, "Maximum message length exceeded"); 37141118Sjdp return -1; 37241118Sjdp } 373197086Smav h->out[h->out_len++] = type; 374197086Smav h->out[h->out_len++] = len + 2; 375197086Smav memcpy(&h->out[h->out_len], value, len); 376197086Smav h->out_len += len; 37741118Sjdp return 0; 37841118Sjdp} 37941118Sjdp 38041118Sjdpint 38141118Sjdprad_add_server(struct rad_handle *h, const char *host, int port, 38241118Sjdp const char *secret, int timeout, int tries) 38341118Sjdp{ 384243956Ssem struct in_addr bindto; 385243956Ssem bindto.s_addr = INADDR_ANY; 386243956Ssem 387243956Ssem return rad_add_server_ex(h, host, port, secret, timeout, tries, 388243956Ssem DEAD_TIME, &bindto); 389243956Ssem} 390243956Ssem 391243956Ssemint 392243956Ssemrad_add_server_ex(struct rad_handle *h, const char *host, int port, 393243956Ssem const char *secret, int timeout, int tries, int dead_time, 394243956Ssem struct in_addr *bindto) 395243956Ssem{ 39641118Sjdp struct rad_server *srvp; 39741118Sjdp 39841118Sjdp if (h->num_servers >= MAXSERVERS) { 39941118Sjdp generr(h, "Too many RADIUS servers specified"); 40041118Sjdp return -1; 40141118Sjdp } 40241118Sjdp srvp = &h->servers[h->num_servers]; 40341118Sjdp 40441118Sjdp memset(&srvp->addr, 0, sizeof srvp->addr); 40541118Sjdp srvp->addr.sin_len = sizeof srvp->addr; 40641118Sjdp srvp->addr.sin_family = AF_INET; 40741118Sjdp if (!inet_aton(host, &srvp->addr.sin_addr)) { 40841118Sjdp struct hostent *hent; 40941118Sjdp 41041118Sjdp if ((hent = gethostbyname(host)) == NULL) { 41141118Sjdp generr(h, "%s: host not found", host); 41241118Sjdp return -1; 41341118Sjdp } 41441118Sjdp memcpy(&srvp->addr.sin_addr, hent->h_addr, 41541118Sjdp sizeof srvp->addr.sin_addr); 41641118Sjdp } 41741118Sjdp if (port != 0) 418128684Sru srvp->addr.sin_port = htons((u_short)port); 41941118Sjdp else { 42041118Sjdp struct servent *sent; 42141118Sjdp 42252709Sjdp if (h->type == RADIUS_AUTH) 42352709Sjdp srvp->addr.sin_port = 42452709Sjdp (sent = getservbyname("radius", "udp")) != NULL ? 42552709Sjdp sent->s_port : htons(RADIUS_PORT); 42652709Sjdp else 42752709Sjdp srvp->addr.sin_port = 42852709Sjdp (sent = getservbyname("radacct", "udp")) != NULL ? 42952709Sjdp sent->s_port : htons(RADACCT_PORT); 43041118Sjdp } 43141118Sjdp if ((srvp->secret = strdup(secret)) == NULL) { 43241118Sjdp generr(h, "Out of memory"); 43341118Sjdp return -1; 43441118Sjdp } 43541118Sjdp srvp->timeout = timeout; 43641118Sjdp srvp->max_tries = tries; 43741118Sjdp srvp->num_tries = 0; 438243956Ssem srvp->is_dead = 0; 439243956Ssem srvp->dead_time = dead_time; 440243956Ssem srvp->next_probe = 0; 441243956Ssem srvp->bindto = bindto->s_addr; 44241118Sjdp h->num_servers++; 44341118Sjdp return 0; 44441118Sjdp} 44541118Sjdp 44641118Sjdpvoid 44741118Sjdprad_close(struct rad_handle *h) 44841118Sjdp{ 44941118Sjdp int srv; 45041118Sjdp 45141118Sjdp if (h->fd != -1) 45241118Sjdp close(h->fd); 45341118Sjdp for (srv = 0; srv < h->num_servers; srv++) { 45441118Sjdp memset(h->servers[srv].secret, 0, 45541118Sjdp strlen(h->servers[srv].secret)); 45641118Sjdp free(h->servers[srv].secret); 45741118Sjdp } 45841118Sjdp clear_password(h); 45941118Sjdp free(h); 46041118Sjdp} 46141118Sjdp 462243956Ssemvoid 463243956Ssemrad_bind_to(struct rad_handle *h, in_addr_t addr) 464243956Ssem{ 465243956Ssem 466243956Ssem h->bindto = addr; 467243956Ssem} 468243956Ssem 46941118Sjdpint 47041118Sjdprad_config(struct rad_handle *h, const char *path) 47141118Sjdp{ 47241118Sjdp FILE *fp; 47341118Sjdp char buf[MAXCONFLINE]; 47441118Sjdp int linenum; 47541118Sjdp int retval; 47641118Sjdp 47741118Sjdp if (path == NULL) 47841118Sjdp path = PATH_RADIUS_CONF; 47941118Sjdp if ((fp = fopen(path, "r")) == NULL) { 48041118Sjdp generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 48141118Sjdp return -1; 48241118Sjdp } 48341118Sjdp retval = 0; 48441118Sjdp linenum = 0; 48541118Sjdp while (fgets(buf, sizeof buf, fp) != NULL) { 48641118Sjdp int len; 487243964Sdelphij char *fields[MAX_FIELDS]; 48841118Sjdp int nfields; 48941118Sjdp char msg[ERRSIZE]; 49052709Sjdp char *type; 49165222Sache char *host, *res; 49241118Sjdp char *port_str; 49341118Sjdp char *secret; 49441118Sjdp char *timeout_str; 49541118Sjdp char *maxtries_str; 496243956Ssem char *dead_time_str; 497243956Ssem char *bindto_str; 49841118Sjdp char *end; 49952709Sjdp char *wanttype; 50041118Sjdp unsigned long timeout; 50141118Sjdp unsigned long maxtries; 502243956Ssem unsigned long dead_time; 50341118Sjdp int port; 504243956Ssem struct in_addr bindto; 50552709Sjdp int i; 50641118Sjdp 50741118Sjdp linenum++; 50841118Sjdp len = strlen(buf); 50941118Sjdp /* We know len > 0, else fgets would have returned NULL. */ 51041118Sjdp if (buf[len - 1] != '\n') { 51141118Sjdp if (len == sizeof buf - 1) 51241118Sjdp generr(h, "%s:%d: line too long", path, 51341118Sjdp linenum); 51441118Sjdp else 51541118Sjdp generr(h, "%s:%d: missing newline", path, 51641118Sjdp linenum); 51741118Sjdp retval = -1; 51841118Sjdp break; 51941118Sjdp } 52041118Sjdp buf[len - 1] = '\0'; 52141118Sjdp 52241118Sjdp /* Extract the fields from the line. */ 523243956Ssem nfields = split(buf, fields, MAX_FIELDS, msg, sizeof msg); 52441118Sjdp if (nfields == -1) { 52541118Sjdp generr(h, "%s:%d: %s", path, linenum, msg); 52641118Sjdp retval = -1; 52741118Sjdp break; 52841118Sjdp } 52941118Sjdp if (nfields == 0) 53041118Sjdp continue; 53152709Sjdp /* 53252709Sjdp * The first field should contain "auth" or "acct" for 53352709Sjdp * authentication or accounting, respectively. But older 53452709Sjdp * versions of the file didn't have that field. Default 53552709Sjdp * it to "auth" for backward compatibility. 53652709Sjdp */ 53752709Sjdp if (strcmp(fields[0], "auth") != 0 && 53852709Sjdp strcmp(fields[0], "acct") != 0) { 539243956Ssem if (nfields >= MAX_FIELDS) { 54052709Sjdp generr(h, "%s:%d: invalid service type", path, 54152709Sjdp linenum); 54252709Sjdp retval = -1; 54352709Sjdp break; 54452709Sjdp } 54552709Sjdp nfields++; 54652709Sjdp for (i = nfields; --i > 0; ) 54752709Sjdp fields[i] = fields[i - 1]; 54852709Sjdp fields[0] = "auth"; 54952709Sjdp } 55052709Sjdp if (nfields < 3) { 55141118Sjdp generr(h, "%s:%d: missing shared secret", path, 55241118Sjdp linenum); 55341118Sjdp retval = -1; 55441118Sjdp break; 55541118Sjdp } 55652709Sjdp type = fields[0]; 55752709Sjdp host = fields[1]; 55852709Sjdp secret = fields[2]; 55952709Sjdp timeout_str = fields[3]; 56052709Sjdp maxtries_str = fields[4]; 561243956Ssem dead_time_str = fields[5]; 562243956Ssem bindto_str = fields[6]; 56341118Sjdp 56452709Sjdp /* Ignore the line if it is for the wrong service type. */ 56552709Sjdp wanttype = h->type == RADIUS_AUTH ? "auth" : "acct"; 56652709Sjdp if (strcmp(type, wanttype) != 0) 56752709Sjdp continue; 56852709Sjdp 56941118Sjdp /* Parse and validate the fields. */ 57065222Sache res = host; 57165222Sache host = strsep(&res, ":"); 57265222Sache port_str = strsep(&res, ":"); 57341118Sjdp if (port_str != NULL) { 57441118Sjdp port = strtoul(port_str, &end, 10); 57541118Sjdp if (*end != '\0') { 57641118Sjdp generr(h, "%s:%d: invalid port", path, 57741118Sjdp linenum); 57841118Sjdp retval = -1; 57941118Sjdp break; 58041118Sjdp } 58141118Sjdp } else 58241118Sjdp port = 0; 58341118Sjdp if (timeout_str != NULL) { 58441118Sjdp timeout = strtoul(timeout_str, &end, 10); 58541118Sjdp if (*end != '\0') { 58641118Sjdp generr(h, "%s:%d: invalid timeout", path, 58741118Sjdp linenum); 58841118Sjdp retval = -1; 58941118Sjdp break; 59041118Sjdp } 59141118Sjdp } else 59241118Sjdp timeout = TIMEOUT; 59341118Sjdp if (maxtries_str != NULL) { 59441118Sjdp maxtries = strtoul(maxtries_str, &end, 10); 59541118Sjdp if (*end != '\0') { 59641118Sjdp generr(h, "%s:%d: invalid maxtries", path, 59741118Sjdp linenum); 59841118Sjdp retval = -1; 59941118Sjdp break; 60041118Sjdp } 60141118Sjdp } else 60241118Sjdp maxtries = MAXTRIES; 60341118Sjdp 604243956Ssem if (dead_time_str != NULL) { 605243956Ssem dead_time = strtoul(dead_time_str, &end, 10); 606243956Ssem if (*end != '\0') { 607243956Ssem generr(h, "%s:%d: invalid dead_time", path, 608243956Ssem linenum); 609243956Ssem retval = -1; 610243956Ssem break; 611243956Ssem } 612243956Ssem } else 613243956Ssem dead_time = DEAD_TIME; 614243956Ssem 615243956Ssem if (bindto_str != NULL) { 616243956Ssem bindto.s_addr = inet_addr(bindto_str); 617243956Ssem if (bindto.s_addr == INADDR_NONE) { 618243956Ssem generr(h, "%s:%d: invalid bindto", path, 619243956Ssem linenum); 620243956Ssem retval = -1; 621243956Ssem break; 622243956Ssem } 623243956Ssem } else 624243956Ssem bindto.s_addr = INADDR_ANY; 625243956Ssem 626243956Ssem if (rad_add_server_ex(h, host, port, secret, timeout, maxtries, 627243956Ssem dead_time, &bindto) == -1) { 62841118Sjdp strcpy(msg, h->errmsg); 62941118Sjdp generr(h, "%s:%d: %s", path, linenum, msg); 63041118Sjdp retval = -1; 63141118Sjdp break; 63241118Sjdp } 63341118Sjdp } 63441118Sjdp /* Clear out the buffer to wipe a possible copy of a shared secret */ 63541118Sjdp memset(buf, 0, sizeof buf); 63641118Sjdp fclose(fp); 63741118Sjdp return retval; 63841118Sjdp} 63941118Sjdp 64043662Sbrian/* 64143662Sbrian * rad_init_send_request() must have previously been called. 64243662Sbrian * Returns: 64343662Sbrian * 0 The application should select on *fd with a timeout of tv before 64443662Sbrian * calling rad_continue_send_request again. 64543662Sbrian * < 0 Failure 64643662Sbrian * > 0 Success 64743662Sbrian */ 64841118Sjdpint 64943662Sbrianrad_continue_send_request(struct rad_handle *h, int selected, int *fd, 65043662Sbrian struct timeval *tv) 65143662Sbrian{ 652243956Ssem int n, cur_srv; 653243956Ssem time_t now; 654243956Ssem struct sockaddr_in sin; 65543662Sbrian 656197086Smav if (h->type == RADIUS_SERVER) { 657197086Smav generr(h, "denied function call"); 658197086Smav return (-1); 659197086Smav } 66043662Sbrian if (selected) { 66143662Sbrian struct sockaddr_in from; 662168341Skan socklen_t fromlen; 66343662Sbrian 66443662Sbrian fromlen = sizeof from; 665197086Smav h->in_len = recvfrom(h->fd, h->in, 66643662Sbrian MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen); 667197086Smav if (h->in_len == -1) { 66843662Sbrian generr(h, "recvfrom: %s", strerror(errno)); 66943662Sbrian return -1; 67043662Sbrian } 67143662Sbrian if (is_valid_response(h, h->srv, &from)) { 672197086Smav h->in_len = h->in[POS_LENGTH] << 8 | 673197086Smav h->in[POS_LENGTH+1]; 674197086Smav h->in_pos = POS_ATTRS; 675197086Smav return h->in[POS_CODE]; 67643662Sbrian } 67743662Sbrian } 67843662Sbrian 67943662Sbrian /* 68043662Sbrian * Scan round-robin to the next server that has some 68143662Sbrian * tries left. There is guaranteed to be one, or we 68243662Sbrian * would have exited this loop by now. 68343662Sbrian */ 684243956Ssem cur_srv = h->srv; 685243956Ssem now = time(NULL); 686243956Ssem if (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) { 687243956Ssem /* Set next probe time for this server */ 688243956Ssem if (h->servers[h->srv].dead_time) { 689243956Ssem h->servers[h->srv].is_dead = 1; 690243956Ssem h->servers[h->srv].next_probe = now + 691243956Ssem h->servers[h->srv].dead_time; 692243956Ssem } 693243956Ssem do { 694243956Ssem h->srv++; 695243956Ssem if (h->srv >= h->num_servers) 696243956Ssem h->srv = 0; 697243956Ssem if (h->servers[h->srv].is_dead == 0) 698243956Ssem break; 699243956Ssem if (h->servers[h->srv].dead_time && 700243956Ssem h->servers[h->srv].next_probe <= now) { 701243956Ssem h->servers[h->srv].is_dead = 0; 702243956Ssem h->servers[h->srv].num_tries = 0; 703243956Ssem break; 704243956Ssem } 705243956Ssem } while (h->srv != cur_srv); 70643662Sbrian 707243956Ssem if (h->srv == cur_srv) { 708243956Ssem generr(h, "No valid RADIUS responses received"); 709243956Ssem return (-1); 710243956Ssem } 711243956Ssem } 712243956Ssem 713243956Ssem /* Rebind */ 714243956Ssem if (h->bindto != h->servers[h->srv].bindto) { 715243956Ssem h->bindto = h->servers[h->srv].bindto; 716243956Ssem close(h->fd); 717243956Ssem if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 718243956Ssem generr(h, "Cannot create socket: %s", strerror(errno)); 719243956Ssem return -1; 720243956Ssem } 721243956Ssem memset(&sin, 0, sizeof sin); 722243956Ssem sin.sin_len = sizeof sin; 723243956Ssem sin.sin_family = AF_INET; 724243956Ssem sin.sin_addr.s_addr = h->bindto; 725243956Ssem sin.sin_port = 0; 726243956Ssem if (bind(h->fd, (const struct sockaddr *)&sin, 727243956Ssem sizeof sin) == -1) { 728243956Ssem generr(h, "bind: %s", strerror(errno)); 729243956Ssem close(h->fd); 730243956Ssem h->fd = -1; 731243956Ssem return (-1); 732243956Ssem } 733243956Ssem } 734243956Ssem 735197086Smav if (h->out[POS_CODE] == RAD_ACCESS_REQUEST) { 73652709Sjdp /* Insert the scrambled password into the request */ 73752709Sjdp if (h->pass_pos != 0) 73852709Sjdp insert_scrambled_password(h, h->srv); 739197086Smav } 740197086Smav insert_message_authenticator(h, 0); 741243956Ssem 742197086Smav if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) { 743197086Smav /* Insert the request authenticator into the request */ 744243956Ssem memset(&h->out[POS_AUTH], 0, LEN_AUTH); 745243956Ssem insert_request_authenticator(h, 0); 746197086Smav } 74743662Sbrian 74843662Sbrian /* Send the request */ 749197086Smav n = sendto(h->fd, h->out, h->out_len, 0, 75043662Sbrian (const struct sockaddr *)&h->servers[h->srv].addr, 75143662Sbrian sizeof h->servers[h->srv].addr); 752197621Smav if (n != h->out_len) 753197621Smav tv->tv_sec = 1; /* Do not wait full timeout if send failed. */ 754197621Smav else 755197621Smav tv->tv_sec = h->servers[h->srv].timeout; 75643662Sbrian h->servers[h->srv].num_tries++; 75743662Sbrian tv->tv_usec = 0; 75843662Sbrian *fd = h->fd; 75943662Sbrian 76043662Sbrian return 0; 76143662Sbrian} 76243662Sbrian 76343662Sbrianint 764197086Smavrad_receive_request(struct rad_handle *h) 765197086Smav{ 766197086Smav struct sockaddr_in from; 767197086Smav socklen_t fromlen; 768197086Smav int n; 769197086Smav 770197086Smav if (h->type != RADIUS_SERVER) { 771197086Smav generr(h, "denied function call"); 772197086Smav return (-1); 773197086Smav } 774197086Smav h->srv = -1; 775197086Smav fromlen = sizeof(from); 776197086Smav h->in_len = recvfrom(h->fd, h->in, 777197086Smav MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen); 778197086Smav if (h->in_len == -1) { 779197086Smav generr(h, "recvfrom: %s", strerror(errno)); 780197086Smav return (-1); 781197086Smav } 782197086Smav for (n = 0; n < h->num_servers; n++) { 783197086Smav if (h->servers[n].addr.sin_addr.s_addr == from.sin_addr.s_addr) { 784197086Smav h->servers[n].addr.sin_port = from.sin_port; 785197086Smav h->srv = n; 786197086Smav break; 787197086Smav } 788197086Smav } 789197086Smav if (h->srv == -1) 790197086Smav return (-2); 791197086Smav if (is_valid_request(h)) { 792197086Smav h->in_len = h->in[POS_LENGTH] << 8 | 793197086Smav h->in[POS_LENGTH+1]; 794197086Smav h->in_pos = POS_ATTRS; 795197086Smav return (h->in[POS_CODE]); 796197086Smav } 797197086Smav return (-3); 798197086Smav} 799197086Smav 800197086Smavint 801197086Smavrad_send_response(struct rad_handle *h) 802197086Smav{ 803197086Smav int n; 804197086Smav 805197086Smav if (h->type != RADIUS_SERVER) { 806197086Smav generr(h, "denied function call"); 807197086Smav return (-1); 808197086Smav } 809197086Smav /* Fill in the length field in the message */ 810197086Smav h->out[POS_LENGTH] = h->out_len >> 8; 811197086Smav h->out[POS_LENGTH+1] = h->out_len; 812197086Smav 813197086Smav insert_message_authenticator(h, 814197086Smav (h->in[POS_CODE] == RAD_ACCESS_REQUEST) ? 1 : 0); 815197086Smav insert_request_authenticator(h, 1); 816197086Smav 817197086Smav /* Send the request */ 818197086Smav n = sendto(h->fd, h->out, h->out_len, 0, 819197086Smav (const struct sockaddr *)&h->servers[h->srv].addr, 820197086Smav sizeof h->servers[h->srv].addr); 821197086Smav if (n != h->out_len) { 822197086Smav if (n == -1) 823197086Smav generr(h, "sendto: %s", strerror(errno)); 824197086Smav else 825197086Smav generr(h, "sendto: short write"); 826197086Smav return -1; 827197086Smav } 828197086Smav 829197086Smav return 0; 830197086Smav} 831197086Smav 832197086Smavint 83341118Sjdprad_create_request(struct rad_handle *h, int code) 83441118Sjdp{ 83541118Sjdp int i; 83641118Sjdp 837197086Smav if (h->type == RADIUS_SERVER) { 838197086Smav generr(h, "denied function call"); 839197086Smav return (-1); 84041118Sjdp } 841243956Ssem if (h->num_servers == 0) { 842243956Ssem generr(h, "No RADIUS servers specified"); 843243956Ssem return (-1); 844243956Ssem } 845197086Smav h->out[POS_CODE] = code; 846197086Smav h->out[POS_IDENT] = ++h->ident; 847197086Smav if (code == RAD_ACCESS_REQUEST) { 848197086Smav /* Create a random authenticator */ 849197086Smav for (i = 0; i < LEN_AUTH; i += 2) { 850197086Smav long r; 851197086Smav r = random(); 852197086Smav h->out[POS_AUTH+i] = (u_char)r; 853197086Smav h->out[POS_AUTH+i+1] = (u_char)(r >> 8); 854197086Smav } 855197086Smav } else 856197086Smav memset(&h->out[POS_AUTH], 0, LEN_AUTH); 857197086Smav h->out_len = POS_ATTRS; 85841118Sjdp clear_password(h); 859197086Smav h->authentic_pos = 0; 860197086Smav h->out_created = 1; 86141118Sjdp return 0; 86241118Sjdp} 86341118Sjdp 864197086Smavint 865197086Smavrad_create_response(struct rad_handle *h, int code) 866197086Smav{ 867197086Smav 868197086Smav if (h->type != RADIUS_SERVER) { 869197086Smav generr(h, "denied function call"); 870197086Smav return (-1); 871197086Smav } 872197086Smav h->out[POS_CODE] = code; 873197086Smav h->out[POS_IDENT] = h->in[POS_IDENT]; 874197086Smav memset(&h->out[POS_AUTH], 0, LEN_AUTH); 875197086Smav h->out_len = POS_ATTRS; 876197086Smav clear_password(h); 877197086Smav h->authentic_pos = 0; 878197086Smav h->out_created = 1; 879197086Smav return 0; 880197086Smav} 881197086Smav 88241118Sjdpstruct in_addr 88341118Sjdprad_cvt_addr(const void *data) 88441118Sjdp{ 88541118Sjdp struct in_addr value; 88641118Sjdp 88741118Sjdp memcpy(&value.s_addr, data, sizeof value.s_addr); 88841118Sjdp return value; 88941118Sjdp} 89041118Sjdp 891243956Ssemstruct in6_addr 892243956Ssemrad_cvt_addr6(const void *data) 893243956Ssem{ 894243956Ssem struct in6_addr value; 895243956Ssem 896243956Ssem memcpy(&value.s6_addr, data, sizeof value.s6_addr); 897243956Ssem return value; 898243956Ssem} 899243956Ssem 90041118Sjdpu_int32_t 90141118Sjdprad_cvt_int(const void *data) 90241118Sjdp{ 90341118Sjdp u_int32_t value; 90441118Sjdp 90541118Sjdp memcpy(&value, data, sizeof value); 90641118Sjdp return ntohl(value); 90741118Sjdp} 90841118Sjdp 90941118Sjdpchar * 91041118Sjdprad_cvt_string(const void *data, size_t len) 91141118Sjdp{ 91241118Sjdp char *s; 91341118Sjdp 91441118Sjdp s = malloc(len + 1); 91541118Sjdp if (s != NULL) { 91641118Sjdp memcpy(s, data, len); 91741118Sjdp s[len] = '\0'; 91841118Sjdp } 91941118Sjdp return s; 92041118Sjdp} 92141118Sjdp 92241118Sjdp/* 92341118Sjdp * Returns the attribute type. If none are left, returns 0. On failure, 92441118Sjdp * returns -1. 92541118Sjdp */ 92641118Sjdpint 92741118Sjdprad_get_attr(struct rad_handle *h, const void **value, size_t *len) 92841118Sjdp{ 92941118Sjdp int type; 93041118Sjdp 931197086Smav if (h->in_pos >= h->in_len) 93241118Sjdp return 0; 933197086Smav if (h->in_pos + 2 > h->in_len) { 93441118Sjdp generr(h, "Malformed attribute in response"); 93541118Sjdp return -1; 93641118Sjdp } 937197086Smav type = h->in[h->in_pos++]; 938197086Smav *len = h->in[h->in_pos++] - 2; 939197086Smav if (h->in_pos + (int)*len > h->in_len) { 94041118Sjdp generr(h, "Malformed attribute in response"); 94141118Sjdp return -1; 94241118Sjdp } 943197086Smav *value = &h->in[h->in_pos]; 944197086Smav h->in_pos += *len; 94541118Sjdp return type; 94641118Sjdp} 94741118Sjdp 94841118Sjdp/* 94943662Sbrian * Returns -1 on error, 0 to indicate no event and >0 for success 95043662Sbrian */ 95143662Sbrianint 95243662Sbrianrad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv) 95343662Sbrian{ 95443662Sbrian int srv; 955243956Ssem time_t now; 956243956Ssem struct sockaddr_in sin; 95743662Sbrian 958197086Smav if (h->type == RADIUS_SERVER) { 959197086Smav generr(h, "denied function call"); 960197086Smav return (-1); 961197086Smav } 96243662Sbrian /* Make sure we have a socket to use */ 96343662Sbrian if (h->fd == -1) { 96443662Sbrian if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 96543662Sbrian generr(h, "Cannot create socket: %s", strerror(errno)); 96643662Sbrian return -1; 96743662Sbrian } 96843662Sbrian memset(&sin, 0, sizeof sin); 96943662Sbrian sin.sin_len = sizeof sin; 97043662Sbrian sin.sin_family = AF_INET; 971228730Smelifaro sin.sin_addr.s_addr = h->bindto; 97243662Sbrian sin.sin_port = htons(0); 97343662Sbrian if (bind(h->fd, (const struct sockaddr *)&sin, 97443662Sbrian sizeof sin) == -1) { 97543662Sbrian generr(h, "bind: %s", strerror(errno)); 97643662Sbrian close(h->fd); 97743662Sbrian h->fd = -1; 97843662Sbrian return -1; 97943662Sbrian } 98043662Sbrian } 98143662Sbrian 982197086Smav if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) { 98352709Sjdp /* Make sure no password given */ 98452709Sjdp if (h->pass_pos || h->chap_pass) { 985128684Sru generr(h, "User or Chap Password" 986128684Sru " in accounting request"); 98752709Sjdp return -1; 98852709Sjdp } 98952709Sjdp } else { 990128684Sru if (h->eap_msg == 0) { 991128684Sru /* Make sure the user gave us a password */ 992128684Sru if (h->pass_pos == 0 && !h->chap_pass) { 993128684Sru generr(h, "No User or Chap Password" 994128684Sru " attributes given"); 995128684Sru return -1; 996128684Sru } 997128684Sru if (h->pass_pos != 0 && h->chap_pass) { 998128684Sru generr(h, "Both User and Chap Password" 999128684Sru " attributes given"); 1000128684Sru return -1; 1001128684Sru } 100252709Sjdp } 100343662Sbrian } 100443662Sbrian 100543662Sbrian /* Fill in the length field in the message */ 1006197086Smav h->out[POS_LENGTH] = h->out_len >> 8; 1007197086Smav h->out[POS_LENGTH+1] = h->out_len; 100843662Sbrian 1009243956Ssem h->srv = 0; 1010243956Ssem now = time(NULL); 1011243956Ssem for (srv = 0; srv < h->num_servers; srv++) 1012243956Ssem h->servers[srv].num_tries = 0; 1013243956Ssem /* Find a first good server. */ 101443662Sbrian for (srv = 0; srv < h->num_servers; srv++) { 1015243956Ssem if (h->servers[srv].is_dead == 0) 1016243956Ssem break; 1017243956Ssem if (h->servers[srv].dead_time && 1018243956Ssem h->servers[srv].next_probe <= now) { 1019243956Ssem h->servers[srv].is_dead = 0; 1020243956Ssem break; 1021243956Ssem } 1022243956Ssem h->srv++; 102343662Sbrian } 1024243956Ssem 1025243956Ssem /* If all servers was dead on the last probe, try from beginning */ 1026243956Ssem if (h->srv == h->num_servers) { 1027243956Ssem for (srv = 0; srv < h->num_servers; srv++) { 1028243956Ssem h->servers[srv].is_dead = 0; 1029243956Ssem h->servers[srv].next_probe = 0; 1030243956Ssem } 1031243956Ssem h->srv = 0; 103243662Sbrian } 103343662Sbrian 103443662Sbrian return rad_continue_send_request(h, 0, fd, tv); 103543662Sbrian} 103643662Sbrian 103743662Sbrian/* 103841118Sjdp * Create and initialize a rad_handle structure, and return it to the 103941118Sjdp * caller. Can fail only if the necessary memory cannot be allocated. 104041118Sjdp * In that case, it returns NULL. 104141118Sjdp */ 104241118Sjdpstruct rad_handle * 104352709Sjdprad_auth_open(void) 104441118Sjdp{ 104541118Sjdp struct rad_handle *h; 104641118Sjdp 104741118Sjdp h = (struct rad_handle *)malloc(sizeof(struct rad_handle)); 104841118Sjdp if (h != NULL) { 104941118Sjdp srandomdev(); 105041118Sjdp h->fd = -1; 105141118Sjdp h->num_servers = 0; 105241118Sjdp h->ident = random(); 105341118Sjdp h->errmsg[0] = '\0'; 105441118Sjdp memset(h->pass, 0, sizeof h->pass); 105541118Sjdp h->pass_len = 0; 105641118Sjdp h->pass_pos = 0; 105743400Sbrian h->chap_pass = 0; 1058128684Sru h->authentic_pos = 0; 105952709Sjdp h->type = RADIUS_AUTH; 1060197086Smav h->out_created = 0; 1061128684Sru h->eap_msg = 0; 1062243956Ssem h->bindto = INADDR_ANY; 106341118Sjdp } 106441118Sjdp return h; 106541118Sjdp} 106641118Sjdp 106752709Sjdpstruct rad_handle * 106852709Sjdprad_acct_open(void) 106952709Sjdp{ 107052709Sjdp struct rad_handle *h; 107152709Sjdp 107252709Sjdp h = rad_open(); 107352709Sjdp if (h != NULL) 107452709Sjdp h->type = RADIUS_ACCT; 107552709Sjdp return h; 107652709Sjdp} 107752709Sjdp 107852709Sjdpstruct rad_handle * 1079197086Smavrad_server_open(int fd) 1080197086Smav{ 1081197086Smav struct rad_handle *h; 1082197086Smav 1083197086Smav h = rad_open(); 1084197086Smav if (h != NULL) { 1085197086Smav h->type = RADIUS_SERVER; 1086197086Smav h->fd = fd; 1087197086Smav } 1088197086Smav return h; 1089197086Smav} 1090197086Smav 1091197086Smavstruct rad_handle * 109252709Sjdprad_open(void) 109352709Sjdp{ 109452709Sjdp return rad_auth_open(); 109552709Sjdp} 109652709Sjdp 109741118Sjdpint 109841118Sjdprad_put_addr(struct rad_handle *h, int type, struct in_addr addr) 109941118Sjdp{ 110041118Sjdp return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr); 110141118Sjdp} 110241118Sjdp 110341118Sjdpint 1104243956Ssemrad_put_addr6(struct rad_handle *h, int type, struct in6_addr addr) 1105243956Ssem{ 1106243956Ssem 1107243956Ssem return rad_put_attr(h, type, &addr.s6_addr, sizeof addr.s6_addr); 1108243956Ssem} 1109243956Ssem 1110243956Ssemint 111141118Sjdprad_put_attr(struct rad_handle *h, int type, const void *value, size_t len) 111241118Sjdp{ 111343400Sbrian int result; 111443400Sbrian 1115197086Smav if (!h->out_created) { 1116128684Sru generr(h, "Please call rad_create_request()" 1117128684Sru " before putting attributes"); 1118128684Sru return -1; 1119128684Sru } 1120128684Sru 1121197086Smav if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) { 1122128684Sru if (type == RAD_EAP_MESSAGE) { 1123128684Sru generr(h, "EAP-Message attribute is not valid" 1124128684Sru " in accounting requests"); 1125128684Sru return -1; 1126128684Sru } 1127128684Sru } 1128128684Sru 1129128684Sru /* 1130128684Sru * When proxying EAP Messages, the Message Authenticator 1131128684Sru * MUST be present; see RFC 3579. 1132128684Sru */ 1133128684Sru if (type == RAD_EAP_MESSAGE) { 1134128684Sru if (rad_put_message_authentic(h) == -1) 1135128684Sru return -1; 1136128684Sru } 1137128684Sru 1138128684Sru if (type == RAD_USER_PASSWORD) { 113943400Sbrian result = put_password_attr(h, type, value, len); 1140128684Sru } else if (type == RAD_MESSAGE_AUTHENTIC) { 1141128684Sru result = rad_put_message_authentic(h); 1142128684Sru } else { 114343400Sbrian result = put_raw_attr(h, type, value, len); 1144128684Sru if (result == 0) { 1145128684Sru if (type == RAD_CHAP_PASSWORD) 1146128684Sru h->chap_pass = 1; 1147128684Sru else if (type == RAD_EAP_MESSAGE) 1148128684Sru h->eap_msg = 1; 1149128684Sru } 115043400Sbrian } 115143400Sbrian 115243400Sbrian return result; 115341118Sjdp} 115441118Sjdp 115541118Sjdpint 115641118Sjdprad_put_int(struct rad_handle *h, int type, u_int32_t value) 115741118Sjdp{ 115841118Sjdp u_int32_t nvalue; 115941118Sjdp 116041118Sjdp nvalue = htonl(value); 116141118Sjdp return rad_put_attr(h, type, &nvalue, sizeof nvalue); 116241118Sjdp} 116341118Sjdp 116441118Sjdpint 116541118Sjdprad_put_string(struct rad_handle *h, int type, const char *str) 116641118Sjdp{ 116741118Sjdp return rad_put_attr(h, type, str, strlen(str)); 116841118Sjdp} 116941118Sjdp 1170128684Sruint 1171128684Srurad_put_message_authentic(struct rad_handle *h) 1172128684Sru{ 1173128684Sru#ifdef WITH_SSL 1174128684Sru u_char md_zero[MD5_DIGEST_LENGTH]; 1175128684Sru 1176197086Smav if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) { 1177128684Sru generr(h, "Message-Authenticator is not valid" 1178128684Sru " in accounting requests"); 1179128684Sru return -1; 1180128684Sru } 1181128684Sru 1182128684Sru if (h->authentic_pos == 0) { 1183197086Smav h->authentic_pos = h->out_len; 1184128684Sru memset(md_zero, 0, sizeof(md_zero)); 1185128684Sru return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero, 1186128684Sru sizeof(md_zero))); 1187128684Sru } 1188128684Sru return 0; 1189128684Sru#else 1190128684Sru generr(h, "Message Authenticator not supported," 1191128684Sru " please recompile libradius with SSL support"); 1192128684Sru return -1; 1193128684Sru#endif 1194128684Sru} 1195128684Sru 119641118Sjdp/* 119741118Sjdp * Returns the response type code on success, or -1 on failure. 119841118Sjdp */ 119941118Sjdpint 120041118Sjdprad_send_request(struct rad_handle *h) 120141118Sjdp{ 120243662Sbrian struct timeval timelimit; 120343662Sbrian struct timeval tv; 120443662Sbrian int fd; 120541118Sjdp int n; 120641118Sjdp 120743662Sbrian n = rad_init_send_request(h, &fd, &tv); 120841118Sjdp 120943662Sbrian if (n != 0) 121043662Sbrian return n; 121141118Sjdp 121243662Sbrian gettimeofday(&timelimit, NULL); 121343662Sbrian timeradd(&tv, &timelimit, &timelimit); 121441118Sjdp 121543662Sbrian for ( ; ; ) { 121643662Sbrian fd_set readfds; 121743400Sbrian 121843662Sbrian FD_ZERO(&readfds); 121943662Sbrian FD_SET(fd, &readfds); 122041118Sjdp 122143662Sbrian n = select(fd + 1, &readfds, NULL, NULL, &tv); 122241118Sjdp 122343662Sbrian if (n == -1) { 122443662Sbrian generr(h, "select: %s", strerror(errno)); 122541118Sjdp return -1; 122641118Sjdp } 122741118Sjdp 122843662Sbrian if (!FD_ISSET(fd, &readfds)) { 122941118Sjdp /* Compute a new timeout */ 123041118Sjdp gettimeofday(&tv, NULL); 123141118Sjdp timersub(&timelimit, &tv, &tv); 123243662Sbrian if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0)) 123343662Sbrian /* Continue the select */ 123443662Sbrian continue; 123541118Sjdp } 123643662Sbrian 123743662Sbrian n = rad_continue_send_request(h, n, &fd, &tv); 123843662Sbrian 123943662Sbrian if (n != 0) 124043662Sbrian return n; 124143662Sbrian 124243662Sbrian gettimeofday(&timelimit, NULL); 124343662Sbrian timeradd(&tv, &timelimit, &timelimit); 124441118Sjdp } 124541118Sjdp} 124641118Sjdp 124741118Sjdpconst char * 124841118Sjdprad_strerror(struct rad_handle *h) 124941118Sjdp{ 125041118Sjdp return h->errmsg; 125141118Sjdp} 125241118Sjdp 125341118Sjdp/* 125441118Sjdp * Destructively split a string into fields separated by white space. 125541118Sjdp * `#' at the beginning of a field begins a comment that extends to the 125641118Sjdp * end of the string. Fields may be quoted with `"'. Inside quoted 125741118Sjdp * strings, the backslash escapes `\"' and `\\' are honored. 125841118Sjdp * 125941118Sjdp * Pointers to up to the first maxfields fields are stored in the fields 126041118Sjdp * array. Missing fields get NULL pointers. 126141118Sjdp * 126241118Sjdp * The return value is the actual number of fields parsed, and is always 126341118Sjdp * <= maxfields. 126441118Sjdp * 126541118Sjdp * On a syntax error, places a message in the msg string, and returns -1. 126641118Sjdp */ 126741118Sjdpstatic int 126841118Sjdpsplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 126941118Sjdp{ 127041118Sjdp char *p; 127141118Sjdp int i; 127241118Sjdp static const char ws[] = " \t"; 127341118Sjdp 127441118Sjdp for (i = 0; i < maxfields; i++) 127541118Sjdp fields[i] = NULL; 127641118Sjdp p = str; 127741118Sjdp i = 0; 127841118Sjdp while (*p != '\0') { 127941118Sjdp p += strspn(p, ws); 128041118Sjdp if (*p == '#' || *p == '\0') 128141118Sjdp break; 128241118Sjdp if (i >= maxfields) { 128341118Sjdp snprintf(msg, msglen, "line has too many fields"); 128441118Sjdp return -1; 128541118Sjdp } 128641118Sjdp if (*p == '"') { 128741118Sjdp char *dst; 128841118Sjdp 128941118Sjdp dst = ++p; 129041118Sjdp fields[i] = dst; 129141118Sjdp while (*p != '"') { 129241118Sjdp if (*p == '\\') { 129341118Sjdp p++; 129441118Sjdp if (*p != '"' && *p != '\\' && 129541118Sjdp *p != '\0') { 129641118Sjdp snprintf(msg, msglen, 129741118Sjdp "invalid `\\' escape"); 129841118Sjdp return -1; 129941118Sjdp } 130041118Sjdp } 130141118Sjdp if (*p == '\0') { 130241118Sjdp snprintf(msg, msglen, 130341118Sjdp "unterminated quoted string"); 130441118Sjdp return -1; 130541118Sjdp } 130641118Sjdp *dst++ = *p++; 130741118Sjdp } 130841118Sjdp *dst = '\0'; 130941118Sjdp p++; 131041118Sjdp if (*fields[i] == '\0') { 131141118Sjdp snprintf(msg, msglen, 131241118Sjdp "empty quoted string not permitted"); 131341118Sjdp return -1; 131441118Sjdp } 131541118Sjdp if (*p != '\0' && strspn(p, ws) == 0) { 131641118Sjdp snprintf(msg, msglen, "quoted string not" 131741118Sjdp " followed by white space"); 131841118Sjdp return -1; 131941118Sjdp } 132041118Sjdp } else { 132141118Sjdp fields[i] = p; 132241118Sjdp p += strcspn(p, ws); 132341118Sjdp if (*p != '\0') 132441118Sjdp *p++ = '\0'; 132541118Sjdp } 132641118Sjdp i++; 132741118Sjdp } 132841118Sjdp return i; 132941118Sjdp} 133096154Sbrian 133196154Sbrianint 133296322Sbrianrad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len) 133396322Sbrian{ 133496322Sbrian struct vendor_attribute *attr; 133596322Sbrian 133696322Sbrian attr = (struct vendor_attribute *)*data; 133796322Sbrian *vendor = ntohl(attr->vendor_value); 133896322Sbrian *data = attr->attrib_data; 133996322Sbrian *len = attr->attrib_len - 2; 134096322Sbrian 134196322Sbrian return (attr->attrib_type); 134296322Sbrian} 134396322Sbrian 134496322Sbrianint 134596154Sbrianrad_put_vendor_addr(struct rad_handle *h, int vendor, int type, 134696154Sbrian struct in_addr addr) 134796154Sbrian{ 134896154Sbrian return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr, 134996154Sbrian sizeof addr.s_addr)); 135096154Sbrian} 135196154Sbrian 135296154Sbrianint 1353243956Ssemrad_put_vendor_addr6(struct rad_handle *h, int vendor, int type, 1354243956Ssem struct in6_addr addr) 1355243956Ssem{ 1356243956Ssem 1357243956Ssem return (rad_put_vendor_attr(h, vendor, type, &addr.s6_addr, 1358243956Ssem sizeof addr.s6_addr)); 1359243956Ssem} 1360243956Ssem 1361243956Ssemint 136296154Sbrianrad_put_vendor_attr(struct rad_handle *h, int vendor, int type, 136396154Sbrian const void *value, size_t len) 136496154Sbrian{ 136596154Sbrian struct vendor_attribute *attr; 136696154Sbrian int res; 136796154Sbrian 1368197086Smav if (!h->out_created) { 1369128684Sru generr(h, "Please call rad_create_request()" 1370128684Sru " before putting attributes"); 1371128684Sru return -1; 1372128684Sru } 1373128684Sru 137496154Sbrian if ((attr = malloc(len + 6)) == NULL) { 1375130490Sstefanf generr(h, "malloc failure (%zu bytes)", len + 6); 137696154Sbrian return -1; 137796154Sbrian } 137896154Sbrian 137996154Sbrian attr->vendor_value = htonl(vendor); 138096154Sbrian attr->attrib_type = type; 138196154Sbrian attr->attrib_len = len + 2; 138296154Sbrian memcpy(attr->attrib_data, value, len); 138396154Sbrian 138496154Sbrian res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6); 138596154Sbrian free(attr); 138696154Sbrian if (res == 0 && vendor == RAD_VENDOR_MICROSOFT 138796154Sbrian && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE 138896154Sbrian || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) { 138996154Sbrian h->chap_pass = 1; 139096154Sbrian } 139196154Sbrian return (res); 139296154Sbrian} 139396154Sbrian 139496154Sbrianint 139596154Sbrianrad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i) 139696154Sbrian{ 139796154Sbrian u_int32_t value; 139896154Sbrian 139996154Sbrian value = htonl(i); 140096154Sbrian return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value)); 140196154Sbrian} 140296154Sbrian 140396154Sbrianint 140496154Sbrianrad_put_vendor_string(struct rad_handle *h, int vendor, int type, 140596154Sbrian const char *str) 140696154Sbrian{ 140796154Sbrian return (rad_put_vendor_attr(h, vendor, type, str, strlen(str))); 140896154Sbrian} 140998131Sbrian 141098131Sbrianssize_t 141198131Sbrianrad_request_authenticator(struct rad_handle *h, char *buf, size_t len) 141298131Sbrian{ 141398131Sbrian if (len < LEN_AUTH) 141498131Sbrian return (-1); 1415197086Smav memcpy(buf, h->out + POS_AUTH, LEN_AUTH); 141698131Sbrian if (len > LEN_AUTH) 141798131Sbrian buf[LEN_AUTH] = '\0'; 141898131Sbrian return (LEN_AUTH); 141998131Sbrian} 142098131Sbrian 1421128684Sruu_char * 1422128684Srurad_demangle(struct rad_handle *h, const void *mangled, size_t mlen) 1423128684Sru{ 1424128684Sru char R[LEN_AUTH]; 1425128684Sru const char *S; 1426128684Sru int i, Ppos; 1427128684Sru MD5_CTX Context; 1428128684Sru u_char b[MD5_DIGEST_LENGTH], *C, *demangled; 1429128684Sru 1430128684Sru if ((mlen % 16 != 0) || mlen > 128) { 1431128684Sru generr(h, "Cannot interpret mangled data of length %lu", 1432128684Sru (u_long)mlen); 1433128684Sru return NULL; 1434128684Sru } 1435128684Sru 1436128684Sru C = (u_char *)mangled; 1437128684Sru 1438128684Sru /* We need the shared secret as Salt */ 1439128684Sru S = rad_server_secret(h); 1440128684Sru 1441128684Sru /* We need the request authenticator */ 1442128684Sru if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) { 1443128684Sru generr(h, "Cannot obtain the RADIUS request authenticator"); 1444128684Sru return NULL; 1445128684Sru } 1446128684Sru 1447128684Sru demangled = malloc(mlen); 1448128684Sru if (!demangled) 1449128684Sru return NULL; 1450128684Sru 1451128684Sru MD5Init(&Context); 1452128684Sru MD5Update(&Context, S, strlen(S)); 1453128684Sru MD5Update(&Context, R, LEN_AUTH); 1454128684Sru MD5Final(b, &Context); 1455128684Sru Ppos = 0; 1456128684Sru while (mlen) { 1457128684Sru 1458128684Sru mlen -= 16; 1459128684Sru for (i = 0; i < 16; i++) 1460128684Sru demangled[Ppos++] = C[i] ^ b[i]; 1461128684Sru 1462128684Sru if (mlen) { 1463128684Sru MD5Init(&Context); 1464128684Sru MD5Update(&Context, S, strlen(S)); 1465128684Sru MD5Update(&Context, C, 16); 1466128684Sru MD5Final(b, &Context); 1467128684Sru } 1468128684Sru 1469128684Sru C += 16; 1470128684Sru } 1471128684Sru 1472128684Sru return demangled; 1473128684Sru} 1474128684Sru 1475128684Sruu_char * 1476128684Srurad_demangle_mppe_key(struct rad_handle *h, const void *mangled, 1477128684Sru size_t mlen, size_t *len) 1478128684Sru{ 1479128684Sru char R[LEN_AUTH]; /* variable names as per rfc2548 */ 1480128684Sru const char *S; 1481128684Sru u_char b[MD5_DIGEST_LENGTH], *demangled; 1482128684Sru const u_char *A, *C; 1483128684Sru MD5_CTX Context; 1484128684Sru int Slen, i, Clen, Ppos; 1485128684Sru u_char *P; 1486128684Sru 1487128684Sru if (mlen % 16 != SALT_LEN) { 1488128684Sru generr(h, "Cannot interpret mangled data of length %lu", 1489128684Sru (u_long)mlen); 1490128684Sru return NULL; 1491128684Sru } 1492128684Sru 1493128684Sru /* We need the RADIUS Request-Authenticator */ 1494128684Sru if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) { 1495128684Sru generr(h, "Cannot obtain the RADIUS request authenticator"); 1496128684Sru return NULL; 1497128684Sru } 1498128684Sru 1499128684Sru A = (const u_char *)mangled; /* Salt comes first */ 1500128684Sru C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */ 1501128684Sru Clen = mlen - SALT_LEN; 1502128684Sru S = rad_server_secret(h); /* We need the RADIUS secret */ 1503128684Sru Slen = strlen(S); 1504128684Sru P = alloca(Clen); /* We derive our plaintext */ 1505128684Sru 1506128684Sru MD5Init(&Context); 1507128684Sru MD5Update(&Context, S, Slen); 1508128684Sru MD5Update(&Context, R, LEN_AUTH); 1509128684Sru MD5Update(&Context, A, SALT_LEN); 1510128684Sru MD5Final(b, &Context); 1511128684Sru Ppos = 0; 1512128684Sru 1513128684Sru while (Clen) { 1514128684Sru Clen -= 16; 1515128684Sru 1516128684Sru for (i = 0; i < 16; i++) 1517128684Sru P[Ppos++] = C[i] ^ b[i]; 1518128684Sru 1519128684Sru if (Clen) { 1520128684Sru MD5Init(&Context); 1521128684Sru MD5Update(&Context, S, Slen); 1522128684Sru MD5Update(&Context, C, 16); 1523128684Sru MD5Final(b, &Context); 1524128684Sru } 1525128684Sru 1526128684Sru C += 16; 1527128684Sru } 1528128684Sru 1529128684Sru /* 1530128684Sru * The resulting plain text consists of a one-byte length, the text and 1531128684Sru * maybe some padding. 1532128684Sru */ 1533128684Sru *len = *P; 1534128684Sru if (*len > mlen - 1) { 1535130490Sstefanf generr(h, "Mangled data seems to be garbage %zu %zu", 1536128684Sru *len, mlen-1); 1537128684Sru return NULL; 1538128684Sru } 1539128684Sru 1540128684Sru if (*len > MPPE_KEY_LEN * 2) { 1541130490Sstefanf generr(h, "Key to long (%zu) for me max. %d", 1542128684Sru *len, MPPE_KEY_LEN * 2); 1543128684Sru return NULL; 1544128684Sru } 1545128684Sru demangled = malloc(*len); 1546128684Sru if (!demangled) 1547128684Sru return NULL; 1548128684Sru 1549128684Sru memcpy(demangled, P + 1, *len); 1550128684Sru return demangled; 1551128684Sru} 1552128684Sru 155398131Sbrianconst char * 155498131Sbrianrad_server_secret(struct rad_handle *h) 155598131Sbrian{ 155698131Sbrian return (h->servers[h->srv].secret); 155798131Sbrian} 1558