Beispiel #1
0
static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
	module_instance_t *modinst;
	rlm_rediswho_t *inst = instance;

	inst->xlat_name = cf_section_name2(conf);

	if (!inst->xlat_name)
		inst->xlat_name = cf_section_name1(conf);

	inst->cs = conf;

	modinst = module_instantiate(cf_section_find("modules"),
				       inst->redis_instance_name);
	if (!modinst) {
		ERROR("rediswho: failed to find module instance \"%s\"",
		       inst->redis_instance_name);
		return -1;
	}

	if (strcmp(modinst->entry->name, "rlm_redis") != 0) {
		ERROR("rediswho: Module \"%s\""
		       " is not an instance of the redis module",
		       inst->redis_instance_name);
		return -1;
	}

	inst->redis_inst = (REDIS_INST *) modinst->insthandle;

	return 0;
}
/*
 *	Do any per-module initialization that is separate to each
 *	configured instance of the module.  e.g. set up connections
 *	to external databases, read configuration files, set up
 *	dictionary entries, etc.
 *
 *	If configuration information is given in the config section
 *	that must be referenced in later calls, store a handle to it
 *	in *instance otherwise put a null pointer there.
 */
static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
	module_instance_t *sqlinst;
	rlm_sqlippool_t *inst = instance;
	char const *pool_name = NULL;

	pool_name = cf_section_name2(conf);
	if (pool_name != NULL) {
		inst->pool_name = talloc_typed_strdup(inst, pool_name);
	} else {
		inst->pool_name = talloc_typed_strdup(inst, "ippool");
	}
	sqlinst = find_module_instance(cf_section_find("modules"),
				       inst->sql_instance_name, 1);
	if (!sqlinst) {
		cf_log_err_cs(conf, "failed to find sql instance named %s",
			   inst->sql_instance_name);
		return -1;
	}

	if (strcmp(sqlinst->entry->name, "rlm_sql") != 0) {
		cf_log_err_cs(conf, "Module \"%s\""
		       " is not an instance of the rlm_sql module",
		       inst->sql_instance_name);
		return -1;
	}

	inst->sql_inst = (rlm_sql_t *) sqlinst->insthandle;
	return 0;
}
Beispiel #3
0
static rlm_rcode_t arp_process(REQUEST *request)
{
	CONF_SECTION *unlang;

	request->server_cs = request->listener->server_cs;
	unlang = cf_section_find(request->server_cs, "arp", NULL);

	request->component = "arp";

	return unlang_interpret(request, unlang, RLM_MODULE_NOOP);
}
static int rediswho_instantiate(CONF_SECTION * conf, void ** instance)
{
	module_instance_t *modinst;
	rlm_rediswho_t *inst;

	/*
	 *	Set up a storage area for instance data
	 */
	inst = *instance = rad_malloc(sizeof (*inst));
	memset(inst, 0, sizeof (*inst));
    
	/*
	 *	If the configuration parameters can't be parsed, then
	 *	fail.
	 */
	if (cf_section_parse(conf, inst, module_config) < 0) {
		free(inst);
		return -1;
	}

	inst->xlat_name = cf_section_name2(conf);

	if (!inst->xlat_name) 
		inst->xlat_name = cf_section_name1(conf);

	inst->xlat_name = strdup(inst->xlat_name);
	inst->cs = conf;

	modinst = find_module_instance(cf_section_find("modules"),
				       inst->redis_instance_name, 1);
	if (!modinst) {
		radlog(L_ERR,
		       "rediswho: failed to find module instance \"%s\"",
		       inst->redis_instance_name);

		rediswho_detach(inst);
		return -1;
	}

	if (strcmp(modinst->entry->name, "rlm_redis") != 0) {
		radlog(L_ERR, "rediswho: Module \"%s\""
		       " is not an instance of the redis module",
		       inst->redis_instance_name);

		rediswho_detach(inst);
		return -1;
	}

	inst->redis_inst = (REDIS_INST *) modinst->insthandle;

	return 0;
}
/** Receive notification of present phase
 *
 * @note This is a callback for the sync_demux function.
 *
 * @param[in] conn	the sync belongs to.
 * @param[in] config	of the sync that entered the refresh present phase.
 * @param[in] sync_id	of the sync that entered the refresh present phase.
 * @param[in] phase	Refresh phase the sync was previously in.
 * @param[in] user_ctx	The listener.
 * @return 0.
 */
static int _proto_ldap_present(fr_ldap_connection_t *conn, sync_config_t const *config,
			       int sync_id, sync_phases_t phase, void *user_ctx)
{
	rad_listen_t		*listen = talloc_get_type_abort(user_ctx, rad_listen_t);

	if (!cf_section_find(listen->server_cs, "recv", "Present")) {
		DEBUG2("Present phase is not supported, reinitialising sync");

		return _proto_ldap_refresh_required(conn, config, sync_id, phase, user_ctx);
	}

	return 0;
}
Beispiel #6
0
/* standard foobar code */
static int sqlhpwippool_instantiate(CONF_SECTION *conf, void **instance)
{
	rlm_sqlhpwippool_t *data;
	module_instance_t *modinst;

	/* set up a storage area for instance data */
	data = rad_malloc(sizeof(*data));
	if (!data) return -1;
	memset(data, 0, sizeof(*data)); /* so _detach will know what to free */

	/* fail if the configuration parameters can't be parsed */
	if (cf_section_parse(conf, data, module_config) < 0) {
		sqlhpwippool_detach(*instance);
		return -1;
	}

	/* save my name */
	data->myname = cf_section_name2(conf);
	if (!data->myname) {
		data->myname = "(no name)";
	}

	data->sincesync = 0;

	modinst = find_module_instance(cf_section_find("modules"), (data->sqlinst_name), 1 );
	if (!modinst) {
		nvp_log(__LINE__, data, L_ERR,
		        "sqlhpwippool_instantiate(): cannot find module instance "
		        "named \"%s\"",
		        data->sqlinst_name);
		return -1;
	}

	/* check if the given instance is really a rlm_sql instance */
	if (strcmp(modinst->entry->name, "rlm_sql") != 0) {
		nvp_log(__LINE__, data, L_ERR,
		        "sqlhpwippool_instantiate(): given instance (%s) is not "
		        "an instance of the rlm_sql module",
		        data->sqlinst_name);
		return -1;
	}

	/* save pointers to useful "objects" */
	data->sqlinst = (SQL_INST *) modinst->insthandle;
	data->db = (rlm_sql_module_t *) data->sqlinst->module;

	/* everything went ok, cleanup pool */
	*instance = data;

	return ((nvp_cleanup(data)) ? 0 : -1);
}
/** Wrapper around dl_instance
 *
 * @param[in] ctx	to allocate data in (instance of proto_radius).
 * @param[out] out	Where to write a dl_instance_t containing the module handle and instance.
 * @param[in] parent	Base structure address.
 * @param[in] ci	#CONF_PAIR specifying the name of the type module.
 * @param[in] rule	unused.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
			   CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
{
	char const	*name = cf_pair_value(cf_item_to_pair(ci));
	dl_instance_t	*parent_inst;
	CONF_SECTION	*cs = cf_item_to_section(cf_parent(ci));
	CONF_SECTION	*transport_cs;

	transport_cs = cf_section_find(cs, name, NULL);

	/*
	 *	Allocate an empty section if one doesn't exist
	 *	this is so defaults get parsed.
	 */
	if (!transport_cs) transport_cs = cf_section_alloc(cs, cs, name, NULL);

	parent_inst = cf_data_value(cf_data_find(cs, dl_instance_t, "rlm_radius"));
	rad_assert(parent_inst);

	return dl_instance(ctx, out, transport_cs, parent_inst, name, DL_TYPE_SUBMODULE);
}
Beispiel #8
0
/** Initialise a module specific exfile handle
 *
 * @see exfile_init
 *
 * @param[in] ctx		to bind the lifetime of the exfile handle to.
 * @param[in] module		section.
 * @param[in] max_entries	Max file descriptors to cache, and manage locks for.
 * @param[in] max_idle		Maximum time a file descriptor can be idle before it's closed.
 * @param[in] locking		Whether	or not to lock the files.
 * @param[in] trigger_prefix	if NULL will be set automatically from the module CONF_SECTION.
 * @param[in] trigger_args	to make available in any triggers executed by the connection pool.
 * @return
 *	- New connection pool.
 *	- NULL on error.
 */
exfile_t *module_exfile_init(TALLOC_CTX *ctx,
			     CONF_SECTION *module,
			     uint32_t max_entries,
			     uint32_t max_idle,
			     bool locking,
			     char const *trigger_prefix,
			     VALUE_PAIR *trigger_args)
{
	char		trigger_prefix_buff[128];
	exfile_t	*handle;

	if (!trigger_prefix) {
		snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.file", cf_section_name1(module));
		trigger_prefix = trigger_prefix_buff;
	}

	handle = exfile_init(ctx, max_entries, max_idle, locking);
	if (!handle) return NULL;

	exfile_enable_triggers(handle, cf_section_find(module, "file", NULL), trigger_prefix, trigger_args);

	return handle;
}
/* standard foobar code */
static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
	rlm_sqlhpwippool_t *inst = instance;
	module_instance_t *sqlinst;

	/* save my name */
	inst->myname = cf_section_name2(conf);
	if (!inst->myname) {
		inst->myname = "(no name)";
	}

	inst->sincesync = 0;

	sqlinst = find_module_instance(cf_section_find("modules"), (inst->sql_module_instance), 1 );
	if (!sqlinst) {
		nvp_log(__LINE__, inst, L_ERR,
			"mod_instantiate(): cannot find module instance "
			"named \"%s\"",
			inst->sql_module_instance);
		return -1;
	}

	/* check if the given instance is really a rlm_sql instance */
	if (strcmp(sqlinst->entry->name, "rlm_sql") != 0) {
		nvp_log(__LINE__, inst, L_ERR,
			"mod_instantiate(): given instance (%s) is not "
			"an instance of the rlm_sql module",
			inst->sql_module_instance);
		return -1;
	}

	/* save pointers to useful "objects" */
	inst->sqlinst = (rlm_sql_t *) sqlinst->insthandle;
	inst->db = (rlm_sql_module_t *) inst->sqlinst->module;

	return ((nvp_cleanup(inst)) ? 0 : -1);
}
/** Initialize the rlm_couchbase module
 *
 * Intialize the module and create the initial Couchbase connection pool.
 *
 * @param  conf     The module configuration.
 * @param  instance The module instance.
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int mod_instantiate(void *instance, CONF_SECTION *conf)
{
	rlm_couchbase_t *inst = instance;   /* our module instance */

	{
		char *server, *p;
		size_t len, i;
		bool sep = false;

		len = talloc_array_length(inst->server_raw);
		server = p = talloc_array(inst, char, len);
		for (i = 0; i < len; i++) {
			switch (inst->server_raw[i]) {
			case '\t':
			case ' ':
			case ',':
				/* Consume multiple separators occurring in sequence */
				if (sep == true) continue;

				sep = true;
				*p++ = ';';
				break;

			default:
				sep = false;
				*p++ = inst->server_raw[i];
				break;
			}
		}

		*p = '\0';
		inst->server = server;
	}

	/* setup item map */
	if (mod_build_attribute_element_map(conf, inst) != 0) {
		/* fail */
		return -1;
	}

	/* initiate connection pool */
	inst->pool = module_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, NULL, NULL, NULL);

	/* check connection pool */
	if (!inst->pool) {
		ERROR("failed to initiate connection pool");
		/* fail */
		return -1;
	}

	/* load clients if requested */
	if (inst->read_clients) {
		CONF_SECTION *cs, *map, *tmpl; /* conf section */

		/* attempt to find client section */
		cs = cf_section_find(conf, "client", NULL);
		if (!cs) {
			ERROR("failed to find client section while loading clients");
			/* fail */
			return -1;
		}

		/* attempt to find attribute subsection */
		map = cf_section_find(cs, "attribute", NULL);
		if (!map) {
			ERROR("failed to find attribute subsection while loading clients");
			/* fail */
			return -1;
		}

		tmpl = cf_section_find(cs, "template", NULL);

		/* debugging */
		DEBUG("preparing to load client documents");

		/* attempt to load clients */
		if (mod_load_client_documents(inst, tmpl, map) != 0) {
			/* fail */
			return -1;
		}
	}

	/* return okay */
	return 0;
}
Beispiel #11
0
/*
 *	Find a module instance.
 */
module_instance_t *find_module_instance(const char *instname)
{
	CONF_SECTION *cs, *inst_cs;
	const char *name1, *name2;
	module_instance_t *node, **last;
	char module_name[256];

	/*
	 *	Look through the global module instance list for the
	 *	named module.
	 */
	last = &module_instance_list;
	for (node = module_instance_list; node != NULL; node = node->next) {
		/*
		 *	Found the named instance.  Return it.
		 */
		if (strcmp(node->name, instname) == 0)
			return node;

		/*
		 *	Keep a pointer to the last entry to update...
		 */
		last = &node->next;
	}

	/*
	 *	Instance doesn't exist yet. Try to find the
	 *	corresponding configuration section and create it.
	 */

	/*
	 *	Look for the 'modules' configuration section.
	 */
	cs = cf_section_find("modules");
	if (cs == NULL) {
		radlog(L_ERR|L_CONS, "ERROR: Cannot find a 'modules' section in the configuration file.\n");
		return NULL;
	}

	/*
	 *	Module instances are declared in the modules{} block
	 *	and referenced later by their name, which is the
	 *	name2 from the config section, or name1 if there was
	 *	no name2.
	 */
	name1 = name2 = NULL;
	for(inst_cs=cf_subsection_find_next(cs, NULL, NULL); 
			inst_cs != NULL;
			inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) {
		name1 = cf_section_name1(inst_cs);
		name2 = cf_section_name2(inst_cs);
		if ( (name2 && !strcmp(name2, instname)) ||
		     (!name2 && !strcmp(name1, instname)) )
			break;
	}
	if (inst_cs == NULL) {
		radlog(L_ERR|L_CONS, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname);
		return NULL;
	}

	/*
	 *	Found the configuration entry.
	 */
	node = rad_malloc(sizeof(*node));
	node->next = NULL;
	node->insthandle = NULL;
	
	/*
	 *	Link to the module by name: rlm_FOO-major.minor
	 */
	if (strncmp(name1, "rlm_", 4)) {
#if 0
		snprintf(module_name, sizeof(module_name), "rlm_%s-%d.%d",
			 name1, RADIUSD_MAJOR_VERSION, RADIUSD_MINOR_VERSION);
#else
		snprintf(module_name, sizeof(module_name), "rlm_%s",
			 name1);
#endif
	} else {
		strNcpy(module_name, name1, sizeof(module_name));

	}

	/*
	 *  FIXME: "radiusd.conf" is wrong here; must find cf filename
	 */
	node->entry = linkto_module(module_name, "radiusd.conf",
				    cf_section_lineno(inst_cs));
	if (!node->entry) {
		free(node);
		/* linkto_module logs any errors */
		return NULL;
	}
	
	/*
	 *	Call the module's instantiation routine.
	 */
	if ((node->entry->module->instantiate) &&
	    ((node->entry->module->instantiate)(inst_cs,
			&node->insthandle) < 0)) {
		radlog(L_ERR|L_CONS,
				"radiusd.conf[%d]: %s: Module instantiation failed.\n",
				cf_section_lineno(inst_cs), instname);
		free(node);
		return NULL;
	}

	/*
	 *	We're done.  Fill in the rest of the data structure,
	 *	and link it to the module instance list.
	 */
	strNcpy(node->name, instname, sizeof(node->name));

#if HAVE_PTHREAD_H
	/*
	 *	If we're threaded, check if the module is thread-safe.
	 *
	 *	If it isn't, we create a mutex.
	 */
	if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) {
		node->mutex = (pthread_mutex_t *) rad_malloc(sizeof(pthread_mutex_t));
		/*
		 *	Initialize the mutex.
		 */
		pthread_mutex_init(node->mutex, NULL);
	} else {
		/*
		 *	The module is thread-safe.  Don't give it a mutex.
		 */
		node->mutex = NULL;
	}

#endif	
	*last = node;

	DEBUG("Module: Instantiated %s (%s) ", name1, node->name);
	
	return node;
}
Beispiel #12
0
/*
 *	Parse the module config sections, and load
 *	and call each module's init() function.
 *
 *	Libtool makes your life a LOT easier, especially with libltdl.
 *	see: http://www.gnu.org/software/libtool/
 */
int setup_modules(void)
{
	int comp;
	CONF_SECTION *cs;

	/*
	 *  FIXME: This should be pulled from somewhere else.
	 */
	const char *filename="radiusd.conf";

	/*
	 *	No current list of modules: Go initialize libltdl.
	 */
	if (!module_list) {
		/*
		 *	Set the default list of preloaded symbols.
		 *	This is used to initialize libltdl's list of
		 *	preloaded modules. 
		 *
		 *	i.e. Static modules.
		 */
		LTDL_SET_PRELOADED_SYMBOLS();

		if (lt_dlinit() != 0) {
			radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n",
					lt_dlerror());
			exit(1); /* FIXME */
			
		}

		/*
		 *	Set the search path to ONLY our library directory.
		 *	This prevents the modules from being found from
		 *	any location on the disk.
		 */
		lt_dlsetsearchpath(radlib_dir);
		
		DEBUG2("Module: Library search path is %s",
				lt_dlgetsearchpath());

		/*
		 *	Initialize the components.
		 */
		for (comp = 0; comp < RLM_COMPONENT_COUNT; comp++) {
			components[comp] = NULL;
		}

	} else {
		detach_modules();
	}

	/*
	 *	Create any DICT_VALUE's for the types.  See
	 *	'doc/configurable_failover' for examples of 'authtype'
	 *	used to create new Auth-Type values.  In order to
	 *	let the user create new names, we've got to look for
	 *	those names, and create DICT_VALUE's for them.
	 */
	for (comp = 0; section_type_value[comp].section != NULL; comp++) {
		const char	*name2;
		DICT_ATTR	*dattr;
		DICT_VALUE	*dval;
		CONF_SECTION	*sub, *next;
		CONF_PAIR	*cp;

		/*
		 *  Big-time YUCK
		 */
		static int my_value = 32767;

		cs = cf_section_find(section_type_value[comp].section);

		if (!cs) continue;

		sub = NULL;
		do {
			/*
			 *	See if there's a sub-section by that
			 *	name.
			 */
			next = cf_subsection_find_next(cs, sub,
						      section_type_value[comp].typename);

			/*
			 *	Allow some old names, too.
			 */
			if (!next && (comp <= 4)) {
				
				next = cf_subsection_find_next(cs, sub,
							       old_section_type_value[comp].typename);
			}
			sub = next;

			/*
			 *	If so, look for it to define a new
			 *	value.
			 */
			name2 = cf_section_name2(sub);
			if (!name2) continue;


			/*
			 *	If the value already exists, don't
			 *	create it again.
			 */
			dval = dict_valbyname(section_type_value[comp].attr,
					      name2);
			if (dval) continue;

			/*
       			 *	Find the attribute for the value.
			 */
			dattr = dict_attrbyvalue(section_type_value[comp].attr);
			if (!dattr) continue;

			/*
			 *	Finally, create the new attribute.
			 */
			if (dict_addvalue(name2, dattr->name, my_value++) < 0) {
				radlog(L_ERR, "%s", librad_errstr);
				exit(1);
			}
		} while (sub != NULL);

		/*
		 *	Loop over the non-sub-sections, too.
		 */
		cp = NULL;
		do {
			/*
			 *	See if there's a conf-pair by that
			 *	name.
			 */
			cp = cf_pair_find_next(cs, cp, NULL);
			if (!cp) break;


			/*
			 *	If the value already exists, don't
			 *	create it again.
			 */
			name2 = cf_pair_attr(cp);
			dval = dict_valbyname(section_type_value[comp].attr,
					      name2);
			if (dval) continue;

			/*
       			 *	Find the attribute for the value.
			 */
			dattr = dict_attrbyvalue(section_type_value[comp].attr);
			if (!dattr) continue;

			/*
			 *	Finally, create the new attribute.
			 */
			if (dict_addvalue(name2, dattr->name, my_value++) < 0) {
				radlog(L_ERR, "%s", librad_errstr);
				exit(1);
			}
		} while (cp != NULL);
	} /* over the sections which can have redundent sub-sections */
Beispiel #13
0
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action)
{
	VALUE_PAIR *vp;
	rlm_rcode_t rcode;
	CONF_SECTION *unlang;
	fr_dict_enum_t const *dv;

	REQUEST_VERIFY(request);

	/*
	 *	Pass this through asynchronously to the module which
	 *	is waiting for something to happen.
	 */
	if (action != FR_IO_ACTION_RUN) {
		unlang_interpret_signal(request, (fr_state_signal_t) action);
		return FR_IO_DONE;
	}

	switch (request->request_state) {
	case REQUEST_INIT:
		if (request->parent && RDEBUG_ENABLED) {
			RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id);
			log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");
		}

		request->component = "radius";

		/*
		 *	We can run CoA-Request or Disconnect-Request sections here
		 */
		dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->packet->code));
		if (!dv) {
			REDEBUG("Failed to find value for &request:Packet-Type");
			return FR_IO_FAIL;
		}

		unlang = cf_section_find(request->server_cs, "recv", dv->alias);
		if (!unlang) {
			REDEBUG("Failed to find 'recv %s' section", dv->alias);
			return FR_IO_FAIL;
		}

		RDEBUG("Running 'recv %s' from file %s", dv->alias, cf_filename(unlang));
		unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			request->reply->code = request->packet->code + 1; /* ACK */
			break;

		case RLM_MODULE_HANDLED:
			break;


		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			request->reply->code = request->packet->code + 2; /* NAK */
			break;
		}

		/*
		 *	Allow for over-ride of reply code.
		 */
		vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
		if (vp) request->reply->code = vp->vp_uint32;

		dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
		unlang = NULL;
		if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias);

		if (!unlang) goto send_reply;

		/*
		 *	Note that for NAKs, we do NOT use
		 *	reject_delay.  This is because we're acting as
		 *	a NAS, and we want to respond to the RADIUS
		 *	server as quickly as possible.
		 */
	rerun_nak:
		RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
		unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);
		rad_assert(request->log.unlang_indent == 0);

		request->request_state = REQUEST_SEND;
		/* FALL-THROUGH */

	case REQUEST_SEND:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
			/*
			 *	We need to send CoA-NAK back if Service-Type
			 *	is Authorize-Only.  Rely on the user's policy
			 *	to do that.  We're not a real NAS, so this
			 *	restriction doesn't (ahem) apply to us.
			 */
		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			/*
			 *	If we over-ride an ACK with a NAK, run
			 *	the NAK section.
			 */
			if (request->reply->code == request->packet->code + 1) {
				dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
				RWDEBUG("Failed running 'send %s', trying corresponding NAK section.", dv->alias);

				request->reply->code = request->packet->code + 2;

				dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
				unlang = NULL;
				if (!dv) goto send_reply;

				unlang = cf_section_find(request->server_cs, "send", dv->alias);
				if (unlang) goto rerun_nak;

				RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias);
			}
			/*
			 *	Else it was already a NAK or something else.
			 */
			break;

		case RLM_MODULE_HANDLED:
		case RLM_MODULE_NOOP:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			/* reply code is already set */
			break;
		}

	send_reply:
		gettimeofday(&request->reply->timestamp, NULL);

		/*
		 *	Check for "do not respond".
		 */
		if (request->reply->code == FR_CODE_DO_NOT_RESPOND) {
			RDEBUG("Not sending reply to client.");
			break;
		}

		if (request->parent && RDEBUG_ENABLED) {
			RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id);
			log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, "");
		}
		break;

	default:
		return FR_IO_FAIL;
	}

	return FR_IO_REPLY;
}
Beispiel #14
0
			 *	Finally, create the new attribute.
			 */
			if (dict_addvalue(name2, dattr->name, my_value++) < 0) {
				radlog(L_ERR, "%s", librad_errstr);
				exit(1);
			}
		} while (cp != NULL);
	} /* over the sections which can have redundent sub-sections */

	/*
	 *  Look for the 'instantiate' section, which tells us
	 *  the instantiation order of the modules, and also allows
	 *  us to load modules with no authorize/authenticate/etc.
	 *  sections.
	 */
	cs = cf_section_find("instantiate");
	if (cs != NULL) {
		CONF_ITEM *ci;
		CONF_PAIR *cp;
		module_instance_t *module;
		const char *name;

		/*
		 *  Loop over the items in the 'instantiate' section.
		 */
		for (ci=cf_item_find_next(cs, NULL); ci != NULL; ci=cf_item_find_next(cs, ci)) {
			if (cf_item_is_section(ci)) {
				radlog(L_ERR|L_CONS,
				       "%s[%d] Subsection for module instantiate is not allowed\n", filename,
				       
				       cf_section_lineno(cf_itemtosection(ci)));
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action)
{
	rlm_rcode_t rcode;
	CONF_SECTION *unlang;

	REQUEST_VERIFY(request);

	/*
	 *	Pass this through asynchronously to the module which
	 *	is waiting for something to happen.
	 */
	if (action != FR_IO_ACTION_RUN) {
		unlang_signal(request, (fr_state_signal_t) action);
		return FR_IO_DONE;
	}

	switch (request->request_state) {
	case REQUEST_INIT:
		request->component = "radius";

		unlang = cf_section_find(request->server_cs, "new", "client");
		if (!unlang) {
			RWDEBUG("Failed to find 'new client' section");
			request->reply->code = FR_CODE_ACCESS_REJECT;
			goto send_reply;
		}

		RDEBUG("Running 'new client' from file %s", cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			request->reply->code = FR_CODE_ACCESS_ACCEPT;
			break;

		case RLM_MODULE_FAIL:
		case RLM_MODULE_HANDLED:
			request->reply->code = 0; /* don't reply */
			break;

		default:
		case RLM_MODULE_REJECT:
			request->reply->code = FR_CODE_ACCESS_REJECT;
			break;
		}

		unlang = cf_section_find(request->server_cs, "add", "client");
		if (!unlang) goto send_reply;

	rerun_nak:
		RDEBUG("Running '%s client' from file %s", cf_section_name1(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_SEND;
		/* FALL-THROUGH */

	case REQUEST_SEND:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_HANDLED:
			/* reply is already set */
			break;

		default:
			/*
			 *	If we over-ride an ACK with a NAK, run
			 *	the NAK section.
			 */
			if (request->reply->code != FR_CODE_ACCESS_REJECT) {
				RWDEBUG("Failed running 'add client', trying 'deny client'.");

			deny:
				request->reply->code = FR_CODE_ACCESS_REJECT;

				unlang = cf_section_find(request->server_cs, "deny", "client");
				if (unlang) goto rerun_nak;

				RWDEBUG("Not running 'deny client' section as it does not exist");
			}
			break;
		}

		if (request->reply->code == FR_CODE_ACCESS_ACCEPT) {
			VALUE_PAIR *vp;

			vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address, TAG_ANY);
			if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address, TAG_ANY);
			if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix, TAG_ANY);
			if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix, TAG_ANY);
			if (!vp) {
				ERROR("The 'control' list MUST contain a FreeRADIUS-Client.. IP address attribute");
				goto deny;
			}

			vp = fr_pair_find_by_da(request->control, attr_freeradius_client_secret, TAG_ANY);
			if (!vp) {
				ERROR("The 'control' list MUST contain a FreeRADIUS-Client-Secret attribute");
				goto deny;
			}

			/*
			 *	Else we're flexible.
			 */
		}

	send_reply:
		/*
		 *	This is an internally generated request.  Don't print IP addresses.
		 */
		if (request->reply->code == FR_CODE_ACCESS_ACCEPT) {
			RDEBUG("Adding client");
		} else {
			RDEBUG("Denying client");
		}
		break;

	default:
		return FR_IO_FAIL;
	}

	return FR_IO_REPLY;
}
Beispiel #16
0
static rlm_rcode_t dhcp_process(REQUEST *request)
{
	rlm_rcode_t	rcode;
	unsigned int	i;
	VALUE_PAIR	*vp;
	dhcp_socket_t	*sock;

	/*
	 *	If there's a giaddr, save it as the Relay-IP-Address
	 *	in the response.  That way the later code knows where
	 *	to send the reply.
	 */
	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */
	if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		VALUE_PAIR *relay;

		/* DHCP-Relay-IP-Address */
		MEM(relay = fr_pair_afrom_num(request->reply, DHCP_MAGIC_VENDOR, 222));
		relay->vp_ipv4addr = vp->vp_ipv4addr;
		fr_pair_add(&request->reply->vps, relay);
	}

	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */
	if (vp) {
		fr_dict_enum_t *dv = fr_dict_enum_by_value(vp->da, &vp->data);

		if (dv) {
			CONF_SECTION *server, *unlang;

			RDEBUG("Trying sub-section dhcp %s {...}", dv->alias);

			server = cf_item_to_section(cf_parent(request->listener->cs));

			unlang = cf_section_find(server, "dhcp", dv->alias);
			rcode = unlang_interpret(request, unlang, RLM_MODULE_NOOP);
		} else {
			REDEBUG("Unknown DHCP-Message-Type %d", vp->vp_uint8);
			rcode = RLM_MODULE_FAIL;
		}
	} else {
		REDEBUG("Failed to find DHCP-Message-Type in packet!");
		rcode = RLM_MODULE_FAIL;
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */
	if (vp) {
		request->reply->code = vp->vp_uint8;
	}
	else switch (rcode) {
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
		if (request->packet->code == FR_DHCP_DISCOVER) {
			request->reply->code = FR_DHCP_OFFER;
			break;

		} else if (request->packet->code == FR_DHCP_REQUEST) {
			request->reply->code = FR_DHCP_ACK;
			break;
		}
		request->reply->code = FR_DHCP_NAK;
		break;

	default:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
		if (request->packet->code == FR_DHCP_DISCOVER) {
			request->reply->code = 0; /* ignore the packet */
		} else {
			request->reply->code = FR_DHCP_NAK;
		}
		break;

	case RLM_MODULE_HANDLED:
		request->reply->code = 0; /* ignore the packet */
		break;
	}

	/*
	 *	TODO: Handle 'output' of RLM_MODULE when acting as a
	 *	DHCP relay We may want to not forward packets in
	 *	certain circumstances.
	 */

	/*
	 * 	Handle requests when acting as a DHCP relay
	 */
	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */
	if (!vp) {
		RPEDEBUG("Someone deleted the DHCP-Opcode!");
		return RLM_MODULE_FAIL;
	}

	/* BOOTREPLY received on port 67 (i.e. from a server) */
	if (vp->vp_uint8 == 2) {
		return dhcprelay_process_server_reply(request);
	}

	/* Packet from client, and we have DHCP-Relay-To-IP-Address */
	if (fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY)) {
		return dhcprelay_process_client_request(request);
	}

	/* else it's a packet from a client, without relaying */
	rad_assert(vp->vp_uint8 == 1); /* BOOTREQUEST */

	sock = request->listener->data;

	/*
	 *	Handle requests when acting as a DHCP server
	 */

	/*
	 *	Releases don't get replies.
	 */
	if (request->packet->code == FR_DHCP_RELEASE) {
		request->reply->code = 0;
	}

	if (request->reply->code == 0) {
		return RLM_MODULE_OK;
	}

	request->reply->sockfd = request->packet->sockfd;

	/*
	 *	Copy specific fields from packet to reply, if they
	 *	don't already exist
	 */
	for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
		uint32_t attr = attrnums[i];

		if (fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY)) continue;

		vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY);
		if (vp) {
			fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
		}
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */
	rad_assert(vp != NULL);
	vp->vp_uint8 = 2; /* BOOTREPLY */

	/*
	 *	Allow NAKs to be delayed for a short period of time.
	 */
	if (request->reply->code == FR_DHCP_NAK) {
		vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY);
		if (vp) {
			if (vp->vp_uint32 <= 10) {
				request->response_delay.tv_sec = vp->vp_uint32;
				request->response_delay.tv_usec = 0;
			} else {
				request->response_delay.tv_sec = 10;
				request->response_delay.tv_usec = 0;
			}
		} else {
#ifndef USEC
#define USEC 1000000
#endif
			vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY);
			if (vp) {
				if (vp->vp_uint32 <= 10 * USEC) {
					request->response_delay.tv_sec = vp->vp_uint32 / USEC;
					request->response_delay.tv_usec = vp->vp_uint32 % USEC;
				} else {
					request->response_delay.tv_sec = 10;
					request->response_delay.tv_usec = 0;
				}
			}
		}
	}

	/*
	 *	Prepare the reply packet for sending through dhcp_socket_send()
	 */
	request->reply->dst_ipaddr.af = AF_INET;
	request->reply->src_ipaddr.af = AF_INET;
	request->reply->src_ipaddr.prefix = 32;

	/*
	 *	Packet-Src-IP-Address has highest precedence
	 */
	vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_SRC_IP_ADDRESS, TAG_ANY);
	if (vp) {
		request->reply->if_index = 0;	/* Must be 0, we don't know the outbound if_index */
		request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	/*
	 *	The request was unicast (via a relay)
	 */
	} else if (request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST) &&
		   request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) {
		request->reply->src_ipaddr.addr.v4.s_addr = request->packet->dst_ipaddr.addr.v4.s_addr;
		request->reply->if_index = request->packet->if_index;
	/*
	 *	The listener was bound to an IP address, or we determined
	 *	the address automatically, as it was the only address bound
	 *	to the interface, and we bound to the interface.
	 */
	} else if (sock->src_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) {
		request->reply->src_ipaddr.addr.v4.s_addr = sock->src_ipaddr.addr.v4.s_addr;
#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
	/*
	 *	We built with udpfromto and have the if_index of the receiving
	 *	interface, which we can now resolve to an IP address.
	 */
	} else if (request->packet->if_index > 0) {
		fr_ipaddr_t primary;

		if (fr_ipaddr_from_ifindex(&primary, request->packet->sockfd, request->packet->dst_ipaddr.af,
					   request->packet->if_index) < 0) {
			RPEDEBUG("Failed determining src_ipaddr from if_index");
			return RLM_MODULE_FAIL;
		}
		request->reply->src_ipaddr.addr.v4.s_addr = primary.addr.v4.s_addr;
#endif
	/*
	 *	There's a Server-Identification attribute
	 */
	} else if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY))) {
		request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	} else {
		REDEBUG("Unable to determine correct src_ipaddr for response");
		return RLM_MODULE_FAIL;
	}
	request->reply->dst_port = request->packet->src_port;
	request->reply->src_port = request->packet->dst_port;

	/*
	 *	Answer to client's nearest DHCP relay.
	 *
	 *	Which may be different than the giaddr given in the
	 *	packet to the client.  i.e. the relay may have a
	 *	public IP, but the gateway a private one.
	 */
	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 272, TAG_ANY); /* DHCP-Relay-IP-Address */
	if (vp && (vp->vp_ipv4addr != ntohl(INADDR_ANY))) {
		RDEBUG2("Reply will be unicast to giaddr from original packet");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		request->reply->dst_port = request->packet->dst_port;

		vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_DST_PORT, TAG_ANY);
		if (vp) request->reply->dst_port = vp->vp_uint16;

		return RLM_MODULE_OK;
	}

	/*
	 *	Answer to client's nearest DHCP gateway.  In this
	 *	case, the client can reach the gateway, as can the
	 *	server.
	 *
	 *	We also use *our* source port as the destination port.
	 *	Gateways are servers, and listen on the server port,
	 *	not the client port.
	 */
	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */
	if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		RDEBUG2("Reply will be unicast to giaddr");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		request->reply->dst_port = request->packet->dst_port;
		return RLM_MODULE_OK;
	}

	/*
	 *	If it's a NAK, or the broadcast flag was set, ond
	 *	there's no client-ip-address, send a broadcast.
	 */
	if ((request->reply->code == FR_DHCP_NAK) ||
	    ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) && /* DHCP-Flags */
	     (vp->vp_uint32 & 0x8000) &&
	     ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */
	      (vp->vp_ipv4addr == htonl(INADDR_ANY))))) {
		/*
		 * RFC 2131, page 23
		 *
		 * Broadcast on
		 * - DHCPNAK
		 * or
		 * - Broadcast flag is set up and ciaddr == NULL
		 */
		RDEBUG2("Reply will be broadcast");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
		return RLM_MODULE_OK;
	}

	/*
	 *	RFC 2131, page 23
	 *
	 *	Unicast to ciaddr if present, otherwise to yiaddr.
	 */
	if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */
	    (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		RDEBUG2("Reply will be sent unicast to &DHCP-Client-IP-Address");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		return RLM_MODULE_OK;
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */
	if (!vp) {
		REDEBUG("Can't assign address to client: Neither &reply:DHCP-Client-IP-Address nor "
			"&reply:DHCP-Your-IP-Address set");
		/*
		 *	There is nowhere to send the response to, so don't bother.
		 */
		request->reply->code = 0;
		return RLM_MODULE_FAIL;
	}

#ifdef SIOCSARP
	/*
	 *	The system is configured to listen for broadcast
	 *	packets, which means we'll need to send unicast
	 *	replies, to IPs which haven't yet been assigned.
	 *	Therefore, we need to update the ARP table.
	 *
	 *	However, they haven't specified a interface.  So we
	 *	can't update the ARP table.  And we must send a
	 *	broadcast response.
	 */
	if (sock->lsock.broadcast && !sock->src_interface) {
		WARN("You MUST set \"interface\" if you have \"broadcast = yes\"");
		RDEBUG2("Reply will be broadcast as no interface was defined");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
		return RLM_MODULE_OK;
	}

	RDEBUG2("Reply will be unicast to &DHCP-Your-IP-Address");
	request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;

	/*
	 *	When sending a DHCP_OFFER, make sure our ARP table
	 *	contains an entry for the client IP address.
	 *	Otherwise the packet may not be sent to the client, as
	 *	the OS has no ARP entry for it.
	 *
	 *	This is a cute hack to avoid us having to create a raw
	 *	socket to send DHCP packets.
	 */
	if (request->reply->code == FR_DHCP_OFFER) {
		VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */

		if (!hwvp) return RLM_MODULE_FAIL;

		if (fr_dhcpv4_udp_add_arp_entry(request->reply->sockfd, sock->src_interface,
						&vp->vp_ip, hwvp->vp_ether) < 0) {
			RPEDEBUG("Failed adding arp entry");
			return RLM_MODULE_FAIL;
		}
	}
#else
	if (request->packet->src_ipaddr.addr.v4.s_addr != ntohl(INADDR_NONE)) {
		RDEBUG2("Reply will be unicast to the unicast source IP address");
		request->reply->dst_ipaddr.addr.v4.s_addr = request->packet->src_ipaddr.addr.v4.s_addr;
	} else {
		RDEBUG2("Reply will be broadcast as this system does not support ARP updates");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
	}
#endif

	return RLM_MODULE_OK;
}
Beispiel #17
0
/** Resolve polymorphic item's from a module's #CONF_SECTION to a subsection in another module
 *
 * This allows certain module sections to reference module sections in other instances
 * of the same module and share #CONF_DATA associated with them.
 *
 * @verbatim
   example {
   	data {
   		...
   	}
   }

   example inst {
   	data = example
   }
 * @endverbatim
 *
 * @param[out] out where to write the pointer to a module's config section.  May be NULL on success,
 *	indicating the config item was not found within the module #CONF_SECTION
 *	or the chain of module references was followed and the module at the end of the chain
 *	did not a subsection.
 * @param[in] module #CONF_SECTION.
 * @param[in] name of the polymorphic sub-section.
 * @return
 *	- 0 on success with referenced section.
 *	- 1 on success with local section.
 *	- -1 on failure.
 */
int module_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, char const *name)
{
	CONF_PAIR		*cp;
	CONF_SECTION		*cs;
	CONF_DATA const		*cd;


	module_instance_t	*mi;
	char const		*inst_name;

#define FIND_SIBLING_CF_KEY "find_sibling"

	*out = NULL;

	/*
	 *	Is a real section (not referencing sibling module).
	 */
	cs = cf_section_find(module, name, NULL);
	if (cs) {
		*out = cs;

		return 0;
	}

	/*
	 *	Item omitted completely from module config.
	 */
	cp = cf_pair_find(module, name);
	if (!cp) return 0;

	if (cf_data_find(module, CONF_SECTION, FIND_SIBLING_CF_KEY)) {
		cf_log_err(cp, "Module reference loop found");

		return -1;
	}
	cd = cf_data_add(module, module, FIND_SIBLING_CF_KEY, false);

	/*
	 *	Item found, resolve it to a module instance.
	 *	This triggers module loading, so we don't have
	 *	instantiation order issues.
	 */
	inst_name = cf_pair_value(cp);
	mi = module_by_name(NULL, inst_name);
	if (!mi) {
		cf_log_err(cp, "Unknown module instance \"%s\"", inst_name);

		return -1;
	}

	if (!mi->instantiated) {
		CONF_SECTION *parent = module;

		/*
		 *	Find the root of the config...
		 */
		do {
			CONF_SECTION *tmp;

			tmp = cf_item_to_section(cf_parent(parent));
			if (!tmp) break;

			parent = tmp;
		} while (true);

		_module_instantiate(module_by_name(NULL, inst_name), NULL);
	}

	/*
	 *	Remove the config data we added for loop
	 *	detection.
	 */
	cf_data_remove(module, cd);

	/*
	 *	Check the module instances are of the same type.
	 */
	if (strcmp(cf_section_name1(mi->dl_inst->conf), cf_section_name1(module)) != 0) {
		cf_log_err(cp, "Referenced module is a rlm_%s instance, must be a rlm_%s instance",
			      cf_section_name1(mi->dl_inst->conf), cf_section_name1(module));

		return -1;
	}

	*out = cf_section_find(mi->dl_inst->conf, name, NULL);

	return 1;
}
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request)
{
	rlm_rcode_t rcode;
	CONF_SECTION *unlang;
	fr_dict_enum_t const *dv;
	VALUE_PAIR *vp;

	REQUEST_VERIFY(request);

	switch (request->request_state) {
	case REQUEST_INIT:
		if (request->parent && RDEBUG_ENABLED) {
			RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id);
			log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");
		}

		request->component = "radius";

		unlang = cf_section_find(request->server_cs, "recv", "Status-Server");
		if (!unlang) {
			RWDEBUG("Failed to find 'recv Status-Server' section");
			request->reply->code = FR_CODE_ACCESS_REJECT;
			goto send_reply;
		}

		RDEBUG("Running 'recv Status-Server' from file %s", cf_filename(unlang));
		unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			request->reply->code = FR_CODE_ACCESS_ACCEPT;
			break;

		case RLM_MODULE_FAIL:
		case RLM_MODULE_HANDLED:
			request->reply->code = 0; /* don't reply */
			break;

		default:
		case RLM_MODULE_REJECT:
			request->reply->code = FR_CODE_ACCESS_REJECT;
			break;
		}

		/*
		 *	Allow for over-ride of reply code.
		 */
		vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
		if (vp) request->reply->code = vp->vp_uint32;

		dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
		unlang = NULL;
		if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias);

		if (!unlang) goto send_reply;

	rerun_nak:
		RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
		unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_SEND;
		/* FALL-THROUGH */

	case REQUEST_SEND:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_HANDLED:
			/* reply is already set */
			break;

		default:
			/*
			 *	If we over-ride an ACK with a NAK, run
			 *	the NAK section.
			 */
			if (request->reply->code != FR_CODE_ACCESS_REJECT) {
				dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
				RWDEBUG("Failed running 'send %s', trying 'send Access-Reject'", dv->alias);

				request->reply->code = FR_CODE_ACCESS_REJECT;

				dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code));
				unlang = NULL;
				if (!dv) goto send_reply;

				unlang = cf_section_find(request->server_cs, "send", dv->alias);
				if (unlang) goto rerun_nak;

				RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias);
			}
			break;
		}

	send_reply:
		gettimeofday(&request->reply->timestamp, NULL);

		/*
		 *	Check for "do not respond".
		 */
		if (request->reply->code == FR_CODE_DO_NOT_RESPOND) {
			RDEBUG("Not sending reply to client.");
			break;
		}

		if (request->parent && RDEBUG_ENABLED) {
			RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id);
			log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, "");
		}
		break;

	default:
		return FR_IO_FAIL;
	}

	return FR_IO_REPLY;
}
/** Synchronously load cookie data
 *
 * FIXME: This should not be synchronous, but integrating it into the event loop
 *	before the server has started processing requests makes my head hurt.
 *
 * @param[in] ctx	to allocate cookie buffer in.
 * @param[out] cookie	Where to write the cookie we loaded.
 * @param[in] listen	structure encapsulating the LDAP
 * @param[in] config	of the sync we're loading the cookie for.
 * @return
 *	- -1 on failure.
 *	- 0 on success.
 *	- 1 no cookie returned.
 */
static int proto_ldap_cookie_load(TALLOC_CTX *ctx, uint8_t **cookie, rad_listen_t *listen, sync_config_t const *config)
{
	proto_ldap_inst_t	*inst = talloc_get_type_abort(listen->data, proto_ldap_inst_t);
	REQUEST			*request;
	CONF_SECTION		*unlang;
	int			ret = 0;

	rlm_rcode_t		rcode;

	request = proto_ldap_request_setup(listen, inst, 0);
	if (!request) return -1;

	proto_ldap_attributes_add(request, config);
	request->packet->code = LDAP_SYNC_CODE_COOKIE_STORE;

	unlang = cf_section_find(request->server_cs, "load", "Cookie");
	if (!unlang) {
		RDEBUG2("Ignoring %s operation.  Add \"load Cookie {}\" to virtual-server \"%s\""
			" to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"),
			cf_section_name2(request->server_cs));
	}

	*cookie = NULL;

	rcode = unlang_interpret_synchronous(request, unlang, RLM_MODULE_NOOP);
	switch (rcode) {
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
	{
		VALUE_PAIR *vp;

		vp = fr_pair_find_by_da(request->reply->vps, attr_ldap_sync_cookie, TAG_ANY);
		if (!vp) {
			if (config->allow_refresh) RDEBUG2("No &reply:Cookie attribute found.  All entries matching "
							   "sync configuration will be returned");
			ret = 1;
			goto finish;
		}

		/*
		 *	So the request pool doesn't hang around indefinitely.
		 */
		MEM(*cookie = talloc_memdup(ctx, vp->vp_octets, vp->vp_length));
		ret = 0;
	}
		break;

	case RLM_MODULE_NOOP:
		if (config->allow_refresh) RDEBUG2("Section returned \"noop\".  All entries matching sync "
						   "configuration will be returned");
		ret = 1;
		break;

	default:
		RERROR("Section must return \"ok\", \"updated\", or \"noop\" for listener instantiation to succeed");
		ret = -1;
		break;
	}

finish:
	talloc_free(request);
	return ret;
}
/** Parse socket configuration
 *
 * @param[in] cs	specifying the listener configuration.
 * @param[in] listen	structure encapsulating the libldap socket.
 * @return
 *	- 0 on success.
 *	- -1 on error.
 */
static int proto_ldap_socket_parse(CONF_SECTION *cs, rad_listen_t *listen)
{
	proto_ldap_inst_t 	*inst = listen->data;
	CONF_SECTION		*sync_cs;
	size_t		 	i;
	int			ret;

	/*
	 *	Always cache the CONF_SECTION of the server.
	 */
	listen->server_cs = virtual_server_find(listen->server);
	if (!listen->server_cs) {
		cf_log_err(cs, "Failed to find virtual server '%s'", listen->server);
		return -1;
	}

	if (cf_section_rules_push(cs, module_config) < 0) return -1;
	ret = cf_section_parse(inst, inst, cs);
	if (ret < 0) return ret;

	talloc_set_type(inst, proto_ldap_inst_t);

	rad_assert(inst->handle_config.server_str[0]);
	inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)", listen->server);

	memcpy(&inst->handle_config.server, &inst->handle_config.server_str[0], sizeof(inst->handle_config.server));

	/*
	 *	Convert scope strings to enumerated constants
	 */
	for (sync_cs = cf_section_find(cs, "sync", NULL), i = 0;
	     sync_cs;
	     sync_cs = cf_section_find_next(cs, sync_cs, "sync", NULL), i++) {
		int		scope;
		void		**tmp;
		CONF_SECTION	*map_cs;

		talloc_set_type(inst->sync_config[i], sync_config_t);

		scope = fr_str2int(fr_ldap_scope, inst->sync_config[i]->scope_str, -1);
		if (scope < 0) {
#ifdef LDAP_SCOPE_CHILDREN
			cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'"
				   ", 'base' or 'children'", inst->sync_config[i]->scope_str);
#else
			cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'"
				   " or 'base'", inst->sync_config[i]->scope_str)
#endif
			return -1;
		}
		inst->sync_config[i]->scope = scope;

		/*
		 *	Needs to be NULL terminated as that's what libldap needs
		 */
		if (inst->sync_config[i]->attrs) {
			memcpy(&tmp, &inst->sync_config[i]->attrs, sizeof(tmp));
			tmp = talloc_array_null_terminate(tmp);
			memcpy(&inst->sync_config[i]->attrs, tmp, sizeof(inst->sync_config[i]->attrs));
		}

		inst->sync_config[i]->persist = true;
		inst->sync_config[i]->user_ctx = listen;
		inst->sync_config[i]->cookie = _proto_ldap_cookie_store;
		inst->sync_config[i]->entry = _proto_ldap_entry;
		inst->sync_config[i]->refresh_required = _proto_ldap_refresh_required;
		inst->sync_config[i]->present = _proto_ldap_present;

		/*
		 *	Parse and validate any maps
		 */
		map_cs = cf_section_find(sync_cs, "update", NULL);
		if (map_cs && map_afrom_cs(inst, &inst->sync_config[i]->entry_map, map_cs,
					   NULL, NULL, fr_ldap_map_verify, NULL,
					   LDAP_MAX_ATTRMAP) < 0) {
			return -1;
		}
	}
/** Very simple state machine to process requests
 *
 * Unlike normal protocol requests which may have multiple distinct states,
 * we really only have REQUEST_INIT and REQUEST_RECV phases.
 *
 * Conversion of LDAPMessage to VALUE_PAIR structs is done in the listener
 * because we cannot easily duplicate the LDAPMessage to send it across to
 * the worker for parsing.
 *
 * Most LDAP directories can only handle between 2000-5000 modifications a second
 * so we're unlikely to be I/O or CPU bound using this division of responsibilities.
 *
 * @param[in] request	to process.
 * @param[in] action	If something has signalled that the request should stop
 *			being processed.
 */
static void request_running(REQUEST *request, fr_state_signal_t action)
{
	CONF_SECTION	*unlang;
	char const	*verb;
	char const	*state;
	rlm_rcode_t	rcode = RLM_MODULE_FAIL;

	REQUEST_VERIFY(request);

	/*
	 *	Async (in the same thread, tho) signal to be done.
	 */
	if (action == FR_SIGNAL_CANCEL) goto done;

	/*
	 *	We ignore all other actions.
	 */
	if (action != FR_SIGNAL_RUN) return;

	switch (request->request_state) {
	case REQUEST_INIT:
		if (RDEBUG_ENABLED) proto_ldap_packet_debug(request, request->packet, true);
		log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");

		request->server_cs = request->listener->server_cs;
		request->component = "ldap";

		switch (request->packet->code) {
		case LDAP_SYNC_CODE_PRESENT:
			verb = "recv";
			state = "Present";
			break;

		case LDAP_SYNC_CODE_ADD:
			verb = "recv";
			state = "Add";
			break;

		case LDAP_SYNC_CODE_MODIFY:
			verb = "recv";
			state = "Modify";
			break;

		case LDAP_SYNC_CODE_DELETE:
			verb = "recv";
			state = "Delete";
			break;

		case LDAP_SYNC_CODE_COOKIE_LOAD:
			verb = "load";
			state = "Cookie";
			break;

		case LDAP_SYNC_CODE_COOKIE_STORE:
			verb = "store";
			state = "Cookie";
			break;

		default:
			rad_assert(0);
			return;
		}
		unlang = cf_section_find(request->server_cs, verb, state);
		if (!unlang) unlang = cf_section_find(request->server_cs, "recv", "*");
		if (!unlang) {
			RDEBUG2("Ignoring %s operation.  Add \"%s %s {}\" to virtual-server \"%s\""
				" to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"),
				verb, state, cf_section_name2(request->server_cs));
			rcode = RLM_MODULE_NOOP;
			goto done;
		}

		RDEBUG("Running '%s %s' from file %s", cf_section_name1(unlang),
		       cf_section_name2(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) goto done;

		if (rcode == RLM_MODULE_YIELD) return;

		/* FALL-THROUGH */
	default:
	done:
		switch (rcode) {
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_OK:
		{

		}

		default:
			break;
		}
		rad_assert(request->log.unlang_indent == 0);
		//request_delete(request);
		break;
	}
}
Beispiel #22
0
/** Initialise a module specific connection pool
 *
 * @see fr_pool_init
 *
 * @param[in] module		section.
 * @param[in] opaque		data pointer to pass to callbacks.
 * @param[in] c			Callback to create new connections.
 * @param[in] a			Callback to check the status of connections.
 * @param[in] log_prefix	override, if NULL will be set automatically from the module CONF_SECTION.
 * @param[in] trigger_prefix	if NULL will be set automatically from the module CONF_SECTION.
 * @param[in] trigger_args	to make available in any triggers executed by the connection pool.
 * @return
 *	- New connection pool.
 *	- NULL on error.
 */
fr_pool_t *module_connection_pool_init(CONF_SECTION *module,
				       void *opaque,
				       fr_pool_connection_create_t c,
				       fr_pool_connection_alive_t a,
				       char const *log_prefix,
				       char const *trigger_prefix,
				       VALUE_PAIR *trigger_args)
{
	CONF_SECTION *cs, *mycs;
	char log_prefix_buff[128];
	char trigger_prefix_buff[128];

	fr_pool_t *pool;
	char const *cs_name1, *cs_name2;

	int ret;

#define parent_name(_x) cf_section_name(cf_item_to_section(cf_parent(_x)))

	cs_name1 = cf_section_name1(module);
	cs_name2 = cf_section_name2(module);
	if (!cs_name2) cs_name2 = cs_name1;

	if (!trigger_prefix) {
		snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.pool", cs_name1);
		trigger_prefix = trigger_prefix_buff;
	}

	if (!log_prefix) {
		snprintf(log_prefix_buff, sizeof(log_prefix_buff), "rlm_%s (%s)", cs_name1, cs_name2);
		log_prefix = log_prefix_buff;
	}

	/*
	 *	Get sibling's pool config section
	 */
	ret = module_sibling_section_find(&cs, module, "pool");
	switch (ret) {
	case -1:
		return NULL;

	case 1:
		DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs));
		break;

	case 0:
		DEBUG4("%s: Using local pool section", log_prefix);
		break;
	}

	/*
	 *	Get our pool config section
	 */
	mycs = cf_section_find(module, "pool", NULL);
	if (!mycs) {
		DEBUG4("%s: Adding pool section to config item \"%s\" to store pool references", log_prefix,
		       cf_section_name(module));

		mycs = cf_section_alloc(module, module, "pool", NULL);
	}

	/*
	 *	Sibling didn't have a pool config section
	 *	Use our own local pool.
	 */
	if (!cs) {
		DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix,
		       parent_name(cs), parent_name(mycs));
		cs = mycs;
	}

	/*
	 *	If fr_pool_init has already been called
	 *	for this config section, reuse the previous instance.
	 *
	 *	This allows modules to pass in the config sections
	 *	they would like to use the connection pool from.
	 */
	pool = cf_data_value(cf_data_find(cs, fr_pool_t, NULL));
	if (!pool) {
		DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs));
		pool = fr_pool_init(cs, cs, opaque, c, a, log_prefix);
		if (!pool) return NULL;

		fr_pool_enable_triggers(pool, trigger_prefix, trigger_args);

		if (fr_pool_start(pool) < 0) {
			ERROR("%s: Starting initial connections failed", log_prefix);
			return NULL;
		}

		DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs));
		cf_data_add(cs, pool, NULL, false);
		return pool;
	}
	fr_pool_ref(pool);

	DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs));

	/*
	 *	We're reusing pool data add it to our local config
	 *	section. This allows other modules to transitively
	 *	re-use a pool through this module.
	 */
	if (mycs != cs) {
		DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"",
		       log_prefix, pool, parent_name(cs), parent_name(mycs));
		cf_data_add(mycs, pool, NULL, false);
	}

	return pool;
}
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action)
{
	VALUE_PAIR		*vp;
	rlm_rcode_t		rcode;
	CONF_SECTION		*unlang;

	REQUEST_VERIFY(request);

	/*
	 *	Pass this through asynchronously to the module which
	 *	is waiting for something to happen.
	 */
	if (action != FR_IO_ACTION_RUN) {
		unlang_signal(request, (fr_state_signal_t) action);
		return FR_IO_DONE;
	}

	switch (request->request_state) {
	case REQUEST_INIT:
		RDEBUG("Received %s ID %i",
		       fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->code)),
		       request->packet->id);
		log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");

		request->component = "radius";

		unlang = cf_section_find(request->server_cs, "recv", NULL);
		if (!unlang) {
			REDEBUG("Failed to find 'recv' section");
			return FR_IO_FAIL;
		}

		RDEBUG("Running 'recv' from file %s", cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_continue(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		/*
		 *	The module has a number of OK return codes.
		 */
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			switch (request->packet->code) {
			case FR_CODE_ACCOUNTING_REQUEST:
				request->reply->code = FR_CODE_ACCOUNTING_RESPONSE;
				break;

			case FR_CODE_COA_REQUEST:
				request->reply->code = FR_CODE_COA_ACK;
				break;

			case FR_CODE_DISCONNECT_REQUEST:
				request->reply->code = FR_CODE_DISCONNECT_ACK;
				break;

			default:
				request->reply->code = 0;
				break;
			}
			break;

		case RLM_MODULE_HANDLED:
			break;

		/*
		 *	The module failed, or said the request is
		 *	invalid, therefore we stop here.
		 */
		case RLM_MODULE_NOOP:
		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			request->reply->code = 0;
			break;
		}

		/*
		 *	Allow for over-ride of reply code.
		 */
		vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY);
		if (vp) request->reply->code = vp->vp_uint32;

		if (request->reply->code == FR_CODE_DO_NOT_RESPOND) {
			RWARN("Ignoring 'do_not_respond' as it does not apply to detail files");
		}

		unlang = cf_section_find(request->server_cs, "send", "ok");
		if (!unlang) goto send_reply;

		RDEBUG("Running 'send %s { ... }' from file %s", cf_section_name2(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_SEND;
		/* FALL-THROUGH */

	case REQUEST_SEND:
		rcode = unlang_interpret_continue(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_HANDLED:
			/* reply is already set */
			break;

		default:
			request->reply->code = 0;
			break;
		}

	send_reply:
		/*
		 *	Failed, but we still reply with a magic code,
		 *	so that the reader can retransmit.
		 */
		if (!request->reply->code) {
			REDEBUG("Failed ID %i", request->reply->id);
		} else {
			RDEBUG("Sent %s ID %i",
			       fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->code)),
			       request->reply->id);
		}

		log_request_proto_pair_list(L_DBG_LVL_1, request, request->reply->vps, "");
		break;

	default:
		return FR_IO_FAIL;
	}

	return FR_IO_REPLY;
}
Beispiel #24
0
/*
 *	Zap a user from the radutmp and radwtmp file.
 */
int main(int argc, char **argv)
{
	CONF_SECTION *cs;
	NAS *nas;
	uint32_t ip = 0;
	uint32_t nas_port = ~0;
	char *user = NULL;
	char *s;
	char buf[256];
	struct radutmp u;
	int argval;

	progname = argv[0];

	radius_dir = strdup(RADIUS_DIR);

        /*  Process the options.  */
        while ((argval = getopt(argc, argv, "d:p:r:")) != EOF) {
                                
                switch(argval) {
                        
			case 'd':
				if (radius_dir) free(radius_dir);
				radius_dir = strdup(optarg);
				break;
                        case 'p':
				acct_port = atoi(optarg);
                                break;
                                 
                        case 'r':
                                if ((radiusip = ip_getaddr(optarg)) == INADDR_NONE) {
                                        fprintf(stderr, "%s: %s: radius server unknown\n",
                                                progname, optarg);
                                        exit(1);
				}
                                break;
                               
                        default:
                                usage();
                                exit(1);
                }
        }


	if (argc == optind) {	/* no terminal server specified */
		usage();
                exit(1);
	}

	if (argc > optind + 1) {	/* NAS port given */
		s = argv[optind+1];
		if (*s == 's' || *s == 'S') s++;
		nas_port = strtoul(s, NULL, 10);
	}

	if (argc > optind + 2) {	/* username (login) given */
		user = argv[optind+2];
	}

	/*
	 *	Find the IP address of the terminal server.
	 */
	if ((nas = nas_findbyname(argv[optind])) == NULL && argv[optind][0] != 0) {
		if ((ip = ip_getaddr(argv[optind])) == INADDR_NONE) {
			fprintf(stderr, "%s: host not found.\n", argv[optind]);
			exit(1);
		}
	}
	if (nas != NULL) 
		ip = nas->ipaddr;

	/*
	 *	Ensure that the configuration is initialized.
	 */
	memset(&mainconfig, 0, sizeof(mainconfig));

        /* Read radiusd.conf */
	if (read_mainconfig(0) < 0) {
		fprintf(stderr, "%s: Error reading radiusd.conf.\n", argv[0]);
		exit(1);
	}

        /* Read the radutmp section of radiusd.conf */
        cs = cf_section_sub_find(cf_section_find("modules"), "radutmp");
        if(!cs) {
                fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf!\n",
                        argv[0]);
                exit(1);
        }

        cf_section_parse(cs, NULL, module_config);

	printf("%s: zapping termserver %s, port %u",
		progname, ip_hostname(buf, sizeof(buf), ip), nas_port);
	if (user != NULL) 
		printf(", user %s", user);
	printf("\n");

	if (nas_port == ~0) {
		return do_accton_packet(ip);
	}

	if (!radutmp_lookup(&u, ip, nas_port, user)) {
		fprintf(stderr, "Entry not found\n");
		return 1;
	}

	return do_stop_packet(&u);
}
Beispiel #25
0
static int rediswho_instantiate(CONF_SECTION * conf, void ** instance)
{
	module_instance_t *modinst;
	rlm_rediswho_t *inst;

	/*
	 *	Set up a storage area for instance data
	 */
	inst = *instance = rad_malloc(sizeof (*inst));
	memset(inst, 0, sizeof (*inst));
    
	/*
	 *	If the configuration parameters can't be parsed, then
	 *	fail.
	 */
	if (cf_section_parse(conf, inst, module_config) < 0) {
		free(inst);
		return -1;
	}

	inst->xlat_name = cf_section_name2(conf);

	if (!inst->xlat_name) 
		inst->xlat_name = cf_section_name1(conf);

	inst->xlat_name = strdup(inst->xlat_name);

	DEBUG("xlat name %s\n", inst->xlat_name);

	/*
	 *	Check that all the queries are in place
	 */
	if ((inst->start_insert == NULL) || !*inst->start_insert) {
		radlog(L_ERR, "rlm_rediswho: the 'start_insert' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->start_trim == NULL) || !*inst->start_trim) {
		radlog(L_ERR, "rlm_rediswho: the 'start_trim' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->start_expire == NULL) || !*inst->start_expire) {
		radlog(L_ERR, "rlm_rediswho: the 'start_expire' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}

	if ((inst->alive_insert == NULL) || !*inst->alive_insert) {
		radlog(L_ERR, "rlm_rediswho: the 'alive_insert' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->alive_trim == NULL) || !*inst->alive_trim) {
		radlog(L_ERR, "rlm_rediswho: the 'alive_trim' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->alive_expire == NULL) || !*inst->alive_expire) {
		radlog(L_ERR, "rlm_rediswho: the 'alive_expire' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}

	if ((inst->stop_insert == NULL) || !*inst->stop_insert) {
		radlog(L_ERR, "rlm_rediswho: the 'stop_insert' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->stop_trim == NULL) || !*inst->stop_trim) {
		radlog(L_ERR, "rlm_rediswho: the 'stop_trim' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}
	if ((inst->stop_expire == NULL) || !*inst->stop_expire) {
		radlog(L_ERR, "rlm_rediswho: the 'stop_expire' statement must be set.");
		rediswho_detach(inst);
		return -1;
	}

	modinst = find_module_instance(cf_section_find("modules"),
				       inst->redis_instance_name, 1);
	if (!modinst) {
		radlog(L_ERR,
		       "rediswho: failed to find module instance \"%s\"",
		       inst->redis_instance_name);

		rediswho_detach(inst);
		return -1;
	}

	if (strcmp(modinst->entry->name, "rlm_redis") != 0) {
		radlog(L_ERR, "rediswho: Module \"%s\""
		       " is not an instance of the redis module",
		       inst->redis_instance_name);

		rediswho_detach(inst);
		return -1;
	}

	inst->redis_inst = (REDIS_INST *) modinst->insthandle;

	return 0;
}
/*
 *	Do any per-module initialization that is separate to each
 *	configured instance of the module.  e.g. set up connections
 *	to external databases, read configuration files, set up
 *	dictionary entries, etc.
 *
 *	If configuration information is given in the config section
 *	that must be referenced in later calls, store a handle to it
 *	in *instance otherwise put a null pointer there.
 */
static int sqlippool_instantiate(CONF_SECTION * conf, void ** instance)
{
	module_instance_t *modinst;
	rlm_sqlippool_t * data;
	const char * pool_name = NULL;

	/*
	 *	Set up a storage area for instance data
	 */
	data = rad_malloc(sizeof(*data));
	memset(data, 0, sizeof(*data));

	/*
	 *	If the configuration parameters can't be parsed, then
	 *	fail.
	 */
	if (cf_section_parse(conf, data, module_config) < 0) {
		free(data);
		return -1;
	}

	if (IS_EMPTY(data->sql_instance_name)) {
		radlog(L_ERR, "rlm_sqlippool: the 'sql-instance-name' variable must be set.");
		sqlippool_detach(data);
		return -1;
	}

	/*
	 *	Check that all the queries are in place
	 */

	if (IS_EMPTY(data->allocate_clear)) {
		radlog(L_ERR, "rlm_sqlippool: the 'allocate-clear' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->allocate_find)) {
		radlog(L_ERR, "rlm_sqlippool: the 'allocate-find' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->allocate_update)) {
		radlog(L_ERR, "rlm_sqlippool: the 'allocate-update' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->start_update)) {
		radlog(L_ERR, "rlm_sqlippool: the 'start-update' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->alive_update)) {
		radlog(L_ERR, "rlm_sqlippool: the 'alive-update' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->stop_clear)) {
		radlog(L_ERR, "rlm_sqlippool: the 'stop-clear' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->on_clear)) {
		radlog(L_ERR, "rlm_sqlippool: the 'on-clear' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	if (IS_EMPTY(data->off_clear)) {
		radlog(L_ERR, "rlm_sqlippool: the 'off-clear' statement must be set.");
		sqlippool_detach(data);
		return -1;
	}

	pool_name = cf_section_name2(conf);
	if (pool_name != NULL)
		data->pool_name = strdup(pool_name);
	else
		data->pool_name = strdup("ippool");

	modinst = find_module_instance(cf_section_find("modules"),
				       data->sql_instance_name, 1);
	if (!modinst) {
		radlog(L_ERR, "sqlippool_instantiate: failed to find sql instance named %s", data->sql_instance_name);
		sqlippool_detach(data);
		return -1;
	}

	if (strcmp(modinst->entry->name, "rlm_sql") != 0) {
		radlog(L_ERR, "sqlippool_instantiate: Module \"%s\""
		       " is not an instance of the rlm_sql module",
		       data->sql_instance_name);
		sqlippool_detach(data);
		return -1;
	}

	data->sql_inst = (SQL_INST *) modinst->insthandle;

	*instance = data;
	return 0;
}
/** Allocate a new IP address from a pool
 *
 */
static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQUEST *request,
					    uint8_t const *key_prefix, size_t key_prefix_len,
					    uint8_t const *device_id, size_t device_id_len,
					    uint8_t const *gateway_id, size_t gateway_id_len,
					    uint32_t expires)
{
	struct			timeval now;
	redisReply		*reply = NULL;

	fr_redis_rcode_t	status;
	ippool_rcode_t		ret = IPPOOL_RCODE_SUCCESS;

	rad_assert(key_prefix);
	rad_assert(device_id);

	gettimeofday(&now, NULL);

	/*
	 *	hiredis doesn't deal well with NULL string pointers
	 */
	if (!gateway_id) gateway_id = (uint8_t const *)"";

	status = ippool_script(&reply, request, inst->cluster,
			       key_prefix, key_prefix_len,
			       inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout),
			       lua_alloc_digest, lua_alloc_cmd,
	 		       "EVALSHA %s 1 %b %u %u %b %b",
	 		       lua_alloc_digest,
			       key_prefix, key_prefix_len,
			       (unsigned int)now.tv_sec, expires,
			       device_id, device_id_len,
			       gateway_id, gateway_id_len);
	if (status != REDIS_RCODE_SUCCESS) {
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	rad_assert(reply);
	if (reply->type != REDIS_REPLY_ARRAY) {
		REDEBUG("Expected result to be array got \"%s\"",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	if (reply->elements == 0) {
		REDEBUG("Got empty result array");
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	/*
	 *	Process return code
	 */
	if (reply->element[0]->type != REDIS_REPLY_INTEGER) {
		REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}
	ret = reply->element[0]->integer;
	if (ret < 0) goto finish;

	/*
	 *	Process IP address
	 */
	if (reply->elements > 1) {
		vp_tmpl_t ip_rhs = {
			.type = TMPL_TYPE_DATA,
			.tmpl_value_type = FR_TYPE_STRING
		};
		vp_map_t ip_map = {
			.lhs = inst->allocated_address_attr,
			.op = T_OP_SET,
			.rhs = &ip_rhs
		};

		switch (reply->element[1]->type) {
		/*
		 *	Destination attribute may not be IPv4, in which case
		 *	we want to pre-convert the integer value to an IPv4
		 *	address before casting it once more to the type of
		 *	the destination attribute.
		 */
		case REDIS_REPLY_INTEGER:
		{
			if (ip_map.lhs->tmpl_da->type != FR_TYPE_IPV4_ADDR) {
				fr_value_box_t tmp;

				memset(&tmp, 0, sizeof(tmp));

				tmp.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer);
				tmp.type = FR_TYPE_UINT32;

				if (fr_value_box_cast(NULL, &ip_map.rhs->tmpl_value, FR_TYPE_IPV4_ADDR,
						      NULL, &tmp)) {
					RPEDEBUG("Failed converting integer to IPv4 address");
					ret = IPPOOL_RCODE_FAIL;
					goto finish;
				}
			} else {
				ip_map.rhs->tmpl_value.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer);
				ip_map.rhs->tmpl_value_type = FR_TYPE_UINT32;
			}
		}
			goto do_ip_map;

		case REDIS_REPLY_STRING:
			ip_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str;
			ip_map.rhs->tmpl_value_length = reply->element[1]->len;
			ip_map.rhs->tmpl_value_type = FR_TYPE_STRING;

		do_ip_map:
			if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) {
				ret = IPPOOL_RCODE_FAIL;
				goto finish;
			}
			break;

		default:
			REDEBUG("Server returned unexpected type \"%s\" for IP element (result[1])",
				fr_int2str(redis_reply_types, reply->element[1]->type, "<UNKNOWN>"));
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}
	}

	/*
	 *	Process Range identifier
	 */
	if (reply->elements > 2) {
		switch (reply->element[2]->type) {
		/*
		 *	Add range ID to request
		 */
		case REDIS_REPLY_STRING:
		{
			vp_tmpl_t range_rhs = {
				.name = "",
				.type = TMPL_TYPE_DATA,
				.tmpl_value_type = FR_TYPE_STRING,
				.quote = T_DOUBLE_QUOTED_STRING
			};
			vp_map_t range_map = {
				.lhs = inst->range_attr,
				.op = T_OP_SET,
				.rhs = &range_rhs
			};

			range_map.rhs->tmpl_value.vb_strvalue = reply->element[2]->str;
			range_map.rhs->tmpl_value_length = reply->element[2]->len;
			range_map.rhs->tmpl_value_type = FR_TYPE_STRING;
			if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) {
				ret = IPPOOL_RCODE_FAIL;
				goto finish;
			}
		}
			break;

		case REDIS_REPLY_NIL:
			break;

		default:
			REDEBUG("Server returned unexpected type \"%s\" for range element (result[2])",
				fr_int2str(redis_reply_types, reply->element[2]->type, "<UNKNOWN>"));
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}
	}

	/*
	 *	Process Expiry time
	 */
	if (inst->expiry_attr && (reply->elements > 3)) {
		vp_tmpl_t expiry_rhs = {
			.name = "",
			.type = TMPL_TYPE_DATA,
			.tmpl_value_type = FR_TYPE_STRING,
			.quote = T_DOUBLE_QUOTED_STRING
		};
		vp_map_t expiry_map = {
			.lhs = inst->expiry_attr,
			.op = T_OP_SET,
			.rhs = &expiry_rhs
		};

		if (reply->element[3]->type != REDIS_REPLY_INTEGER) {
			REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])",
				fr_int2str(redis_reply_types, reply->element[3]->type, "<UNKNOWN>"));
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}

		expiry_map.rhs->tmpl_value.vb_uint32 = reply->element[3]->integer;
		expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32;
		if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) {
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}
	}
finish:
	fr_redis_reply_free(&reply);
	return ret;
}

/** Update an existing IP address in a pool
 *
 */
static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, REQUEST *request,
					  uint8_t const *key_prefix, size_t key_prefix_len,
					  fr_ipaddr_t *ip,
					  uint8_t const *device_id, size_t device_id_len,
					  uint8_t const *gateway_id, size_t gateway_id_len,
					  uint32_t expires)
{
	struct			timeval now;
	redisReply		*reply = NULL;

	fr_redis_rcode_t	status;
	ippool_rcode_t		ret = IPPOOL_RCODE_SUCCESS;

	vp_tmpl_t		range_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING };
	vp_map_t		range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs };

	gettimeofday(&now, NULL);

	/*
	 *	hiredis doesn't deal well with NULL string pointers
	 */
	if (!device_id) device_id = (uint8_t const *)"";
	if (!gateway_id) gateway_id = (uint8_t const *)"";

	if ((ip->af == AF_INET) && inst->ipv4_integer) {
		status = ippool_script(&reply, request, inst->cluster,
				       key_prefix, key_prefix_len,
				       inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout),
				       lua_update_digest, lua_update_cmd,
				       "EVALSHA %s 1 %b %u %u %u %b %b",
				       lua_update_digest,
				       key_prefix, key_prefix_len,
				       (unsigned int)now.tv_sec, expires,
				       htonl(ip->addr.v4.s_addr),
				       device_id, device_id_len,
				       gateway_id, gateway_id_len);
	} else {
		char ip_buff[FR_IPADDR_PREFIX_STRLEN];

		IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix);
		status = ippool_script(&reply, request, inst->cluster,
				       key_prefix, key_prefix_len,
				       inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout),
				       lua_update_digest, lua_update_cmd,
				       "EVALSHA %s 1 %b %u %u %s %b %b",
				       lua_update_digest,
				       key_prefix, key_prefix_len,
				       (unsigned int)now.tv_sec, expires,
				       ip_buff,
				       device_id, device_id_len,
				       gateway_id, gateway_id_len);
	}
	if (status != REDIS_RCODE_SUCCESS) {
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	if (reply->type != REDIS_REPLY_ARRAY) {
		REDEBUG("Expected result to be array got \"%s\"",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	if (reply->elements == 0) {
		REDEBUG("Got empty result array");
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	/*
	 *	Process return code
	 */
	if (reply->element[0]->type != REDIS_REPLY_INTEGER) {
		REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}
	ret = reply->element[0]->integer;
	if (ret < 0) goto finish;

	/*
	 *	Process Range identifier
	 */
	if (reply->elements > 1) {
		switch (reply->element[1]->type) {
		/*
		 *	Add range ID to request
		 */
		case REDIS_REPLY_STRING:
			range_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str;
			range_map.rhs->tmpl_value_length = reply->element[1]->len;
			range_map.rhs->tmpl_value_type = FR_TYPE_STRING;
			if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) {
				ret = IPPOOL_RCODE_FAIL;
				goto finish;
			}
			break;

		case REDIS_REPLY_NIL:
			break;

		default:
			REDEBUG("Server returned unexpected type \"%s\" for range element (result[1])",
				fr_int2str(redis_reply_types, reply->element[0]->type, "<UNKNOWN>"));
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}
	}

	/*
	 *	Copy expiry time to expires attribute (if set)
	 */
	if (inst->expiry_attr) {
		vp_tmpl_t expiry_rhs = {
			.name = "",
			.type = TMPL_TYPE_DATA,
			.tmpl_value_type = FR_TYPE_STRING,
			.quote = T_DOUBLE_QUOTED_STRING
		};
		vp_map_t expiry_map = {
			.lhs = inst->expiry_attr,
			.op = T_OP_SET,
			.rhs = &expiry_rhs
		};

		expiry_map.rhs->tmpl_value.vb_uint32 = expires;
		expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32;
		if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) {
			ret = IPPOOL_RCODE_FAIL;
			goto finish;
		}
	}

finish:
	fr_redis_reply_free(&reply);

	return ret;
}

/** Release an existing IP address in a pool
 *
 */
static ippool_rcode_t redis_ippool_release(rlm_redis_ippool_t const *inst, REQUEST *request,
					   uint8_t const *key_prefix, size_t key_prefix_len,
					   fr_ipaddr_t *ip,
					   uint8_t const *device_id, size_t device_id_len)
{
	struct			timeval now;
	redisReply		*reply = NULL;

	fr_redis_rcode_t	status;
	ippool_rcode_t		ret = IPPOOL_RCODE_SUCCESS;

	gettimeofday(&now, NULL);

	/*
	 *	hiredis doesn't deal well with NULL string pointers
	 */
	if (!device_id) device_id = (uint8_t const *)"";

	if ((ip->af == AF_INET) && inst->ipv4_integer) {
		status = ippool_script(&reply, request, inst->cluster,
				       key_prefix, key_prefix_len,
				       inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout),
				       lua_release_digest, lua_release_cmd,
				       "EVALSHA %s 1 %b %u %u %b",
				       lua_release_digest,
				       key_prefix, key_prefix_len,
				       (unsigned int)now.tv_sec,
				       htonl(ip->addr.v4.s_addr),
				       device_id, device_id_len);
	} else {
		char ip_buff[FR_IPADDR_PREFIX_STRLEN];

		IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix);
		status = ippool_script(&reply, request, inst->cluster,
				       key_prefix, key_prefix_len,
				       inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout),
				       lua_release_digest, lua_release_cmd,
				       "EVALSHA %s 1 %b %u %s %b",
				       lua_release_digest,
				       key_prefix, key_prefix_len,
				       (unsigned int)now.tv_sec,
				       ip_buff,
				       device_id, device_id_len);
	}
	if (status != REDIS_RCODE_SUCCESS) {
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	if (reply->type != REDIS_REPLY_ARRAY) {
		REDEBUG("Expected result to be array got \"%s\"",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	if (reply->elements == 0) {
		REDEBUG("Got empty result array");
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}

	/*
	 *	Process return code
	 */
	if (reply->element[0]->type != REDIS_REPLY_INTEGER) {
		REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = IPPOOL_RCODE_FAIL;
		goto finish;
	}
	ret = reply->element[0]->integer;
	if (ret < 0) goto finish;

finish:
	fr_redis_reply_free(&reply);

	return ret;
}

/** Find the pool name we'll be allocating from
 *
 * @param[out] out	Where to write the pool name.
 * @param[out] buff	Where to write the pool name (in the case of an expansion).
 * @param[in] bufflen	Size of the output buffer.
 * @param[in] inst	This instance of the rlm_redis_ippool module.
 * @param[in] request	The current request.
 * @return
 *	- < 0 on error.
 *	- 0 if no pool attribute exists, or the pool name is a zero length string.
 *	- > 0 on success (length of data written to out).
 */
static inline ssize_t ippool_pool_name(uint8_t const **out, uint8_t buff[], size_t bufflen,
				       rlm_redis_ippool_t const *inst, REQUEST *request)
{
	ssize_t slen;

	slen = tmpl_expand(out, (char *)buff, bufflen, request, inst->pool_name, NULL, NULL);
	if (slen < 0) {
		if (inst->pool_name->type == TMPL_TYPE_ATTR) {
			RDEBUG2("Pool attribute not present in request.  Doing nothing");
			return 0;
		}
		REDEBUG("Failed expanding pool name");
		return -1;
	}
	if (slen == 0) {
		RDEBUG2("Empty pool name.  Doing nothing");
		return 0;
	}

	if ((*out == buff) && is_truncated((size_t)slen, bufflen)) {
		REDEBUG("Pool name too long.  Expected %zu bytes, got %zu bytes", bufflen, (size_t)slen);
		return -1;
	}

	return slen;
}

static rlm_rcode_t mod_action(rlm_redis_ippool_t const *inst, REQUEST *request, ippool_action_t action)
{
	uint8_t		key_prefix_buff[IPPOOL_MAX_KEY_PREFIX_SIZE], device_id_buff[256], gateway_id_buff[256];
	uint8_t const	*key_prefix, *device_id = NULL, *gateway_id = NULL;
	size_t		key_prefix_len, device_id_len = 0, gateway_id_len = 0;
	ssize_t		slen;
	fr_ipaddr_t	ip;
	char		expires_buff[20];
	char const	*expires_str;
	unsigned long	expires = 0;
	char		*q;

	slen = ippool_pool_name(&key_prefix, (uint8_t *)&key_prefix_buff, sizeof(key_prefix_len), inst, request);
	if (slen < 0) return RLM_MODULE_FAIL;
	if (slen == 0) return RLM_MODULE_NOOP;

	key_prefix_len = (size_t)slen;

	if (inst->device_id) {
		slen = tmpl_expand((char const **)&device_id,
				   (char *)&device_id_buff, sizeof(device_id_buff),
				   request, inst->device_id, NULL, NULL);
		if (slen < 0) {
			REDEBUG("Failed expanding device (%s)", inst->device_id->name);
			return RLM_MODULE_FAIL;
		}
		device_id_len = (size_t)slen;
	}

	if (inst->gateway_id) {
		slen = tmpl_expand((char const **)&gateway_id,
				   (char *)&gateway_id_buff, sizeof(gateway_id_buff),
				   request, inst->gateway_id, NULL, NULL);
		if (slen < 0) {
			REDEBUG("Failed expanding gateway (%s)", inst->gateway_id->name);
			return RLM_MODULE_FAIL;
		}
		gateway_id_len = (size_t)slen;
	}

	switch (action) {
	case POOL_ACTION_ALLOCATE:
		if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff),
				request, inst->offer_time, NULL, NULL) < 0) {
			REDEBUG("Failed expanding offer_time (%s)", inst->offer_time->name);
			return RLM_MODULE_FAIL;
		}

		expires = strtoul(expires_str, &q, 10);
		if (q != (expires_str + strlen(expires_str))) {
			REDEBUG("Invalid offer_time.  Must be an integer value");
			return RLM_MODULE_FAIL;
		}

		ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, NULL,
				    device_id, device_id_len, gateway_id, gateway_id_len, expires);
		switch (redis_ippool_allocate(inst, request, key_prefix, key_prefix_len,
					      device_id, device_id_len,
					      gateway_id, gateway_id_len, (uint32_t)expires)) {
		case IPPOOL_RCODE_SUCCESS:
			RDEBUG2("IP address lease allocated");
			return RLM_MODULE_UPDATED;

		case IPPOOL_RCODE_POOL_EMPTY:
			RWDEBUG("Pool contains no free addresses");
			return RLM_MODULE_NOTFOUND;

		default:
			return RLM_MODULE_FAIL;
		}

	case POOL_ACTION_UPDATE:
	{
		char		ip_buff[INET6_ADDRSTRLEN + 4];
		char const	*ip_str;

		if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff),
				request, inst->lease_time, NULL, NULL) < 0) {
			REDEBUG("Failed expanding lease_time (%s)", inst->lease_time->name);
			return RLM_MODULE_FAIL;
		}

		expires = strtoul(expires_str, &q, 10);
		if (q != (expires_str + strlen(expires_str))) {
			REDEBUG("Invalid expires.  Must be an integer value");
			return RLM_MODULE_FAIL;
		}

		if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) {
			REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name);
			return RLM_MODULE_FAIL;
		}

		if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) {
			RPEDEBUG("Failed parsing address");
			return RLM_MODULE_FAIL;
		}

		ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len,
				    ip_str, device_id, device_id_len, gateway_id, gateway_id_len, expires);
		switch (redis_ippool_update(inst, request, key_prefix, key_prefix_len,
					    &ip, device_id, device_id_len,
					    gateway_id, gateway_id_len, (uint32_t)expires)) {
		case IPPOOL_RCODE_SUCCESS:
			RDEBUG2("Requested IP address' \"%s\" lease updated", ip_str);

			/*
			 *	Copy over the input IP address to the reply attribute
			 */
			if (inst->copy_on_update) {
				vp_tmpl_t ip_rhs = {
					.name = "",
					.type = TMPL_TYPE_DATA,
					.quote = T_BARE_WORD,
				};
				vp_map_t ip_map = {
					.lhs = inst->allocated_address_attr,
					.op = T_OP_SET,
					.rhs = &ip_rhs
				};

				ip_rhs.tmpl_value_length = strlen(ip_str);
				ip_rhs.tmpl_value.vb_strvalue = ip_str;
				ip_rhs.tmpl_value_type = FR_TYPE_STRING;

				if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) return RLM_MODULE_FAIL;
			}
			return RLM_MODULE_UPDATED;

		/*
		 *	It's useful to be able to identify the 'not found' case
		 *	as we can relay to a server where the IP address might
		 *	be found.  This extremely useful for migrations.
		 */
		case IPPOOL_RCODE_NOT_FOUND:
			REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str);
			return RLM_MODULE_NOTFOUND;

		case IPPOOL_RCODE_EXPIRED:
			REDEBUG("Requested IP address' \"%s\" lease already expired at time of renewal", ip_str);
			return RLM_MODULE_INVALID;

		case IPPOOL_RCODE_DEVICE_MISMATCH:
			REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str);
			return RLM_MODULE_INVALID;

		default:
			return RLM_MODULE_FAIL;
		}
	}

	case POOL_ACTION_RELEASE:
	{
		char		ip_buff[INET6_ADDRSTRLEN + 4];
		char const	*ip_str;

		if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) {
			REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name);
			return RLM_MODULE_FAIL;
		}

		if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) {
			RPEDEBUG("Failed parsing address");
			return RLM_MODULE_FAIL;
		}

		ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len,
				    ip_str, device_id, device_id_len, gateway_id, gateway_id_len, 0);
		switch (redis_ippool_release(inst, request, key_prefix, key_prefix_len,
					     &ip, device_id, device_id_len)) {
		case IPPOOL_RCODE_SUCCESS:
			RDEBUG2("IP address \"%s\" released", ip_str);
			return RLM_MODULE_UPDATED;

		/*
		 *	It's useful to be able to identify the 'not found' case
		 *	as we can relay to a server where the IP address might
		 *	be found.  This extremely useful for migrations.
		 */
		case IPPOOL_RCODE_NOT_FOUND:
			REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str);
			return RLM_MODULE_NOTFOUND;

		case IPPOOL_RCODE_DEVICE_MISMATCH:
			REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str);
			return RLM_MODULE_INVALID;

		default:
			return RLM_MODULE_FAIL;
		}
	}

	case POOL_ACTION_BULK_RELEASE:
		RDEBUG2("Bulk release not yet implemented");
		return RLM_MODULE_NOOP;

	default:
		rad_assert(0);
		return RLM_MODULE_FAIL;
	}
}

static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull);
static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_redis_ippool_t const	*inst = instance;
	VALUE_PAIR			*vp;

	/*
	 *	Pool-Action override
	 */
	vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
	if (vp) return mod_action(inst, request, vp->vp_uint32);

	/*
	 *	Otherwise, guess the action by Acct-Status-Type
	 */
	vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY);
	if (!vp) {
		RDEBUG2("Couldn't find &request:Acct-Status-Type or &control:Pool-Action, doing nothing...");
		return RLM_MODULE_NOOP;
	}

	switch (vp->vp_uint32) {
	case FR_STATUS_START:
	case FR_STATUS_ALIVE:
		return mod_action(inst, request, POOL_ACTION_UPDATE);

	case FR_STATUS_STOP:
		return mod_action(inst, request, POOL_ACTION_RELEASE);

	case FR_STATUS_ACCOUNTING_OFF:
	case FR_STATUS_ACCOUNTING_ON:
		return mod_action(inst, request, POOL_ACTION_BULK_RELEASE);

	default:
		return RLM_MODULE_NOOP;
	}
}

static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull);
static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_redis_ippool_t const	*inst = instance;
	VALUE_PAIR			*vp;

	/*
	 *	Unless it's overridden the default action is to allocate
	 *	when called in Post-Auth.
	 */
	vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
	return mod_action(inst, request, vp ? vp->vp_uint32 : POOL_ACTION_ALLOCATE);
}

static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull);
static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_redis_ippool_t const	*inst = instance;
	VALUE_PAIR			*vp;
	ippool_action_t			action = POOL_ACTION_ALLOCATE;

	/*
	 *	Unless it's overridden the default action is to allocate
	 *	when called in Post-Auth.
	 */
	vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY);
	if (vp) {
		if ((vp->vp_uint32 > 0) && (vp->vp_uint32 <= POOL_ACTION_BULK_RELEASE)) {
			action = vp->vp_uint32;

		} else {
			RWDEBUG("Ignoring invalid action %d", vp->vp_uint32);
			return RLM_MODULE_NOOP;
		}
#ifdef WITH_DHCP
	} else if (request->dict == dict_dhcpv4) {
		vp = fr_pair_find_by_da(request->control, attr_message_type, TAG_ANY);
		if (!vp) goto run;

		if (vp->vp_uint8 == FR_DHCP_REQUEST) action = POOL_ACTION_UPDATE;
#endif
	}

run:
	return mod_action(inst, request, action);
}

static int mod_instantiate(void *instance, CONF_SECTION *conf)
{
	static bool			done_hash = false;
	CONF_SECTION			*subcs = cf_section_find(conf, "redis", NULL);

	rlm_redis_ippool_t		*inst = instance;

	rad_assert(inst->allocated_address_attr->type == TMPL_TYPE_ATTR);
	rad_assert(subcs);

	inst->cluster = fr_redis_cluster_alloc(inst, subcs, &inst->conf, true, NULL, NULL, NULL);
	if (!inst->cluster) return -1;

	if (!fr_redis_cluster_min_version(inst->cluster, "3.0.2")) {
		PERROR("Cluster error");
		return -1;
	}

	/*
	 *	Pre-Compute the SHA1 hashes of the Lua scripts
	 */
	if (!done_hash) {
		fr_sha1_ctx	sha1_ctx;
		uint8_t		digest[SHA1_DIGEST_LENGTH];

		fr_sha1_init(&sha1_ctx);
		fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_alloc_cmd, sizeof(lua_alloc_cmd) - 1);
		fr_sha1_final(digest, &sha1_ctx);
		fr_bin2hex(lua_alloc_digest, digest, sizeof(digest));

		fr_sha1_init(&sha1_ctx);
		fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_update_cmd, sizeof(lua_update_cmd) - 1);
		fr_sha1_final(digest, &sha1_ctx);
		fr_bin2hex(lua_update_digest, digest, sizeof(digest));

		fr_sha1_init(&sha1_ctx);
		fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_release_cmd, sizeof(lua_release_cmd) - 1);
		fr_sha1_final(digest, &sha1_ctx);
		fr_bin2hex(lua_release_digest, digest, sizeof(digest));
	}

	/*
	 *	If we don't have a separate time specifically for offers
	 *	just use the lease time.
	 */
	if (!inst->offer_time) inst->offer_time = inst->lease_time;

	return 0;
}

static int mod_load(void)
{
	fr_redis_version_print();

	return 0;
}

extern module_t rlm_redis_ippool;
module_t rlm_redis_ippool = {
	.magic		= RLM_MODULE_INIT,
	.name		= "redis",
	.type		= RLM_TYPE_THREAD_SAFE,
	.inst_size	= sizeof(rlm_redis_ippool_t),
	.config		= module_config,
	.onload		= mod_load,
	.instantiate	= mod_instantiate,
	.methods = {
		[MOD_ACCOUNTING]	= mod_accounting,
		[MOD_AUTHORIZE]		= mod_authorize,
		[MOD_POST_AUTH]		= mod_post_auth,
	},
};
Beispiel #28
0
/*
 *	Do any per-module initialization that is separate to each
 *	configured instance of the module.  e.g. set up connections
 *	to external databases, read configuration files, set up
 *	dictionary entries, etc.
 *
 *	If configuration information is given in the config section
 *	that must be referenced in later calls, store a handle to it
 *	in *instance otherwise put a null pointer there.
 *
 *	Setup a hashes wich we will use later
 *	parse a module and give him a chance to live
 *
 */
static int mod_instantiate(void *instance, CONF_SECTION *conf)
{
	rlm_perl_t	*inst = instance;
	AV		*end_AV;

	char const	**embed_c;	/* Stupid Perl and lack of const consistency */
	char		**embed;
	char		**envp = NULL;
	int		exitstatus = 0, argc=0;
	char		arg[] = "0";

	CONF_SECTION	*cs;

#ifdef USE_ITHREADS
	/*
	 *	Create pthread key. This key will be stored in instance
	 */
	pthread_mutex_init(&inst->clone_mutex, NULL);

	MEM(inst->thread_key = talloc_zero(inst, pthread_key_t));
	rlm_perl_make_key(inst->thread_key);
#endif

	/*
	 *	Setup the argument array we pass to the perl interpreter
	 */
	MEM(embed_c = talloc_zero_array(inst, char const *, 4));
	memcpy(&embed, &embed_c, sizeof(embed));
	embed_c[0] = NULL;
	if (inst->perl_flags) {
		embed_c[1] = inst->perl_flags;
		embed_c[2] = inst->module;
		embed_c[3] = arg;
		argc = 4;
	} else {
		embed_c[1] = inst->module;
		embed_c[2] = arg;
		argc = 3;
	}

	/*
	 *	Create tweak the server's environment to support
	 *	perl. Docs say only call this once... Oops.
	 */
	if (!perl_sys_init3_called) {
		PERL_SYS_INIT3(&argc, &embed, &envp);
		perl_sys_init3_called = 1;
	}

	/*
	 *	Allocate a new perl interpreter to do the parsing
	 */
	if ((inst->perl = perl_alloc()) == NULL) {
		ERROR("No memory for allocating new perl interpretor!");
		return -1;
	}
	perl_construct(inst->perl);	/* ...and initialise it */

#ifdef USE_ITHREADS
	PL_perl_destruct_level = 2;

	{
		dTHXa(inst->perl);
	}
	PERL_SET_CONTEXT(inst->perl);
#endif

#if PERL_REVISION >= 5 && PERL_VERSION >=8
	PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
#endif

	exitstatus = perl_parse(inst->perl, xs_init, argc, embed, NULL);

	end_AV = PL_endav;
	PL_endav = (AV *)NULL;

	if (exitstatus) {
		ERROR("Perl_parse failed: %s not found or has syntax errors", inst->module);
		return -1;
	}

	/* parse perl configuration sub-section */
	cs = cf_section_find(conf, "config", NULL);
	if (cs) {
		inst->rad_perlconf_hv = get_hv("RAD_PERLCONF", 1);
		perl_parse_config(cs, 0, inst->rad_perlconf_hv);
	}

	inst->perl_parsed = true;
	perl_run(inst->perl);

	PL_endav = end_AV;

	return 0;
}
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, UNUSED fr_io_action_t action)
{
	rlm_rcode_t rcode;
	CONF_SECTION *unlang;
	fr_dict_enum_t const *dv;
	fr_dict_attr_t const *da = NULL;
	VALUE_PAIR *vp;

	REQUEST_VERIFY(request);
	rad_assert(request->packet->code > 0);
	rad_assert(request->packet->code <= FR_DHCP_INFORM);

	switch (request->request_state) {
	case REQUEST_INIT:
		RDEBUG("Received %s ID %08x", dhcp_message_types[request->packet->code], request->packet->id);
		log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");

		request->component = "dhcpv4";

		dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->packet->code));
		if (!dv) {
			REDEBUG("Failed to find value for &request:DHCP-Message-Type");
			return FR_IO_FAIL;
		}

		unlang = cf_section_find(request->server_cs, "recv", dv->alias);
		if (!unlang) {
			RWDEBUG("Failed to find 'recv %s' section", dv->alias);
			request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND;
			goto send_reply;
		}

		RDEBUG("Running 'recv %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_continue(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		/*
		 *	Allow the admin to explicitly set the reply
		 *	type.
		 */
		vp = fr_pair_find_by_da(request->reply->vps, attr_message_type, TAG_ANY);
		if (vp) {
			request->reply->code = vp->vp_uint8;
		} else switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			request->reply->code = reply_ok[request->packet->code];
			break;

		default:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_FAIL:
			request->reply->code = reply_fail[request->packet->code];
			break;

		case RLM_MODULE_HANDLED:
			if (!request->reply->code) request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND;
			break;
		}

		/*
		 *	DHCP-Release / Decline doesn't send a reply, and doesn't run "send DHCP-Do-Not-Respond"
		 */
		if (!request->reply->code) {
			return FR_IO_DONE;
		}

		/*
		 *	Offer and ACK MUST have YIADDR.
		 */
		if ((request->reply->code == FR_DHCP_OFFER) || (request->reply->code == FR_DHCP_ACK)) {
			vp = fr_pair_find_by_da(request->reply->vps, attr_yiaddr, TAG_ANY);
			if (!vp) {
				REDEBUG("%s packet does not have YIADDR.  The client will not receive an IP address.",
					dhcp_message_types[request->reply->code]);
			}
		}

		dv = fr_dict_enum_by_value(da, fr_box_uint8(request->reply->code));
		unlang = NULL;
		if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias);

		if (!unlang) goto send_reply;

	rerun_nak:
		RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_SEND;
		/* FALL-THROUGH */

	case REQUEST_SEND:
		rcode = unlang_interpret_continue(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE;

		if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD;

		rad_assert(request->log.unlang_indent == 0);

		switch (rcode) {
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_HANDLED:
			/* reply is already set */
			break;

		default:
			/*
			 *	If we over-ride an ACK with a NAK, run
			 *	the NAK section.
			 */
			if (request->reply->code != FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) {
				dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code));
				RWDEBUG("Failed running 'send %s', trying 'send Do-Not-Respond'", dv->alias);

				request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND;

				dv = fr_dict_enum_by_value(da, fr_box_uint8(request->reply->code));
				unlang = NULL;
				if (!dv) goto send_reply;

				unlang = cf_section_find(request->server_cs, "send", dv->alias);
				if (unlang) goto rerun_nak;

				RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias);
			}
			break;
		}

	send_reply:
		/*
		 *	Check for "do not respond".
		 */
		if (request->reply->code == FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) {
			RDEBUG("Not sending reply to client");
			return FR_IO_DONE;
		}

		if (RDEBUG_ENABLED) common_packet_debug(request, request->reply, false);
		break;

	default:
		return FR_IO_FAIL;
	}

	return FR_IO_REPLY;
}