summaryrefslogtreecommitdiff
path: root/accel-pptpd/radius/radius.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pptpd/radius/radius.c')
-rw-r--r--accel-pptpd/radius/radius.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/accel-pptpd/radius/radius.c b/accel-pptpd/radius/radius.c
new file mode 100644
index 00000000..9f5c7ffc
--- /dev/null
+++ b/accel-pptpd/radius/radius.c
@@ -0,0 +1,373 @@
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "mempool.h"
+#include "events.h"
+#include "log.h"
+#include "ppp.h"
+#include "pwdb.h"
+#include "ipdb.h"
+
+#include "radius_p.h"
+#include "attr_defs.h"
+
+#include "memdebug.h"
+
+#define CHAP_MD5 5
+#define MSCHAP_V1 0x80
+#define MSCHAP_V2 0x81
+
+int conf_max_try = 3;
+int conf_timeout = 3;
+char *conf_nas_identifier = "accel-pptpd";
+char *conf_nas_ip_address;
+char *conf_gw_ip_address;
+int conf_verbose = 0;
+
+char *conf_auth_server;
+int conf_auth_server_port = 1812;
+char *conf_auth_secret;
+
+char *conf_acct_server;
+int conf_acct_server_port = 1813;
+char *conf_acct_secret;
+char *conf_dm_coa_secret;
+
+static LIST_HEAD(sessions);
+static pthread_rwlock_t sessions_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+static void *pd_key;
+static struct ipdb_t ipdb;
+
+static mempool_t rpd_pool;
+
+void rad_proc_attrs(struct rad_req_t *req)
+{
+ struct rad_attr_t *attr;
+
+ list_for_each_entry(attr, &req->reply->attrs, entry) {
+ if (attr->vendor)
+ continue;
+ switch(attr->attr->id) {
+ case Framed_IP_Address:
+ if (!conf_gw_ip_address)
+ log_ppp_warn("radius: gw-ip-address not specified, cann't assign IP address...\n");
+ else {
+ req->rpd->ipaddr.owner = &ipdb;
+ req->rpd->ipaddr.peer_addr = attr->val.ipaddr;
+ req->rpd->ipaddr.addr = inet_addr(conf_gw_ip_address);
+ }
+ break;
+ case Acct_Interim_Interval:
+ req->rpd->acct_interim_interval = attr->val.integer;
+ break;
+ case Session_Timeout:
+ req->rpd->session_timeout.expire_tv.tv_sec = attr->val.integer;
+ break;
+ }
+ }
+}
+
+static int check(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username, int type, va_list _args)
+{
+ int r = PWDB_NO_IMPL;
+ va_list args;
+ int chap_type;
+ struct radius_pd_t *rpd = find_pd(ppp);
+
+ va_copy(args, _args);
+
+ switch(type) {
+ case PPP_PAP:
+ r = rad_auth_pap(rpd, username, args);
+ break;
+ case PPP_CHAP:
+ chap_type = va_arg(args, int);
+ switch(chap_type) {
+ case CHAP_MD5:
+ r = rad_auth_chap_md5(rpd, username, args);
+ break;
+ case MSCHAP_V1:
+ r = rad_auth_mschap_v1(rpd, username, args);
+ break;
+ case MSCHAP_V2:
+ r = rad_auth_mschap_v2(rpd, username, args);
+ break;
+ }
+ break;
+ }
+
+ va_end(args);
+
+ return r;
+}
+
+static struct ipdb_item_t *get_ip(struct ppp_t *ppp)
+{
+ struct radius_pd_t *rpd = find_pd(ppp);
+
+ if (rpd->ipaddr.peer_addr)
+ return &rpd->ipaddr;
+ return NULL;
+}
+
+static void session_timeout(struct triton_timer_t *t)
+{
+ struct radius_pd_t *rpd = container_of(t, typeof(*rpd), session_timeout);
+
+ log_ppp_msg("radius: session timed out\n");
+ ppp_terminate(rpd->ppp, 0);
+}
+
+static void ppp_starting(struct ppp_t *ppp)
+{
+ struct radius_pd_t *rpd = mempool_alloc(rpd_pool);
+
+ memset(rpd, 0, sizeof(*rpd));
+ rpd->pd.key = &pd_key;
+ rpd->ppp = ppp;
+ pthread_mutex_init(&rpd->lock, NULL);
+ list_add_tail(&rpd->pd.entry, &ppp->pd_list);
+
+ pthread_rwlock_wrlock(&sessions_lock);
+ list_add_tail(&rpd->entry, &sessions);
+ pthread_rwlock_unlock(&sessions_lock);
+}
+
+static void ppp_started(struct ppp_t *ppp)
+{
+ struct radius_pd_t *rpd = find_pd(ppp);
+
+ if (rad_acct_start(rpd))
+ ppp_terminate(rpd->ppp, 0);
+
+ if (rpd->session_timeout.expire_tv.tv_sec) {
+ rpd->session_timeout.expire = session_timeout;
+ triton_timer_add(ppp->ctrl->ctx, &rpd->session_timeout, 0);
+ }
+}
+static void ppp_finishing(struct ppp_t *ppp)
+{
+ struct radius_pd_t *rpd = find_pd(ppp);
+
+ rad_acct_stop(rpd);
+}
+static void ppp_finished(struct ppp_t *ppp)
+{
+ struct radius_pd_t *rpd = find_pd(ppp);
+
+ pthread_rwlock_wrlock(&sessions_lock);
+ pthread_mutex_lock(&rpd->lock);
+ list_del(&rpd->entry);
+ pthread_mutex_unlock(&rpd->lock);
+ pthread_rwlock_unlock(&sessions_lock);
+
+ if (rpd->acct_req)
+ rad_req_free(rpd->acct_req);
+
+ if (rpd->dm_coa_req)
+ rad_packet_free(rpd->dm_coa_req);
+
+ if (rpd->session_timeout.tpd)
+ triton_timer_del(&rpd->session_timeout);
+
+ list_del(&rpd->pd.entry);
+
+ mempool_free(rpd);
+}
+
+struct radius_pd_t *find_pd(struct ppp_t *ppp)
+{
+ struct ppp_pd_t *pd;
+ struct radius_pd_t *rpd;
+
+ list_for_each_entry(pd, &ppp->pd_list, entry) {
+ if (pd->key == &pd_key) {
+ rpd = container_of(pd, typeof(*rpd), pd);
+ return rpd;
+ }
+ }
+ log_emerg("radius:BUG: rpd not found\n");
+ abort();
+}
+
+
+struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, int port_id, in_addr_t ipaddr, const char *csid)
+{
+ struct radius_pd_t *rpd;
+
+ pthread_rwlock_rdlock(&sessions_lock);
+ list_for_each_entry(rpd, &sessions, entry) {
+ if (sessionid && strcmp(sessionid, rpd->ppp->sessionid))
+ continue;
+ if (username && strcmp(username, rpd->ppp->username))
+ continue;
+ if (port_id >= 0 && port_id != rpd->ppp->unit_idx)
+ continue;
+ if (ipaddr && ipaddr != rpd->ipaddr.peer_addr)
+ continue;
+ if (csid && rpd->ppp->ctrl->calling_station_id && strcmp(csid, rpd->ppp->ctrl->calling_station_id))
+ continue;
+ pthread_mutex_lock(&rpd->lock);
+ pthread_rwlock_unlock(&sessions_lock);
+ return rpd;
+ }
+ pthread_rwlock_unlock(&sessions_lock);
+ return NULL;
+}
+
+struct radius_pd_t *rad_find_session_pack(struct rad_packet_t *pack)
+{
+ struct rad_attr_t *attr;
+ const char *sessionid = NULL;
+ const char *username = NULL;
+ const char *csid = NULL;
+ int port_id = -1;
+ in_addr_t ipaddr = 0;
+
+ list_for_each_entry(attr, &pack->attrs, entry) {
+ if (!strcmp(attr->attr->name, "Acct-Session-Id"))
+ sessionid = attr->val.string;
+ else if (!strcmp(attr->attr->name, "User-Name"))
+ username = attr->val.string;
+ else if (!strcmp(attr->attr->name, "NAS-Port"))
+ port_id = attr->val.integer;
+ else if (!strcmp(attr->attr->name, "Framed-IP-Address"))
+ ipaddr = attr->val.ipaddr;
+ else if (!strcmp(attr->attr->name, "Calling-Station-Id"))
+ csid = attr->val.string;
+ }
+
+ if (!sessionid && !username && port_id == -1 && ipaddr == 0 && !csid)
+ return NULL;
+
+ if (username && !sessionid)
+ return NULL;
+
+ return rad_find_session(sessionid, username, port_id, ipaddr, csid);
+}
+
+int rad_check_nas_pack(struct rad_packet_t *pack)
+{
+ struct rad_attr_t *attr;
+ const char *ident = NULL;
+ in_addr_t ipaddr = 0;
+
+ list_for_each_entry(attr, &pack->attrs, entry) {
+ if (!strcmp(attr->attr->name, "NAS-Identifier"))
+ ident = attr->val.string;
+ else if (!strcmp(attr->attr->name, "NAS-IP-Address"))
+ ipaddr = attr->val.ipaddr;
+ }
+
+ if (conf_nas_identifier && (!ident || strcmp(conf_nas_identifier, ident)))
+ return -1;
+ if (conf_nas_ip_address && inet_addr(conf_nas_ip_address) != ipaddr)
+ return -1;
+
+ return 0;
+}
+
+static struct ipdb_t ipdb = {
+ .get = get_ip,
+};
+
+static struct pwdb_t pwdb = {
+ .check = check,
+};
+
+static int parse_server(const char *opt, char **name, int *port, char **secret)
+{
+ char *str = _strdup(opt);
+ char *p1, *p2;
+
+ p1 = strstr(str, ":");
+ p2 = strstr(str, ",");
+
+ if (p1)
+ *p1 = 0;
+ if (p2)
+ *p2 = 0;
+ else
+ return -1;
+
+ *name = str;
+ if (p1) {
+ *port = atoi(p1 + 1);
+ if (*port <=0 )
+ return -1;
+ }
+ *secret = p2 + 1;
+
+ return 0;
+}
+
+static void __init radius_init(void)
+{
+ char *opt;
+ char *dict = DICT_PATH;
+
+ rpd_pool = mempool_create(sizeof(struct radius_pd_t));
+
+ opt = conf_get_opt("radius", "max-try");
+ if (opt && atoi(opt) > 0)
+ conf_max_try = atoi(opt);
+
+ opt = conf_get_opt("radius", "timeout");
+ if (opt && atoi(opt) > 0)
+ conf_timeout = atoi(opt);
+
+ opt = conf_get_opt("radius", "verbose");
+ if (opt && atoi(opt) > 0)
+ conf_verbose = 1;
+
+ opt = conf_get_opt("radius", "nas-ip-address");
+ if (opt)
+ conf_nas_ip_address = opt;
+
+ opt = conf_get_opt("radius", "nas-identifier");
+ if (opt)
+ conf_nas_identifier = opt;
+
+ opt = conf_get_opt("radius", "gw-ip-address");
+ if (opt)
+ conf_gw_ip_address = opt;
+
+ opt = conf_get_opt("radius", "auth_server");
+ if (!opt) {
+ log_emerg("radius: auth_server not specified\n");
+ _exit(EXIT_FAILURE);
+ } else if (parse_server(opt, &conf_auth_server, &conf_auth_server_port, &conf_auth_secret)) {
+ log_emerg("radius: failed to parse auth_server\n");
+ _exit(EXIT_FAILURE);
+ }
+
+ opt = conf_get_opt("radius", "acct_server");
+ if (opt && parse_server(opt, &conf_acct_server, &conf_acct_server_port, &conf_acct_secret)) {
+ log_emerg("radius: failed to parse acct_server\n");
+ _exit(EXIT_FAILURE);
+ }
+
+ opt = conf_get_opt("radius", "dm_coa_secret");
+ if (opt)
+ conf_dm_coa_secret = opt;
+
+ opt = conf_get_opt("radius", "dictionary");
+ if (opt)
+ dict = opt;
+
+ if (rad_dict_load(dict))
+ _exit(EXIT_FAILURE);
+
+ pwdb_register(&pwdb);
+ ipdb_register(&ipdb);
+
+ triton_event_register_handler(EV_PPP_STARTING, (triton_event_func)ppp_starting);
+ triton_event_register_handler(EV_PPP_STARTED, (triton_event_func)ppp_started);
+ triton_event_register_handler(EV_PPP_FINISHING, (triton_event_func)ppp_finishing);
+ triton_event_register_handler(EV_PPP_FINISHED, (triton_event_func)ppp_finished);
+}