예제 #1
0
/** Find an existing module instance and verify it implements the specified method
 *
 * Extracts the method from the module name where the format is @verbatim <module>.<method> @endverbatim
 * and ensures the module implements the specified method.
 *
 * @param[out] method		the method component we found associated with the module. May be NULL.
 * @param[in] modules		section in the main config.
 * @param[in] name 		The name of the module we're attempting to find, concatenated with
 *				the method.
 * @return
 *	- The module instance on success.
 *	- NULL on error (or not found).
 */
module_instance_t *module_find_with_method(rlm_components_t *method, CONF_SECTION *modules, char const *name)
{
	char			*p;
	rlm_components_t	i;
	module_instance_t	*mi;

	/*
	 *	Module names are allowed to contain '.'
	 *	so we search for the bare module name first.
	 */
	mi = module_find(modules, name);
	if (mi) return mi;

	/*
	 *	Find out if the instance name contains
	 *	a method, if it doesn't, then the module
	 *	doesn't exist.
	 */
	p = strrchr(name, '.');
	if (!p) return NULL;

	/*
	 *	Find the component.
	 */
	for (i = MOD_AUTHENTICATE; i < MOD_COUNT; i++) {
		if (strcmp(p + 1, section_type_value[i].section) == 0) {
			char *inst_name;

			inst_name = talloc_bstrndup(NULL, name, p - name);
			mi = module_find(modules, inst_name);
			talloc_free(inst_name);
			if (!mi) return NULL;

			/*
			 *	Verify the module actually implements
			 *	the specified method.
			 */
			if (!mi->module->methods[i]) {
				cf_log_debug(modules, "%s does not implement method \"%s\"",
					     mi->module->name, p + 1);
				return NULL;
			}
			if (method) *method = i;

			return mi;
		}
	}

	return mi;
}
예제 #2
0
/** Convert attr tmpl to an xlat for &attr[*]
 *
 * @param ctx to allocate new xlat_expt_t in.
 * @param vpt to convert.
 * @return
 *	- NULL if unable to convert (not necessarily error).
 *	- a new #vp_tmpl_t.
 */
xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, vp_tmpl_t *vpt)
{
	xlat_exp_t *node;

	if (vpt->type != TMPL_TYPE_ATTR) return NULL;

	node = talloc_zero(ctx, xlat_exp_t);
	node->type = XLAT_ATTRIBUTE;
	node->fmt = talloc_bstrndup(node, vpt->name, vpt->len);
	node->attr = tmpl_alloc(node, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1, T_BARE_WORD);
	memcpy(&node->attr->data, &vpt->data, sizeof(vpt->data));

	return node;
}
예제 #3
0
/** Extract a subcapture value from the request
 *
 * @note This is the POSIX variant of the function.
 *
 * @param[in] ctx	To allocate subcapture buffer in.
 * @param[out] out	Where to write the subcapture string.
 * @param[in] request	to extract.
 * @param[in] num	Subcapture index (0 for entire match).
 * @return
 *	- 0 on success.
 *	- -1 on notfound.
 */
int regex_request_to_sub(TALLOC_CTX *ctx, char **out, REQUEST *request, uint32_t num)
{
	fr_regcapture_t	*rc;
	char 		*buff;
	char const	*start;
	size_t		len;
	regmatch_t	*match_data;

	rc = request_data_reference(request, request, REQUEST_DATA_REGEX);
	if (!rc) {
		RDEBUG4("No subcapture data found");
		*out = NULL;
		return -1;
	}
	match_data = rc->regmatch->match_data;

	/*
	 *	Greater than our capture array
	 *
	 *	-1 means no value in this capture group.
	 */
	if ((num >= rc->regmatch->used) || (match_data[num].rm_eo == -1) || (match_data[num].rm_so == -1)) {
		RDEBUG4("%i/%zu Not found", num + 1, rc->regmatch->used);
		*out = NULL;
		return -1;
	}

	/*
	 *	Sanity checks on the offsets
	 */
	rad_assert(match_data[num].rm_eo <= (regoff_t)talloc_array_length(rc->regmatch->subject));
	rad_assert(match_data[num].rm_so <= (regoff_t)talloc_array_length(rc->regmatch->subject));

	start = rc->regmatch->subject + match_data[num].rm_so;
	len = match_data[num].rm_eo - match_data[num].rm_so;

	MEM(buff = talloc_bstrndup(ctx, start, len));
	RDEBUG4("%i/%zu Found: %pV (%zu)", num + 1, rc->regmatch->used, fr_box_strvalue_buffer(buff), len);

	*out = buff;

	return 0;
}
예제 #4
0
/** Process the Peer's response and advantage the state machine
 *
 */
static rlm_rcode_t mod_process(UNUSED void *instance, eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);

	fr_sim_decode_ctx_t	ctx = {
					.keys = &eap_aka_session->keys,
				};
	VALUE_PAIR		*vp, *vps, *subtype_vp;
	fr_cursor_t		cursor;

	eap_aka_subtype_t	subtype;

	int			ret;

	/*
	 *	RFC 4187 says we ignore the contents of the
	 *	next packet after we send our success notification
	 *	and always send a success.
	 */
	if (eap_aka_session->state == EAP_AKA_SERVER_SUCCESS_NOTIFICATION) {
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_SUCCESS);
		return RLM_MODULE_HANDLED;
	}

	/* vps is the data from the client */
	vps = request->packet->vps;

	fr_cursor_init(&cursor, &request->packet->vps);
	fr_cursor_tail(&cursor);

	ret = fr_sim_decode(eap_session->request,
			    &cursor,
			    dict_eap_aka,
			    eap_session->this_round->response->type.data,
			    eap_session->this_round->response->type.length,
			    &ctx);
	/*
	 *	RFC 4187 says we *MUST* notify, not just
	 *	send an EAP-Failure in this case where
	 *	we cannot decode an EAP-AKA packet.
	 */
	if (ret < 0) {
		RPEDEBUG2("Failed decoding EAP-AKA attributes");
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;	/* We need to process more packets */
	}

	vp = fr_cursor_current(&cursor);
	if (vp && RDEBUG_ENABLED2) {
		RDEBUG2("EAP-AKA decoded attributes");
		log_request_pair_list(L_DBG_LVL_2, request, vp, NULL);
	}

	subtype_vp = fr_pair_find_by_da(vps, attr_eap_aka_subtype, TAG_ANY);
	if (!subtype_vp) {
		REDEBUG("Missing EAP-AKA-Subtype");
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
	subtype = subtype_vp->vp_uint16;

	switch (eap_aka_session->state) {
	/*
	 *	Here we expected the peer to send
	 *	us identities for validation.
	 */
	case EAP_AKA_SERVER_IDENTITY:
		switch (subtype) {
		case EAP_AKA_IDENTITY:
			if (process_eap_aka_identity(eap_session, vps) == 0) return RLM_MODULE_HANDLED;
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;	/* We need to process more packets */

		/*
		 *	Case 1 where we're allowed to send an EAP-Failure
		 *
		 *	This can happen in the case of a conservative
		 *	peer, where it refuses to provide the permanent
		 *	identity.
		 */
		case EAP_AKA_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-AKA Peer rejected AKA-Identity (%s) with client-error message but "
					"has not supplied a client error code",
					fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
			} else {
				REDEBUG("Client rejected AKA-Identity (%s) with error: %s (%i)",
					fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"),
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		case EAP_AKA_NOTIFICATION:
		notification:
		{
			char buff[20];

			vp = fr_pair_afrom_da(vps, attr_eap_aka_notification);
			if (!vp) {
				REDEBUG2("Received AKA-Notification with no notification code");
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}

			/*
			 *	Case 3 where we're allowed to send an EAP-Failure
			 */
			if (!(vp->vp_uint16 & 0x8000)) {
				REDEBUG2("AKA-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
					 vp->vp_uint16);
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
				return RLM_MODULE_REJECT;
			}

			/*
			 *	...if it's not a failure, then re-enter the
			 *	current state.
			 */
			REDEBUG2("Got AKA-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
			eap_aka_state_enter(eap_session, eap_aka_session->state);
			return RLM_MODULE_HANDLED;
		}

		default:
		unexpected_subtype:
			/*
			 *	RFC 4187 says we *MUST* notify, not just
			 *	send an EAP-Failure in this case.
			 */
			REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;				/* We need to process more packets */
		}

	/*
	 *	Process the response to our previous challenge.
	 */
	case EAP_AKA_SERVER_CHALLENGE:
		switch (subtype) {
		case EAP_AKA_CHALLENGE:
			switch (process_eap_aka_challenge(eap_session, vps)) {
			case 1:
				return RLM_MODULE_HANDLED;

			case 0:
				return RLM_MODULE_OK;

			case -1:
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}


		case EAP_AKA_SYNCHRONIZATION_FAILURE:
			REDEBUG("EAP-AKA Peer synchronization failure");	/* We can't handle these yet */
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;				/* We need to process more packets */

		/*
		 *	Case 1 where we're allowed to send an EAP-Failure
		 */
		case EAP_AKA_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-AKA Peer rejected AKA-Challenge with client-error message but "
					"has not supplied a client error code");
			} else {
				REDEBUG("Client rejected AKA-Challenge with error: %s (%i)",
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	Case 2 where we're allowed to send an EAP-Failure
		 */
		case EAP_AKA_AUTHENTICATION_REJECT:
			REDEBUG("EAP-AKA Peer Rejected AUTN");
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;

		case EAP_AKA_NOTIFICATION:
			goto notification;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Peer acked our failure
	 */
	case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
		switch (subtype) {
		case EAP_AKA_NOTIFICATION:
			RDEBUG2("AKA-Notification ACKed, sending EAP-Failure");
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Something bad happened...
	 */
	default:
		rad_assert(0);
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
}

/** Initiate the EAP-SIM session by starting the state machine
 *
 */
static rlm_rcode_t mod_session_init(void *instance, eap_session_t *eap_session)
{
	REQUEST				*request = eap_session->request;
	eap_aka_session_t		*eap_aka_session;
	rlm_eap_aka_t			*inst = instance;
	fr_sim_id_type_t		type;
	fr_sim_method_hint_t		method;

	MEM(eap_aka_session = talloc_zero(eap_session, eap_aka_session_t));

	eap_session->opaque = eap_aka_session;

	/*
	 *	Set default configuration, we may allow these
	 *	to be toggled by attributes later.
	 */
	eap_aka_session->request_identity = inst->request_identity;
	eap_aka_session->send_result_ind = inst->protected_success;
	eap_aka_session->id_req = SIM_NO_ID_REQ;	/* Set the default */

	/*
	 *	This value doesn't have be strong, but it is
	 *	good if it is different now and then.
	 */
	eap_aka_session->aka_id = (fr_rand() & 0xff);

	/*
	 *	Process the identity that we received in the
	 *	EAP-Identity-Response and use it to determine
	 *	the initial request we send to the Supplicant.
	 */
	if (fr_sim_id_type(&type, &method,
			   eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
		RPWDEBUG2("Failed parsing identity, continuing anyway");
	}

	/*
	 *	Unless AKA-Prime is explicitly disabled,
	 *	use it... It has stronger keying, and
	 *	binds authentication to the network.
	 */
	switch (eap_session->type) {
	case FR_EAP_AKA_PRIME:
	default:
		RDEBUG2("New EAP-AKA' session");
		eap_aka_session->type = FR_EAP_AKA_PRIME;
		eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA_PRIME_WITH_CK_PRIME_IK_PRIME;
		eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha256();
		eap_aka_session->keys.network = (uint8_t *)talloc_bstrndup(eap_aka_session, inst->network_name,
									   talloc_array_length(inst->network_name) - 1);
		eap_aka_session->keys.network_len = talloc_array_length(eap_aka_session->keys.network) - 1;
		switch (method) {
		default:
			RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
				"attempting EAP-AKA'", fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
			break;

		case SIM_METHOD_HINT_AKA_PRIME:
		case SIM_METHOD_HINT_UNKNOWN:
			break;
		}
		break;

	case FR_EAP_AKA:
		RDEBUG2("New EAP-AKA session");
		eap_aka_session->type = FR_EAP_AKA;
		eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA;	/* Not actually sent */
		eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha1();
		eap_aka_session->send_at_bidding = true;
		switch (method) {
		default:
			RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
				"attempting EAP-AKA", fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
			break;

		case SIM_METHOD_HINT_AKA:
		case SIM_METHOD_HINT_UNKNOWN:
			break;
		}
		break;
	}
	eap_session->process = mod_process;

	/*
	 *	Admin wants us to always request an identity
	 *	initially.  The RFC says this is also the
	 *	better way to operate, as the supplicant
	 *	can 'decorate' the identity in the identity
	 *	response.
	 */
	if (inst->request_identity) {
	request_id:
		/*
		 *	We always start by requesting
		 *	any ID initially as we can
		 *	always negotiate down.
		 */
		eap_aka_session->id_req = SIM_ANY_ID_REQ;
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_IDENTITY);
		return RLM_MODULE_HANDLED;
	}
	/*
	 *	Figure out what type of identity we have
	 *	and use it to determine the initial
	 *	request we send.
	 */
	switch (type) {
	/*
	 *	If there's no valid tag on the identity
	 *	then it's probably been decorated by the
	 *	supplicant.
	 *
	 *	Request the unmolested identity
	 */
	case SIM_ID_TYPE_UNKNOWN:
		RWDEBUG("Identity format unknown, sending Identity request");
		goto request_id;

	/*
	 *	These types need to be transformed into something
	 *	usable before we can do anything.
	 */
	case SIM_ID_TYPE_PSEUDONYM:
	case SIM_ID_TYPE_FASTAUTH:

	/*
	 *	Permanent ID means we can just send the challenge
	 */
	case SIM_ID_TYPE_PERMANENT:
		eap_aka_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
		MEM(eap_aka_session->keys.identity = talloc_memdup(eap_aka_session, eap_session->identity,
								   eap_aka_session->keys.identity_len));
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_CHALLENGE);
		return RLM_MODULE_HANDLED;
	}

	return RLM_MODULE_HANDLED;
}
예제 #5
0
/** Execute an arbitrary SQL query
 *
 *  For selects the first value of the first column will be returned,
 *  for inserts, updates and deletes the number of rows affected will be
 *  returned instead.
 */
static ssize_t sql_xlat(char **out, UNUSED size_t outlen,
			void const *mod_inst, UNUSED void const *xlat_inst,
			REQUEST *request, char const *fmt)
{
	rlm_sql_handle_t	*handle = NULL;
	rlm_sql_row_t		row;
	rlm_sql_t const		*inst = mod_inst;
	sql_rcode_t		rcode;
	ssize_t			ret = 0;

	/*
	 *	Add SQL-User-Name attribute just in case it is needed
	 *	We could search the string fmt for SQL-User-Name to see if this is
	 * 	needed or not
	 */
	sql_set_user(inst, request, NULL);

	handle = fr_connection_get(inst->pool);	/* connection pool should produce error */
	if (!handle) return 0;

	rlm_sql_query_log(inst, request, NULL, fmt);

	/*
	 *	If the query starts with any of the following prefixes,
	 *	then return the number of rows affected
	 */
	if ((strncasecmp(fmt, "insert", 6) == 0) ||
	    (strncasecmp(fmt, "update", 6) == 0) ||
	    (strncasecmp(fmt, "delete", 6) == 0)) {
		int numaffected;

		rcode = rlm_sql_query(inst, request, &handle, fmt);
		if (rcode != RLM_SQL_OK) {
		query_error:
			RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, rcode, "<INVALID>"));

			ret = -1;
			goto finish;
		}

		numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
		if (numaffected < 1) {
			RDEBUG("SQL query affected no rows");

			goto finish;
		}

		MEM(*out = talloc_asprintf(request, "%d", numaffected));
		ret = talloc_array_length(*out) - 1;

		(inst->module->sql_finish_query)(handle, inst->config);

		goto finish;
	} /* else it's a SELECT statement */

	rcode = rlm_sql_select_query(inst, request, &handle, fmt);
	if (rcode != RLM_SQL_OK) goto query_error;

	rcode = rlm_sql_fetch_row(&row, inst, request, &handle);
	if (rcode) {
		(inst->module->sql_finish_select_query)(handle, inst->config);
		goto query_error;
	}

	if (!row) {
		RDEBUG("SQL query returned no results");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	if (!row[0]){
		RDEBUG("NULL value in first column of result");
		(inst->module->sql_finish_select_query)(handle, inst->config);
		ret = -1;

		goto finish;
	}

	*out = talloc_bstrndup(request, row[0], strlen(row[0]));
	ret = talloc_array_length(*out) - 1;

	(inst->module->sql_finish_select_query)(handle, inst->config);

finish:
	fr_connection_release(inst->pool, handle);

	return ret;
}