Exemple #1
0
/*
 *	The "free" functions are here, for no particular reason.
 */
void rlm_policy_free_item(policy_item_t *item)
{
	while (item) {
		policy_item_t *next = item->next;

		switch (item->type) {
		default:
		case POLICY_TYPE_BAD:
			break;

		case POLICY_TYPE_ASSIGNMENT:
			{
				policy_assignment_t *this;

				this = (policy_assignment_t *) item;
				if (this->lhs) free(this->lhs);
				if (this->rhs) free(this->rhs);
			}
			break;

		case POLICY_TYPE_CONDITIONAL:
			{
				policy_condition_t *this;

				this = (policy_condition_t *) item;
				if (this->lhs) free(this->lhs);
				if (this->rhs) free(this->rhs);

				if (this->child) {
					rlm_policy_free_item(this->child);
					this->child = NULL;
				}
			}
			break;

		case POLICY_TYPE_IF:
			{
				policy_if_t *this;

				this = (policy_if_t *) item;
				if (this->condition) {
					rlm_policy_free_item(this->condition);
					this->condition = NULL;
				}
				if (this->if_true) {
					rlm_policy_free_item(this->if_true);
					this->if_true = NULL;
				}
				if (this->if_false) {
					rlm_policy_free_item(this->if_false);
					this->if_false = NULL;
				}
			}
			break;

		case POLICY_TYPE_ATTRIBUTE_LIST:
			{
				policy_attributes_t *this;

				this = (policy_attributes_t *) item;
				rlm_policy_free_item(this->attributes);
			}
			break;

		case POLICY_TYPE_NAMED_POLICY:
			{
				policy_named_t *this;

				this = (policy_named_t *) item;
				rad_assert(this->name != NULL);
				free(this->name);
				rlm_policy_free_item(this->policy);
			}
			break;

		case POLICY_TYPE_CALL:
			{
				policy_call_t *this;

				this = (policy_call_t *) item;
				if (this->name) free(this->name);
			}
			break;

		case POLICY_TYPE_RETURN:
			break;	/* do nothing */

		case POLICY_TYPE_MODULE:
			{
				policy_module_t *this;

				this = (policy_module_t *) item;
				if (this->cs) cf_section_free(&this->cs);
				if (this->mc) modcallable_free(&this->mc);
			}
			break;
		} /* switch over type */
		item->next = NULL; /* for debugging & sanity checks */
		item->type = POLICY_TYPE_BAD;
		free(item);

		item = next;
	}
}
int dual_tls_recv(rad_listen_t *listener)
{
	RADIUS_PACKET *packet;
	REQUEST *request;
	RAD_REQUEST_FUNP fun = NULL;
	listen_socket_t *sock = listener->data;
	RADCLIENT	*client = sock->client;

	if (!tls_socket_recv(listener)) {
		return 0;
	}

	rad_assert(sock->request != NULL);
	rad_assert(sock->request->packet != NULL);
	rad_assert(sock->packet != NULL);
	rad_assert(sock->ssn != NULL);
	rad_assert(client != NULL);

	request = sock->request;
	packet = sock->packet;

	/*
	 *	Some sanity checks, based on the packet code.
	 */
	switch(packet->code) {
	case PW_CODE_AUTHENTICATION_REQUEST:
		if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
		FR_STATS_INC(auth, total_requests);
		fun = rad_authenticate;
		break;

	case PW_CODE_ACCOUNTING_REQUEST:
		if (listener->type != RAD_LISTEN_ACCT) goto bad_packet;
		FR_STATS_INC(acct, total_requests);
		fun = rad_accounting;
		break;

	case PW_CODE_STATUS_SERVER:
		if (!mainconfig.status_server) {
			FR_STATS_INC(auth, total_unknown_types);
			WDEBUG("Ignoring Status-Server request due to security configuration");
			rad_free(&sock->packet);
			request->packet = NULL;
			return 0;
		}
		fun = rad_status_server;
		break;

	default:
	bad_packet:
		FR_STATS_INC(auth, total_unknown_types);

		DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED",
		      packet->code, client->shortname, packet->src_port);
		rad_free(&sock->packet);
		request->packet = NULL;
		return 0;
	} /* switch over packet types */

	if (!request_receive(listener, packet, client, fun)) {
		FR_STATS_INC(auth, total_packets_dropped);
		rad_free(&sock->packet);
		request->packet = NULL;
		return 0;
	}

	sock->packet = NULL;	/* we have no need for more partial reads */
	request->packet = NULL;

	return 1;
}
/*
 *	Recieve packets from a proxy socket.
 */
static int proxy_socket_recv(rad_listen_t *listener,
			      RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
{
	REQUEST		*request;
	RADIUS_PACKET	*packet;
	char		buffer[128];
	RAD_REQUEST_FUNP fun = NULL;

	packet = rad_recv(listener->fd, 0);
	if (!packet) {
		radlog(L_ERR, "%s", fr_strerror());
		return 0;
	}

	/*
	 *	FIXME: Client MIB updates?
	 */
	switch(packet->code) {
	case PW_AUTHENTICATION_ACK:
	case PW_ACCESS_CHALLENGE:
	case PW_AUTHENTICATION_REJECT:
#ifdef WITH_ACCOUNTING
	case PW_ACCOUNTING_RESPONSE:
#endif
		break;

#ifdef WITH_COA
	case PW_DISCONNECT_ACK:
	case PW_DISCONNECT_NAK:
	case PW_COA_ACK:
	case PW_COA_NAK:
		fun = rad_coa_reply; /* run NEW function */
		break;
#endif

	default:
		/*
		 *	FIXME: Update MIB for packet types?
		 */
		radlog(L_ERR, "Invalid packet code %d sent to a proxy port "
		       "from home server %s port %d - ID %d : IGNORED",
		       packet->code,
		       ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
 		       packet->src_port, packet->id);
		rad_free(&packet);
		return 0;
	}

	request = received_proxy_response(packet);
	if (!request) {
		return 0;
	}

	rad_assert(request->process != NULL);

#ifdef WITH_COA
	if (!fun)
#endif
	  fun = request->process; /* re-run original function */
	*pfun = fun;
	*prequest = request;

	return 1;
}
/*
 *	The "detail.work" file exists, and is open in the 'fd'.
 */
static int work_exists(proto_detail_file_thread_t *thread, int fd)
{
	proto_detail_file_t const *inst = thread->inst;
	bool			opened = false;
	proto_detail_work_thread_t     *work;
	fr_listen_t		*li = NULL;
	struct stat		st;

	fr_event_vnode_func_t	funcs = { .delete = mod_vnode_delete };

	DEBUG3("proto_detail (%s): Trying to lock %s", thread->name, inst->filename_work);

	/*
	 *	"detail.work" exists, try to lock it.
	 */
	if (rad_lockfd_nonblock(fd, 0) < 0) {
		struct timeval when, now;

		DEBUG3("proto_detail (%s): Failed locking %s: %s",
		       thread->name, inst->filename_work, fr_syserror(errno));

		close(fd);

		when.tv_usec = thread->lock_interval % USEC;
		when.tv_sec = thread->lock_interval / USEC;

		/*
		 *	Ensure that we don't do massive busy-polling.
		 */
		thread->lock_interval += thread->lock_interval / 2;
		if (thread->lock_interval > (30 * USEC)) thread->lock_interval = 30 * USEC;

		DEBUG3("proto_detail (%s): Waiting %d.%06ds for lock on file %s",
		       thread->name, (int) when.tv_sec, (int) when.tv_usec, inst->filename_work);

		gettimeofday(&now, NULL);
		fr_timeval_add(&when, &when, &now);

		if (fr_event_timer_insert(thread, thread->el, &thread->ev,
					  &when, work_retry_timer, thread) < 0) {
			ERROR("Failed inserting retry timer for %s", inst->filename_work);
		}
		return 0;
	}

	DEBUG3("proto_detail (%s): Obtained lock and starting to process file %s",
	       thread->name, inst->filename_work);

	/*
	 *	Ignore empty files.
	 */
	if (fstat(fd, &st) < 0) {
		ERROR("Failed opening %s: %s", inst->filename_work,
		      fr_syserror(errno));
		unlink(inst->filename_work);
		close(fd);
		return 1;
	}

	if (!st.st_size) {
		DEBUG3("proto_detail (%s): %s file is empty, ignoring it.",
		       thread->name, inst->filename_work);
		unlink(inst->filename_work);
		close(fd);
		return 1;
	}

	/*
	 *	This listener is allocated in a thread-specific
	 *	context, so it doesn't need a destructor,
	 */
	MEM(li = talloc_zero(NULL, fr_listen_t));

	/*
	 *	Create a new listener, and insert it into the
	 *	scheduler.  Shamelessly copied from proto_detail.c
	 *	mod_open(), with changes.
	 *
	 *	This listener is parented from the worker.  So that
	 *	when the worker goes away, so does the listener.
	 */
	li->app_io = inst->parent->work_io;

	li->app = inst->parent->self;
	li->app_instance = inst->parent;
	li->server_cs = inst->parent->server_cs;

	/*
	 *	The worker may be in a different thread, so avoid
	 *	talloc threading issues by using a NULL TALLOC_CTX.
	 */
	MEM(li->thread_instance = work = talloc_zero(li, proto_detail_work_thread_t));

	li->app_io_instance = inst->parent->work_io_instance;
	work->inst = li->app_io_instance;
	work->file_parent = thread;
	work->ev = NULL;

	li->fd = work->fd = dup(fd);
	if (work->fd < 0) {
		DEBUG("proto_detail (%s): Failed opening %s: %s",
		      thread->name, inst->filename_work, fr_syserror(errno));

		close(fd);
		talloc_free(li);
		return -1;
	}

	/*
	 *	Don't do anything until the file has been deleted.
	 *
	 *	@todo - ensure that proto_detail_work is done the file...
	 *	maybe by creating a new instance?
	 */
	if (fr_event_filter_insert(thread, thread->el, fd, FR_EVENT_FILTER_VNODE,
				   &funcs, NULL, thread) < 0) {
		PERROR("Failed adding work socket to event loop");
		close(fd);
		talloc_free(li);
		return -1;
	}

	/*
	 *	Remember this for later.
	 */
	thread->vnode_fd = fd;

	/*
	 *	For us, this is the worker listener.
	 *	For the worker, this is it's own parent
	 */
	thread->listen = li;

	work->filename_work = talloc_strdup(work, inst->filename_work);

	/*
	 *	Set configurable parameters for message ring buffer.
	 */
	li->default_message_size = inst->parent->max_packet_size;
	li->num_messages = inst->parent->num_messages;

	pthread_mutex_lock(&thread->worker_mutex);
	thread->num_workers++;
	pthread_mutex_unlock(&thread->worker_mutex);

	/*
	 *	Open the detail.work file.
	 */
	if (li->app_io->open(li) < 0) {
		ERROR("Failed opening %s", li->app_io->name);
		goto error;
	}
	opened = true;

	rad_assert(li->app_io->get_name);
	li->name = li->app_io->get_name(li);

	if (!fr_schedule_listen_add(inst->parent->sc, li)) {
	error:
		if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) {
			PERROR("Failed removing DELETE callback when opening work file");
		}
		close(thread->vnode_fd);
		thread->vnode_fd = -1;

		if (opened) {
			(void) li->app_io->close(li);
			thread->listen = NULL;
			li = NULL;
		}

		talloc_free(li);
		return -1;
	}

	/*
	 *	Tell the worker to clean itself up.
	 */
	work->listen = li;

	return 0;
}
static int mod_bootstrap(void *instance, CONF_SECTION *cs)
{
	proto_detail_file_t	*inst = talloc_get_type_abort(instance, proto_detail_file_t);
	dl_instance_t const	*dl_inst;
	char			*p;

#ifdef __linux__
	/*
	 *	The kqueue API takes an FD, but inotify requires a filename.
	 *	libkqueue uses /proc/PID/fd/# to look up the FD -> filename mapping.
	 *
	 *	However, if you start the server as "root", and then swap to "radiusd",
	 *	/proc/PID will be owned by "root" for security reasons.  The only way
	 *	to make /proc/PID owned by "radiusd" is to set the DUMPABLE flag.
	 *
	 *	Instead of making the poor sysadmin figure this out,
	 *	we check for this situation, and give them a
	 *	descriptive message telling them what to do.
	 */
	if (!main_config->allow_core_dumps &&
	    main_config->uid_is_set &&
	    main_config->server_uid != 0) {
		cf_log_err(cs, "Cannot start detail file reader due to Linux limitations.");
		cf_log_err(cs, "Please set 'allow_core_dumps = true' in the main configuration file.");
		return -1;
	}
#endif

	/*
	 *	Find the dl_instance_t holding our instance data
	 *	so we can find out what the parent of our instance
	 *	was.
	 */
	dl_inst = dl_instance_by_data(instance);
	rad_assert(dl_inst);

#ifndef __linux__
	/*
	 *	Linux inotify works.  So we allow poll_interval==0
	 */
	FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, >=, 1);
#endif
	FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, <=, 3600);

	inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_detail_t);
	inst->cs = cs;

	inst->directory = p = talloc_strdup(inst, inst->filename);

	p = strrchr(p, '/');
	if (!p) {
		cf_log_err(cs, "Filename must contain '/'");
		return -1;
	}

	*p = '\0';

	if (!inst->filename_work) {
		inst->filename_work = talloc_typed_asprintf(inst, "%s/detail.work", inst->directory);
	}

	/*
	 *	We need this for the lock.
	 */
	inst->mode = O_RDWR;

	return 0;
}
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
			 RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
{
	ssize_t rcode;
	char *str = NULL, *child;
	REQUEST *ref;

	XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);

	switch (node->type) {
		/*
		 *	Don't escape this.
		 */
	case XLAT_LITERAL:
		XLAT_DEBUG("xlat_aprint LITERAL");
		return talloc_strdup(ctx, node->fmt);

		/*
		 *	Do a one-character expansion.
		 */
	case XLAT_PERCENT:
	{
		char const *p;
		char *nl;
		size_t freespace = 256;
		struct tm ts;
		time_t when;

		XLAT_DEBUG("xlat_aprint PERCENT");

		str = talloc_array(ctx, char, freespace); /* @todo do better allocation */
		p = node->fmt;

		when = request->timestamp;
		if (request->packet) {
			when = request->packet->timestamp.tv_sec;
		}

		switch (*p) {
		case '%':
			str[0] = '%';
			str[1] = '\0';
			break;

		case 'd': /* request day */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%d", &ts);
			break;

		case 'l': /* request timestamp */
			snprintf(str, freespace, "%lu",
				 (unsigned long) when);
			break;

		case 'm': /* request month */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%m", &ts);
			break;

		case 't': /* request timestamp */
			CTIME_R(&when, str, freespace);
			nl = strchr(str, '\n');
			if (nl) *nl = '\0';
			break;

		case 'D': /* request date */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y%m%d", &ts);
			break;

		case 'G': /* request minute */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%M", &ts);
			break;

		case 'H': /* request hour */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%H", &ts);
			break;

		case 'I': /* Request ID */
			if (request->packet) {
				snprintf(str, freespace, "%i", request->packet->id);
			}
			break;

		case 'S': /* request timestamp in SQL format*/
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts);
			break;

		case 'T': /* request timestamp */
			if (!localtime_r(&when, &ts)) goto error;
			strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts);
			break;

		case 'Y': /* request year */
			if (!localtime_r(&when, &ts)) {
				error:
				REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno));
				talloc_free(str);
				return NULL;
			}
			strftime(str, freespace, "%Y", &ts);
			break;

		default:
			rad_assert(0 == 1);
			break;
		}
	}
		break;

	case XLAT_ATTRIBUTE:
		XLAT_DEBUG("xlat_aprint ATTRIBUTE");
		ref = request;
		if (radius_request(&ref, node->ref) < 0) {
			return NULL;
		}

		/*
		 *	Some attributes are virtual <sigh>
		 */
		str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true);
		if (str) {
			XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str);
		}
		break;

	case XLAT_VIRTUAL:
		XLAT_DEBUG("xlat_aprint VIRTUAL");
		str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
		rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024);
		if (rcode < 0) {
			talloc_free(str);
			return NULL;
		}
		break;

	case XLAT_MODULE:
		XLAT_DEBUG("xlat_aprint MODULE");
		if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
			return NULL;
		}

		XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child);

		str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
		*str = '\0';	/* Be sure the string is NULL terminated, we now only free on error */

		rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024);
		talloc_free(child);
		if (rcode < 0) {
			talloc_free(str);
			return NULL;
		}
		break;

#ifdef HAVE_REGEX_H
	case XLAT_REGEX:
		XLAT_DEBUG("xlat_aprint REGEX");
		child = request_data_reference(request, request,
					       REQUEST_DATA_REGEX | node->num);
		if (!child) return NULL;

		str = talloc_strdup(ctx, child);
		break;
#endif

	case XLAT_ALTERNATE:
		XLAT_DEBUG("xlat_aprint ALTERNATE");
		rad_assert(node->child != NULL);
		rad_assert(node->alternate != NULL);

		str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
		if (str) break;

		str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
		break;

	}

	/*
	 *	Escape the non-literals we found above.
	 */
	if (str && escape) {
		char *escaped;

		escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */
		escape(request, escaped, 1024, str, escape_ctx);
		talloc_free(str);
		str = escaped;
	}
static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
					 char const **error)
{
	ssize_t slen;
	char *p;
	xlat_exp_t *node;

	rad_assert(fmt[0] == '%');
	rad_assert(fmt[1] == '{');
	rad_assert(fmt[2] == '%');
	rad_assert(fmt[3] == '{');

	XLAT_DEBUG("ALTERNATE: %s", fmt);

	node = talloc_zero(ctx, xlat_exp_t);
	node->type = XLAT_ALTERNATE;

	p = fmt + 2;
	slen = xlat_tokenize_expansion(node, p, &node->child, error);
	if (slen <= 0) {
		talloc_free(node);
		return slen - (p - fmt);
	}
	p += slen;

	if (p[0] != ':') {
		talloc_free(node);
		*error = "Expected ':' after first expansion";
		return -(p - fmt);
	}
	p++;

	if (p[0] != '-') {
		talloc_free(node);
		*error = "Expected '-' after ':'";
		return -(p - fmt);
	}
	p++;

	/*
	 *	Allow the RHS to be empty as a special case.
	 */
	if (*p == '}') {
		/*
		 *	Hack up an empty string.
		 */
		node->alternate = talloc_zero(node, xlat_exp_t);
		node->alternate->type = XLAT_LITERAL;
		node->alternate->fmt = talloc_strdup(node->alternate, "");
		*(p++) = '\0';

	} else {
		slen = xlat_tokenize_literal(node, p,  &node->alternate, true, error);
		if (slen <= 0) {
			talloc_free(node);
			return slen - (p - fmt);
		}

		if (!node->alternate) {
			talloc_free(node);
			*error = "Empty expansion is invalid";
			return -(p - fmt);
		}
		p += slen;
	}

	*head = node;
	return p - fmt;
}
Exemple #8
0
/*
 *	Allocate the thread pool, and seed it with an initial number
 *	of threads.
 *
 *	FIXME: What to do on a SIGHUP???
 */
int thread_pool_init(CONF_SECTION *cs, int *spawn_flag)
{
	int		i, rcode;
	CONF_SECTION	*pool_cf;
	time_t		now;

	now = time(NULL);

	rad_assert(spawn_flag != NULL);
	rad_assert(*spawn_flag == TRUE);
	rad_assert(pool_initialized == FALSE); /* not called on HUP */

	pool_cf = cf_subsection_find_next(cs, NULL, "thread");
	if (!pool_cf) *spawn_flag = FALSE;

	/*
	 *	Initialize the thread pool to some reasonable values.
	 */
	memset(&thread_pool, 0, sizeof(THREAD_POOL));
	thread_pool.head = NULL;
	thread_pool.tail = NULL;
	thread_pool.total_threads = 0;
	thread_pool.max_thread_num = 1;
	thread_pool.cleanup_delay = 5;
	thread_pool.spawn_flag = *spawn_flag;
	
	/*
	 *	Don't bother initializing the mutexes or
	 *	creating the hash tables.  They won't be used.
	 */
	if (!*spawn_flag) return 0;
	
#ifdef WNOHANG
	if ((pthread_mutex_init(&thread_pool.wait_mutex,NULL) != 0)) {
		radlog(L_ERR, "FATAL: Failed to initialize wait mutex: %s",
		       strerror(errno));
		return -1;
	}
	
	/*
	 *	Create the hash table of child PID's
	 */
	thread_pool.waiters = fr_hash_table_create(pid_hash,
						   pid_cmp,
						   free);
	if (!thread_pool.waiters) {
		radlog(L_ERR, "FATAL: Failed to set up wait hash");
		return -1;
	}
#endif

	if (cf_section_parse(pool_cf, NULL, thread_config) < 0) {
		return -1;
	}

	/*
	 *	Catch corner cases.
	 */
	if (thread_pool.min_spare_threads < 1)
		thread_pool.min_spare_threads = 1;
	if (thread_pool.max_spare_threads < 1)
		thread_pool.max_spare_threads = 1;
	if (thread_pool.max_spare_threads < thread_pool.min_spare_threads)
		thread_pool.max_spare_threads = thread_pool.min_spare_threads;

	/*
	 *	The pool has already been initialized.  Don't spawn
	 *	new threads, and don't forget about forked children,
	 */
	if (pool_initialized) {
		return 0;
	}

	/*
	 *	Initialize the queue of requests.
	 */
	memset(&thread_pool.semaphore, 0, sizeof(thread_pool.semaphore));
	rcode = sem_init(&thread_pool.semaphore, 0, SEMAPHORE_LOCKED);
	if (rcode != 0) {
		radlog(L_ERR, "FATAL: Failed to initialize semaphore: %s",
		       strerror(errno));
		return -1;
	}

	rcode = pthread_mutex_init(&thread_pool.queue_mutex,NULL);
	if (rcode != 0) {
		radlog(L_ERR, "FATAL: Failed to initialize queue mutex: %s",
		       strerror(errno));
		return -1;
	}

	/*
	 *	Allocate multiple fifos.
	 */
	for (i = 0; i < RAD_LISTEN_MAX; i++) {
		thread_pool.fifo[i] = fr_fifo_create(65536, NULL);
		if (!thread_pool.fifo[i]) {
			radlog(L_ERR, "FATAL: Failed to set up request fifo");
			return -1;
		}
	}

#ifdef HAVE_OPENSSL_CRYPTO_H
	/*
	 *	If we're linking with OpenSSL too, then we need
	 *	to set up the mutexes and enable the thread callbacks.
	 */
	if (!setup_ssl_mutexes()) {
		radlog(L_ERR, "FATAL: Failed to set up SSL mutexes");
		return -1;
	}
#endif


	/*
	 *	Create a number of waiting threads.
	 *
	 *	If we fail while creating them, do something intelligent.
	 */
	for (i = 0; i < thread_pool.start_threads; i++) {
		if (spawn_thread(now) == NULL) {
			return -1;
		}
	}

	DEBUG2("Thread pool initialized");
	pool_initialized = TRUE;
	return 0;
}
/** Evaluate a template
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] vpt the template to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
			 value_pair_tmpl_t const *vpt)
{
	int rcode;
	int modcode;
	char *buffer;

	switch (vpt->type) {
	case VPT_TYPE_LITERAL:
		modcode = fr_str2int(modreturn_table, vpt->name, RLM_MODULE_UNKNOWN);
		if (modcode != RLM_MODULE_UNKNOWN) {
			rcode = (modcode == modreturn);
			break;
		}

		/*
		 *	Else it's a literal string.  Empty string is
		 *	false, non-empty string is true.
		 *
		 *	@todo: Maybe also check for digits?
		 *
		 *	The VPT *doesn't* have a "bare word" type,
		 *	which arguably it should.
		 */
		rcode = (vpt->name != '\0');
		break;

	case VPT_TYPE_ATTR:
	case VPT_TYPE_LIST:
		if (radius_tmpl_get_vp(NULL, request, vpt) == 0) {
			rcode = true;
		} else {
			rcode = false;
		}
		break;

		/*
		 *	FIXME: expand the strings
		 *	if not empty, return!
		 */
	case VPT_TYPE_XLAT_STRUCT:
	case VPT_TYPE_XLAT:
	case VPT_TYPE_EXEC:
		if (!*vpt->name) return false;
		rcode = radius_expand_tmpl(&buffer, request, vpt);
		if (rcode < 0) {
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}
		rcode = (buffer && (*buffer != '\0'));
		talloc_free(buffer);
		break;

		/*
		 *	Can't have a bare ... (/foo/) ...
		 */
	case VPT_TYPE_REGEX:
	case VPT_TYPE_REGEX_STRUCT:
		EVAL_DEBUG("FAIL %d", __LINE__);
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		rcode = -1;
		break;
	}

	return rcode;
}
Exemple #10
0
/*
 *	The main thread handler for requests.
 *
 *	Wait on the semaphore until we have it, and process the request.
 */
static void *request_handler_thread(void *arg)
{
	RAD_REQUEST_FUNP  fun;
	THREAD_HANDLE	  *self = (THREAD_HANDLE *) arg;

	/*
	 *	Loop forever, until told to exit.
	 */
	do {
		/*
		 *	Wait to be signalled.
		 */
		DEBUG2("Thread %d waiting to be assigned a request",
		       self->thread_num);
	re_wait:
		if (sem_wait(&thread_pool.semaphore) != 0) {
			/*
			 *	Interrupted system call.  Go back to
			 *	waiting, but DON'T print out any more
			 *	text.
			 */
			if (errno == EINTR) {
				DEBUG2("Re-wait %d", self->thread_num);
				goto re_wait;
			}
			radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n",
			       self->thread_num, strerror(errno));
			break;
		}

		DEBUG2("Thread %d got semaphore", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
 		/*
		 *	Clear the error queue for the current thread.
		 */
		ERR_clear_error ();
#endif

		/*
		 *	Try to grab a request from the queue.
		 *
		 *	It may be empty, in which case we fail
		 *	gracefully.
		 */
		if (!request_dequeue(&self->request, &fun)) continue;

		self->request->child_pid = self->pthread_id;
		self->request_count++;

		DEBUG2("Thread %d handling request %d, (%d handled so far)",
		       self->thread_num, self->request->number,
		       self->request_count);

		radius_handle_request(self->request, fun);

		/*
		 *	Update the active threads.
		 */
		pthread_mutex_lock(&thread_pool.queue_mutex);
		rad_assert(thread_pool.active_threads > 0);
		thread_pool.active_threads--;
		pthread_mutex_unlock(&thread_pool.queue_mutex);
	} while (self->status != THREAD_CANCELLED);

	DEBUG2("Thread %d exiting...", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
	/*
	 *	If we linked with OpenSSL, the application
	 *	must remove the thread's error queue before
	 *	exiting to prevent memory leaks.
	 */
	ERR_remove_state(0);
#endif

	/*
	 *  Do this as the LAST thing before exiting.
	 */
	self->request = NULL;
	self->status = THREAD_EXITED;

	return NULL;
}
Exemple #11
0
/*
 *	Spawn a new thread, and place it in the thread pool.
 *
 *	The thread is started initially in the blocked state, waiting
 *	for the semaphore.
 */
static THREAD_HANDLE *spawn_thread(time_t now)
{
	int rcode;
	THREAD_HANDLE *handle;
	pthread_attr_t attr;

	/*
	 *	Ensure that we don't spawn too many threads.
	 */
	if (thread_pool.total_threads >= thread_pool.max_threads) {
		DEBUG2("Thread spawn failed.  Maximum number of threads (%d) already running.", thread_pool.max_threads);
		return NULL;
	}

	/*
	 *	Allocate a new thread handle.
	 */
	handle = (THREAD_HANDLE *) rad_malloc(sizeof(THREAD_HANDLE));
	memset(handle, 0, sizeof(THREAD_HANDLE));
	handle->prev = NULL;
	handle->next = NULL;
	handle->thread_num = thread_pool.max_thread_num++;
	handle->request_count = 0;
	handle->status = THREAD_RUNNING;
	handle->timestamp = time(NULL);

	/*
	 *	Initialize the thread's attributes to detached.
	 *
	 *	We could call pthread_detach() later, but if the thread
	 *	exits between the create & detach calls, it will need to
	 *	be joined, which will never happen.
	 */
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	/*
	 *	Create the thread detached, so that it cleans up it's
	 *	own memory when it exits.
	 *
	 *	Note that the function returns non-zero on error, NOT
	 *	-1.  The return code is the error, and errno isn't set.
	 */
	rcode = pthread_create(&handle->pthread_id, &attr,
			request_handler_thread, handle);
	if (rcode != 0) {
		radlog(L_ERR, "Thread create failed: %s",
		       strerror(rcode));
		return NULL;
	}
	pthread_attr_destroy(&attr);

	/*
	 *	One more thread to go into the list.
	 */
	thread_pool.total_threads++;
	DEBUG2("Thread spawned new child %d. Total threads in pool: %d",
			handle->thread_num, thread_pool.total_threads);

	/*
	 *	Add the thread handle to the tail of the thread pool list.
	 */
	if (thread_pool.tail) {
		thread_pool.tail->next = handle;
		handle->prev = thread_pool.tail;
		thread_pool.tail = handle;
	} else {
		rad_assert(thread_pool.head == NULL);
		thread_pool.head = thread_pool.tail = handle;
	}

	/*
	 *	Update the time we last spawned a thread.
	 */
	thread_pool.time_last_spawned = now;

	/*
	 *	And return the new handle to the caller.
	 */
	return handle;
}
Exemple #12
0
/*
 *	Remove a request from the queue.
 */
static int request_dequeue(REQUEST **prequest, RAD_REQUEST_FUNP *fun)
{
	RAD_LISTEN_TYPE i, start;
	REQUEST *request = NULL;

	reap_children();

	pthread_mutex_lock(&thread_pool.queue_mutex);

	/*
	 *	Clear old requests from all queues.
	 *
	 *	We only do one pass over the queue, in order to
	 *	amortize the work across the child threads.  Since we
	 *	do N checks for one request de-queued, the old
	 *	requests will be quickly cleared.
	 */
	for (i = 0; i < RAD_LISTEN_MAX; i++) {
		request = fr_fifo_peek(thread_pool.fifo[i]);
		if (!request ||
		    (request->master_state != REQUEST_STOP_PROCESSING)) {
			continue;
}
		/*
		 *	This entry was marked to be stopped.  Acknowledge it.
		 */
		request = fr_fifo_pop(thread_pool.fifo[i]);
		rad_assert(request != NULL);
		request->child_state = REQUEST_DONE;
		thread_pool.num_queued--;
	}

	start = 0;
 retry:
	/*
	 *	Pop results from the top of the queue
	 */
	for (i = start; i < RAD_LISTEN_MAX; i++) {
		request = fr_fifo_pop(thread_pool.fifo[i]);
		if (request) {
			start = i;
			break;
		}
	}

	if (!request) {
		pthread_mutex_unlock(&thread_pool.queue_mutex);
		*prequest = NULL;
		*fun = NULL;
		return 0;
	}

	rad_assert(thread_pool.num_queued > 0);
	thread_pool.num_queued--;
	*prequest = request;
	*fun = request->process;

	rad_assert(request->magic == REQUEST_MAGIC);
	rad_assert(*fun != NULL);

	/*
	 *	If the request has sat in the queue for too long,
	 *	kill it.
	 *
	 *	The main clean-up code can't delete the request from
	 *	the queue, and therefore won't clean it up until we
	 *	have acknowledged it as "done".
	 */
	if (request->master_state == REQUEST_STOP_PROCESSING) {
		request->child_state = REQUEST_DONE;
		goto retry;
	}

	/*
	 *	The thread is currently processing a request.
	 */
	thread_pool.active_threads++;

	pthread_mutex_unlock(&thread_pool.queue_mutex);

	return 1;
}
/*
 *	Read config files.
 *
 *	This function can ONLY be called from the main server process.
 */
int read_mainconfig(int reload)
{
	char const *p = NULL;
	CONF_SECTION *cs;
	struct stat statbuf;
	cached_config_t *cc;
	char buffer[1024];

	if (reload != 0) {
		ERROR("Reload is not implemented");
		return -1;
	}

	if (stat(radius_dir, &statbuf) < 0) {
		ERROR("Errors reading %s: %s",
		       radius_dir, fr_syserror(errno));
		return -1;
	}

#ifdef S_IWOTH
	if ((statbuf.st_mode & S_IWOTH) != 0) {
		ERROR("Configuration directory %s is globally writable.  Refusing to start due to insecure configuration.",
		       radius_dir);
	  return -1;
	}
#endif

#ifdef S_IROTH
	if (0 && (statbuf.st_mode & S_IROTH) != 0) {
		ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
		       radius_dir);
		return -1;
	}
#endif
	INFO("Starting - reading configuration files ...");

	/* Initialize the dictionary */
	if (!mainconfig.dictionary_dir) mainconfig.dictionary_dir = radius_dir;
	DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
	if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
		ERROR("Errors reading dictionary: %s",
				fr_strerror());
		return -1;
	}

	/* Read the configuration file */
	snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
		 radius_dir, mainconfig.name);
	if ((cs = cf_file_read(buffer)) == NULL) {
		ERROR("Errors reading or parsing %s", buffer);
		return -1;
	}

	/*
	 *	If there was no log destination set on the command line,
	 *	set it now.
	 */
	if (default_log.dest == L_DST_NULL) {
		if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
			fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
			cf_file_free(cs);
			return -1;
		}

		if (!radlog_dest) {
			fprintf(stderr, "radiusd: Error: No log destination specified.\n");
			cf_file_free(cs);
			return -1;
		}

		default_log.dest = fr_str2int(log_str2dst, radlog_dest,
					      L_DST_NUM_DEST);
		if (default_log.dest == L_DST_NUM_DEST) {
			fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n",
				radlog_dest);
			cf_file_free(cs);
			return -1;
		}

		if (default_log.dest == L_DST_SYSLOG) {
			/*
			 *	Make sure syslog_facility isn't NULL
			 *	before using it
			 */
			if (!syslog_facility) {
				fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n");
				cf_file_free(cs);
				return -1;
			}
			mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
			if (mainconfig.syslog_facility < 0) {
				fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
					syslog_facility);
				cf_file_free(cs);
				return -1;
			}

#ifdef HAVE_SYSLOG_H
			/*
			 *	Call openlog only once, when the
			 *	program starts.
			 */
			openlog(progname, LOG_PID, mainconfig.syslog_facility);
#endif

		} else if (default_log.dest == L_DST_FILES) {
			if (!mainconfig.log_file) {
				fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
				cf_file_free(cs);
				return -1;
			}
		}
	}

#ifdef HAVE_SETUID
	/*
	 *	Switch users as early as possible.
	 */
	if (!switch_users(cs)) exit(1);
#endif

	/*
	 *	Open the log file AFTER switching uid / gid.  If we
	 *	did switch uid/gid, then the code in switch_users()
	 *	took care of setting the file permissions correctly.
	 */
	if ((default_log.dest == L_DST_FILES) &&
	    (default_log.fd < 0)) {
		default_log.fd = open(mainconfig.log_file,
					    O_WRONLY | O_APPEND | O_CREAT, 0640);
		if (default_log.fd < 0) {
			fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
			cf_file_free(cs);
			return -1;
		}
	}

	/*
	 *	This allows us to figure out where, relative to
	 *	radiusd.conf, the other configuration files exist.
	 */
	if (cf_section_parse(cs, NULL, server_config) < 0) {
		return -1;
	}

	/*
	 *	We ignore colourization of output until after the
	 *	configuration files have been parsed.
	 */
	if (do_colourise) {
		p = getenv("TERM");
		if (!p || !isatty(default_log.fd) ||
		    (strstr(p, "xterm") == 0)) {
			mainconfig.colourise = false;
		} else {
			mainconfig.colourise = true;
		}
		p = NULL;
	}

	if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100;
	if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5;
	if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5;

	/*
	 *	Free the old configuration items, and replace them
	 *	with the new ones.
	 *
	 *	Note that where possible, we do atomic switch-overs,
	 *	to ensure that the pointers are always valid.
	 */
	rad_assert(mainconfig.config == NULL);
	root_config = mainconfig.config = cs;

	DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name);
	if (!realms_init(cs)) {
		return -1;
	}

	DEBUG2("%s: #### Loading Clients ####", mainconfig.name);
	if (!clients_parse_section(cs)) {
		return -1;
	}

	/*
	 *  Register the %{config:section.subsection} xlat function.
	 */
	xlat_register("config", xlat_config, NULL, NULL);
	xlat_register("client", xlat_client, NULL, NULL);

	/*
	 *	Starting the server, WITHOUT "-x" on the
	 *	command-line: use whatever is in the config
	 *	file.
	 */
	if (debug_flag == 0) {
		debug_flag = mainconfig.debug_level;
	}
	fr_debug_flag = debug_flag;

	/*
	 *  Go update our behaviour, based on the configuration
	 *  changes.
	 */

	/*
	 *	Sanity check the configuration for internal
	 *	consistency.
	 */
	if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
		mainconfig.reject_delay = mainconfig.cleanup_delay;
	}
	if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0;

	/*  Reload the modules.  */
	if (setup_modules(reload, mainconfig.config) < 0) {
		return -1;
	}

	if (chroot_dir) {
		if (chdir(radlog_dir) < 0) {
			ERROR("Failed to 'chdir %s' after chroot: %s",
			       radlog_dir, fr_syserror(errno));
			return -1;
		}
	}

	cc = rad_malloc(sizeof(*cc));
	memset(cc, 0, sizeof(*cc));

	cc->cs = cs;
	rad_assert(cs_cache == NULL);
	cs_cache = cc;

	return 0;
}
/** Logging callback to write log messages to a destination
 *
 * This allows the logging destination to be customised on a per request basis.
 *
 * @note Function does not write log output immediately
 *
 * @param[in] type	What type of message this is (error, warn, info, debug).
 * @param[in] lvl	At what logging level this message should be output.
 * @param[in] request	The current request.
 * @param[in] file	src file the log message was generated in.
 * @param[in] line	number the log message was generated on.
 * @param[in] fmt	sprintf style fmt string.
 * @param[in] ap	Arguments for the fmt string.
 * @param[in] uctx	Context data for the log function.
 */
static void logtee_it(fr_log_type_t type, fr_log_lvl_t lvl, REQUEST *request,
		      UNUSED char const *file, UNUSED int line,
		      char const *fmt, va_list ap, void *uctx)
{
	rlm_logtee_thread_t	*t = talloc_get_type_abort(uctx, rlm_logtee_thread_t);
	rlm_logtee_t const	*inst = t->inst;
	char			*msg, *exp;
	fr_cursor_t		cursor;
	VALUE_PAIR		*vp;
	log_dst_t		*dst;

	rad_assert(t->msg->vp_length == 0);	/* Should have been cleared before returning */

	/*
	 *	None of this should involve mallocs unless msg > 1k
	 */
	msg = talloc_typed_vasprintf(t->msg, fmt, ap);
	fr_value_box_strdup_buffer_shallow(NULL, &t->msg->data, attr_log_message, msg, true);

	t->type->vp_uint32 = (uint32_t) type;
	t->lvl->vp_uint32 = (uint32_t) lvl;

	fr_cursor_init(&cursor, &request->packet->vps);
	fr_cursor_prepend(&cursor, t->msg);
	fr_cursor_prepend(&cursor, t->type);
	fr_cursor_prepend(&cursor, t->lvl);
	fr_cursor_head(&cursor);

	/*
	 *	Now expand our fmt string to encapsulate the
	 *	message and any metadata
	 *
	 *	Fixme: Would be better to call tmpl_expand
	 *	into a variable length ring buffer.
	 */
	dst = request->log.dst;
	request->log.dst = NULL;
	if (tmpl_aexpand(t, &exp, request, inst->log_fmt, NULL, NULL) < 0) goto finish;
	request->log.dst = dst;

	fr_fring_overwrite(t->fring, exp);	/* Insert it into the buffer */

	if (!t->pending) {
		t->pending = true;
		logtee_fd_active(t);		/* Listen for when the fd is writable */
	}

finish:
	/*
	 *	Don't free, we re-use the VALUE_PAIRs for the next message
	 */
	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->lvl)) fr_cursor_append(&cursor, vp);

	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->type)) fr_cursor_append(&cursor, vp);

	vp = fr_cursor_remove(&cursor);
	if (!fr_cond_assert(vp == t->msg)) fr_cursor_append(&cursor, vp);

	fr_value_box_clear(&t->msg->data);		/* Clear message data */
}
Exemple #15
0
size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
{
	size_t len;
	char *p, *end;

	if (!node) {
		*buffer = '\0';
		return 0;
	}

	p = buffer;
	end = buffer + bufsize;

	while (node) {
		switch (node->type) {
		case XLAT_LITERAL:
			strlcpy(p, node->fmt, end - p);
			p += strlen(p);
			break;

		case XLAT_PERCENT:
			p[0] = '%';
			p[1] = node->fmt[0];
			p += 2;
			break;

		case XLAT_ATTRIBUTE:
			*(p++) = '%';
			*(p++) = '{';

			if (node->ref != REQUEST_CURRENT) {
				strlcpy(p, fr_int2str(request_refs, node->ref, "??"), end - p);
				p += strlen(p);
				*(p++) = '.';
			}

			if ((node->ref != REQUEST_CURRENT) ||
			    (node->list != PAIR_LIST_REQUEST)) {
				strlcpy(p, fr_int2str(pair_lists, node->list, "??"), end - p);
				p += strlen(p);
				*(p++) = ':';
			}

			strlcpy(p, node->da->name, end - p);
			p += strlen(p);

			if (node->tag != TAG_ANY) {
				*(p++) = ':';
				snprintf(p, end - p, "%u", node->tag);
				p += strlen(p);
			}

			if (node->num != 0) {
				*(p++) = '[';

				if (node->num == 65536) {
					*(p++) = '#';

				} else if (node->num == 65537) {
					*(p++) = '*';

				} else {
					snprintf(p, end - p, "%u", node->num);
					p += strlen(p);
				}
				*(p++) = ']';
			}
			*(p++) = '}';
			break;
#ifdef HAVE_REGEX_H
		case XLAT_REGEX:
			snprintf(p, end - p, "%%{%u}", node->num);
			p += strlen(p);
			break;
#endif
		case XLAT_VIRTUAL:
			*(p++) = '%';
			*(p++) = '{';
			strlcpy(p, node->fmt, end - p);
			p += strlen(p);
			*(p++) = '}';
			break;

		case XLAT_MODULE:
			*(p++) = '%';
			*(p++) = '{';
			strlcpy(p, node->xlat->name, end - p);
			p += strlen(p);
			*(p++) = ':';
			rad_assert(node->child != NULL);
			len = xlat_sprint(p, end - p, node->child);
			p += len;
			*(p++) = '}';
			break;

		case XLAT_ALTERNATE:
			*(p++) = '%';
			*(p++) = '{';

			len = xlat_sprint(p, end - p, node->child);
			p += len;

			*(p++) = ':';
			*(p++) = '-';

			len = xlat_sprint(p, end - p, node->alternate);
			p += len;

			*(p++) = '}';
			break;
		}


		if (p == end) break;

		node = node->next;
	}

	*p = '\0';

	return p - buffer;
}
Exemple #16
0
static int do_regex(REQUEST *request, value_pair_map_t const *map)
{
	int compare, rcode, ret;
	regex_t reg, *preg;
	char *lhs, *rhs;
	regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];

	/*
	 *  Expand and then compile it.
	 */
	switch (map->src->type) {
	case VPT_TYPE_REGEX:
		rcode = radius_expand_tmpl(&rhs, request, map->src);
		if (rcode < 0) {
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}
		rad_assert(rhs != NULL);

		compare = regcomp(&reg, rhs, REG_EXTENDED | (map->src->vpt_iflag ? REG_ICASE : 0));
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				ERROR("Failed compiling regular expression: %s", errbuf);
			}
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		preg = &reg;
		break;

	case VPT_TYPE_REGEX_STRUCT:
		preg = map->src->vpt_preg;
		break;

	default:
		rad_assert(0);
		return -1;
	}

	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		ret = -1;
		goto finish;
	}
	rad_assert(lhs != NULL);

	/*
	 *  regexec doesn't initialise unused elements
	 */
	memset(&rxmatch, 0, sizeof(rxmatch));
	compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
	rad_regcapture(request, compare, lhs, rxmatch);
	ret = (compare == 0);

finish:
	/*
	 *  regcomp allocs extra memory for the expression, so if the
	 *  result wasn't cached we need to free it here.
	 */
	if (preg == &reg) regfree(&reg);

	return ret;
}
Exemple #17
0
static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da, int8_t tag,
			int num, bool return_null)
{
	VALUE_PAIR *vp, *vps = NULL;
	RADIUS_PACKET *packet = NULL;
	DICT_VALUE *dv;
	VALUE_PAIR myvp;

	/*
	 *	Arg.  Too much abstraction is annoying.
	 */
	switch (list) {
	default:
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);

	case PAIR_LIST_CONTROL:
		vps = request->config_items;
		break;

	case PAIR_LIST_REQUEST:
		packet = request->packet;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_REPLY:
		packet = request->reply;
		if (packet) vps = packet->vps;
		break;

#ifdef WITH_PROXY
	case PAIR_LIST_PROXY_REQUEST:
		packet = request->proxy;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_PROXY_REPLY:
		packet = request->proxy_reply;
		if (packet) vps = packet->vps;
		break;
#endif

#ifdef WITH_COA
	case PAIR_LIST_COA:
	case PAIR_LIST_DM:
		if (request->coa) packet = request->coa->packet;
		if (packet) vps = packet->vps;
		break;

	case PAIR_LIST_COA_REPLY:
	case PAIR_LIST_DM_REPLY:
		if (request->coa) packet = request->coa->reply;
		if (packet) vps = packet->vps;
		break;

#endif
	}

	/*
	 *	Now that we have the list, etc. handled,
	 *	find the VP and print it.
	 */
	if ((da->vendor != 0) || (da->attr < 256) || (list == PAIR_LIST_CONTROL)) {
	print_vp:
		vp = pairfind(vps, da->attr, da->vendor, tag);
		if (!vp) {
			return NULL;
		}
		goto do_print;
	}

	/*
	 *	Some non-packet expansions
	 */
	switch (da->attr) {
	default:
		break;		/* ignore them */

	case PW_CLIENT_SHORTNAME:
		if (request->client && request->client->shortname) {
			return talloc_strdup(ctx, request->client->shortname);
		}
		return talloc_strdup(ctx, "<UNKNOWN-CLIENT>");

	case PW_REQUEST_PROCESSING_STAGE:
		if (request->component) {
			return talloc_strdup(ctx, request->component);
		}
		return talloc_strdup(ctx, "server_core");

	case PW_VIRTUAL_SERVER:
		if (!request->server) return NULL;
		return talloc_strdup(ctx, request->server);

	case PW_MODULE_RETURN_CODE:
		return talloc_asprintf(ctx, "%d", request->simul_max); /* hack */
	}

	/*
	 *	All of the attributes must now refer to a packet.  If
	 *	there's no packet, we can't print any attribute
	 *	referencing it.
	 */
	if (!packet) {
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);
	}

	memset(&myvp, 0, sizeof(myvp));
	myvp.da = da;
	vp = NULL;

	switch (da->attr) {
	default:
		goto print_vp;

	case PW_PACKET_TYPE:
		dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
		if (dv) return talloc_strdup(ctx, dv->name);
		return talloc_asprintf(ctx, "%d", packet->code);

	case PW_RESPONSE_PACKET_TYPE:
	{
		int code = 0;

#ifdef WITH_PROXY
		if (request->proxy_reply && (!request->reply || !request->reply->code)) {
			code = request->proxy_reply->code;
		} else
#endif
			if (request->reply) {
				code = request->reply->code;
			}

		return talloc_strdup(ctx, fr_packet_codes[code]);
	}

	case PW_PACKET_AUTHENTICATION_VECTOR:
		myvp.length = sizeof(packet->vector);
		memcpy(&myvp.vp_octets, packet->vector, sizeof(packet->vector));
		vp = &myvp;
		break;

	case PW_CLIENT_IP_ADDRESS:
	case PW_PACKET_SRC_IP_ADDRESS:
		if (packet->src_ipaddr.af == AF_INET) {
			myvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
			vp = &myvp;
		}
		break;

	case PW_PACKET_DST_IP_ADDRESS:
		if (packet->dst_ipaddr.af == AF_INET) {
			myvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
			vp = &myvp;
		}
		break;

	case PW_PACKET_SRC_IPV6_ADDRESS:
		if (packet->src_ipaddr.af == AF_INET6) {
			memcpy(&myvp.vp_ipv6addr,
			       &packet->src_ipaddr.ipaddr.ip6addr,
			       sizeof(packet->src_ipaddr.ipaddr.ip6addr));
			vp = &myvp;
		}
		break;

	case PW_PACKET_DST_IPV6_ADDRESS:
		if (packet->dst_ipaddr.af == AF_INET6) {
			memcpy(&myvp.vp_ipv6addr,
			       &packet->dst_ipaddr.ipaddr.ip6addr,
			       sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
			vp = &myvp;
		}
		break;

	case PW_PACKET_SRC_PORT:
		myvp.vp_integer = packet->src_port;
		vp = &myvp;
		break;

	case PW_PACKET_DST_PORT:
		myvp.vp_integer = packet->dst_port;
		vp = &myvp;
		break;
	}

do_print:
	/*
	 *	Hack up the virtual attributes.
	 */
	if (num && (vp == &myvp)) {
		char *p, *q;

		/*
		 *	[*] means only one.
		 */
		if (num == 65537) num = 0;

		/*
		 *	[n] means NULL, as there's only one.
		 */
		if ((num > 0) && (num < 65536)) {
			return NULL;
		}

		p = vp_aprint(ctx, vp);
		rad_assert(p != NULL);

		/*
		 *	Get the length of it.
		 */
		if (num == 65536) {
			q = talloc_asprintf(ctx, "%d", (int) strlen(p));
			talloc_free(p);
			return q;
		}

		return p;
	}

	/*
	 *	We want the N'th VP.
	 */
	if (num) {
		int count = 0;
		vp_cursor_t cursor;

		/*
		 *	Return a count of the VPs.
		 */
		if (num == 65536) {
			fr_cursor_init(&cursor, &vp);
			while (fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag) != NULL) {
				count++;
			}

			return talloc_asprintf(ctx, "%d", count);
		}

		/*
		 *	Ugly, but working.
		 */
		if (num == 65537) {
			char *p, *q;

			(void) fr_cursor_init(&cursor, &vp);
			vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag);
			if (!vp) return NULL;
			p = vp_aprint(ctx, vp);
			while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) {
				q = vp_aprint(ctx, vp);
				p = talloc_strdup_append(p, ",");
				p = talloc_strdup_append(p, q);
			}

			return p;
		}

		(void) fr_cursor_init(&cursor, &vp);
		while ((vp = fr_cursor_next_by_num(&cursor, da->attr, da->vendor, tag)) != NULL) {
			if (count == num) {
				break;
			}
			count++;
		}
	}

	if (!vp) {
		if (return_null) return NULL;
		return vp_aprinttype(ctx, da->type);
	}

	return vp_aprint(ctx, vp);
}
Exemple #18
0
/*
 *	Copy data from src to dst, where the attributes are of
 *	different type.
 */
static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
{
	rad_assert(dst->da->type != src->da->type);

	if (dst->da->type == PW_TYPE_STRING) {
		dst->vp_strvalue = vp_aprint_value(dst, src);
		dst->length = strlen(dst->vp_strvalue);
		return 0;
	}

	if (dst->da->type == PW_TYPE_OCTETS) {
		if (src->da->type == PW_TYPE_STRING) {
			pairmemcpy(dst, src->vp_octets, src->length);	/* Copy embedded NULLs */
		} else {
			pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
		}
		return 0;
	}

	if (src->da->type == PW_TYPE_STRING) {
		return pairparsevalue(dst, src->vp_strvalue, 0);
	}

	if ((src->da->type == PW_TYPE_INTEGER64) &&
	    (dst->da->type == PW_TYPE_ETHERNET)) {
		uint8_t array[8];
		uint64_t i;

		i = htonll(src->vp_integer64);
		memcpy(array, &i, 8);

		/*
		 *	For OUIs in the DB.
		 */
		if ((array[0] != 0) || (array[1] != 0)) return -1;

		memcpy(&dst->vp_ether, &array[2], 6);
		dst->length = 6;
		return 0;
	}

	/*
	 *	The attribute we've found has to have a size which is
	 *	compatible with the type of the destination cast.
	 */
	if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
	    (src->length > dict_attr_sizes[dst->da->type][1])) {
		EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
		return -1;
	}

	if (src->da->type == PW_TYPE_OCTETS) {
		switch (dst->da->type) {
		case PW_TYPE_INTEGER64:
			dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
			break;


		case PW_TYPE_INTEGER:
		case PW_TYPE_DATE:
		case PW_TYPE_SIGNED:
			dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
			break;

		case PW_TYPE_SHORT:
			dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
			break;

		case PW_TYPE_BYTE:
			dst->vp_integer = src->vp_octets[0];
			break;

		default:
			memcpy(&dst->data, src->vp_octets, src->length);
			break;
		}

		dst->length = src->length;
		return 0;
	}

	/*
	 *	Convert host order to network byte order.
	 */
	if ((dst->da->type == PW_TYPE_IPV4_ADDR) &&
	    ((src->da->type == PW_TYPE_INTEGER) ||
	     (src->da->type == PW_TYPE_DATE) ||
	     (src->da->type == PW_TYPE_SIGNED))) {
		dst->vp_ipaddr = htonl(src->vp_integer);

	} else if ((src->da->type == PW_TYPE_IPV4_ADDR) &&
		   ((dst->da->type == PW_TYPE_INTEGER) ||
		    (dst->da->type == PW_TYPE_DATE) ||
		    (dst->da->type == PW_TYPE_SIGNED))) {
		dst->vp_integer = htonl(src->vp_ipaddr);

	} else {		/* they're of the same byte order */
		memcpy(&dst->data, &src->data, src->length);
	}

	dst->length = src->length;

	return 0;
}
Exemple #19
0
/** Register an xlat function.
 *
 * @param[in] name xlat name.
 * @param[in] func xlat function to be called.
 * @param[in] escape function to sanitize any sub expansions passed to the xlat function.
 * @param[in] instance of module that's registering the xlat function.
 * @return 0 on success, -1 on failure
 */
int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
{
	xlat_t	*c;
	xlat_t	my_xlat;
	rbnode_t *node;

	if (!name || !*name) {
		DEBUG("xlat_register: Invalid xlat name");
		return -1;
	}

	/*
	 *	First time around, build up the tree...
	 *
	 *	FIXME: This code should be hoisted out of this function,
	 *	and into a global "initialization".  But it isn't critical...
	 */
	if (!xlat_root) {
#ifdef WITH_UNLANG
		int i;
#endif

		xlat_root = rbtree_create(xlat_cmp, NULL, 0);
		if (!xlat_root) {
			DEBUG("xlat_register: Failed to create tree");
			return -1;
		}

#ifdef WITH_UNLANG
		for (i = 0; xlat_foreach_names[i] != NULL; i++) {
			xlat_register(xlat_foreach_names[i],
				      xlat_foreach, NULL, &xlat_inst[i]);
			c = xlat_find(xlat_foreach_names[i]);
			rad_assert(c != NULL);
			c->internal = true;
		}
#endif

#define XLAT_REGISTER(_x) xlat_register(STRINGIFY(_x), xlat_ ## _x, NULL, NULL); \
		c = xlat_find(STRINGIFY(_x)); \
		rad_assert(c != NULL); \
		c->internal = true

		XLAT_REGISTER(integer);
		XLAT_REGISTER(strlen);
		XLAT_REGISTER(length);
		XLAT_REGISTER(hex);
		XLAT_REGISTER(string);
		XLAT_REGISTER(xlat);
		XLAT_REGISTER(module);
		XLAT_REGISTER(debug_attr);

		xlat_register("debug", xlat_debug, NULL, &xlat_inst[0]);
		c = xlat_find("debug");
		rad_assert(c != NULL);
		c->internal = true;
	}

	/*
	 *	If it already exists, replace the instance.
	 */
	strlcpy(my_xlat.name, name, sizeof(my_xlat.name));
	my_xlat.length = strlen(my_xlat.name);
	c = rbtree_finddata(xlat_root, &my_xlat);
	if (c) {
		if (c->internal) {
			DEBUG("xlat_register: Cannot re-define internal xlat");
			return -1;
		}

		c->func = func;
		c->escape = escape;
		c->instance = instance;
		return 0;
	}

	/*
	 *	Doesn't exist.  Create it.
	 */
	c = talloc_zero(xlat_root, xlat_t);

	c->func = func;
	c->escape = escape;
	strlcpy(c->name, name, sizeof(c->name));
	c->length = strlen(c->name);
	c->instance = instance;

	node = rbtree_insertnode(xlat_root, c);
	if (!node) {
		talloc_free(c);
		return -1;
	}

	/*
	 *	Ensure that the data is deleted when the node is
	 *	deleted.
	 *
	 *	@todo: Maybe this should be the other way around...
	 *	when a thing IN the tree is deleted, it's automatically
	 *	removed from the tree.  But for now, this works.
	 */
	(void) talloc_steal(node, c);
	return 0;
}
Exemple #20
0
/** Evaluate a map
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] c the condition to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth,
			fr_cond_t const *c)
{
	int rcode;
	char *lhs, *rhs;
	value_pair_map_t *map;

	rad_assert(c->type == COND_TYPE_MAP);
	map = c->data.map;

	rad_assert(map->dst->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->src->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->dst->type != VPT_TYPE_LIST);
	rad_assert(map->src->type != VPT_TYPE_LIST);
	rad_assert(map->dst->type != VPT_TYPE_REGEX);
	rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT);

	EVAL_DEBUG("Map %s ? %s",
		   fr_int2str(template_names, map->dst->type, "???"),
		   fr_int2str(template_names, map->src->type, "???"));

	/*
	 *	Verify regexes.
	 */
	if ((map->src->type == VPT_TYPE_REGEX) ||
	    (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
		rad_assert(map->op == T_OP_REG_EQ);
	} else {
		rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
	}

	/*
	 *	They're both attributes.  Do attribute-specific work.
	 *
	 *	LHS is DST.  RHS is SRC <sigh>
	 */
	if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to ATTR");
		if ((radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) ||
		    (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0)) {
		    return -2;
		}

		return paircmp_op(lhs_vp, map->op, rhs_vp);
	}

	/*
	 *	LHS is a cast.  Do type-specific comparisons, as if
	 *	the LHS was a real attribute.
	 */
	if (c->cast) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		/*
		 *	Try to copy data from the VP which is being
		 *	casted, instead of printing it to a string and
		 *	then re-parsing it.
		 */
		if (map->dst->type == VPT_TYPE_ATTR) {
			VALUE_PAIR *cast_vp;

			if (radius_tmpl_get_vp(&cast_vp, request, map->dst) < 0) {
				return false;
			}

			lhs_vp = pairalloc(request, c->cast);
			if (!lhs_vp) return false;

			/*
			 *	In a separate function for clarity
			 */
			if (do_cast_copy(lhs_vp, cast_vp) < 0) {
				talloc_free(lhs_vp);
				return false;
			}

		} else {
			rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast);
			if (rcode < 0) {
				return rcode;
			}
		}
		rad_assert(lhs_vp);

		/*
		 *	Get either a real VP, or parse the RHS into a
		 *	VP, and return that.
		 */
		if (map->src->type == VPT_TYPE_ATTR) {
			if (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0) {
				return -2;
			}
		} else {
			rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast);
			if (rcode < 0) {
				return rcode;
			}
			rad_assert(rhs_vp);
		}
		if (!rhs_vp) return -2;

		EVAL_DEBUG("CAST to %s",
			   fr_int2str(dict_attr_types,
				      c->cast->type, "?Unknown?"));

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&lhs_vp);
		if (map->src->type != VPT_TYPE_ATTR) {
			pairfree(&rhs_vp);
		}
		return rcode;
	}

	/*
	 *	Might be a virtual comparison
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type != VPT_TYPE_REGEX) &&
	    (map->src->type != VPT_TYPE_REGEX_STRUCT) &&
	    (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
		int ret;
		VALUE_PAIR *lhs_vp;

		EVAL_DEBUG("virtual ATTR to DATA");

		rcode = get_cast_vp(&lhs_vp, request, map->src, map->dst->vpt_da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(lhs_vp);

		/*
		 *	paircompare requires the operator be set for the
		 *	check attribute.
		 */
		lhs_vp->op = map->op;
		ret = paircompare(request, request->packet->vps, lhs_vp, NULL);
		talloc_free(lhs_vp);
		if (ret == 0) {
			return true;
		}
		return false;
	}
	rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE);

	/*
	 *	RHS has been pre-parsed into binary data.  Go check
	 *	that.
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type == VPT_TYPE_DATA)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to DATA");

		if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
			return -2;
		}

		rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->vpt_da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(rhs_vp);

#ifdef WITH_EVAL_DEBUG
		debug_pair(lhs_vp);
		debug_pair(rhs_vp);
#endif

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&rhs_vp);
		return rcode;
	}

	rad_assert(map->src->type != VPT_TYPE_DATA);
	rad_assert(map->dst->type != VPT_TYPE_DATA);

#ifdef HAVE_REGEX_H
	/*
	 *	Parse regular expressions.
	 */
	if ((map->src->type == VPT_TYPE_REGEX) ||
	    (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
		return do_regex(request, map);
	}
#endif

	/*
	 *	The RHS now needs to be expanded into a string.
	 */
	rcode = radius_expand_tmpl(&rhs, request, map->src);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(rhs != NULL);

	/*
	 *	User-Name == FOO
	 *
	 *	Parse the RHS to be the same DA as the LHS.  do
	 *	comparisons.  So long as it's not a regex, which does
	 *	string comparisons.
	 *
	 *	The LHS may be a virtual attribute, too.
	 */
	if (map->dst->type == VPT_TYPE_ATTR) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to non-REGEX");

		/*
		 *	No LHS means no match
		 */
		if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
			/*
			 *	Not a real attr: might be a dynamic comparison.
			 */
			if ((map->dst->type == VPT_TYPE_ATTR) &&
			    (map->dst->vpt_da->vendor == 0) &&
			    radius_find_compare(map->dst->vpt_da)) {
				rhs_vp = pairalloc(request, map->dst->vpt_da);
				rad_assert(rhs_vp != NULL);
				if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
					talloc_free(rhs);
					EVAL_DEBUG("FAIL %d", __LINE__);
					return -1;
				}
				talloc_free(rhs);

				rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0);
				pairfree(&rhs_vp);
				return rcode;
			}

			return -2;
		}

		/*
		 *	Get VP for RHS
		 */
		rhs_vp = pairalloc(request, map->dst->vpt_da);
		rad_assert(rhs_vp != NULL);
		if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
			talloc_free(rhs);
			pairfree(&rhs_vp);
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		talloc_free(rhs);
		pairfree(&rhs_vp);
		return rcode;
	}

	/*
	 *	The LHS is a string.  Expand it.
	 */
	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(lhs != NULL);

	EVAL_DEBUG("LHS is %s", lhs);

	/*
	 *	Loop over the string, doing comparisons
	 */
	if (all_digits(lhs) && all_digits(rhs)) {
		int lint, rint;

		lint = strtoul(lhs, NULL, 0);
		rint = strtoul(rhs, NULL, 0);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (lint == rint);

		case T_OP_NE:
			return (lint != rint);

		case T_OP_LT:
			return (lint < rint);

		case T_OP_GT:
			return (lint > rint);

		case T_OP_LE:
			return (lint <= rint);

		case T_OP_GE:
			return (lint >= rint);

		default:
			break;
		}

	} else {
		rad_assert(lhs != NULL);
		rad_assert(rhs != NULL);

		rcode = strcmp(lhs, rhs);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (rcode == 0);

		case T_OP_NE:
			return (rcode != 0);

		case T_OP_LT:
			return (rcode < 0);

		case T_OP_GT:
			return (rcode > 0);

		case T_OP_LE:
			return (rcode <= 0);

		case T_OP_GE:
			return (rcode >= 0);

		default:
			break;
		}
	}

	EVAL_DEBUG("FAIL %d", __LINE__);
	return -1;
}
Exemple #21
0
static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
				       char const **error)
{
	ssize_t slen;
	char *p, *q, *brace;
	char const *attrname;
	xlat_exp_t *node;

	rad_assert(fmt[0] == '%');
	rad_assert(fmt[1] == '{');

	/*
	 *	%{%{...}:-bar}
	 */
	if ((fmt[2] == '%') && (fmt[3] == '{')) {
		return xlat_tokenize_alternation(ctx, fmt, head, error);
	}

	XLAT_DEBUG("EXPANSION: %s", fmt);
	node = talloc_zero(ctx, xlat_exp_t);
	attrname = node->fmt = fmt + 2;
	node->len = 0;

#ifdef HAVE_REGEX_H
	/*
	 *	Handle regex's specially.
	 */
	if (isdigit((int) fmt[2]) && (fmt[3] == '}')) {
		if (fmt[2] == '9') {
			talloc_free(node);
			*error = "Invalid regex reference";
			return -2;
		}

		XLAT_DEBUG("REGEX: %s", fmt);
		fmt[3] = '\0';
		node->num = fmt[2] - '0'; /* ASCII */

		node->type = XLAT_REGEX;
		*head = node;
		return 4;
	}
#endif /* HAVE_REGEX_H */

	/*
	 *	%{Attr-Name}
	 *	%{Attr-Name[#]}
	 *	%{Tunnel-Password:1}
	 *	%{Tunnel-Password:1[#]}
	 *	%{request:Attr-Name}
	 *	%{request:Tunnel-Password:1}
	 *	%{request:Tunnel-Password:1[#]}
	 *	%{mod:foo}
	 */
	q = brace = NULL;
	for (p = fmt + 2; *p != '\0'; p++) {
		if (*p == ':') break;

		if (isspace((int) *p)) break;

		if (*p == '[') break;

		if (*p == '}') break;
	}

	if (*p != ':') p = NULL;

	/*
	 *	Might be a module name reference.
	 */
	if (p) {
		*p = '\0';

		/*
		 *	%{mod:foo}
		 */
		node->xlat = xlat_find(node->fmt);
		if (node->xlat) {
			node->type = XLAT_MODULE;

			XLAT_DEBUG("MOD: %s --> %s", node->fmt, p);
			slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
			if (slen <= 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}
			p += slen + 1;

			*head = node;
			rad_assert(node->next == NULL);
			return p - fmt;
		}

		/*
		 *	Modules can have '}' in their RHS, so we
		 *	didn't check for that until now.
		 *
		 *	As of now, node->fmt MUST be a reference to an
		 *	attribute, however complicated.  So it MUST have a closing brace.
		 */
		brace = strchr(p + 1, '}');
		if (!brace) goto no_brace;
		*brace = '\0';

		/*
		 *	%{User-Name}
		 *	%{User-Name[1]}
		 *	%{Tunnel-Password:1}
		 *	%{request:Tunnel-Password:1}
		 *
		 *	<sigh>  The syntax is fairly poor.
		 */
		XLAT_DEBUG("Looking for list in '%s'", attrname);

		/*
		 *	Not a module.  Has to be an attribute
		 *	reference.
		 *
		 *	As of v3, we've removed %{request: ..>} as
		 *	internally registered xlats.
		 */
		*p = ':';
		node->ref = radius_request_name(&attrname, REQUEST_CURRENT);
		rad_assert(node->ref != REQUEST_UNKNOWN);

		node->list = radius_list_name(&attrname, PAIR_LIST_REQUEST);
		if (node->list == PAIR_LIST_UNKNOWN) {
			talloc_free(node);
			*error = "Unknown module";
			return -2;
		}

		/*
		 *	Check for a trailing tag.
		 */
		p = strchr(attrname, ':');
		if (p) *p = '\0';

	} else {
		brace = strchr(attrname, '}');
		if (!brace) {
		no_brace:
			talloc_free(node);
			*error = "No matching closing brace";
			return -1;	/* second character of format string */
		}
		*brace = '\0';

		node->ref = REQUEST_CURRENT;
		node->list = PAIR_LIST_REQUEST;
	}

	*brace = '\0';

	XLAT_DEBUG("Looking for attribute name in %s", attrname);

	/*
	 *	Allow for an array reference.  They come AFTER the
	 *	tag, if the tag exists.  Otherwise, they come after
	 *	the attribute name.
	 */
	if (p) {
		q = strchr(p + 1, '[');
	} else {
		q = strchr(attrname, '[');
	}
	if (q) *(q++) = '\0';

	if (!*attrname) {
		talloc_free(node);
		*error = "Empty expression is invalid";
		return -(attrname - fmt);
	}

	/*
	 *	It's either an attribute name, or a Tunnel-Password:TAG
	 *	with the ':' already set to NULL.
	 */
	node->da = dict_attrbyname(attrname);
	if (!node->da) {
		/*
		 *	Foreach.  Maybe other stuff, too.
		 */
		node->xlat = xlat_find(attrname);
		if (node->xlat) {
			node->type = XLAT_VIRTUAL;
			node->fmt = attrname;

			XLAT_DEBUG("VIRTUAL: %s", node->fmt);
			*head = node;
			rad_assert(node->next == NULL);
			brace++;
			return brace - fmt;
		}

		talloc_free(node);
		*error = "Unknown attribute";
		return -(attrname - fmt);
	}

	/*
	 *	Parse the tag.
	 */
	if (p) {
		unsigned long tag;
		char *end;

		if (!node->da->flags.has_tag) {
			talloc_free(node);
			*error = "Attribute cannot have a tag";
			return - (p - fmt);
		}

		tag = strtoul(p + 1, &end, 10);
		p++;

		if (tag == ULONG_MAX) {
			talloc_free(node);
			*error = "Invalid tag value";
			return - (p - fmt);
		}

		node->tag = tag;
		p = end;

		if (*p) {
			talloc_free(node);
			*error = "Unexpected text after tag";
			return - (p - fmt);
		}

	} else {
		node->tag = TAG_ANY;
		/* leave p alone */
	}

	/*
	 *	Check for array reference
	 */
	if (q) {
		unsigned long num;
		char *end;

		p = q;
		if (*p== '#') {
			node->num = 65536;
			p++;

		} else if (*p == '*') {
			node->num = 65537;
			p++;

		} else if (isdigit((int) *p)) {
			num = strtoul(p, &end, 10);
			if ((num == ULONG_MAX) || (num > 65535)) {
				talloc_free(node);
				*error = "Invalid number";
				return - (p - fmt);
			}
			p = end;
			DEBUG("END %s", p);
			node->num = num;

		} else {
			talloc_free(node);
			*error = "Invalid array reference";
			return - (p - fmt);
		}

		if (*p != ']') {
			talloc_free(node);
			*error = "Expected ']'";
			return - (p - fmt);
		}

		p++;
		if (*p) {
			talloc_free(node);
			*error = "Unexpected text after array reference";
			return - (p - fmt);
		}
	}

	rad_assert(!p || (p == brace));

	node->type = XLAT_ATTRIBUTE;
	p = brace + 1;

	*head = node;
	rad_assert(node->next == NULL);
	return p - fmt;
}
Exemple #22
0
/** Expand the RHS of a template
 *
 * @note Length of expanded string can be found with talloc_array_length(*out) - 1
 *
 * @param out where to write a pointer to the newly allocated buffer.
 * @param request Current request.
 * @param vpt to evaluate.
 * @return -1 on error, else 0.
 */
static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR *vp;
	*out = NULL;

	rad_assert(vpt->type != VPT_TYPE_LIST);

	switch (vpt->type) {
	case VPT_TYPE_LITERAL:
		EVAL_DEBUG("TMPL LITERAL");
		*out = talloc_typed_strdup(request, vpt->name);
		break;

	case VPT_TYPE_EXEC:
		EVAL_DEBUG("TMPL EXEC");
		*out = talloc_array(request, char, 1024);
		if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) {
			TALLOC_FREE(*out);
			return -1;
		}
		break;

	case VPT_TYPE_REGEX:
		EVAL_DEBUG("TMPL REGEX");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		break;

	case VPT_TYPE_XLAT:
		EVAL_DEBUG("TMPL XLAT");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		break;

	case VPT_TYPE_XLAT_STRUCT:
		EVAL_DEBUG("TMPL XLAT_STRUCT");
		/* Error in expansion, this is distinct from zero length expansion */
		if (radius_axlat_struct(out, request, vpt->vpt_xlat, NULL, NULL) < 0) {
			rad_assert(!*out);
			return -1;
		}
		RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
		RDEBUG2("   --> %s", *out);
		break;

	case VPT_TYPE_ATTR:
		EVAL_DEBUG("TMPL ATTR");
		if (radius_tmpl_get_vp(&vp, request, vpt) < 0) {
			return -2;
		}
		*out = vp_aprint_value(request, vp);
		if (!*out) {
			return -1;
		}
		break;

	case VPT_TYPE_DATA:
	case VPT_TYPE_REGEX_STRUCT:
		rad_assert(0 == 1);
		/* FALL-THROUGH */

	default:
		break;
	}

	EVAL_DEBUG("Expand tmpl --> %s", *out);
	return 0;
}
static void work_init(proto_detail_file_thread_t *thread)
{
	proto_detail_file_t const *inst = thread->inst;
	int fd, rcode;
	bool has_worker;

	pthread_mutex_lock(&thread->worker_mutex);
	has_worker = (thread->num_workers != 0);
	pthread_mutex_unlock(&thread->worker_mutex);

	/*
	 *	The worker is still processing the file, poll until
	 *	it's done.
	 */
	if (has_worker) {
		DEBUG3("proto_detail (%s): worker %s is still alive, waiting for it to finish.",
		       thread->name, inst->filename_work);
		goto delay;
	}

	rad_assert(thread->vnode_fd < 0);

	/*
	 *	See if there is a "detail.work" file.  If not, try to
	 *	rename an existing file to "detail.work".
	 */
	DEBUG3("Trying to open %s", inst->filename_work);
	fd = open(inst->filename_work, inst->mode);

	/*
	 *	If the work file didn't exist, try to rename detail* ->
	 *	detail.work, and return the newly opened file.
	 */
	if (fd < 0) {
		if (errno != ENOENT) {
			DEBUG("proto_detail (%s): Failed opening %s: %s",
			      thread->name, inst->filename_work,
			      fr_syserror(errno));
			goto delay;
		}

retry:
		fd = work_rename(thread);
	}

	/*
	 *	The work file still doesn't exist.  Go set up timers,
	 *	or wait for an event which signals us that something
	 *	in the directory changed.
	 */
	if (fd < 0) {
		struct timeval when, now;

		/*
		 *	Wait for the directory to change before
		 *	looking for another "detail" file.
		 */
		if (!inst->poll_interval) return;

delay:
		/*
		 *	Check every N seconds.
		 */
		when.tv_sec = inst->poll_interval;
		when.tv_usec = 0;

		DEBUG3("Waiting %d.%06ds for new files in %s",
		       (int) when.tv_sec, (int) when.tv_usec, thread->name);

		gettimeofday(&now, NULL);

		fr_timeval_add(&when, &when, &now);

		if (fr_event_timer_insert(thread, thread->el, &thread->ev,
					  &when, work_retry_timer, thread) < 0) {
			ERROR("Failed inserting poll timer for %s", inst->filename_work);
		}
		return;
	}

	thread->lock_interval = USEC / 10;

	/*
	 *	It exists, go process it!
	 *
	 *	We will get back to the main loop when the
	 *	"detail.work" file is deleted.
	 */
	rcode = work_exists(thread, fd);
	if (rcode < 0) goto delay;

	/*
	 *	The file was empty, so we try to get another one.
	 */
	if (rcode == 1) goto retry;

	/*
	 *	Otherwise the child is successfully processing the
	 *	file.
	 */
}
Exemple #24
0
/*
 *	The pairmove() function in src/lib/valuepair.c does all sorts of
 *	extra magic that we don't want here.
 *
 *	FIXME: integrate this with the code calling it, so that we
 *	only paircopy() those attributes that we're really going to
 *	use.
 */
void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
{
	int i, j, count, from_count, to_count, tailto;
	vp_cursor_t cursor;
	VALUE_PAIR *vp, *next, **last;
	VALUE_PAIR **from_list, **to_list;
	bool *edited = NULL;
	REQUEST *fixup = NULL;

	if (!request) return;

	/*
	 *	Set up arrays for editing, to remove some of the
	 *	O(N^2) dependencies.  This also makes it easier to
	 *	insert and remove attributes.
	 *
	 *	It also means that the operators apply ONLY to the
	 *	attributes in the original list.  With the previous
	 *	implementation of pairmove(), adding two attributes
	 *	via "+=" and then "=" would mean that the second one
	 *	wasn't added, because of the existence of the first
	 *	one in the "to" list.  This implementation doesn't
	 *	have that bug.
	 *
	 *	Also, the previous implementation did NOT implement
	 *	"-=" correctly.  If two of the same attributes existed
	 *	in the "to" list, and you tried to subtract something
	 *	matching the *second* value, then the pairdelete()
	 *	function was called, and the *all* attributes of that
	 *	number were deleted.  With this implementation, only
	 *	the matching attributes are deleted.
	 */
	count = 0;
	for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++;
	from_list = talloc_array(request, VALUE_PAIR *, count);

	for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
	to_list = talloc_array(request, VALUE_PAIR *, count);

	/*
	 *	Move the lists to the arrays, and break the list
	 *	chains.
	 */
	from_count = 0;
	for (vp = from; vp != NULL; vp = next) {
		next = vp->next;
		from_list[from_count++] = vp;
		vp->next = NULL;
	}

	to_count = 0;
	for (vp = *to; vp != NULL; vp = next) {
		next = vp->next;
		to_list[to_count++] = vp;
		vp->next = NULL;
	}
	tailto = to_count;
	edited = talloc_zero_array(request, bool, to_count);

	RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);

	/*
	 *	Now that we have the lists initialized, start working
	 *	over them.
	 */
	for (i = 0; i < from_count; i++) {
		int found;

		RDEBUG4("::: Examining %s", from_list[i]->da->name);

		if (do_xlat) radius_xlat_do(request, from_list[i]);

		/*
		 *	Attribute should be appended, OR the "to" list
		 *	is empty, and we're supposed to replace or
		 *	"add if not existing".
		 */
		if (from_list[i]->op == T_OP_ADD) goto append;

		found = false;
		for (j = 0; j < to_count; j++) {
			if (edited[j] || !to_list[j] || !from_list[i]) continue;

			/*
			 *	Attributes aren't the same, skip them.
			 */
			if (from_list[i]->da != to_list[j]->da) {
				continue;
			}

			/*
			 *	We don't use a "switch" statement here
			 *	because we want to break out of the
			 *	"for" loop over 'j' in most cases.
			 */

			/*
			 *	Over-write the FIRST instance of the
			 *	matching attribute name.  We free the
			 *	one in the "to" list, and move over
			 *	the one in the "from" list.
			 */
			if (from_list[i]->op == T_OP_SET) {
				RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
				       to_list[j]->da->name, i, j);
				pairfree(&to_list[j]);
				to_list[j] = from_list[i];
				from_list[i] = NULL;
				edited[j] = true;
				break;
			}

			/*
			 *	Add the attribute only if it does not
			 *	exist... but it exists, so we stop
			 *	looking.
			 */
			if (from_list[i]->op == T_OP_EQ) {
				found = true;
				break;
			}

			/*
			 *	Delete every attribute, independent
			 *	of its value.
			 */
			if (from_list[i]->op == T_OP_CMP_FALSE) {
				goto delete;
			}

			/*
			 *	Delete all matching attributes from
			 *	"to"
			 */
			if ((from_list[i]->op == T_OP_SUB) ||
			    (from_list[i]->op == T_OP_CMP_EQ) ||
			    (from_list[i]->op == T_OP_LE) ||
			    (from_list[i]->op == T_OP_GE)) {
				int rcode;
				int old_op = from_list[i]->op;

				/*
				 *	Check for equality.
				 */
				from_list[i]->op = T_OP_CMP_EQ;

				/*
				 *	If equal, delete the one in
				 *	the "to" list.
				 */
				rcode = radius_compare_vps(NULL, from_list[i],
							   to_list[j]);
				/*
				 *	We may want to do more
				 *	subtractions, so we re-set the
				 *	operator back to it's original
				 *	value.
				 */
				from_list[i]->op = old_op;

				switch (old_op) {
				case T_OP_CMP_EQ:
					if (rcode != 0) goto delete;
					break;

				case T_OP_SUB:
					if (rcode == 0) {
					delete:
						RDEBUG4("::: DELETING %s FROM %d TO %d",
						       from_list[i]->da->name, i, j);
						pairfree(&to_list[j]);
						to_list[j] = NULL;
					}
					break;

					/*
					 *	Enforce <=.  If it's
					 *	>, replace it.
					 */
				case T_OP_LE:
					if (rcode > 0) {
						RDEBUG4("::: REPLACING %s FROM %d TO %d",
						       from_list[i]->da->name, i, j);
						pairfree(&to_list[j]);
						to_list[j] = from_list[i];
						from_list[i] = NULL;
						edited[j] = true;
					}
					break;

				case T_OP_GE:
					if (rcode < 0) {
						RDEBUG4("::: REPLACING %s FROM %d TO %d",
						       from_list[i]->da->name, i, j);
						pairfree(&to_list[j]);
						to_list[j] = from_list[i];
						from_list[i] = NULL;
						edited[j] = true;
					}
					break;
				}

				continue;
			}

			rad_assert(0 == 1); /* panic! */
		}
Exemple #25
0
static int tls_socket_recv(rad_listen_t *listener)
{
	int doing_init = false;
	ssize_t rcode;
	RADIUS_PACKET *packet;
	REQUEST *request;
	listen_socket_t *sock = listener->data;
	fr_tls_status_t status;
	RADCLIENT *client = sock->client;

	if (!sock->packet) {
		sock->packet = rad_alloc(sock, 0);
		if (!sock->packet) return 0;

		sock->packet->sockfd = listener->fd;
		sock->packet->src_ipaddr = sock->other_ipaddr;
		sock->packet->src_port = sock->other_port;
		sock->packet->dst_ipaddr = sock->my_ipaddr;
		sock->packet->dst_port = sock->my_port;

		if (sock->request) {
			(void) talloc_steal(sock->request, sock->packet);
			sock->request->packet = sock->packet;
		}
	}

	/*
	 *	Allocate a REQUEST for debugging.
	 */
	if (!sock->request) {
		sock->request = request = request_alloc(NULL);
		if (!sock->request) {
			ERROR("Out of memory");
			return 0;
		}

		rad_assert(request->packet == NULL);
		rad_assert(sock->packet != NULL);
		request->packet = sock->packet;

		request->component = "<core>";
		request->component = "<tls-connect>";

		/*
		 *	Not sure if we should do this on every packet...
		 */
		request->reply = rad_alloc(request, 0);
		if (!request->reply) return 0;

		request->options = RAD_REQUEST_OPTION_DEBUG2;

		rad_assert(sock->ssn == NULL);

		sock->ssn = tls_new_session(listener->tls, sock->request,
					    listener->tls->require_client_cert);
		if (!sock->ssn) {
			request_free(&sock->request);
			sock->packet = NULL;
			return 0;
		}

		(void) talloc_steal(sock, sock->ssn);
		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_CERTS, (void *)&request->packet->vps);

		doing_init = true;
	}

	rad_assert(sock->request != NULL);
	rad_assert(sock->request->packet != NULL);
	rad_assert(sock->packet != NULL);
	rad_assert(sock->ssn != NULL);

	request = sock->request;

	RDEBUG3("Reading from socket %d", request->packet->sockfd);
	PTHREAD_MUTEX_LOCK(&sock->mutex);
	rcode = read(request->packet->sockfd,
		     sock->ssn->dirty_in.data,
		     sizeof(sock->ssn->dirty_in.data));
	if ((rcode < 0) && (errno == ECONNRESET)) {
	do_close:
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		tls_socket_close(listener);
		return 0;
	}

	if (rcode < 0) {
		RDEBUG("Error reading TLS socket: %s", fr_syserror(errno));
		goto do_close;
	}

	/*
	 *	Normal socket close.
	 */
	if (rcode == 0) goto do_close;

	sock->ssn->dirty_in.used = rcode;

	dump_hex("READ FROM SSL", sock->ssn->dirty_in.data, sock->ssn->dirty_in.used);

	/*
	 *	Catch attempts to use non-SSL.
	 */
	if (doing_init && (sock->ssn->dirty_in.data[0] != handshake)) {
		RDEBUG("Non-TLS data sent to TLS socket: closing");
		goto do_close;
	}

	/*
	 *	Skip ahead to reading application data.
	 */
	if (SSL_is_init_finished(sock->ssn->ssl)) goto app;

	if (!tls_handshake_recv(request, sock->ssn)) {
		RDEBUG("FAILED in TLS handshake receive");
		goto do_close;
	}

	if (sock->ssn->dirty_out.used > 0) {
		tls_socket_write(listener, request);
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

app:
	/*
	 *	FIXME: Run the packet through a virtual server in
	 *	order to see if we like the certificate presented by
	 *	the client.
	 */

	status = tls_application_data(sock->ssn, request);
	RDEBUG("Application data status %d", status);

	if (status == FR_TLS_MORE_FRAGMENTS) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	if (sock->ssn->clean_out.used == 0) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	dump_hex("TUNNELED DATA", sock->ssn->clean_out.data, sock->ssn->clean_out.used);

	/*
	 *	If the packet is a complete RADIUS packet, return it to
	 *	the caller.  Otherwise...
	 */
	if ((sock->ssn->clean_out.used < 20) ||
	    (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) {
		RDEBUG("Received bad packet: Length %d contents %d",
		       sock->ssn->clean_out.used,
		       (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]);
		goto do_close;
	}

	packet = sock->packet;
	packet->data = talloc_array(packet, uint8_t, sock->ssn->clean_out.used);
	packet->data_len = sock->ssn->clean_out.used;
	sock->ssn->record_minus(&sock->ssn->clean_out, packet->data, packet->data_len);
	packet->vps = NULL;
	PTHREAD_MUTEX_UNLOCK(&sock->mutex);

	if (!rad_packet_ok(packet, 0, NULL)) {
		RDEBUG("Received bad packet: %s", fr_strerror());
		tls_socket_close(listener);
		return 0;	/* do_close unlocks the mutex */
	}

	/*
	 *	Copied from src/lib/radius.c, rad_recv();
	 */
	if (fr_debug_flag) {
		char host_ipaddr[128];

		if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
			RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d",
			       fr_packet_codes[packet->code],
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->id, (int) packet->data_len);
		} else {
			RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d",
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->code,
			       packet->id, (int) packet->data_len);
		}
	}

	FR_STATS_INC(auth, total_requests);

	return 1;
}
Exemple #26
0
static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
				     int brace, char const **error)
{
	char *p, *q;
	xlat_exp_t *node;

	if (!*fmt) return 0;

	XLAT_DEBUG("LITERAL: %s", fmt);

	node = talloc_zero(ctx, xlat_exp_t);
	node->fmt = fmt;
	node->len = 0;
	node->type = XLAT_LITERAL;

	p = fmt;
	q = fmt;

	while (*p) {
		/*
		 *	Convert \n to it's literal representation.
		 */
		if (p[0] == '\\') switch (p[1]) {
			case 't':
				*(q++) = '\t';
				p += 2;
				node->len++;
				continue;

			case 'n':
				*(q++) = '\n';
				p += 2;
				node->len++;
				continue;

			case 'x':
				p += 2;
				if (!p[0] || !p[1]) {
					talloc_free(node);
					*error = "Hex expansion requires two hex digits";
					return -(p - fmt);
				}

				if (!fr_hex2bin((uint8_t *) q, p, 2)) {
					talloc_free(node);
					*error = "Invalid hex characters";
					return -(p - fmt);
				}

				/*
				 *	Don't let people shoot themselves in the foot.
				 *	\x00 is forbidden.
				 */
				if (!*q) {
					talloc_free(node);
					*error = "Cannot add zero byte to printable string";
					return -(p - fmt);
				}

				p += 2;
				q++;
				node->len++;
				continue;

			default:
				*(q++) = *p;
				p += 2;
				node->len++;
				continue;
			}

		/*
		 *	Process the expansion.
		 */
		if ((p[0] == '%') && (p[1] == '{')) {
			ssize_t slen;

			XLAT_DEBUG("LITERAL: %s --> %s", node->fmt, p);
			slen = xlat_tokenize_expansion(node, p, &node->next, error);
			if (slen <= 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}
			*p = '\0'; /* end the literal */
			p += slen;

			rad_assert(node->next != NULL);

			/*
			 *	Short-circuit the recursive call.
			 *	This saves another function call and
			 *	memory allocation.
			 */
			if (!*p) break;

			/*
			 *	"foo %{User-Name} bar"
			 *	LITERAL		"foo "
			 *	EXPANSION	User-Name
			 *	LITERAL		" bar"
			 */
			slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
			rad_assert(slen != 0);
			if (slen < 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}

			p += slen;
			break;	/* stop processing the string */
		}

		/*
		 *	Check for valid single-character expansions.
		 */
		if (p[0] == '%') {
			ssize_t slen;
			xlat_exp_t *next;

			if (!p[1] || !strchr("%dlmtDGHISTY", p[1])) {
					talloc_free(node);
					*error = "Invalid variable expansion";
					p++;
					return - (p - fmt);
			}

			XLAT_DEBUG("PERCENT: %s --> %c", node->fmt, p[1]);
			next = talloc_zero(node, xlat_exp_t);
			next->fmt = p + 1;
			next->len = 1;
			next->type = XLAT_PERCENT;

			node->next = next;
			*p = '\0';
			p += 2;

			if (!*p) break;

			/*
			 *	And recurse.
			 */
			slen = xlat_tokenize_literal(node->next, p, &(node->next->next), brace, error);
			rad_assert(slen != 0);
			if (slen < 0) {
				talloc_free(node);
				return slen - (p - fmt);
			}

			p += slen;
			break;	/* stop processing the string */
		}

		/*
		 *	If required, eat the brace.
		 */
		if (brace && (*p == '}')) {
			*q = '\0';
			p++;
			break;
		}

		*(q++) = *(p++);
		node->len++;
	}

	/*
	 *	Squash zero-width literals
	 */
	if (node->len > 0) {
		*head = node;

	} else {
		(void) talloc_steal(ctx, node->next);
		*head = node->next;
		talloc_free(node);
	}

	return p - fmt;
}
/*
 *	End a session by faking a Stop packet to all accounting modules.
 */
int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
		char const *user,
		char const *sessionid, uint32_t cliaddr, char proto,
		int session_time)
{
	REQUEST *stopreq;
	VALUE_PAIR *vp, *userpair;
	int ret;

	stopreq = request_alloc_fake(request);
	stopreq->packet->code = PW_CODE_ACCOUNTING_REQUEST; /* just to be safe */
	stopreq->listener = request->listener;
	rad_assert(stopreq != NULL);

	/* Hold your breath */
#define PAIR(n,v,e) do { \
		if(!(vp = paircreate(stopreq->packet,n, 0))) {	\
			request_free(&stopreq); \
			ERROR("no memory"); \
			pairfree(&(stopreq->packet->vps)); \
			return 0; \
		} \
		vp->e = v; \
		pairadd(&(stopreq->packet->vps), vp); \
	} while(0)
#define INTPAIR(n,v) PAIR(n,v,vp_integer)
#define IPPAIR(n,v) PAIR(n,v,vp_ipaddr)
#define STRINGPAIR(n,v) do { \
	  if(!(vp = paircreate(stopreq->packet,n, 0))) {	\
		request_free(&stopreq); \
		ERROR("no memory"); \
		pairfree(&(stopreq->packet->vps)); \
		return 0; \
	} \
	pairstrcpy(vp, v);	\
	pairadd(&(stopreq->packet->vps), vp); \
	} while(0)

	INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
	IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
	INTPAIR(PW_ACCT_DELAY_TIME, 0);
	STRINGPAIR(PW_USER_NAME, user);
	userpair = vp;
	INTPAIR(PW_NAS_PORT, port);
	STRINGPAIR(PW_ACCT_SESSION_ID, sessionid);
	if(proto == 'P') {
		INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
		INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
	} else if(proto == 'S') {
		INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
		INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
	} else {
		INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
	}
	if(cliaddr != 0)
		IPPAIR(PW_FRAMED_IP_ADDRESS, cliaddr);
	INTPAIR(PW_ACCT_SESSION_TIME, session_time);
	INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
	INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
	INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
	INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);

	stopreq->username = userpair;
	stopreq->password = NULL;

	ret = rad_accounting(stopreq);

	/*
	 *  We've got to clean it up by hand, because no one else will.
	 */
	request_free(&stopreq);

	return ret;
}
Exemple #28
0
static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
{
	rad_assert(node != NULL);

	if (lvl >= (int) sizeof(xlat_tabs)) lvl = sizeof(xlat_tabs);

	while (node) {
		switch (node->type) {
		case XLAT_LITERAL:
			DEBUG("%.*sliteral: '%s'", lvl, xlat_tabs, node->fmt);
			break;

		case XLAT_PERCENT:
			DEBUG("%.*sliteral (with %%): '%c'", lvl, xlat_tabs, node->fmt[0]);
			break;

		case XLAT_ATTRIBUTE:
			rad_assert(node->da != NULL);
			DEBUG("%.*sattribute: %s", lvl, xlat_tabs, node->da->name);
			rad_assert(node->child == NULL);
			if ((node->tag != 0) || (node->num != 0)) {
				DEBUG("%.*s{", lvl, xlat_tabs);

				DEBUG("%.*sref  %d", lvl + 1, xlat_tabs, node->ref);
				DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->list);

				if (node->tag) DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag);
				if (node->num) {
					if (node->num == 65536) {
						DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
					} else if (node->num == 65537) {
						DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
					} else {
						DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->num);
					}
				}

				DEBUG("%.*s}", lvl, xlat_tabs);
			}
			break;

		case XLAT_VIRTUAL:
			rad_assert(node->fmt != NULL);
			DEBUG("%.*svirtual: %s", lvl, xlat_tabs, node->fmt);
			break;

		case XLAT_MODULE:
			rad_assert(node->xlat != NULL);
			DEBUG("%.*sxlat: %s", lvl, xlat_tabs, node->xlat->name);
			if (node->child) {
				DEBUG("%.*s{", lvl, xlat_tabs);
				xlat_tokenize_debug(node->child, lvl + 1);
				DEBUG("%.*s}", lvl, xlat_tabs);
			}
			break;

#ifdef HAVE_REGEX_H
		case XLAT_REGEX:
			DEBUG("%.*sregex-var: %d", lvl, xlat_tabs, node->num);
			break;
#endif

		case XLAT_ALTERNATE:
			DEBUG("%.*sif {", lvl, xlat_tabs);
			xlat_tokenize_debug(node->child, lvl + 1);
			DEBUG("%.*s}", lvl, xlat_tabs);
			DEBUG("%.*selse {", lvl, xlat_tabs);
			xlat_tokenize_debug(node->alternate, lvl + 1);
			DEBUG("%.*s}", lvl, xlat_tabs);
			break;
		}
		node = node->next;
	}
}
Exemple #29
0
/*
 *	For more than 2 modules with redundancy + load balancing
 *	across all of them, layering the "redundant" and
 *	"load-balance" groups gets too complicated.  As a result, we
 *	implement a special function to do this.
 */
static int call_modredundantloadbalance(int component, modgroup *g, REQUEST *request,
					int default_result)
{
	int count = 1;
	int myresult = default_result;
	int priority = 0;	/* default result has lowest priority  */
	modcallable *p, *child = NULL;

	/*
	 *	Catch people who have issues.
	 */
	if (!g->children) {
		DEBUG2("  WARNING! Asked to process empty redundant-load-balance group.  Returning %s.", lrad_int2str(rcode_table, default_result, "??"));
		return default_result;
	}

	/*
	 *	Pick a random child.
	 */

	/* Loop over the children */
	for(p = g->children; p; p = p->next) {
		if (!child) {
			child = p;
			count = 1;
			continue;
		}

		/*
		 *	Keep track of how many load balancing servers
		 *	we've gone through.
		 */
		count++;

		/*
		 *	See the "camel book" for why this works.
		 *
		 *	If (rand(0..n) < 1), pick the current realm.
		 *	We add a scale factor of 65536, to avoid
		 *	floating point.
		 */
		if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
			child = p;
		}
	}
	rad_assert(child != NULL);

	/*
	 *	Call the chosen child, with fail-over to the next one
	 *	if it is down.
	 */
	p = child;
	do {
		/*
		 *	Call the chosen entry.  If we're done, then
		 *	stop.
		 */
		if (!call_one(component, p, request, &priority, &myresult)) {
			break;
		}
		
		/*
		 *	Go to the next one, and wrap around to the beginning if
		 *	we reach the end.
		 */
		p = p->next;
		if (!p) p = g->children;
	} while (p != child);

	/*
	 *	And return whatever was decided.
	 */
	return myresult;
}
/*
 *	Read config files.
 *
 *	This function can ONLY be called from the main server process.
 */
int main_config_init(void)
{
	char const *p = NULL;
	CONF_SECTION *cs;
	struct stat statbuf;
	cached_config_t *cc;
	char buffer[1024];

	if (stat(radius_dir, &statbuf) < 0) {
		ERROR("Errors reading %s: %s",
		       radius_dir, fr_syserror(errno));
		return -1;
	}

#ifdef S_IWOTH
	if ((statbuf.st_mode & S_IWOTH) != 0) {
		ERROR("Configuration directory %s is globally writable.  Refusing to start due to insecure configuration.",
		       radius_dir);
	  return -1;
	}
#endif

#ifdef S_IROTH
	if (0 && (statbuf.st_mode & S_IROTH) != 0) {
		ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
		       radius_dir);
		return -1;
	}
#endif
	INFO("Starting - reading configuration files ...");

	/*
	 *	We need to load the dictionaries before reading the
	 *	configuration files.  This is because of the
	 *	pre-compilation in conffile.c.  That should probably
	 *	be fixed to be done as a second stage.
	 */
	if (!main_config.dictionary_dir) {
		main_config.dictionary_dir = talloc_typed_strdup(NULL, DICTDIR);
	}

	/*
	 *	Read the distribution dictionaries first, then
	 *	the ones in raddb.
	 */
	DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
	if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
		ERROR("Errors reading dictionary: %s",
		      fr_strerror());
		return -1;
	}

#define DICT_READ_OPTIONAL(_d, _n) \
do {\
	switch (dict_read(_d, _n)) {\
	case -1:\
		ERROR("Errors reading %s/%s: %s", _d, _n, fr_strerror());\
		return -1;\
	case 0:\
		DEBUG2("including dictionary file %s/%s", _d,_n);\
		break;\
	default:\
		break;\
	}\
} while (0)

	/*
	 *	Try to load protocol-specific dictionaries.  It's OK
	 *	if they don't exist.
	 */
#ifdef WITH_DHCP
	DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.dhcp");
#endif

#ifdef WITH_VMPS
	DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.vqp");
#endif

	/*
	 *	It's OK if this one doesn't exist.
	 */
	DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY);

	/* Read the configuration file */
	snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
		 radius_dir, main_config.name);
	if ((cs = cf_file_read(buffer)) == NULL) {
		ERROR("Errors reading or parsing %s", buffer);
		return -1;
	}

	/*
	 *	If there was no log destination set on the command line,
	 *	set it now.
	 */
	if (default_log.dst == L_DST_NULL) {
		if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
			fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
			cf_file_free(cs);
			return -1;
		}

		if (!radlog_dest) {
			fprintf(stderr, "radiusd: Error: No log destination specified.\n");
			cf_file_free(cs);
			return -1;
		}

		default_log.dst = fr_str2int(log_str2dst, radlog_dest,
					      L_DST_NUM_DEST);
		if (default_log.dst == L_DST_NUM_DEST) {
			fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n",
				radlog_dest);
			cf_file_free(cs);
			return -1;
		}

		if (default_log.dst == L_DST_SYSLOG) {
			/*
			 *	Make sure syslog_facility isn't NULL
			 *	before using it
			 */
			if (!syslog_facility) {
				fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n");
				cf_file_free(cs);
				return -1;
			}
			main_config.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
			if (main_config.syslog_facility < 0) {
				fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
					syslog_facility);
				cf_file_free(cs);
				return -1;
			}

#ifdef HAVE_SYSLOG_H
			/*
			 *	Call openlog only once, when the
			 *	program starts.
			 */
			openlog(progname, LOG_PID, main_config.syslog_facility);
#endif

		} else if (default_log.dst == L_DST_FILES) {
			if (!main_config.log_file) {
				fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
				cf_file_free(cs);
				return -1;
			}
		}
	}

#ifdef HAVE_SETUID
	/*
	 *	Switch users as early as possible.
	 */
	if (!switch_users(cs)) fr_exit(1);
#endif

	/*
	 *	Open the log file AFTER switching uid / gid.  If we
	 *	did switch uid/gid, then the code in switch_users()
	 *	took care of setting the file permissions correctly.
	 */
	if ((default_log.dst == L_DST_FILES) &&
	    (default_log.fd < 0)) {
		default_log.fd = open(main_config.log_file,
					    O_WRONLY | O_APPEND | O_CREAT, 0640);
		if (default_log.fd < 0) {
			fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
			cf_file_free(cs);
			return -1;
		}
	}

	/*
	 *	This allows us to figure out where, relative to
	 *	radiusd.conf, the other configuration files exist.
	 */
	if (cf_section_parse(cs, NULL, server_config) < 0) {
		return -1;
	}

	/*
	 *	We ignore colourization of output until after the
	 *	configuration files have been parsed.
	 */
	p = getenv("TERM");
	if (do_colourise && p && isatty(default_log.fd) && strstr(p, "xterm")) {
		default_log.colourise = true;
	} else {
		default_log.colourise = false;
	}

	/*
	 *	Starting the server, WITHOUT "-x" on the
	 *	command-line: use whatever is in the config
	 *	file.
	 */
	if (debug_flag == 0) {
		debug_flag = main_config.debug_level;
	}
	fr_debug_flag = debug_flag;

	FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100);
	FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, 10);
	FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10);

	/*
	 * Set default initial request processing delay to 1/3 of a second.
	 * Will be updated by the lowest response window across all home servers,
	 * if it is less than this.
	 */
	main_config.init_delay.tv_sec = 0;
	main_config.init_delay.tv_usec = 2* (1000000 / 3);

	/*
	 *	Free the old configuration items, and replace them
	 *	with the new ones.
	 *
	 *	Note that where possible, we do atomic switch-overs,
	 *	to ensure that the pointers are always valid.
	 */
	rad_assert(main_config.config == NULL);
	root_config = main_config.config = cs;

	DEBUG2("%s: #### Loading Realms and Home Servers ####", main_config.name);
	if (!realms_init(cs)) {
		return -1;
	}

	DEBUG2("%s: #### Loading Clients ####", main_config.name);
	if (!clients_parse_section(cs, false)) {
		return -1;
	}

	/*
	 *  Register the %{config:section.subsection} xlat function.
	 */
	xlat_register("config", xlat_config, NULL, NULL);
	xlat_register("client", xlat_client, NULL, NULL);
	xlat_register("getclient", xlat_getclient, NULL, NULL);

	/*
	 *  Go update our behaviour, based on the configuration
	 *  changes.
	 */

	/*
	 *	Sanity check the configuration for internal
	 *	consistency.
	 */
	FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, main_config.cleanup_delay);

	if (chroot_dir) {
		if (chdir(radlog_dir) < 0) {
			ERROR("Failed to 'chdir %s' after chroot: %s",
			       radlog_dir, fr_syserror(errno));
			return -1;
		}
	}

	cc = talloc_zero(NULL, cached_config_t);
	if (!cc) return -1;

	cc->cs = talloc_steal(cc ,cs);
	rad_assert(cs_cache == NULL);
	cs_cache = cc;

	/* Clear any unprocessed configuration errors */
	(void) fr_strerror();

	return 0;
}