📄 rlm_jradius.c
字号:
/** * rlm_jradius - The FreeRADIUS JRadius Server Module * Copyright (C) 2004-2006 PicoPoint, B.V. * Copyright (c) 2007 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 <dbird@acm.org> * * Connection pooling code based on rlm_sql, see rlm_sql/sql.c for copyright and license. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/signal.h>#include <sys/types.h>#include <fcntl.h>#include <poll.h>#include <freeradius-devel/ident.h>RCSID("$Id: rlm_jradius.c,v 1.3 2007/11/23 13:46:57 aland Exp $")#include <freeradius-devel/autoconf.h>#include <freeradius-devel/libradius.h>#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/conffile.h>#ifdef HAVE_PTHREAD_H#include <pthread.h>#endif#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#ifndef O_NONBLOCK#define O_NONBLOCK O_NDELAY#endifstatic 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;#define LOG_PREFIX "rlm_jradius: "#define MAX_HOSTS 4typedef 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 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"}, { "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 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 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; 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--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -