/**
* rlm_jradius - The FreeRADIUS JRadius Server Module
* Copyright (C) 2004-2006 PicoPoint, B.V.
- * Copyright (c) 2007 David Bird
+ * Copyright (c) 2007-2008 David Bird
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/conffile.h>
-#define STR_VALUE(p) ((p)->data.strvalue)
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
static const int JRADIUS_pre_proxy = 6;
static const int JRADIUS_post_proxy = 7;
static const int JRADIUS_post_auth = 8;
+#ifdef WITH_COA
+static const int JRADIUS_recv_coa = 9;
+static const int JRADIUS_send_coa = 10;
+#endif
#define LOG_PREFIX "rlm_jradius: "
#define MAX_HOSTS 4
uint32_t ipaddr [MAX_HOSTS];
int port [MAX_HOSTS];
int timeout;
+ int read_timeout;
+ int write_timeout;
int allow_codechange;
int allow_idchange;
int onfail;
{ "secondary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[1]), NULL, NULL},
{ "tertiary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[2]), NULL, NULL},
{ "timeout", PW_TYPE_INTEGER, offsetof(JRADIUS, timeout), NULL, "5"},
+ { "read_timeout", PW_TYPE_INTEGER, offsetof(JRADIUS, read_timeout), NULL, "90"},
+ { "write_timeout",PW_TYPE_INTEGER, offsetof(JRADIUS, write_timeout),NULL, "90"},
{ "onfail", PW_TYPE_STRING_PTR, offsetof(JRADIUS, onfail_s), NULL, NULL},
{ "keepalive", PW_TYPE_BOOLEAN, offsetof(JRADIUS, keepalive), NULL, "yes"},
{ "connections", PW_TYPE_INTEGER, offsetof(JRADIUS, jrsock_cnt), NULL, "8"},
{ NULL, -1, 0, NULL, NULL }
};
+static int
+sock_read(JRADIUS * inst, JRSOCK *jrsock, uint8_t *b, size_t blen) {
+ int fd = jrsock->con.sock;
+ int timeout = inst->read_timeout;
+ struct timeval tv;
+ ssize_t c;
+ size_t recd = 0;
+ fd_set fds;
+
+ while (recd < blen) {
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (select(fd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &tv) == -1)
+ return -1;
+
+ if (FD_ISSET(fd, &fds))
+#ifdef WIN32
+ c = recv(fd, b + recd, blen-recd, 0);
+#else
+ c = read(fd, b + recd, blen-recd);
+#endif
+ else
+ return -1;
+
+ if (c <= 0) return -1;
+ recd += c;
+ }
+
+ if (recd < blen) return -1;
+ return recd;
+}
+
+static int
+sock_write(JRADIUS * inst, JRSOCK *jrsock, char *b, size_t blen) {
+ int fd = jrsock->con.sock;
+ int timeout = inst->write_timeout;
+ struct timeval tv;
+ ssize_t c;
+ size_t sent = 0;
+ fd_set fds;
+
+ while (sent < blen) {
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (select(fd + 1, (fd_set *) 0, &fds, (fd_set *) 0, &tv) == -1)
+ return -1;
+
+ if (FD_ISSET(fd, &fds))
+#ifdef WIN32
+ c = send(fd, b+sent, blen-sent, 0);
+#else
+ c = write(fd, b+sent, blen-sent);
+#endif
+ else
+ return -1;
+
+ if (c <= 0) return -1;
+ sent += c;
+ }
+
+ if (sent != blen) return -1;
+ return sent;
+}
+
static int connect_socket(JRSOCK *jrsock, JRADIUS *inst)
{
struct sockaddr_in local_addr, serv_addr;
/*
* If we previously set the socket to non-blocking, restore blocking
- */
if (inst->timeout > 0 &&
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) == -1) {
radlog(L_ERR, LOG_PREFIX "could not set blocking on socket");
goto failed;
}
+ */
jrsock->state = is_connected;
jrsock->con.sock = sock;
return 0;
}
-static void close_socket(JRADIUS * inst, JRSOCK *jrsock) {
+static void close_socket(UNUSED JRADIUS * inst, JRSOCK *jrsock)
+{
radlog(L_INFO, "rlm_jradius: Closing JRadius connection %d", jrsock->id);
if (jrsock->con.sock > 0) {
shutdown(jrsock->con.sock, 2);
}
}
-static int socket_send(JRSOCK *jrsock, unsigned char *b, unsigned int blen) {
- return send(jrsock->con.sock, b, blen, 0);
-}
-
static int init_socketpool(JRADIUS * inst)
{
int i, rcode;
return NULL;
}
-static int release_socket(JRADIUS * inst, JRSOCK * jrsock)
+static int release_socket(UNUSED JRADIUS * inst, JRSOCK * jrsock)
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_unlock(&jrsock->mutex);
if (sscanf(b, "%[^:]:%d", host, &port) == 2) { h = host; p = port; }
if (h) {
- lrad_ipaddr_t ipaddr;
+ fr_ipaddr_t ipaddr;
if (ip_hton(h, AF_INET, &ipaddr) < 0) {
radlog(L_ERR, "Can't find IP address for host %s", h);
continue;
/*
* Pack an integer into a byte array buffer (adjusting for byte-order)
*/
-static int pack_int(byte_array * ba, unsigned int i)
+static int pack_uint32(byte_array * ba, uint32_t i)
{
if (ba->left < 4) return -1;
}
/*
+ * Pack a short into a byte array buffer (adjusting for byte-order)
+ */
+static int pack_uint16(byte_array * ba, uint16_t i)
+{
+ if (ba->left < 2) return -1;
+
+ i = htons(i);
+
+ memcpy((void *)(ba->b + ba->pos), (void *)&i, 2);
+ ba->pos += 2;
+ ba->left -= 2;
+
+ return 0;
+}
+
+/*
+ * Pack a byte into a byte array buffer
+ */
+static int pack_uint8(byte_array * ba, uint8_t i)
+{
+ if (ba->left < 1) return -1;
+
+ memcpy((void *)(ba->b + ba->pos), (void *)&i, 1);
+ ba->pos += 1;
+ ba->left -= 1;
+
+ return 0;
+}
+
+/*
* Pack one byte array buffer into another byte array buffer
*/
static int pack_array(byte_array * ba, byte_array * a)
*/
static int pack_vps(byte_array * ba, VALUE_PAIR * vps)
{
+ uint32_t i;
VALUE_PAIR * vp;
for (vp = vps; vp != NULL; vp = vp->next) {
- radlog(L_DBG, LOG_PREFIX "packing attribute %s (type: %d; len: %d)",
- vp->name, vp->attribute, vp->length);
+ radlog(L_DBG, LOG_PREFIX "packing attribute %s (type: %d; len: %d)", vp->name, vp->attribute, vp->length);
- if (pack_int(ba, vp->attribute) == -1) return -1;
- if (pack_int(ba, vp->length) == -1) return -1;
- if (pack_int(ba, vp->operator) == -1) return -1;
+ i = vp->attribute; /* element is int, not uint32_t */
+ if (pack_uint32(ba, i) == -1) return -1;
+ i = vp->length;
+ if (pack_uint32(ba, i) == -1) return -1;
+ i = vp->operator;
+ if (pack_uint32(ba, i) == -1) return -1;
switch (vp->type) {
+ case PW_TYPE_BYTE:
+ if (pack_uint8(ba, vp->lvalue) == -1) return -1;
+ break;
+ case PW_TYPE_SHORT:
+ if (pack_uint16(ba, vp->lvalue) == -1) return -1;
+ break;
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
- if (pack_int(ba, vp->lvalue) == -1) return -1;
+ if (pack_uint32(ba, vp->lvalue) == -1) return -1;
break;
case PW_TYPE_IPADDR:
- if (pack_bytes(ba, (void *)&vp->lvalue, vp->length) == -1) return -1;
+ if (pack_bytes(ba, (void *)&vp->vp_ipaddr, vp->length) == -1) return -1;
break;
default:
- if (pack_bytes(ba, (void *)STR_VALUE(vp), vp->length) == -1) return -1;
+ if (pack_bytes(ba, (void *)vp->vp_octets, vp->length) == -1) return -1;
break;
}
}
init_byte_array(&pba, buff, sizeof(buff));
- if (pack_vps (&pba, p->vps) == -1) return -1;
+ if (pack_vps(&pba, p->vps) == -1) return -1;
radlog(L_DBG, LOG_PREFIX "packing packet with code: %d (attr length: %d)", p->code, pba.pos);
- if (pack_byte (ba, p->code) == -1) return -1;
- if (pack_byte (ba, p->id) == -1) return -1;
- if (pack_int (ba, pba.pos) == -1) return -1;
+#ifdef EXTENDED_FMT
+ if (pack_uint32(ba, p->code) == -1) return -1;
+ if (pack_uint32(ba, p->id) == -1) return -1;
+#else
+ if (pack_byte(ba, p->code) == -1) return -1;
+ if (pack_byte(ba, p->id) == -1) return -1;
+#endif
+ if (pack_uint32(ba, pba.pos) == -1) return -1;
if (pba.pos == 0) return 0;
- if (pack_array (ba, &pba) == -1) return -1;
+ if (pack_array(ba, &pba) == -1) return -1;
return 0;
}
init_byte_array(&pba, buff, sizeof(buff));
- if (pack_vps (&pba, r->config_items) == -1) return -1;
- if (pack_int (ba, pba.pos) == -1) return -1;
+ if (pack_vps(&pba, r->config_items) == -1) return -1;
+ if (pack_uint32(ba, pba.pos) == -1) return -1;
if (pba.pos == 0) return 0;
- if (pack_array (ba, &pba) == -1) return -1;
+ if (pack_array(ba, &pba) == -1) return -1;
return 0;
}
-/*
- * Read a single byte from socket
- */
-static int read_byte(JRSOCK *jrsock, unsigned char *b)
+static uint32_t unpack_uint32(unsigned char *c)
{
- return (read(jrsock->con.sock, b, 1) == 1) ? 0 : -1;
+ uint32_t ii;
+ memcpy((void *)&ii, c, 4);
+ return ntohl(ii);
}
-static void unpack_int(unsigned char *c, unsigned int *i)
+static uint16_t unpack_uint16(unsigned char *c)
{
- unsigned int ii;
- memcpy((void *)&ii, c, 4);
- *i = ntohl(ii);
+ uint16_t ii;
+ memcpy((void *)&ii, c, 2);
+ return ntohs(ii);
+}
+
+static uint8_t unpack_uint8(unsigned char *c)
+{
+ uint8_t ii;
+ memcpy((void *)&ii, c, 1);
+ return ii;
+}
+
+
+
+/*
+ * Read a single byte from socket
+ */
+static int read_byte(JRADIUS *inst, JRSOCK *jrsock, uint8_t *b)
+{
+ return (sock_read(inst, jrsock, b, 1) == 1) ? 0 : -1;
}
/*
* Read an integer from the socket (adjusting for byte-order)
*/
-static int read_int(JRSOCK *jrsock, unsigned int *i)
+static int read_uint32(JRADIUS *inst, JRSOCK *jrsock, uint32_t *i)
{
- unsigned char c[4];
+ uint32_t ii;
- if (read(jrsock->con.sock, c, 4) != 4) return -1;
- unpack_int(c, i);
+ if (sock_read(inst, jrsock, (uint8_t *)&ii, 4) != 4) return -1;
+ *i = ntohl(ii);
return 0;
}
/*
* Read a value-pair list from the socket
*/
-static int read_vps(JRSOCK *jrsock, VALUE_PAIR **pl, int plen)
+static int read_vps(JRADIUS *inst, JRSOCK *jrsock, VALUE_PAIR **pl, int plen)
{
VALUE_PAIR *vp;
unsigned char buff[MESSAGE_LEN];
- unsigned int alen, atype, aop;
+ uint32_t alen, atype, aop;
int rlen = 0;
while (rlen < plen) {
- if (read_int (jrsock, &atype) == -1) return -1; rlen += 4;
- if (read_int (jrsock, &alen) == -1) return -1; rlen += 4;
- if (read_int (jrsock, &aop) == -1) return -1; rlen += 4;
+ if (read_uint32(inst, jrsock, &atype) == -1) return -1; rlen += 4;
+ if (read_uint32(inst, jrsock, &alen) == -1) return -1; rlen += 4;
+ if (read_uint32(inst, jrsock, &aop) == -1) return -1; rlen += 4;
radlog(L_DBG, LOG_PREFIX "reading attribute: type=%d; len=%d", atype, alen);
return -1;
}
- if (read(jrsock->con.sock, buff, alen) != (int)alen) return -1; rlen += alen;
+ if (sock_read(inst, jrsock, buff, alen) != (int)alen) return -1; rlen += alen;
buff[alen]=0;
/*
* Create new attribute
*/
- vp = paircreate(atype, -1);
+ vp = paircreate(atype, 0, -1);
vp->operator = aop;
if (vp->type == -1) {
}
/*
+ * WiMAX combo-ip address
+ * paircreate() cannot recognize the real type of the address.
+ * ..ugly code...
+ */
+ if (vp->type==PW_TYPE_COMBO_IP) {
+ switch (alen) {
+ case 4:
+ vp->type = PW_TYPE_IPADDR;
+ break;
+ case 16:
+ vp->type = PW_TYPE_IPV6ADDR;
+ break;
+ }
+ }
+
+ /*
* Fill in the attribute value based on type
*/
switch (vp->type) {
+ case PW_TYPE_BYTE:
+ vp->lvalue = unpack_uint8(buff);
+ vp->length = 1;
+ break;
+
+ case PW_TYPE_SHORT:
+ vp->lvalue = unpack_uint16(buff);
+ vp->length = 2;
+ break;
+
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
- {
- unpack_int(buff, &vp->lvalue);
- vp->length = 4;
- }
+ vp->lvalue = unpack_uint32(buff);
+ vp->length = 4;
break;
case PW_TYPE_IPADDR:
- memcpy((void *)&vp->lvalue, buff, 4);
+ memcpy((void *)&vp->vp_ipaddr, buff, 4);
vp->length = 4;
break;
default:
- if (alen < sizeof(STR_VALUE(vp))) {
- memcpy((void *)STR_VALUE(vp), buff, alen);
- vp->length = alen;
- }
+ if (alen >= sizeof(vp->vp_octets)) alen = sizeof(vp->vp_octets) - 1;
+ memcpy((void *)vp->vp_octets, buff, alen);
+ vp->length = alen;
break;
}
*/
static int read_packet(JRADIUS * inst, JRSOCK *jrsock, RADIUS_PACKET *p)
{
- unsigned char code;
- unsigned char id;
- unsigned int plen;
+ uint32_t code;
+ uint32_t id;
+ uint32_t plen;
+
+#ifdef EXTENDED_FMT
+ if (read_uint32(inst, jrsock, &code) == -1) return -1;
+ if (read_uint32(inst, jrsock, &id) == -1) return -1;
+#else
+ { uint8_t c = 0;
+ if (read_byte(inst, jrsock, &c) == -1) return -1;
+ code = c;
+ if (read_byte(inst, jrsock, &c) == -1) return -1;
+ id = c; }
+#endif
- if (read_byte (jrsock, &code) == -1) return -1;
- if (read_byte (jrsock, &id) == -1) return -1;
- if (read_int (jrsock, &plen) == -1) return -1;
+ if (read_uint32(inst, jrsock, &plen) == -1) return -1;
radlog(L_DBG, LOG_PREFIX "reading packet: code=%d len=%d", (int)code, plen);
}
if (inst->allow_idchange)
- if (id != p->id) {
+ if ((int)id != p->id) {
radlog(L_INFO, LOG_PREFIX "changing packet id from %d to %d", p->id, id);
- p->id = id;
+ p->id = (int)id;
}
/*
if (plen == 0) return 0;
- if (read_vps (jrsock, &p->vps, plen) == -1) return -1;
+ if (read_vps(inst, jrsock, &p->vps, plen) == -1) return -1;
return 0;
}
-static int read_request(JRSOCK *jrsock, REQUEST *p)
+static int read_request(JRADIUS *inst, JRSOCK *jrsock, REQUEST *p)
{
unsigned int plen;
- if (read_int(jrsock, &plen) == -1) return -1;
+ if (read_uint32(inst, jrsock, &plen) == -1) return -1;
radlog(L_DBG, LOG_PREFIX "reading request: config_item: len=%d", plen);
if (plen == 0) return 0;
- if (read_vps(jrsock, &p->config_items, plen) == -1) return -1;
+ if (read_vps(inst, jrsock, &p->config_items, plen) == -1) return -1;
return 0;
}
static int rlm_jradius_call(char func, void *instance, REQUEST *req, int isproxy)
{
JRADIUS * inst = instance;
- RADIUS_PACKET * request = isproxy ? req->proxy : req->packet;
- RADIUS_PACKET * reply = isproxy ? req->proxy_reply : req->reply;
+ RADIUS_PACKET * request = req->packet;
+ RADIUS_PACKET * reply = req->reply;
JRSOCK * jrsock = 0;
JRSOCK sjrsock;
#define W_ERR(s) { err=s; goto packerror; }
#define R_ERR(s) { err=s; goto parseerror; }
+#ifdef WITH_PROXY
+ if (isproxy) {
+ request = req->proxy;
+ reply = req->proxy_reply;
+ }
+#endif
+
if (inst->keepalive) {
jrsock = get_socket(inst);
if (!jrsock) return exitstatus;
/*
* Create byte array to send to jradius
*/
- if ((rc = pack_int (&ba, nlen)) == -1) W_ERR("pack_int(nlen)");
+ if ((rc = pack_uint32 (&ba, nlen)) == -1) W_ERR("pack_uint32(nlen)");
if ((rc = pack_bytes (&ba, (void *)n, nlen)) == -1) W_ERR("pack_bytes(name)");
if ((rc = pack_byte (&ba, func)) == -1) W_ERR("pack_byte(fun)");
if ((rc = pack_byte (&ba, pcount)) == -1) W_ERR("pack_byte(pcnt)");
}
}
radlog(L_DBG, LOG_PREFIX "sending %d bytes to socket %d", ba.pos, jrsock->id);
- if (socket_send(jrsock, ba.b, ba.pos) != (int)ba.pos ||
- (rc = read_byte (jrsock, &rcode)) == -1) {
+ if (sock_write(inst, jrsock, ba.b, ba.pos) != (int)ba.pos ||
+ (rc = read_byte(inst, jrsock, &rcode)) == -1) {
/*
* With an error on the write or the first read, try closing the socket
* and reconnecting to see if that improves matters any (tries this only once)
/*
* Read result
*/
- if ((rc = read_byte (jrsock, &pcount)) == -1) R_ERR("read_byte(pcnt)");
+ if ((rc = read_byte(inst, jrsock, &pcount)) == -1) R_ERR("read_byte(pcnt)");
radlog(L_DBG, LOG_PREFIX "return code %d; receiving %d packets", (int)rcode, (int)pcount);
if (pcount > 0 && request) if ((rc = read_packet (inst, jrsock, request)) == -1) R_ERR("read_packet(req)");
if (pcount > 1 && reply) if ((rc = read_packet (inst, jrsock, reply)) == -1) R_ERR("read_packet(rep)");
- if ((rc = read_request (jrsock, req)) == -1) R_ERR("read_request()");
+ if ((rc = read_request(inst, jrsock, req)) == -1) R_ERR("read_request()");
/*
* Since we deleted all the attribute lists in the request,
* we need to reconfigure a few pointers in the REQUEST object
*/
if (req->username) {
- req->username = pairfind(request->vps, PW_USER_NAME);
+ req->username = pairfind(request->vps, PW_USER_NAME, 0);
}
if (req->password) {
- req->password = pairfind(request->vps, PW_PASSWORD);
- if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD);
+ req->password = pairfind(request->vps, PW_PASSWORD, 0);
+ if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD, 0);
}
/*
return rlm_jradius_call(JRADIUS_post_auth, instance, request, 0);
}
+#ifdef WITH_COA
+static int jradius_recv_coa(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_recv_coa, instance, request, 0);
+}
+static int jradius_send_coa(void *instance, REQUEST *request)
+{
+ return rlm_jradius_call(JRADIUS_send_coa, instance, request, 0);
+}
+#endif
+
static int jradius_detach(void *instance)
{
JRADIUS *inst = (JRADIUS *) instance;
jradius_pre_proxy,
jradius_post_proxy,
jradius_post_auth
+#ifdef WITH_COA
+ , jradius_recv_coa,
+ jradius_send_coa
+#endif
},
};