/** * rlm_jradius - The FreeRADIUS JRadius Server Module * Copyright (C) 2004-2006 PicoPoint, B.V. * 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 * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This module is used to connect FreeRADIUS to the JRadius server. * JRadius is a Java RADIUS client and server framework, see doc/rlm_jradius * and http://jradius.net/ for more information. * * Author(s): David Bird * * Connection pooling code based on rlm_sql, see rlm_sql/sql.c for copyright and license. */ #include #include #include #include #include #include #include #include RCSID("$Id$") #include #include #include #include #include #ifdef HAVE_PTHREAD_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif static const int JRADIUS_PORT = 1814; static const int HALF_MESSAGE_LEN = 16384; static const int MESSAGE_LEN = 32768; static const int JRADIUS_authenticate = 1; static const int JRADIUS_authorize = 2; static const int JRADIUS_preacct = 3; static const int JRADIUS_accounting = 4; static const int JRADIUS_checksimul = 5; 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 typedef struct jradius_socket { int id; #ifdef HAVE_PTHREAD_H pthread_mutex_t mutex; #endif struct jradius_socket *next; enum { is_connected, not_connected } state; union { int sock; } con; } JRSOCK; typedef struct jradius_inst { time_t connect_after; JRSOCK * sock_pool; JRSOCK * last_used; char * name; char * host [MAX_HOSTS]; 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; char * onfail_s; int keepalive; int jrsock_cnt; } JRADIUS; typedef struct _byte_array { unsigned int size; unsigned int pos; unsigned int left; unsigned char * b; } byte_array; static CONF_PARSER module_config[] = { { "name", PW_TYPE_STRING_PTR, offsetof(JRADIUS, name), NULL, "localhost"}, { "primary", PW_TYPE_STRING_PTR, offsetof(JRADIUS, host[0]), NULL, "localhost"}, { "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"}, { "allow_codechange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_codechange), NULL, "no"}, { "allow_idchange", PW_TYPE_BOOLEAN, offsetof(JRADIUS, allow_idchange), NULL, "no"}, { 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; int i, connected = 0; char buff[128]; int sock; /* * Connect to jradius servers until we succeed or die trying */ for (i = 0; !connected && i < MAX_HOSTS && inst->ipaddr[i] > 0; i++) { /* * Allocate a TCP socket */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { radlog(L_ERR, LOG_PREFIX "could not allocate TCP socket"); goto failed; } /* * If we have a timeout value set, make the socket non-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 non-blocking on socket"); goto failed; } /* * Bind to any local port */ memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = htonl(INADDR_ANY); local_addr.sin_port = htons(0); if (bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { radlog(L_ERR, LOG_PREFIX "could not locally bind TCP socket"); goto failed; } /* * Attempt connection to remote server */ memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; memcpy((char *) &serv_addr.sin_addr, &(inst->ipaddr[i]), 4); serv_addr.sin_port = htons(inst->port[i]); if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { if (inst->timeout > 0 && (errno == EINPROGRESS || errno == EWOULDBLOCK)) { /* * Wait to see if non-blocking socket connects or times-out */ struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; pfd.events = POLLOUT; if (poll(&pfd, 1, inst->timeout * 1000) == 1 && pfd.revents) { /* * Lets make absolutely sure we are connected */ struct sockaddr_in sa; unsigned int salen = sizeof(sa); if (getpeername(sock, (struct sockaddr *) &sa, &salen) != -1) { /* * CONNECTED! break out of for-loop */ connected = 1; break; } } } /* * Timed-out */ radlog(L_ERR, LOG_PREFIX "could not connect to %s:%d", ip_ntoa(buff, inst->ipaddr[i]), inst->port[i]); } else { /* * CONNECTED (instantly)! break out of for-loop */ connected = 1; break; } /* * Unable to connect, cleanup and start over */ close(sock); sock=0; } if (!connected) { radlog(L_ERR, LOG_PREFIX "could not find any jradius server!"); goto failed; } /* * 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 1; failed: if (sock > 0) { shutdown(sock, 2); close(sock); } jrsock->state = not_connected; return 0; } 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); close(jrsock->con.sock); } jrsock->state = not_connected; jrsock->con.sock = 0; } static void free_socket(JRADIUS * inst, JRSOCK *jrsock) { close_socket(inst, jrsock); if (inst->keepalive) { #ifdef HAVE_PTHREAD_H pthread_mutex_destroy(&jrsock->mutex); #endif free(jrsock); } } static int init_socketpool(JRADIUS * inst) { int i, rcode; int success = 0; JRSOCK *jrsock; inst->connect_after = 0; inst->sock_pool = NULL; for (i = 0; i < inst->jrsock_cnt; i++) { radlog(L_INFO, "rlm_jradius: starting JRadius connection %d", i); if ((jrsock = rad_malloc(sizeof(*jrsock))) == 0) return -1; memset(jrsock, 0, sizeof(*jrsock)); jrsock->id = i; jrsock->state = not_connected; #ifdef HAVE_PTHREAD_H rcode = pthread_mutex_init(&jrsock->mutex,NULL); if (rcode != 0) { radlog(L_ERR, "rlm_jradius: Failed to init lock: %s", strerror(errno)); return 0; } #endif if (time(NULL) > inst->connect_after) if (connect_socket(jrsock, inst)) success = 1; jrsock->next = inst->sock_pool; inst->sock_pool = jrsock; } inst->last_used = NULL; if (!success) { radlog(L_DBG, "rlm_jradius: Failed to connect to JRadius server."); } return 1; } static void free_socketpool(JRADIUS * inst) { JRSOCK *cur; JRSOCK *next; for (cur = inst->sock_pool; cur; cur = next) { next = cur->next; free_socket(inst, cur); } inst->sock_pool = NULL; } static JRSOCK * get_socket(JRADIUS * inst) { JRSOCK *cur, *start; int tried_to_connect = 0; int unconnected = 0; start = inst->last_used; if (!start) start = inst->sock_pool; cur = start; while (cur) { #ifdef HAVE_PTHREAD_H if (pthread_mutex_trylock(&cur->mutex) != 0) { goto next; } #endif if ((cur->state == not_connected) && (time(NULL) > inst->connect_after)) { radlog(L_INFO, "rlm_jradius: Trying to (re)connect unconnected handle %d", cur->id); tried_to_connect++; connect_socket(cur, inst); } if (cur->state == not_connected) { radlog(L_DBG, "rlm_jradius: Ignoring unconnected handle %d", cur->id); unconnected++; #ifdef HAVE_PTHREAD_H pthread_mutex_unlock(&cur->mutex); #endif goto next; } radlog(L_DBG, "rlm_jradius: Reserving JRadius socket id: %d", cur->id); if (unconnected != 0 || tried_to_connect != 0) { radlog(L_INFO, "rlm_jradius: got socket %d after skipping %d unconnected handles, tried to reconnect %d though", cur->id, unconnected, tried_to_connect); } inst->last_used = cur->next; return cur; next: cur = cur->next; if (!cur) cur = inst->sock_pool; if (cur == start) break; } radlog(L_INFO, "rlm_jradius: There are no sockets to use! skipped %d, tried to connect %d", unconnected, tried_to_connect); return NULL; } static int release_socket(UNUSED JRADIUS * inst, JRSOCK * jrsock) { #ifdef HAVE_PTHREAD_H pthread_mutex_unlock(&jrsock->mutex); #endif radlog(L_DBG, "rlm_jradius: Released JRadius socket id: %d", jrsock->id); return 0; } /* * Initialize the jradius module */ static int jradius_instantiate(CONF_SECTION *conf, void **instance) { JRADIUS *inst = (JRADIUS *) instance; char host[128], b[128], *h; int i, p, idx, port; inst = rad_malloc(sizeof(JRADIUS)); memset(inst, 0, sizeof(JRADIUS)); if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } for (i = 0, idx = 0; i < MAX_HOSTS; i++) { if (inst->host[i] && strlen(inst->host[i]) < sizeof(host)) { h = inst->host[i]; p = JRADIUS_PORT; strcpy(b, h); if (sscanf(b, "%[^:]:%d", host, &port) == 2) { h = host; p = port; } if (h) { 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; } if ((inst->ipaddr[idx] = ipaddr.ipaddr.ip4addr.s_addr) != htonl(INADDR_NONE)) { inst->port[idx] = p; radlog(L_INFO, LOG_PREFIX "configuring jradius server %s:%d", h, p); idx++; } else { radlog(L_ERR, LOG_PREFIX "invalid jradius server %s", h); } } } } if (inst->keepalive) init_socketpool(inst); inst->onfail = RLM_MODULE_FAIL; if (inst->onfail_s) { if (!strcmp(inst->onfail_s, "NOOP")) inst->onfail = RLM_MODULE_NOOP; else if (!strcmp(inst->onfail_s, "REJECT")) inst->onfail = RLM_MODULE_REJECT; else if (!strcmp(inst->onfail_s, "OK")) inst->onfail = RLM_MODULE_OK; else if (!strcmp(inst->onfail_s, "FAIL")) inst->onfail = RLM_MODULE_FAIL; else radlog(L_ERR, LOG_PREFIX "invalid jradius 'onfail' state %s", inst->onfail_s); } *instance = inst; return 0; } /* * Initialize a byte array buffer structure */ static void init_byte_array(byte_array * ba, unsigned char *b, int blen) { ba->b = b; ba->size = ba->left = blen; ba->pos = 0; } /* * Pack a single byte into a byte array buffer */ static int pack_byte(byte_array * ba, unsigned char c) { if (ba->left < 1) return -1; ba->b[ba->pos] = c; ba->pos++; ba->left--; return 0; } /* * Pack an array of bytes into a byte array buffer */ static int pack_bytes(byte_array * ba, unsigned char *d, unsigned int dlen) { if (ba->left < dlen) return -1; memcpy((void *)(ba->b + ba->pos), d, dlen); ba->pos += dlen; ba->left -= dlen; return 0; } /* * Pack an integer into a byte array buffer (adjusting for byte-order) */ static int pack_uint32(byte_array * ba, uint32_t i) { if (ba->left < 4) return -1; i = htonl(i); memcpy((void *)(ba->b + ba->pos), (void *)&i, 4); ba->pos += 4; ba->left -= 4; return 0; } /* * 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) { if (ba->left < a->pos) return -1; memcpy((void *)(ba->b + ba->pos), (void *)a->b, a->pos); ba->pos += a->pos; ba->left -= a->pos; return 0; } /* * Pack radius attributes into a byte array buffer */ 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); 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_uint32(ba, vp->lvalue) == -1) return -1; break; case PW_TYPE_IPADDR: if (pack_bytes(ba, (void *)&vp->vp_ipaddr, vp->length) == -1) return -1; break; default: if (pack_bytes(ba, (void *)vp->vp_octets, vp->length) == -1) return -1; break; } } return 0; } /* * Pack a radius packet into a byte array buffer */ static int pack_packet(byte_array * ba, RADIUS_PACKET * p) { /*unsigned char code = p->code;*/ unsigned char buff[HALF_MESSAGE_LEN]; byte_array pba; init_byte_array(&pba, buff, sizeof(buff)); 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); #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; return 0; } static int pack_request(byte_array * ba, REQUEST *r) { unsigned char buff[HALF_MESSAGE_LEN]; byte_array pba; init_byte_array(&pba, buff, sizeof(buff)); 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; return 0; } static uint32_t unpack_uint32(unsigned char *c) { uint32_t ii; memcpy((void *)&ii, c, 4); return ntohl(ii); } static uint16_t unpack_uint16(unsigned char *c) { 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_uint32(JRADIUS *inst, JRSOCK *jrsock, uint32_t *i) { uint32_t ii; 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(JRADIUS *inst, JRSOCK *jrsock, VALUE_PAIR **pl, int plen) { VALUE_PAIR *vp; unsigned char buff[MESSAGE_LEN]; uint32_t alen, atype, aop; int rlen = 0; while (rlen < plen) { 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); if (alen >= sizeof(buff)) { radlog(L_ERR, LOG_PREFIX "packet value too large (len: %d)", alen); return -1; } if (sock_read(inst, jrsock, buff, alen) != (int)alen) return -1; rlen += alen; buff[alen]=0; /* * Create new attribute */ vp = paircreate(atype, -1); vp->operator = aop; if (vp->type == -1) { /* * FreeRADIUS should know about the same attributes that JRadius knows */ radlog(L_ERR, LOG_PREFIX "received attribute we do not recognize (type: %d)", atype); pairbasicfree(vp); continue; } /* * 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: vp->lvalue = unpack_uint32(buff); vp->length = 4; break; case PW_TYPE_IPADDR: memcpy((void *)&vp->vp_ipaddr, buff, 4); vp->length = 4; break; default: if (alen >= sizeof(vp->vp_octets)) alen = sizeof(vp->vp_octets) - 1; memcpy((void *)vp->vp_octets, buff, alen); vp->length = alen; break; } /* * Add the attribute to the packet */ pairadd(pl, vp); } return rlen; } /* * Read a radius packet from the socket */ static int read_packet(JRADIUS * inst, JRSOCK *jrsock, RADIUS_PACKET *p) { 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_uint32(inst, jrsock, &plen) == -1) return -1; radlog(L_DBG, LOG_PREFIX "reading packet: code=%d len=%d", (int)code, plen); if (inst->allow_codechange) if (code != p->code) { radlog(L_INFO, LOG_PREFIX "changing packet code from %d to %d", p->code, code); p->code = code; } if (inst->allow_idchange) if ((int)id != p->id) { radlog(L_INFO, LOG_PREFIX "changing packet id from %d to %d", p->id, id); p->id = (int)id; } /* * Delete previous attribute list */ pairfree(&p->vps); if (plen == 0) return 0; if (read_vps(inst, jrsock, &p->vps, plen) == -1) return -1; return 0; } static int read_request(JRADIUS *inst, JRSOCK *jrsock, REQUEST *p) { unsigned int plen; if (read_uint32(inst, jrsock, &plen) == -1) return -1; radlog(L_DBG, LOG_PREFIX "reading request: config_item: len=%d", plen); /* * Delete previous attribute list */ pairfree(&p->config_items); if (plen == 0) return 0; 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; JRSOCK * jrsock = 0; JRSOCK sjrsock; int exitstatus = inst->onfail; unsigned char rcode, pcount; unsigned char buff[MESSAGE_LEN]; byte_array ba; char * n = inst->name; unsigned int nlen = strlen(n); const char * err = 0; int rc, attempt2=0; #define W_ERR(s) { err=s; goto packerror; } #define R_ERR(s) { err=s; goto parseerror; } if (inst->keepalive) { jrsock = get_socket(inst); if (!jrsock) return exitstatus; } else { jrsock = &sjrsock; memset(jrsock, 0, sizeof(*jrsock)); jrsock->state = not_connected; } init_byte_array(&ba, buff, sizeof(buff)); pcount = 0; if (request) pcount++; if (reply) pcount++; /* * Create byte array to send to jradius */ 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)"); if (pcount > 0 && (rc = pack_packet (&ba, request)) == -1) W_ERR("pack_packet(req)"); if (pcount > 1 && (rc = pack_packet (&ba, reply)) == -1) W_ERR("pack_packet(rep)"); if ((rc = pack_request(&ba, req)) == -1) W_ERR("pack_request()"); /* * Send data */ start_over: if (jrsock->state == not_connected) { if (attempt2) radlog(L_ERR, LOG_PREFIX "reconnecting socket id %d", jrsock->id); if (!connect_socket(jrsock, inst)) { if (attempt2) radlog(L_ERR, LOG_PREFIX "could not reconnect socket %d, giving up", jrsock->id); goto cleanup; } } radlog(L_DBG, LOG_PREFIX "sending %d bytes to socket %d", ba.pos, jrsock->id); 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) */ radlog(L_ERR, LOG_PREFIX "error sending request with socket %d", jrsock->id); if (!inst->keepalive || attempt2) W_ERR("socket_send/first_read"); close_socket(inst, jrsock); attempt2 = 1; goto start_over; } /* * Read result */ 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(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); } if (req->password) { req->password = pairfind(request->vps, PW_PASSWORD); if (!req->password) req->password = pairfind(request->vps, PW_CHAP_PASSWORD); } /* * All done, set return code and cleanup */ exitstatus = (int)rcode; goto cleanup; parseerror: radlog(L_ERR, LOG_PREFIX "problem parsing the data [%s]",err); if (inst->keepalive) close_socket(inst, jrsock); goto cleanup; packerror: radlog(L_ERR, LOG_PREFIX "problem packing the data[%s]",err); if (inst->keepalive) close_socket(inst, jrsock); cleanup: if (inst->keepalive) release_socket(inst, jrsock); else close_socket(inst, jrsock); return exitstatus; } static int jradius_authenticate(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_authenticate, instance, request, 0); } static int jradius_authorize(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_authorize, instance, request, 0); } static int jradius_preacct(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_preacct, instance, request, 0); } static int jradius_accounting(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_accounting, instance, request, 0); } static int jradius_checksimul(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_checksimul, instance, request, 0); } static int jradius_pre_proxy(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_pre_proxy, instance, request, 1); } static int jradius_post_proxy(void *instance, REQUEST *request) { return rlm_jradius_call(JRADIUS_post_proxy, instance, request, 1); } static int jradius_post_auth(void *instance, REQUEST *request) { 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; free_socketpool(inst); free(inst); return 0; } module_t rlm_jradius = { RLM_MODULE_INIT, "jradius", RLM_TYPE_THREAD_SAFE, jradius_instantiate, jradius_detach, { jradius_authenticate, jradius_authorize, jradius_preacct, jradius_accounting, jradius_checksimul, jradius_pre_proxy, jradius_post_proxy, jradius_post_auth #ifdef WITH_COA , jradius_recv_coa, jradius_send_coa #endif }, };