radlib.c revision 128684
11556Srgrimes/*- 21556Srgrimes * Copyright 1998 Juniper Networks, Inc. 31556Srgrimes * All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 141556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241556Srgrimes * SUCH DAMAGE. 251556Srgrimes */ 261556Srgrimes 271556Srgrimes#include <sys/cdefs.h> 281556Srgrimes__FBSDID("$FreeBSD: head/lib/libradius/radlib.c 128684 2004-04-27 15:00:29Z ru $"); 291556Srgrimes 301556Srgrimes#include <sys/types.h> 311556Srgrimes#include <sys/socket.h> 321556Srgrimes#include <sys/time.h> 331556Srgrimes#include <netinet/in.h> 3436150Scharnier#include <arpa/inet.h> 3536150Scharnier#ifdef WITH_SSL 3636150Scharnier#include <openssl/hmac.h> 371556Srgrimes#include <openssl/md5.h> 3899110Sobrien#define MD5Init MD5_Init 3999110Sobrien#define MD5Update MD5_Update 401556Srgrimes#define MD5Final MD5_Final 41100437Stjr#else 4217987Speter#define MD5_DIGEST_LENGTH 16 43102576Skeramida#include <md5.h> 4417987Speter#endif 45153091Sstefanf 4645266Scracauer/* We need the MPPE_KEY_LEN define */ 4753891Scracauer#include <netgraph/ng_mppc.h> 4817987Speter 491556Srgrimes#include <errno.h> 501556Srgrimes#include <netdb.h> 511556Srgrimes#include <stdarg.h> 521556Srgrimes#include <stddef.h> 531556Srgrimes#include <stdio.h> 541556Srgrimes#include <stdlib.h> 551556Srgrimes#include <string.h> 561556Srgrimes#include <unistd.h> 571556Srgrimes 581556Srgrimes#include "radlib_private.h" 591556Srgrimes 601556Srgrimesstatic void clear_password(struct rad_handle *); 611556Srgrimesstatic void generr(struct rad_handle *, const char *, ...) 621556Srgrimes __printflike(2, 3); 631556Srgrimesstatic void insert_scrambled_password(struct rad_handle *, int); 641556Srgrimesstatic void insert_request_authenticator(struct rad_handle *, int); 651556Srgrimesstatic void insert_message_authenticator(struct rad_handle *, int); 661556Srgrimesstatic int is_valid_response(struct rad_handle *, int, 671556Srgrimes const struct sockaddr_in *); 681556Srgrimesstatic int put_password_attr(struct rad_handle *, int, 691556Srgrimes const void *, size_t); 7017987Speterstatic int put_raw_attr(struct rad_handle *, int, 711556Srgrimes const void *, size_t); 7217987Speterstatic int split(char *, char *[], int, char *, size_t); 731556Srgrimes 7417987Speterstatic void 751556Srgrimesclear_password(struct rad_handle *h) 761556Srgrimes{ 771556Srgrimes if (h->pass_len != 0) { 781556Srgrimes memset(h->pass, 0, h->pass_len); 791556Srgrimes h->pass_len = 0; 801556Srgrimes } 811556Srgrimes h->pass_pos = 0; 821556Srgrimes} 831556Srgrimes 841556Srgrimesstatic void 851556Srgrimesgenerr(struct rad_handle *h, const char *format, ...) 86193169Sstefanf{ 871556Srgrimes va_list ap; 881556Srgrimes 891556Srgrimes va_start(ap, format); 901556Srgrimes vsnprintf(h->errmsg, ERRSIZE, format, ap); 911556Srgrimes va_end(ap); 9217987Speter} 931556Srgrimes 941556Srgrimesstatic void 95149933Sstefanfinsert_scrambled_password(struct rad_handle *h, int srv) 96149933Sstefanf{ 9790111Simp MD5_CTX ctx; 9890111Simp unsigned char md5[MD5_DIGEST_LENGTH]; 9990111Simp const struct rad_server *srvp; 10090111Simp int padded_len; 10190111Simp int pos; 10290111Simp 1031556Srgrimes srvp = &h->servers[srv]; 1041556Srgrimes padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf; 1051556Srgrimes 1061556Srgrimes memcpy(md5, &h->request[POS_AUTH], LEN_AUTH); 1071556Srgrimes for (pos = 0; pos < padded_len; pos += 16) { 1081556Srgrimes int i; 1091556Srgrimes 1101556Srgrimes /* Calculate the new scrambler */ 1111556Srgrimes MD5Init(&ctx); 1121556Srgrimes MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 1131556Srgrimes MD5Update(&ctx, md5, 16); 1141556Srgrimes MD5Final(md5, &ctx); 1151556Srgrimes 1161556Srgrimes /* 1171556Srgrimes * Mix in the current chunk of the password, and copy 1181556Srgrimes * the result into the right place in the request. Also 1191556Srgrimes * modify the scrambler in place, since we will use this 1201556Srgrimes * in calculating the scrambler for next time. 1211556Srgrimes */ 1221556Srgrimes for (i = 0; i < 16; i++) 1231556Srgrimes h->request[h->pass_pos + pos + i] = 1241556Srgrimes md5[i] ^= h->pass[pos + i]; 1251556Srgrimes } 12646684Skris} 1271556Srgrimes 1281556Srgrimesstatic void 12917987Speterinsert_request_authenticator(struct rad_handle *h, int srv) 13090111Simp{ 1311556Srgrimes MD5_CTX ctx; 1321556Srgrimes const struct rad_server *srvp; 1331556Srgrimes 1341556Srgrimes srvp = &h->servers[srv]; 1351556Srgrimes 1361556Srgrimes /* Create the request authenticator */ 1371556Srgrimes MD5Init(&ctx); 1381556Srgrimes MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE); 1391556Srgrimes MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH); 1401556Srgrimes MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS); 1411556Srgrimes MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 1421556Srgrimes MD5Final(&h->request[POS_AUTH], &ctx); 1431556Srgrimes} 1441556Srgrimes 1451556Srgrimesstatic void 1461556Srgrimesinsert_message_authenticator(struct rad_handle *h, int srv) 1471556Srgrimes{ 1481556Srgrimes#ifdef WITH_SSL 1491556Srgrimes u_char md[EVP_MAX_MD_SIZE]; 1501556Srgrimes u_int md_len; 151193169Sstefanf const struct rad_server *srvp; 1521556Srgrimes HMAC_CTX ctx; 1531556Srgrimes srvp = &h->servers[srv]; 1541556Srgrimes 1551556Srgrimes if (h->authentic_pos != 0) { 1561556Srgrimes HMAC_CTX_init(&ctx); 1571556Srgrimes HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5()); 1581556Srgrimes HMAC_Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE); 1591556Srgrimes HMAC_Update(&ctx, &h->request[POS_AUTH], LEN_AUTH); 1601556Srgrimes HMAC_Update(&ctx, &h->request[POS_ATTRS], 1611556Srgrimes h->req_len - POS_ATTRS); 162193169Sstefanf HMAC_Final(&ctx, md, &md_len); 16390111Simp HMAC_CTX_cleanup(&ctx); 1641556Srgrimes HMAC_cleanup(&ctx); 1651556Srgrimes memcpy(&h->request[h->authentic_pos + 2], md, md_len); 1661556Srgrimes } 1671556Srgrimes#endif 1681556Srgrimes} 1691556Srgrimes 170190698Sstefanf/* 171193169Sstefanf * Return true if the current response is valid for a request to the 1721556Srgrimes * specified server. 1731556Srgrimes */ 1741556Srgrimesstatic int 1751556Srgrimesis_valid_response(struct rad_handle *h, int srv, 1761556Srgrimes const struct sockaddr_in *from) 1771556Srgrimes{ 1781556Srgrimes MD5_CTX ctx; 1791556Srgrimes unsigned char md5[MD5_DIGEST_LENGTH]; 1801556Srgrimes const struct rad_server *srvp; 1811556Srgrimes int len; 1821556Srgrimes#ifdef WITH_SSL 1831556Srgrimes HMAC_CTX hctx; 1841556Srgrimes u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE]; 1851556Srgrimes int pos, md_len; 18690111Simp#endif 18717987Speter 188149927Sstefanf srvp = &h->servers[srv]; 189149927Sstefanf 190149927Sstefanf /* Check the source address */ 1911556Srgrimes if (from->sin_family != srvp->addr.sin_family || 1921556Srgrimes from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr || 1931556Srgrimes from->sin_port != srvp->addr.sin_port) 1941556Srgrimes return 0; 1951556Srgrimes 19617987Speter /* Check the message length */ 1971556Srgrimes if (h->resp_len < POS_ATTRS) 19817987Speter return 0; 199149802Sstefanf len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; 2001556Srgrimes if (len > h->resp_len) 2011556Srgrimes return 0; 202149932Sstefanf 2031556Srgrimes /* Check the response authenticator */ 2041556Srgrimes MD5Init(&ctx); 2051556Srgrimes MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE); 2061556Srgrimes MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH); 2071556Srgrimes MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS); 2081556Srgrimes MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); 20918754Ssteve MD5Final(md5, &ctx); 2101556Srgrimes if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0) 21118754Ssteve return 0; 2121556Srgrimes 2131556Srgrimes#ifdef WITH_SSL 2141556Srgrimes /* 2151556Srgrimes * For non accounting responses check the message authenticator, 2161556Srgrimes * if any. 2171556Srgrimes */ 2181556Srgrimes if (h->response[POS_CODE] != RAD_ACCOUNTING_RESPONSE) { 2191556Srgrimes 2201556Srgrimes memcpy(resp, h->response, MSGSIZE); 2211556Srgrimes pos = POS_ATTRS; 2221556Srgrimes 2231556Srgrimes /* Search and verify the Message-Authenticator */ 2241556Srgrimes while (pos < len - 2) { 2251556Srgrimes 2261556Srgrimes if (h->response[pos] == RAD_MESSAGE_AUTHENTIC) { 2271556Srgrimes /* zero fill the Message-Authenticator */ 228149927Sstefanf memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH); 2291556Srgrimes 2301556Srgrimes HMAC_CTX_init(&hctx); 2311556Srgrimes HMAC_Init(&hctx, srvp->secret, 2321556Srgrimes strlen(srvp->secret), EVP_md5()); 2331556Srgrimes HMAC_Update(&hctx, &h->response[POS_CODE], 2341556Srgrimes POS_AUTH - POS_CODE); 2351556Srgrimes HMAC_Update(&hctx, &h->request[POS_AUTH], 2361556Srgrimes LEN_AUTH); 23720425Ssteve HMAC_Update(&hctx, &resp[POS_ATTRS], 2381556Srgrimes h->resp_len - POS_ATTRS); 23917987Speter HMAC_Final(&hctx, md, &md_len); 2401556Srgrimes HMAC_CTX_cleanup(&hctx); 24120425Ssteve HMAC_cleanup(&hctx); 24220425Ssteve if (memcmp(md, &h->response[pos + 2], 2431556Srgrimes MD5_DIGEST_LENGTH) != 0) 2441556Srgrimes return 0; 2451556Srgrimes break; 2461556Srgrimes } 247149933Sstefanf pos += h->response[pos + 1]; 2481556Srgrimes } 2491556Srgrimes } 250149933Sstefanf#endif 2511556Srgrimes return 1; 2521556Srgrimes} 2531556Srgrimes 2541556Srgrimesstatic int 2551556Srgrimesput_password_attr(struct rad_handle *h, int type, const void *value, size_t len) 2561556Srgrimes{ 2571556Srgrimes int padded_len; 2581556Srgrimes int pad_len; 2591556Srgrimes 2601556Srgrimes if (h->pass_pos != 0) { 2611556Srgrimes generr(h, "Multiple User-Password attributes specified"); 2621556Srgrimes return -1; 2631556Srgrimes } 2641556Srgrimes if (len > PASSSIZE) 2651556Srgrimes len = PASSSIZE; 266149927Sstefanf padded_len = len == 0 ? 16 : (len+15) & ~0xf; 2671556Srgrimes pad_len = padded_len - len; 2681556Srgrimes 2691556Srgrimes /* 270149927Sstefanf * Put in a place-holder attribute containing all zeros, and 2711556Srgrimes * remember where it is so we can fill it in later. 2721556Srgrimes */ 2731556Srgrimes clear_password(h); 2741556Srgrimes put_raw_attr(h, type, h->pass, padded_len); 2751556Srgrimes h->pass_pos = h->req_len - padded_len; 2761556Srgrimes 2771556Srgrimes /* Save the cleartext password, padded as necessary */ 2781556Srgrimes memcpy(h->pass, value, len); 2791556Srgrimes h->pass_len = len; 280149927Sstefanf memset(h->pass + len, 0, pad_len); 2811556Srgrimes return 0; 2821556Srgrimes} 2831556Srgrimes 2841556Srgrimesstatic int 2851556Srgrimesput_raw_attr(struct rad_handle *h, int type, const void *value, size_t len) 286149933Sstefanf{ 28717987Speter if (len > 253) { 2881556Srgrimes generr(h, "Attribute too long"); 2891556Srgrimes return -1; 2901556Srgrimes } 2911556Srgrimes if (h->req_len + 2 + len > MSGSIZE) { 2921556Srgrimes generr(h, "Maximum message length exceeded"); 2931556Srgrimes return -1; 2941556Srgrimes } 2951556Srgrimes h->request[h->req_len++] = type; 2961556Srgrimes h->request[h->req_len++] = len + 2; 2971556Srgrimes memcpy(&h->request[h->req_len], value, len); 2981556Srgrimes h->req_len += len; 2991556Srgrimes return 0; 3001556Srgrimes} 3011556Srgrimes 3021556Srgrimesint 3031556Srgrimesrad_add_server(struct rad_handle *h, const char *host, int port, 3041556Srgrimes const char *secret, int timeout, int tries) 3051556Srgrimes{ 3061556Srgrimes struct rad_server *srvp; 3071556Srgrimes 3081556Srgrimes if (h->num_servers >= MAXSERVERS) { 3091556Srgrimes generr(h, "Too many RADIUS servers specified"); 310149933Sstefanf return -1; 3111556Srgrimes } 3121556Srgrimes srvp = &h->servers[h->num_servers]; 3131556Srgrimes 3141556Srgrimes memset(&srvp->addr, 0, sizeof srvp->addr); 3151556Srgrimes srvp->addr.sin_len = sizeof srvp->addr; 3161556Srgrimes srvp->addr.sin_family = AF_INET; 3171556Srgrimes if (!inet_aton(host, &srvp->addr.sin_addr)) { 3181556Srgrimes struct hostent *hent; 3191556Srgrimes 3201556Srgrimes if ((hent = gethostbyname(host)) == NULL) { 3211556Srgrimes generr(h, "%s: host not found", host); 322149933Sstefanf return -1; 32317987Speter } 3241556Srgrimes memcpy(&srvp->addr.sin_addr, hent->h_addr, 3251556Srgrimes sizeof srvp->addr.sin_addr); 3261556Srgrimes } 3271556Srgrimes if (port != 0) 3281556Srgrimes srvp->addr.sin_port = htons((u_short)port); 3291556Srgrimes else { 3301556Srgrimes struct servent *sent; 3311556Srgrimes 33217987Speter if (h->type == RADIUS_AUTH) 3331556Srgrimes srvp->addr.sin_port = 3341556Srgrimes (sent = getservbyname("radius", "udp")) != NULL ? 3351556Srgrimes sent->s_port : htons(RADIUS_PORT); 3361556Srgrimes else 3371556Srgrimes srvp->addr.sin_port = 3381556Srgrimes (sent = getservbyname("radacct", "udp")) != NULL ? 3391556Srgrimes sent->s_port : htons(RADACCT_PORT); 3401556Srgrimes } 3411556Srgrimes if ((srvp->secret = strdup(secret)) == NULL) { 3421556Srgrimes generr(h, "Out of memory"); 343149933Sstefanf return -1; 3441556Srgrimes } 3451556Srgrimes srvp->timeout = timeout; 3461556Srgrimes srvp->max_tries = tries; 3471556Srgrimes srvp->num_tries = 0; 3481556Srgrimes h->num_servers++; 3491556Srgrimes return 0; 3501556Srgrimes} 3511556Srgrimes 3521556Srgrimesvoid 3531556Srgrimesrad_close(struct rad_handle *h) 3541556Srgrimes{ 3551556Srgrimes int srv; 3561556Srgrimes 3571556Srgrimes if (h->fd != -1) 3581556Srgrimes close(h->fd); 3591556Srgrimes for (srv = 0; srv < h->num_servers; srv++) { 3601556Srgrimes memset(h->servers[srv].secret, 0, 3611556Srgrimes strlen(h->servers[srv].secret)); 36290111Simp free(h->servers[srv].secret); 36317987Speter } 3641556Srgrimes clear_password(h); 3651556Srgrimes free(h); 3661556Srgrimes} 3671556Srgrimes 3681556Srgrimesint 3691556Srgrimesrad_config(struct rad_handle *h, const char *path) 3701556Srgrimes{ 37117987Speter FILE *fp; 372172440Sstefanf char buf[MAXCONFLINE]; 3731556Srgrimes int linenum; 3741556Srgrimes int retval; 3751556Srgrimes 3761556Srgrimes if (path == NULL) 3771556Srgrimes path = PATH_RADIUS_CONF; 3781556Srgrimes if ((fp = fopen(path, "r")) == NULL) { 3791556Srgrimes generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 3801556Srgrimes return -1; 3811556Srgrimes } 3821556Srgrimes retval = 0; 3831556Srgrimes linenum = 0; 3841556Srgrimes while (fgets(buf, sizeof buf, fp) != NULL) { 3851556Srgrimes int len; 3861556Srgrimes char *fields[5]; 3871556Srgrimes int nfields; 3881556Srgrimes char msg[ERRSIZE]; 3891556Srgrimes char *type; 3901556Srgrimes char *host, *res; 3911556Srgrimes char *port_str; 3921556Srgrimes char *secret; 3931556Srgrimes char *timeout_str; 3941556Srgrimes char *maxtries_str; 39590111Simp char *end; 39617987Speter char *wanttype; 3971556Srgrimes unsigned long timeout; 3981556Srgrimes unsigned long maxtries; 3991556Srgrimes int port; 4001556Srgrimes int i; 4011556Srgrimes 4021556Srgrimes linenum++; 4031556Srgrimes len = strlen(buf); 4041556Srgrimes /* We know len > 0, else fgets would have returned NULL. */ 4051556Srgrimes if (buf[len - 1] != '\n') { 4061556Srgrimes if (len == sizeof buf - 1) 4071556Srgrimes generr(h, "%s:%d: line too long", path, 4081556Srgrimes linenum); 4091556Srgrimes else 41045916Scracauer generr(h, "%s:%d: missing newline", path, 4111556Srgrimes linenum); 4121556Srgrimes retval = -1; 4131556Srgrimes break; 4141556Srgrimes } 4151556Srgrimes buf[len - 1] = '\0'; 4161556Srgrimes 4171556Srgrimes /* Extract the fields from the line. */ 4181556Srgrimes nfields = split(buf, fields, 5, msg, sizeof msg); 4191556Srgrimes if (nfields == -1) { 4201556Srgrimes generr(h, "%s:%d: %s", path, linenum, msg); 4211556Srgrimes retval = -1; 42290111Simp break; 42317987Speter } 42425222Ssteve if (nfields == 0) 4251556Srgrimes continue; 4261556Srgrimes /* 42717987Speter * The first field should contain "auth" or "acct" for 42817987Speter * authentication or accounting, respectively. But older 42917987Speter * versions of the file didn't have that field. Default 43017987Speter * it to "auth" for backward compatibility. 43117987Speter */ 43217987Speter if (strcmp(fields[0], "auth") != 0 && 43366612Sbrian strcmp(fields[0], "acct") != 0) { 43417987Speter if (nfields >= 5) { 43596922Stjr generr(h, "%s:%d: invalid service type", path, 4361556Srgrimes linenum); 4371556Srgrimes retval = -1; 43817987Speter break; 43917987Speter } 44017987Speter nfields++; 44117987Speter for (i = nfields; --i > 0; ) 442181017Sstefanf fields[i] = fields[i - 1]; 44317987Speter fields[0] = "auth"; 44417987Speter } 44517987Speter if (nfields < 3) { 4461556Srgrimes generr(h, "%s:%d: missing shared secret", path, 4471556Srgrimes linenum); 4481556Srgrimes retval = -1; 4491556Srgrimes break; 4501556Srgrimes } 4511556Srgrimes type = fields[0]; 4521556Srgrimes host = fields[1]; 4531556Srgrimes secret = fields[2]; 4541556Srgrimes timeout_str = fields[3]; 4551556Srgrimes maxtries_str = fields[4]; 4561556Srgrimes 4571556Srgrimes /* Ignore the line if it is for the wrong service type. */ 4581556Srgrimes wanttype = h->type == RADIUS_AUTH ? "auth" : "acct"; 4591556Srgrimes if (strcmp(type, wanttype) != 0) 46090111Simp continue; 46117987Speter 4621556Srgrimes /* Parse and validate the fields. */ 4631556Srgrimes res = host; 4641556Srgrimes host = strsep(&res, ":"); 4651556Srgrimes port_str = strsep(&res, ":"); 4661556Srgrimes if (port_str != NULL) { 4671556Srgrimes port = strtoul(port_str, &end, 10); 468149802Sstefanf if (*end != '\0') { 4691556Srgrimes generr(h, "%s:%d: invalid port", path, 4701556Srgrimes linenum); 4711556Srgrimes retval = -1; 4721556Srgrimes break; 4731556Srgrimes } 4741556Srgrimes } else 4751556Srgrimes port = 0; 4761556Srgrimes if (timeout_str != NULL) { 4771556Srgrimes timeout = strtoul(timeout_str, &end, 10); 4781556Srgrimes if (*end != '\0') { 4791556Srgrimes generr(h, "%s:%d: invalid timeout", path, 4801556Srgrimes linenum); 48153891Scracauer retval = -1; 4821556Srgrimes break; 4831556Srgrimes } 4841556Srgrimes } else 4851556Srgrimes timeout = TIMEOUT; 4861556Srgrimes if (maxtries_str != NULL) { 487124780Sdes maxtries = strtoul(maxtries_str, &end, 10); 4881556Srgrimes if (*end != '\0') { 4891556Srgrimes generr(h, "%s:%d: invalid maxtries", path, 4901556Srgrimes linenum); 49153282Scracauer retval = -1; 49252900Scracauer break; 4931556Srgrimes } 494124780Sdes } else 4951556Srgrimes maxtries = MAXTRIES; 4961556Srgrimes 4971556Srgrimes if (rad_add_server(h, host, port, secret, timeout, maxtries) == 4981556Srgrimes -1) { 4991556Srgrimes strcpy(msg, h->errmsg); 5001556Srgrimes generr(h, "%s:%d: %s", path, linenum, msg); 5011556Srgrimes retval = -1; 5021556Srgrimes break; 5031556Srgrimes } 5041556Srgrimes } 5051556Srgrimes /* Clear out the buffer to wipe a possible copy of a shared secret */ 5061556Srgrimes memset(buf, 0, sizeof buf); 5071556Srgrimes fclose(fp); 50845916Scracauer return retval; 5091556Srgrimes} 5101556Srgrimes 5111556Srgrimes/* 5121556Srgrimes * rad_init_send_request() must have previously been called. 5131556Srgrimes * Returns: 5141556Srgrimes * 0 The application should select on *fd with a timeout of tv before 5151556Srgrimes * calling rad_continue_send_request again. 5161556Srgrimes * < 0 Failure 5171556Srgrimes * > 0 Success 5181556Srgrimes */ 5191556Srgrimesint 5201556Srgrimesrad_continue_send_request(struct rad_handle *h, int selected, int *fd, 5211556Srgrimes struct timeval *tv) 5221556Srgrimes{ 5231556Srgrimes int n; 52490111Simp 52517987Speter if (selected) { 5261556Srgrimes struct sockaddr_in from; 5271556Srgrimes int fromlen; 5281556Srgrimes 5291556Srgrimes fromlen = sizeof from; 5301556Srgrimes h->resp_len = recvfrom(h->fd, h->response, 5311556Srgrimes MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen); 5321556Srgrimes if (h->resp_len == -1) { 5331556Srgrimes generr(h, "recvfrom: %s", strerror(errno)); 5341556Srgrimes return -1; 53517987Speter } 53617987Speter if (is_valid_response(h, h->srv, &from)) { 5371556Srgrimes h->resp_len = h->response[POS_LENGTH] << 8 | 53817987Speter h->response[POS_LENGTH+1]; 5391556Srgrimes h->resp_pos = POS_ATTRS; 54017987Speter return h->response[POS_CODE]; 5411556Srgrimes } 5421556Srgrimes } 54317987Speter 5441556Srgrimes if (h->try == h->total_tries) { 54553891Scracauer generr(h, "No valid RADIUS responses received"); 5461556Srgrimes return -1; 5471556Srgrimes } 5481556Srgrimes 5491556Srgrimes /* 5501556Srgrimes * Scan round-robin to the next server that has some 551124780Sdes * tries left. There is guaranteed to be one, or we 5521556Srgrimes * would have exited this loop by now. 5531556Srgrimes */ 5541556Srgrimes while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) 5551556Srgrimes if (++h->srv >= h->num_servers) 5561556Srgrimes h->srv = 0; 5571556Srgrimes 5581556Srgrimes if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) 5591556Srgrimes /* Insert the request authenticator into the request */ 5601556Srgrimes insert_request_authenticator(h, h->srv); 5611556Srgrimes else 562109627Stjr /* Insert the scrambled password into the request */ 5631556Srgrimes if (h->pass_pos != 0) 5641556Srgrimes insert_scrambled_password(h, h->srv); 5651556Srgrimes 5661556Srgrimes insert_message_authenticator(h, h->srv); 5671556Srgrimes 5681556Srgrimes /* Send the request */ 5691556Srgrimes n = sendto(h->fd, h->request, h->req_len, 0, 5701556Srgrimes (const struct sockaddr *)&h->servers[h->srv].addr, 5711556Srgrimes sizeof h->servers[h->srv].addr); 5721556Srgrimes if (n != h->req_len) { 57390111Simp if (n == -1) 57417987Speter generr(h, "sendto: %s", strerror(errno)); 5751556Srgrimes else 5761556Srgrimes generr(h, "sendto: short write"); 5771556Srgrimes return -1; 5781556Srgrimes } 5791556Srgrimes 5801556Srgrimes h->try++; 5811556Srgrimes h->servers[h->srv].num_tries++; 5821556Srgrimes tv->tv_sec = h->servers[h->srv].timeout; 5831556Srgrimes tv->tv_usec = 0; 5841556Srgrimes *fd = h->fd; 5851556Srgrimes 5861556Srgrimes return 0; 5871556Srgrimes} 5881556Srgrimes 5891556Srgrimesint 5901556Srgrimesrad_create_request(struct rad_handle *h, int code) 5911556Srgrimes{ 5921556Srgrimes int i; 5931556Srgrimes 5941556Srgrimes h->request[POS_CODE] = code; 59545916Scracauer h->request[POS_IDENT] = ++h->ident; 59654884Scracauer /* Create a random authenticator */ 597193222Srse for (i = 0; i < LEN_AUTH; i += 2) { 59817987Speter long r; 59917987Speter r = random(); 60017987Speter h->request[POS_AUTH+i] = (u_char)r; 60117987Speter h->request[POS_AUTH+i+1] = (u_char)(r >> 8); 60217987Speter } 60354884Scracauer h->req_len = POS_ATTRS; 60417987Speter clear_password(h); 6051556Srgrimes h->request_created = 1; 6061556Srgrimes return 0; 607149802Sstefanf} 6081556Srgrimes 6091556Srgrimesstruct in_addr 6101556Srgrimesrad_cvt_addr(const void *data) 6111556Srgrimes{ 61254884Scracauer struct in_addr value; 61317987Speter 61417987Speter memcpy(&value.s_addr, data, sizeof value.s_addr); 6151556Srgrimes return value; 61617987Speter} 6171556Srgrimes 6181556Srgrimesu_int32_t 6191556Srgrimesrad_cvt_int(const void *data) 6201556Srgrimes{ 6211556Srgrimes u_int32_t value; 6221556Srgrimes 6231556Srgrimes memcpy(&value, data, sizeof value); 6241556Srgrimes return ntohl(value); 6251556Srgrimes} 6261556Srgrimes 6271556Srgrimeschar * 6281556Srgrimesrad_cvt_string(const void *data, size_t len) 6291556Srgrimes{ 6301556Srgrimes char *s; 6311556Srgrimes 6321556Srgrimes s = malloc(len + 1); 6331556Srgrimes if (s != NULL) { 6341556Srgrimes memcpy(s, data, len); 6351556Srgrimes s[len] = '\0'; 6361556Srgrimes } 6371556Srgrimes return s; 6381556Srgrimes} 6391556Srgrimes 6401556Srgrimes/* 6411556Srgrimes * Returns the attribute type. If none are left, returns 0. On failure, 6421556Srgrimes * returns -1. 6431556Srgrimes */ 6441556Srgrimesint 6451556Srgrimesrad_get_attr(struct rad_handle *h, const void **value, size_t *len) 6461556Srgrimes{ 6471556Srgrimes int type; 6481556Srgrimes 649159632Sstefanf if (h->resp_pos >= h->resp_len) 650159632Sstefanf return 0; 6511556Srgrimes if (h->resp_pos + 2 > h->resp_len) { 652159632Sstefanf generr(h, "Malformed attribute in response"); 653159632Sstefanf return -1; 6541556Srgrimes } 655159632Sstefanf type = h->response[h->resp_pos++]; 6561556Srgrimes *len = h->response[h->resp_pos++] - 2; 6571556Srgrimes if (h->resp_pos + (int)*len > h->resp_len) { 658159632Sstefanf generr(h, "Malformed attribute in response"); 659159632Sstefanf return -1; 6601556Srgrimes } 661159632Sstefanf *value = &h->response[h->resp_pos]; 6621556Srgrimes h->resp_pos += *len; 6631556Srgrimes return type; 6641556Srgrimes} 6651556Srgrimes 6661556Srgrimes/* 6671556Srgrimes * Returns -1 on error, 0 to indicate no event and >0 for success 6681556Srgrimes */ 669157601Sstefanfint 6701556Srgrimesrad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv) 6711556Srgrimes{ 672157601Sstefanf int srv; 6731556Srgrimes 67417987Speter /* Make sure we have a socket to use */ 67517987Speter if (h->fd == -1) { 67617987Speter struct sockaddr_in sin; 67717987Speter 67817987Speter if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 67917987Speter generr(h, "Cannot create socket: %s", strerror(errno)); 68017987Speter return -1; 68117987Speter } 68254884Scracauer memset(&sin, 0, sizeof sin); 68317987Speter sin.sin_len = sizeof sin; 684155301Sschweikh sin.sin_family = AF_INET; 68554884Scracauer sin.sin_addr.s_addr = INADDR_ANY; 68654884Scracauer sin.sin_port = htons(0); 68754884Scracauer if (bind(h->fd, (const struct sockaddr *)&sin, 688155301Sschweikh sizeof sin) == -1) { 68954884Scracauer generr(h, "bind: %s", strerror(errno)); 69054884Scracauer close(h->fd); 69154884Scracauer h->fd = -1; 692155301Sschweikh return -1; 69354884Scracauer } 69454884Scracauer } 69554884Scracauer 69654884Scracauer if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) { 69754884Scracauer /* Make sure no password given */ 69854884Scracauer if (h->pass_pos || h->chap_pass) { 69954884Scracauer generr(h, "User or Chap Password" 700123996Smaxim " in accounting request"); 70154884Scracauer return -1; 70254884Scracauer } 70354884Scracauer } else { 70454884Scracauer if (h->eap_msg == 0) { 70517987Speter /* Make sure the user gave us a password */ 70617987Speter if (h->pass_pos == 0 && !h->chap_pass) { 7071556Srgrimes generr(h, "No User or Chap Password" 70820425Ssteve " attributes given"); 7091556Srgrimes return -1; 7101556Srgrimes } 7111556Srgrimes if (h->pass_pos != 0 && h->chap_pass) { 7121556Srgrimes generr(h, "Both User and Chap Password" 7131556Srgrimes " attributes given"); 7141556Srgrimes return -1; 7151556Srgrimes } 7161556Srgrimes } 7171556Srgrimes } 718157601Sstefanf 719157601Sstefanf /* Fill in the length field in the message */ 7201556Srgrimes h->request[POS_LENGTH] = h->req_len >> 8; 72120425Ssteve h->request[POS_LENGTH+1] = h->req_len; 7221556Srgrimes 7231556Srgrimes /* 7241556Srgrimes * Count the total number of tries we will make, and zero the 7251556Srgrimes * counter for each server. 7261556Srgrimes */ 7271556Srgrimes h->total_tries = 0; 7281556Srgrimes for (srv = 0; srv < h->num_servers; srv++) { 7291556Srgrimes h->total_tries += h->servers[srv].max_tries; 7301556Srgrimes h->servers[srv].num_tries = 0; 7311556Srgrimes } 7321556Srgrimes if (h->total_tries == 0) { 73345221Scracauer generr(h, "No RADIUS servers specified"); 734194127Sjilles return -1; 73517987Speter } 7361556Srgrimes 73748896Ssheldonh h->try = h->srv = 0; 7381556Srgrimes 739100437Stjr return rad_continue_send_request(h, 0, fd, tv); 740100437Stjr} 741100437Stjr 7421556Srgrimes/* 7431556Srgrimes * Create and initialize a rad_handle structure, and return it to the 7441556Srgrimes * caller. Can fail only if the necessary memory cannot be allocated. 7451556Srgrimes * In that case, it returns NULL. 7461556Srgrimes */ 74753891Scracauerstruct rad_handle * 7481556Srgrimesrad_auth_open(void) 7491556Srgrimes{ 7501556Srgrimes struct rad_handle *h; 7511556Srgrimes 7521556Srgrimes h = (struct rad_handle *)malloc(sizeof(struct rad_handle)); 7531556Srgrimes if (h != NULL) { 7541556Srgrimes srandomdev(); 755124780Sdes h->fd = -1; 7561556Srgrimes h->num_servers = 0; 7571556Srgrimes h->ident = random(); 7581556Srgrimes h->errmsg[0] = '\0'; 7591556Srgrimes memset(h->pass, 0, sizeof h->pass); 7601556Srgrimes h->pass_len = 0; 7611556Srgrimes h->pass_pos = 0; 7621556Srgrimes h->chap_pass = 0; 7631556Srgrimes h->authentic_pos = 0; 7641556Srgrimes h->type = RADIUS_AUTH; 76520425Ssteve h->request_created = 0; 7661556Srgrimes h->eap_msg = 0; 76720425Ssteve } 7681556Srgrimes return h; 7691556Srgrimes} 7701556Srgrimes 77120425Sstevestruct rad_handle * 7721556Srgrimesrad_acct_open(void) 7731556Srgrimes{ 7741556Srgrimes struct rad_handle *h; 7751556Srgrimes 7761556Srgrimes h = rad_open(); 7771556Srgrimes if (h != NULL) 7781556Srgrimes h->type = RADIUS_ACCT; 7791556Srgrimes return h; 7801556Srgrimes} 7811556Srgrimes 7821556Srgrimesstruct rad_handle * 7831556Srgrimesrad_open(void) 7841556Srgrimes{ 7851556Srgrimes return rad_auth_open(); 7861556Srgrimes} 7871556Srgrimes 7881556Srgrimesint 7891556Srgrimesrad_put_addr(struct rad_handle *h, int type, struct in_addr addr) 7901556Srgrimes{ 7911556Srgrimes return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr); 7921556Srgrimes} 7931556Srgrimes 7941556Srgrimesint 7951556Srgrimesrad_put_attr(struct rad_handle *h, int type, const void *value, size_t len) 796185231Sstefanf{ 79735675Scracauer int result; 79835675Scracauer 79935675Scracauer if (!h->request_created) { 80035675Scracauer generr(h, "Please call rad_create_request()" 8011556Srgrimes " before putting attributes"); 8021556Srgrimes return -1; 8031556Srgrimes } 8041556Srgrimes 8051556Srgrimes if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) { 8061556Srgrimes if (type == RAD_EAP_MESSAGE) { 8071556Srgrimes generr(h, "EAP-Message attribute is not valid" 8081556Srgrimes " in accounting requests"); 8091556Srgrimes return -1; 8101556Srgrimes } 8111556Srgrimes } 8121556Srgrimes 8131556Srgrimes /* 8141556Srgrimes * When proxying EAP Messages, the Message Authenticator 8151556Srgrimes * MUST be present; see RFC 3579. 8161556Srgrimes */ 81720425Ssteve if (type == RAD_EAP_MESSAGE) { 8181556Srgrimes if (rad_put_message_authentic(h) == -1) 81920425Ssteve return -1; 8201556Srgrimes } 8211556Srgrimes 8221556Srgrimes if (type == RAD_USER_PASSWORD) { 8231556Srgrimes result = put_password_attr(h, type, value, len); 8241556Srgrimes } else if (type == RAD_MESSAGE_AUTHENTIC) { 8251556Srgrimes result = rad_put_message_authentic(h); 8261556Srgrimes } else { 8271556Srgrimes result = put_raw_attr(h, type, value, len); 8281556Srgrimes if (result == 0) { 8291556Srgrimes if (type == RAD_CHAP_PASSWORD) 8301556Srgrimes h->chap_pass = 1; 8311556Srgrimes else if (type == RAD_EAP_MESSAGE) 8321556Srgrimes h->eap_msg = 1; 8331556Srgrimes } 8341556Srgrimes } 8351556Srgrimes 8361556Srgrimes return result; 837157601Sstefanf} 838157601Sstefanf 839157601Sstefanfint 8401556Srgrimesrad_put_int(struct rad_handle *h, int type, u_int32_t value) 8411556Srgrimes{ 8421556Srgrimes u_int32_t nvalue; 843193169Sstefanf 8441556Srgrimes nvalue = htonl(value); 8451556Srgrimes return rad_put_attr(h, type, &nvalue, sizeof nvalue); 8461556Srgrimes} 84760592Scracauer 8481556Srgrimesint 8491556Srgrimesrad_put_string(struct rad_handle *h, int type, const char *str) 8501556Srgrimes{ 8511556Srgrimes return rad_put_attr(h, type, str, strlen(str)); 8521556Srgrimes} 8531556Srgrimes 8541556Srgrimesint 8551556Srgrimesrad_put_message_authentic(struct rad_handle *h) 8561556Srgrimes{ 8571556Srgrimes#ifdef WITH_SSL 8581556Srgrimes u_char md_zero[MD5_DIGEST_LENGTH]; 85920425Ssteve 860157601Sstefanf if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) { 8611556Srgrimes generr(h, "Message-Authenticator is not valid" 8621556Srgrimes " in accounting requests"); 8631556Srgrimes return -1; 8641556Srgrimes } 8651556Srgrimes 8661556Srgrimes if (h->authentic_pos == 0) { 8671556Srgrimes h->authentic_pos = h->req_len; 8681556Srgrimes memset(md_zero, 0, sizeof(md_zero)); 8691556Srgrimes return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero, 8701556Srgrimes sizeof(md_zero))); 8711556Srgrimes } 87220425Ssteve return 0; 8731556Srgrimes#else 87420425Ssteve generr(h, "Message Authenticator not supported," 8751556Srgrimes " please recompile libradius with SSL support"); 8761556Srgrimes return -1; 8771556Srgrimes#endif 8781556Srgrimes} 8791556Srgrimes 88017987Speter/* 8811556Srgrimes * Returns the response type code on success, or -1 on failure. 8821556Srgrimes */ 8831556Srgrimesint 8841556Srgrimesrad_send_request(struct rad_handle *h) 8851556Srgrimes{ 8861556Srgrimes struct timeval timelimit; 8871556Srgrimes struct timeval tv; 88845916Scracauer int fd; 8891556Srgrimes int n; 89045916Scracauer 89145266Scracauer n = rad_init_send_request(h, &fd, &tv); 89245266Scracauer 89345266Scracauer if (n != 0) 8941556Srgrimes return n; 8951556Srgrimes 8961556Srgrimes gettimeofday(&timelimit, NULL); 8971556Srgrimes timeradd(&tv, &timelimit, &timelimit); 8981556Srgrimes 8991556Srgrimes for ( ; ; ) { 9001556Srgrimes fd_set readfds; 9011556Srgrimes 9021556Srgrimes FD_ZERO(&readfds); 90354884Scracauer FD_SET(fd, &readfds); 90454884Scracauer 9051556Srgrimes n = select(fd + 1, &readfds, NULL, NULL, &tv); 9061556Srgrimes 9071556Srgrimes if (n == -1) { 9081556Srgrimes generr(h, "select: %s", strerror(errno)); 9091556Srgrimes return -1; 9101556Srgrimes } 9111556Srgrimes 9121556Srgrimes if (!FD_ISSET(fd, &readfds)) { 9131556Srgrimes /* Compute a new timeout */ 9141556Srgrimes gettimeofday(&tv, NULL); 9151556Srgrimes timersub(&timelimit, &tv, &tv); 9161556Srgrimes if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0)) 9171556Srgrimes /* Continue the select */ 91890111Simp continue; 91917987Speter } 9201556Srgrimes 9211556Srgrimes n = rad_continue_send_request(h, n, &fd, &tv); 922159633Sstefanf 92317987Speter if (n != 0) 92417987Speter return n; 92517987Speter 9261556Srgrimes gettimeofday(&timelimit, NULL); 9271556Srgrimes timeradd(&tv, &timelimit, &timelimit); 9281556Srgrimes } 9291556Srgrimes} 9301556Srgrimes 9311556Srgrimesconst char * 9321556Srgrimesrad_strerror(struct rad_handle *h) 9331556Srgrimes{ 9341556Srgrimes return h->errmsg; 9351556Srgrimes} 936157601Sstefanf 9371556Srgrimes/* 9381556Srgrimes * Destructively split a string into fields separated by white space. 93917987Speter * `#' at the beginning of a field begins a comment that extends to the 94090111Simp * end of the string. Fields may be quoted with `"'. Inside quoted 94117987Speter * strings, the backslash escapes `\"' and `\\' are honored. 94220425Ssteve * 94317987Speter * Pointers to up to the first maxfields fields are stored in the fields 94420425Ssteve * array. Missing fields get NULL pointers. 94517987Speter * 9461556Srgrimes * The return value is the actual number of fields parsed, and is always 9471556Srgrimes * <= maxfields. 9481556Srgrimes * 9491556Srgrimes * On a syntax error, places a message in the msg string, and returns -1. 9501556Srgrimes */ 9511556Srgrimesstatic int 9521556Srgrimessplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 9531556Srgrimes{ 9541556Srgrimes char *p; 9551556Srgrimes int i; 9561556Srgrimes static const char ws[] = " \t"; 9571556Srgrimes 9581556Srgrimes for (i = 0; i < maxfields; i++) 9591556Srgrimes fields[i] = NULL; 9601556Srgrimes p = str; 96117987Speter i = 0; 96290111Simp while (*p != '\0') { 96317987Speter p += strspn(p, ws); 96420425Ssteve if (*p == '#' || *p == '\0') 9651556Srgrimes break; 9661556Srgrimes if (i >= maxfields) { 9671556Srgrimes snprintf(msg, msglen, "line has too many fields"); 9681556Srgrimes return -1; 9691556Srgrimes } 9701556Srgrimes if (*p == '"') { 9711556Srgrimes char *dst; 9721556Srgrimes 9731556Srgrimes dst = ++p; 9741556Srgrimes fields[i] = dst; 975100437Stjr while (*p != '"') { 976100437Stjr if (*p == '\\') { 977100437Stjr p++; 978100437Stjr if (*p != '"' && *p != '\\' && 979100437Stjr *p != '\0') { 980100437Stjr snprintf(msg, msglen, 981100437Stjr "invalid `\\' escape"); 982100437Stjr return -1; 983100437Stjr } 984100437Stjr } 985100437Stjr if (*p == '\0') { 986151810Sstefanf snprintf(msg, msglen, 9871556Srgrimes "unterminated quoted string"); 988100437Stjr return -1; 989100437Stjr } 990100437Stjr *dst++ = *p++; 991100437Stjr } 992100437Stjr *dst = '\0'; 993100663Stjr p++; 994151810Sstefanf if (*fields[i] == '\0') { 995100437Stjr snprintf(msg, msglen, 996100437Stjr "empty quoted string not permitted"); 997100437Stjr return -1; 998100437Stjr } 999151810Sstefanf if (*p != '\0' && strspn(p, ws) == 0) { 1000151810Sstefanf snprintf(msg, msglen, "quoted string not" 1001151810Sstefanf " followed by white space"); 1002151810Sstefanf return -1; 1003151810Sstefanf } 1004151810Sstefanf } else { 1005100437Stjr fields[i] = p; 1006100437Stjr p += strcspn(p, ws); 1007100437Stjr if (*p != '\0') 1008100437Stjr *p++ = '\0'; 1009100437Stjr } 1010100437Stjr i++; 1011100437Stjr } 1012100437Stjr return i; 1013151810Sstefanf} 1014151810Sstefanf 1015151810Sstefanfint 1016151810Sstefanfrad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len) 1017151810Sstefanf{ 1018100437Stjr struct vendor_attribute *attr; 1019100437Stjr 1020100437Stjr attr = (struct vendor_attribute *)*data; 1021100437Stjr *vendor = ntohl(attr->vendor_value); 1022100437Stjr *data = attr->attrib_data; 1023100437Stjr *len = attr->attrib_len - 2; 1024100437Stjr 1025100437Stjr return (attr->attrib_type); 1026100437Stjr} 1027100437Stjr 1028100437Stjrint 1029100437Stjrrad_put_vendor_addr(struct rad_handle *h, int vendor, int type, 1030100437Stjr struct in_addr addr) 1031100437Stjr{ 1032100437Stjr return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr, 1033100437Stjr sizeof addr.s_addr)); 1034100437Stjr} 1035100437Stjr 1036100437Stjrint 10371556Srgrimesrad_put_vendor_attr(struct rad_handle *h, int vendor, int type, 10381556Srgrimes const void *value, size_t len) 10391556Srgrimes{ 10401556Srgrimes struct vendor_attribute *attr; 104117987Speter int res; 104290111Simp 104317987Speter if (!h->request_created) { 104420425Ssteve generr(h, "Please call rad_create_request()" 10451556Srgrimes " before putting attributes"); 10461556Srgrimes return -1; 10471556Srgrimes } 10481556Srgrimes 104920425Ssteve if ((attr = malloc(len + 6)) == NULL) { 105020425Ssteve generr(h, "malloc failure (%d bytes)", len + 6); 105120425Ssteve return -1; 105220425Ssteve } 10531556Srgrimes 10541556Srgrimes attr->vendor_value = htonl(vendor); 10551556Srgrimes attr->attrib_type = type; 10561556Srgrimes attr->attrib_len = len + 2; 10571556Srgrimes memcpy(attr->attrib_data, value, len); 105817987Speter 105990111Simp res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6); 106017987Speter free(attr); 106117987Speter if (res == 0 && vendor == RAD_VENDOR_MICROSOFT 106217987Speter && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE 106317987Speter || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) { 106417987Speter h->chap_pass = 1; 106517987Speter } 106690111Simp return (res); 106717987Speter} 10681556Srgrimes 10691556Srgrimesint 10701556Srgrimesrad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i) 10711556Srgrimes{ 107217987Speter u_int32_t value; 107390111Simp 107417987Speter value = htonl(i); 10751556Srgrimes return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value)); 107617987Speter} 107717987Speter 10781556Srgrimesint 10791556Srgrimesrad_put_vendor_string(struct rad_handle *h, int vendor, int type, 10801556Srgrimes const char *str) 108117987Speter{ 108217987Speter return (rad_put_vendor_attr(h, vendor, type, str, strlen(str))); 10831556Srgrimes} 10841556Srgrimes 10851556Srgrimesssize_t 10861556Srgrimesrad_request_authenticator(struct rad_handle *h, char *buf, size_t len) 10871556Srgrimes{ 1088153091Sstefanf if (len < LEN_AUTH) 1089153091Sstefanf return (-1); 1090153091Sstefanf memcpy(buf, h->request + POS_AUTH, LEN_AUTH); 1091153091Sstefanf if (len > LEN_AUTH) 1092153091Sstefanf buf[LEN_AUTH] = '\0'; 1093153091Sstefanf return (LEN_AUTH); 1094153091Sstefanf} 1095153091Sstefanf 1096153091Sstefanfu_char * 1097153091Sstefanfrad_demangle(struct rad_handle *h, const void *mangled, size_t mlen) 1098153091Sstefanf{ 1099153091Sstefanf char R[LEN_AUTH]; 1100153091Sstefanf const char *S; 1101153091Sstefanf int i, Ppos; 1102153091Sstefanf MD5_CTX Context; 1103153091Sstefanf u_char b[MD5_DIGEST_LENGTH], *C, *demangled; 1104153091Sstefanf 1105153091Sstefanf if ((mlen % 16 != 0) || mlen > 128) { 1106153091Sstefanf generr(h, "Cannot interpret mangled data of length %lu", 1107153091Sstefanf (u_long)mlen); 1108153091Sstefanf return NULL; 1109153091Sstefanf } 1110153091Sstefanf 1111153091Sstefanf C = (u_char *)mangled; 1112153091Sstefanf 1113 /* We need the shared secret as Salt */ 1114 S = rad_server_secret(h); 1115 1116 /* We need the request authenticator */ 1117 if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) { 1118 generr(h, "Cannot obtain the RADIUS request authenticator"); 1119 return NULL; 1120 } 1121 1122 demangled = malloc(mlen); 1123 if (!demangled) 1124 return NULL; 1125 1126 MD5Init(&Context); 1127 MD5Update(&Context, S, strlen(S)); 1128 MD5Update(&Context, R, LEN_AUTH); 1129 MD5Final(b, &Context); 1130 Ppos = 0; 1131 while (mlen) { 1132 1133 mlen -= 16; 1134 for (i = 0; i < 16; i++) 1135 demangled[Ppos++] = C[i] ^ b[i]; 1136 1137 if (mlen) { 1138 MD5Init(&Context); 1139 MD5Update(&Context, S, strlen(S)); 1140 MD5Update(&Context, C, 16); 1141 MD5Final(b, &Context); 1142 } 1143 1144 C += 16; 1145 } 1146 1147 return demangled; 1148} 1149 1150u_char * 1151rad_demangle_mppe_key(struct rad_handle *h, const void *mangled, 1152 size_t mlen, size_t *len) 1153{ 1154 char R[LEN_AUTH]; /* variable names as per rfc2548 */ 1155 const char *S; 1156 u_char b[MD5_DIGEST_LENGTH], *demangled; 1157 const u_char *A, *C; 1158 MD5_CTX Context; 1159 int Slen, i, Clen, Ppos; 1160 u_char *P; 1161 1162 if (mlen % 16 != SALT_LEN) { 1163 generr(h, "Cannot interpret mangled data of length %lu", 1164 (u_long)mlen); 1165 return NULL; 1166 } 1167 1168 /* We need the RADIUS Request-Authenticator */ 1169 if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) { 1170 generr(h, "Cannot obtain the RADIUS request authenticator"); 1171 return NULL; 1172 } 1173 1174 A = (const u_char *)mangled; /* Salt comes first */ 1175 C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */ 1176 Clen = mlen - SALT_LEN; 1177 S = rad_server_secret(h); /* We need the RADIUS secret */ 1178 Slen = strlen(S); 1179 P = alloca(Clen); /* We derive our plaintext */ 1180 1181 MD5Init(&Context); 1182 MD5Update(&Context, S, Slen); 1183 MD5Update(&Context, R, LEN_AUTH); 1184 MD5Update(&Context, A, SALT_LEN); 1185 MD5Final(b, &Context); 1186 Ppos = 0; 1187 1188 while (Clen) { 1189 Clen -= 16; 1190 1191 for (i = 0; i < 16; i++) 1192 P[Ppos++] = C[i] ^ b[i]; 1193 1194 if (Clen) { 1195 MD5Init(&Context); 1196 MD5Update(&Context, S, Slen); 1197 MD5Update(&Context, C, 16); 1198 MD5Final(b, &Context); 1199 } 1200 1201 C += 16; 1202 } 1203 1204 /* 1205 * The resulting plain text consists of a one-byte length, the text and 1206 * maybe some padding. 1207 */ 1208 *len = *P; 1209 if (*len > mlen - 1) { 1210 generr(h, "Mangled data seems to be garbage %d %d", 1211 *len, mlen-1); 1212 return NULL; 1213 } 1214 1215 if (*len > MPPE_KEY_LEN * 2) { 1216 generr(h, "Key to long (%d) for me max. %d", 1217 *len, MPPE_KEY_LEN * 2); 1218 return NULL; 1219 } 1220 demangled = malloc(*len); 1221 if (!demangled) 1222 return NULL; 1223 1224 memcpy(demangled, P + 1, *len); 1225 return demangled; 1226} 1227 1228const char * 1229rad_server_secret(struct rad_handle *h) 1230{ 1231 return (h->servers[h->srv].secret); 1232} 1233