⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 radius_client.c

📁 hostapd无线AP工具
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Host AP (software wireless LAN access point) user space daemon for * Host AP kernel driver / RADIUS client * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <netinet/in.h>#include <string.h>#include <time.h>#include <sys/types.h>#include <sys/time.h>#include <sys/socket.h>#include <arpa/inet.h>#include <errno.h>#include "hostapd.h"#include "radius.h"#include "radius_client.h"#include "eloop.h"/* Defaults for RADIUS retransmit values (exponential backoff) */#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts				      * before entry is removed from retransmit				      * list */#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit				      * list (oldest will be removed, if this				      * limit is exceeded) */#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this				      * many failed retry attempts */struct radius_rx_handler {	RadiusRxResult (*handler)(struct radius_msg *msg,				  struct radius_msg *req,				  u8 *shared_secret, size_t shared_secret_len,				  void *data);	void *data;};/* RADIUS message retransmit list */struct radius_msg_list {	u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages			    * for the same STA. */	struct radius_msg *msg;	RadiusType msg_type;	time_t first_try;	time_t next_try;	int attempts;	int next_wait;	struct timeval last_attempt;	u8 *shared_secret;	size_t shared_secret_len;	/* TODO: server config with failover to backup server(s) */	struct radius_msg_list *next;};struct radius_client_data {	void *ctx;	struct hostapd_radius_servers *conf;	int auth_serv_sock; /* socket for authentication RADIUS messages */	int acct_serv_sock; /* socket for accounting RADIUS messages */	int auth_serv_sock6;	int acct_serv_sock6;	int auth_sock; /* currently used socket */	int acct_sock; /* currently used socket */	struct radius_rx_handler *auth_handlers;	size_t num_auth_handlers;	struct radius_rx_handler *acct_handlers;	size_t num_acct_handlers;	struct radius_msg_list *msgs;	size_t num_msgs;	u8 next_radius_identifier;};static intradius_change_server(struct radius_client_data *radius,		     struct hostapd_radius_server *nserv,		     struct hostapd_radius_server *oserv,		     int sock, int sock6, int auth);static int radius_client_init_acct(struct radius_client_data *radius);static int radius_client_init_auth(struct radius_client_data *radius);static void radius_client_msg_free(struct radius_msg_list *req){	radius_msg_free(req->msg);	free(req->msg);	free(req);}int radius_client_register(struct radius_client_data *radius,			   RadiusType msg_type,			   RadiusRxResult (*handler)(struct radius_msg *msg,						     struct radius_msg *req,						     u8 *shared_secret,						     size_t shared_secret_len,						     void *data),			   void *data){	struct radius_rx_handler **handlers, *newh;	size_t *num;	if (msg_type == RADIUS_ACCT) {		handlers = &radius->acct_handlers;		num = &radius->num_acct_handlers;	} else {		handlers = &radius->auth_handlers;		num = &radius->num_auth_handlers;	}	newh = (struct radius_rx_handler *)		realloc(*handlers,			(*num + 1) * sizeof(struct radius_rx_handler));	if (newh == NULL)		return -1;	newh[*num].handler = handler;	newh[*num].data = data;	(*num)++;	*handlers = newh;	return 0;}static void radius_client_handle_send_error(struct radius_client_data *radius,					    int s, RadiusType msg_type){	int _errno = errno;	perror("send[RADIUS]");	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) {		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,			       HOSTAPD_LEVEL_INFO,			       "Send failed - maybe interface status changed -"			       " try to connect again");		eloop_unregister_read_sock(s);		close(s);		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)			radius_client_init_acct(radius);		else			radius_client_init_auth(radius);	}}static int radius_client_retransmit(struct radius_client_data *radius,				    struct radius_msg_list *entry, time_t now){	struct hostapd_radius_servers *conf = radius->conf;	int s;	if (entry->msg_type == RADIUS_ACCT ||	    entry->msg_type == RADIUS_ACCT_INTERIM) {		s = radius->acct_sock;		if (entry->attempts == 0)			conf->acct_server->requests++;		else {			conf->acct_server->timeouts++;			conf->acct_server->retransmissions++;		}	} else {		s = radius->auth_sock;		if (entry->attempts == 0)			conf->auth_server->requests++;		else {			conf->auth_server->timeouts++;			conf->auth_server->retransmissions++;		}	}	/* retransmit; remove entry if too many attempts */	entry->attempts++;	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",		       entry->msg->hdr->identifier);	gettimeofday(&entry->last_attempt, NULL);	if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)		radius_client_handle_send_error(radius, s, entry->msg_type);	entry->next_try = now + entry->next_wait;	entry->next_wait *= 2;	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {		printf("Removing un-ACKed RADIUS message due to too many "		       "failed retransmit attempts\n");		return 1;	}	return 0;}static void radius_client_timer(void *eloop_ctx, void *timeout_ctx){	struct radius_client_data *radius = eloop_ctx;	struct hostapd_radius_servers *conf = radius->conf;	time_t now, first;	struct radius_msg_list *entry, *prev, *tmp;	int auth_failover = 0, acct_failover = 0;	char abuf[50];	entry = radius->msgs;	if (!entry)		return;	time(&now);	first = 0;	prev = NULL;	while (entry) {		if (now >= entry->next_try &&		    radius_client_retransmit(radius, entry, now)) {			if (prev)				prev->next = entry->next;			else				radius->msgs = entry->next;			tmp = entry;			entry = entry->next;			radius_client_msg_free(tmp);			radius->num_msgs--;			continue;		}		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {			if (entry->msg_type == RADIUS_ACCT ||			    entry->msg_type == RADIUS_ACCT_INTERIM)				acct_failover++;			else				auth_failover++;		}		if (first == 0 || entry->next_try < first)			first = entry->next_try;		prev = entry;		entry = entry->next;	}	if (radius->msgs) {		if (first < now)			first = now;		eloop_register_timeout(first - now, 0,				       radius_client_timer, radius, NULL);		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "			       "retransmit in %ld seconds",			       (long int) (first - now));	}	if (auth_failover && conf->num_auth_servers > 1) {		struct hostapd_radius_server *next, *old;		old = conf->auth_server;		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,			       HOSTAPD_LEVEL_NOTICE,			       "No response from Authentication server "			       "%s:%d - failover",			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),			       old->port);		for (entry = radius->msgs; entry; entry = entry->next) {			if (entry->msg_type == RADIUS_AUTH)				old->timeouts++;		}		next = old + 1;		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))			next = conf->auth_servers;		conf->auth_server = next;		radius_change_server(radius, next, old,				     radius->auth_serv_sock,				     radius->auth_serv_sock6, 1);	}	if (acct_failover && conf->num_acct_servers > 1) {		struct hostapd_radius_server *next, *old;		old = conf->acct_server;		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,			       HOSTAPD_LEVEL_NOTICE,			       "No response from Accounting server "			       "%s:%d - failover",			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),			       old->port);		for (entry = radius->msgs; entry; entry = entry->next) {			if (entry->msg_type == RADIUS_ACCT ||			    entry->msg_type == RADIUS_ACCT_INTERIM)				old->timeouts++;		}		next = old + 1;		if (next > &conf->acct_servers[conf->num_acct_servers - 1])			next = conf->acct_servers;		conf->acct_server = next;		radius_change_server(radius, next, old,				     radius->acct_serv_sock,				     radius->acct_serv_sock6, 0);	}}static void radius_client_update_timeout(struct radius_client_data *radius){	time_t now, first;	struct radius_msg_list *entry;	eloop_cancel_timeout(radius_client_timer, radius, NULL);	if (radius->msgs == NULL) {		return;	}	first = 0;	for (entry = radius->msgs; entry; entry = entry->next) {		if (first == 0 || entry->next_try < first)			first = entry->next_try;	}	time(&now);	if (first < now)		first = now;	eloop_register_timeout(first - now, 0, radius_client_timer, radius,			       NULL);	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"		       " %ld seconds\n", (long int) (first - now));}static void radius_client_list_add(struct radius_client_data *radius,				   struct radius_msg *msg,				   RadiusType msg_type, u8 *shared_secret,				   size_t shared_secret_len, u8 *addr){	struct radius_msg_list *entry, *prev;	if (eloop_terminated()) {		/* No point in adding entries to retransmit queue since event		 * loop has already been terminated. */		radius_msg_free(msg);		free(msg);		return;	}	entry = malloc(sizeof(*entry));	if (entry == NULL) {		printf("Failed to add RADIUS packet into retransmit list\n");		radius_msg_free(msg);		free(msg);		return;	}	memset(entry, 0, sizeof(*entry));	if (addr)		memcpy(entry->addr, addr, ETH_ALEN);	entry->msg = msg;	entry->msg_type = msg_type;	entry->shared_secret = shared_secret;	entry->shared_secret_len = shared_secret_len;	time(&entry->first_try);	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;	entry->attempts = 1;	gettimeofday(&entry->last_attempt, NULL);	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;	entry->next = radius->msgs;	radius->msgs = entry;	radius_client_update_timeout(radius);	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {		printf("Removing the oldest un-ACKed RADIUS packet due to "		       "retransmit list limits.\n");		prev = NULL;		while (entry->next) {			prev = entry;			entry = entry->next;		}		if (prev) {			prev->next = NULL;			radius_client_msg_free(entry);		}	} else		radius->num_msgs++;}static void radius_client_list_del(struct radius_client_data *radius,				   RadiusType msg_type, u8 *addr){	struct radius_msg_list *entry, *prev, *tmp;	if (addr == NULL)		return;	entry = radius->msgs;	prev = NULL;	while (entry) {		if (entry->msg_type == msg_type &&		    memcmp(entry->addr, addr, ETH_ALEN) == 0) {			if (prev)				prev->next = entry->next;			else				radius->msgs = entry->next;			tmp = entry;			entry = entry->next;			hostapd_logger(radius->ctx, addr,				       HOSTAPD_MODULE_RADIUS,				       HOSTAPD_LEVEL_DEBUG,				       "Removing matching RADIUS message");			radius_client_msg_free(tmp);			radius->num_msgs--;			continue;		}		prev = entry;		entry = entry->next;	}}int radius_client_send(struct radius_client_data *radius,		       struct radius_msg *msg, RadiusType msg_type, u8 *addr){	struct hostapd_radius_servers *conf = radius->conf;	u8 *shared_secret;	size_t shared_secret_len;	char *name;	int s, res;	if (msg_type == RADIUS_ACCT_INTERIM) {		/* Remove any pending interim acct update for the same STA. */		radius_client_list_del(radius, msg_type, addr);	}	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {		shared_secret = conf->acct_server->shared_secret;		shared_secret_len = conf->acct_server->shared_secret_len;		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);		name = "accounting";		s = radius->acct_sock;		conf->acct_server->requests++;	} else {		shared_secret = conf->auth_server->shared_secret;		shared_secret_len = conf->auth_server->shared_secret_len;		radius_msg_finish(msg, shared_secret, shared_secret_len);		name = "authentication";		s = radius->auth_sock;		conf->auth_server->requests++;	}	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "		       "server", name);	if (conf->msg_dumps)		radius_msg_dump(msg);	res = send(s, msg->buf, msg->buf_used, 0);	if (res < 0)		radius_client_handle_send_error(radius, s, msg_type);	radius_client_list_add(radius, msg, msg_type, shared_secret,			       shared_secret_len, addr);	return res;}static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx){	struct radius_client_data *radius = eloop_ctx;	struct hostapd_radius_servers *conf = radius->conf;	RadiusType msg_type = (RadiusType) sock_ctx;	int len, i, roundtrip;	unsigned char buf[3000];	struct radius_msg *msg;	struct radius_rx_handler *handlers;	size_t num_handlers;	struct radius_msg_list *req, *prev_req;	struct timeval tv;	struct hostapd_radius_server *rconf;	int invalid_authenticator = 0;	if (msg_type == RADIUS_ACCT) {		handlers = radius->acct_handlers;		num_handlers = radius->num_acct_handlers;		rconf = conf->acct_server;	} else {		handlers = radius->auth_handlers;		num_handlers = radius->num_auth_handlers;		rconf = conf->auth_server;	}	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);	if (len < 0) {		perror("recv[RADIUS]");		return;	}	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "		       "server", len);	if (len == sizeof(buf)) {		printf("Possibly too long UDP frame for our buffer - "		       "dropping it\n");		return;	}	msg = radius_msg_parse(buf, len);	if (msg == NULL) {		printf("Parsing incoming RADIUS frame failed\n");		rconf->malformed_responses++;		return;	}	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");	if (conf->msg_dumps)		radius_msg_dump(msg);	switch (msg->hdr->code) {	case RADIUS_CODE_ACCESS_ACCEPT:		rconf->access_accepts++;		break;	case RADIUS_CODE_ACCESS_REJECT:		rconf->access_rejects++;		break;	case RADIUS_CODE_ACCESS_CHALLENGE:		rconf->access_challenges++;		break;	case RADIUS_CODE_ACCOUNTING_RESPONSE:		rconf->responses++;		break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -