Ejemplo n.º 1
0
USES_APPLE_DEPRECATED_API	/* OpenSSL API has been deprecated by Apple */

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>

#include <openssl/hmac.h>

#include <freeradius-devel/util/sha1.h>
#include <freeradius-devel/tls/base.h>
#include <freeradius-devel/tls/missing.h>

#include "tls.h"
#include "base.h"
#include "attrs.h"

#define EAP_TLS_MPPE_KEY_LEN     32

/** Generate keys according to RFC 2716 and add to the reply
 *
 */
int eap_crypto_mppe_keys(REQUEST *request, SSL *ssl, char const *prf_label, size_t prf_label_len)
{
	uint8_t		out[4 * EAP_TLS_MPPE_KEY_LEN];
	uint8_t		*p;

	if (SSL_export_keying_material(ssl, out, sizeof(out), prf_label, prf_label_len, NULL, 0, 0) != 1) {
		tls_log_error(request, "Failed generating MPPE keys");
		return -1;
	}

	if (RDEBUG_ENABLED3) {
		uint8_t	random[SSL3_RANDOM_SIZE];
		size_t random_len;
		uint8_t	master_key[SSL_MAX_MASTER_KEY_LENGTH];
		size_t master_key_len;

		RDEBUG3("Key Derivation Function input");
		RINDENT();
		RDEBUG3("prf label          : %pV", fr_box_strvalue_len(prf_label, prf_label_len));
		master_key_len = SSL_SESSION_get_master_key(SSL_get_session(ssl), master_key, sizeof(master_key));
		RDEBUG3("master session key : %pH", fr_box_octets(master_key, master_key_len));
		random_len = SSL_get_client_random(ssl, random, SSL3_RANDOM_SIZE);
		RDEBUG3("client random      : %pH", fr_box_octets(random, random_len));
		random_len = SSL_get_server_random(ssl, random, SSL3_RANDOM_SIZE);
		RDEBUG3("server random      : %pH", fr_box_octets(random, random_len));
		REXDENT();
	}

	RDEBUG2("Adding session keys");
	p = out;
	eap_add_reply(request, attr_ms_mppe_recv_key, p, EAP_TLS_MPPE_KEY_LEN);
	p += EAP_TLS_MPPE_KEY_LEN;
	eap_add_reply(request, attr_ms_mppe_send_key, p, EAP_TLS_MPPE_KEY_LEN);

	eap_add_reply(request, attr_eap_msk, out, 64);
	eap_add_reply(request, attr_eap_emsk, out + 64, 64);

	return 0;
}
Ejemplo n.º 2
0
/** Prints information to the debug log on the current timeout settings
 *
 * There are so many different timers in LDAP it's often hard to debug
 * issues with them, hence the need for this function.
 */
void fr_ldap_timeout_debug(REQUEST *request, fr_ldap_connection_t const *conn,
			   struct timeval const *timeout, char const *prefix)
{
	struct timeval 			*net = NULL, *client = NULL;
	int				server = 0;
	fr_ldap_config_t const	*handle_config = conn->config;

	if (request) RINDENT();

#ifdef LDAP_OPT_NETWORK_TIMEOUT
	if (ldap_get_option(conn->handle, LDAP_OPT_NETWORK_TIMEOUT, &net) != LDAP_OPT_SUCCESS) {
		ROPTIONAL(REDEBUG, ERROR, "Failed getting LDAP_OPT_NETWORK_TIMEOUT");
	}
#endif

#ifdef LDAP_OPT_TIMEOUT
	if (ldap_get_option(conn->handle, LDAP_OPT_TIMEOUT, &client) != LDAP_OPT_SUCCESS) {
		ROPTIONAL(REDEBUG, ERROR, "Failed getting LDAP_OPT_TIMEOUT");
	}
#endif

	if (ldap_get_option(conn->handle, LDAP_OPT_TIMELIMIT, &server) != LDAP_OPT_SUCCESS) {
		ROPTIONAL(REDEBUG, ERROR, "Failed getting LDAP_OPT_TIMELIMIT");
	}

	ROPTIONAL(RDEBUG4, DEBUG4, "%s: Timeout settings", prefix);

	if (timeout) {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side result timeout (ovr): %ld.%06ld",
			  (long)timeout->tv_sec, (long)timeout->tv_usec);
	} else {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side result timeout (ovr): unset");
	}

#ifdef LDAP_OPT_TIMEOUT
	if (client && (client->tv_sec != -1)) {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side result timeout (dfl): %ld.%06ld",
			  (long)client->tv_sec, (long)client->tv_usec);

	} else {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side result timeout (dfl): unset");
	}
#endif

#ifdef LDAP_OPT_NETWORK_TIMEOUT
	if (net && (net->tv_sec != -1)) {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side network I/O timeout : %ld.%06ld",
			  (long)net->tv_sec, (long)net->tv_usec);
	} else {
		ROPTIONAL(RDEBUG4, DEBUG4, "Client side network I/O timeout : unset");

	}
#endif
	ROPTIONAL(RDEBUG4, DEBUG4, "Server side result timeout      : %i", server);
	if (request) REXDENT();

	free(net);
	free(client);
}
Ejemplo n.º 3
0
static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c)
{
	VALUE_PAIR	*vp;
	vp_map_t	*map;

	vp = pairfind(request->config, PW_CACHE_MERGE, 0, TAG_ANY);
	if (vp && (vp->vp_integer == 0)) {
		RDEBUG2("Told not to merge entry into request");
		return;
	}

	RDEBUG2("Merging cache entry into request");

	RINDENT();
	for (map = c->maps; map; map = map->next) {
		/*
		 *	The only reason that the application of a map entry
		 *	can fail, is if the destination list or request
		 *	isn't valid. For now we don't consider this fatal
		 *	and continue merging the rest of the maps.
		 */
		if (map_to_request(request, map, map_to_vp, NULL) < 0) {
			char buffer[1024];

			map_prints(buffer, sizeof(buffer), map);
			REXDENT();
			RDEBUG("Skipping %s", buffer);
			RINDENT();
			continue;
		}
	}
	REXDENT();

	if (inst->stats) {
		rad_assert(request->packet != NULL);
		vp = pairfind(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY);
		if (!vp) {
			vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0);
			rad_assert(vp != NULL);
			pairadd(&request->packet->vps, vp);
		}
		vp->vp_integer = c->hits;
	}
}
Ejemplo n.º 4
0
/** Perform a search and map the result of the search to server attributes
 *
 * @param[in] mod_inst #rlm_csv_t
 * @param[in] proc_inst mapping map entries to field numbers.
 * @param[in,out] request The current request.
 * @param[in] key key to look for
 * @param[in] maps Head of the map list.
 * @return
 *	- #RLM_MODULE_NOOP no rows were returned.
 *	- #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST.
 *	- #RLM_MODULE_FAIL if an error occurred.
 */
static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request,
				char const *key, vp_map_t const *maps)
{
	rlm_csv_t		*inst = mod_inst;
	rlm_csv_entry_t		*e, my_entry;
	vp_map_t const		*map;

	my_entry.key = key;

	e = rbtree_finddata(inst->tree, &my_entry);
	if (!e) return RLM_MODULE_NOOP;

	RINDENT();
	for (map = maps;
	     map != NULL;
	     map = map->next) {
		int field;
		char *field_name;

		/*
		 *	Avoid memory allocations if possible.
		 */
		if (map->rhs->type != TMPL_TYPE_LITERAL) {
			if (tmpl_aexpand(request, &field_name, request, map->rhs, NULL, NULL) < 0) {
				RDEBUG("Failed expanding RHS at %s", map->lhs->name);
				return RLM_MODULE_FAIL;
			}
		} else {
			memcpy(&field_name, &map->rhs->name, sizeof(field_name)); /* const */
		}

		field = fieldname2offset(inst, field_name);

		if (field_name != map->rhs->name) talloc_free(field_name);

		if (field < 0) {
			RDEBUG("No such field name %s", map->rhs->name);
			return RLM_MODULE_FAIL;
		}

		/*
		 *	Pass the raw data to the callback, which will
		 *	create the VP and add it to the map.
		 */
		if (map_to_request(request, map, csv_map_getvalue, e->data[field]) < 0) {
			return RLM_MODULE_FAIL;
		}
	}

	return RLM_MODULE_UPDATED;
}
Ejemplo n.º 5
0
static rlm_rcode_t cache_merge(rlm_cache_t const *inst, REQUEST *request, rlm_cache_entry_t *c)
{
	VALUE_PAIR	*vp;
	vp_map_t	*map;
	int		merged = 0;

	RDEBUG2("Merging cache entry into request");
	RINDENT();
	for (map = c->maps; map; map = map->next) {
		/*
		 *	The only reason that the application of a map entry
		 *	can fail, is if the destination list or request
		 *	isn't valid. For now we don't consider this fatal
		 *	and continue merging the rest of the maps.
		 */
		if (map_to_request(request, map, map_to_vp, NULL) < 0) {
			char buffer[1024];

			map_snprint(buffer, sizeof(buffer), map);
			REXDENT();
			RDEBUG2("Skipping %s", buffer);
			RINDENT();
			continue;
		}
		merged++;
	}
	REXDENT();

	if (inst->config.stats) {
		rad_assert(request->packet != NULL);
		MEM(pair_update_request(&vp, attr_cache_entry_hits) >= 0);
		vp->vp_uint32 = c->hits;
	}

	return merged > 0 ?
		RLM_MODULE_UPDATED :
		RLM_MODULE_OK;
}
Ejemplo n.º 6
0
/** Print a list of VALUE_PAIRs.
 *
 * @param[in] lvl	Debug lvl (1-4).
 * @param[in] request	to read logging params from.
 * @param[in] vp	to print.
 * @param[in] prefix	(optional).
 */
void log_request_pair_list(fr_log_lvl_t lvl, REQUEST *request, VALUE_PAIR *vp, char const *prefix)
{
	fr_cursor_t cursor;

	if (!vp || !request || !request->log.dst) return;

	if (!log_debug_enabled(L_DBG, lvl, request)) return;

	RINDENT();
	for (vp = fr_cursor_init(&cursor, &vp);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		VP_VERIFY(vp);

		RDEBUGX(lvl, "%s%pP", prefix ? prefix : "&", vp);
	}
	REXDENT();
}
Ejemplo n.º 7
0
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
{
	if (!fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
		return RLM_MODULE_NOOP;
	}

	if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY) != NULL) {
		RWDEBUG2("&control:Auth-Type already set.  Not setting to CHAP");
		return RLM_MODULE_NOOP;
	}

	RINDENT();
	RDEBUG("&control:Auth-Type := CHAP");
	REXDENT();
	pair_make_config("Auth-Type", "CHAP", T_OP_EQ);

	return RLM_MODULE_OK;
}
Ejemplo n.º 8
0
static inline VALUE_PAIR *tls_session_cert_attr_add(TALLOC_CTX *ctx, REQUEST *request, fr_cursor_t *cursor,
					    	    int attr, int attr_index, char const *value)
{
	VALUE_PAIR *vp;
	fr_dict_attr_t const *da = *(cert_attr_names[attr][attr_index]);

	MEM(vp = fr_pair_afrom_da(ctx, da));
	if (value) {
		if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) {
			RPWDEBUG("Failed creating attribute %s", da->name);
			talloc_free(vp);
			return NULL;
		}
	}
	RINDENT();
	RDEBUG3("%pP", vp);
	REXDENT();
	fr_cursor_append(cursor, vp);

	return vp;
}
Ejemplo n.º 9
0
/** Print the response data in a useful treelike form
 *
 * @param lvl to print data at.
 * @param reply to print.
 * @param request The current request.
 * @param idx Response number.
 */
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, REQUEST *request, int idx)
{
	size_t i = 0;

	if (!reply) return;

	switch (reply->type) {
	case REDIS_REPLY_ERROR:
		REDEBUG("(%i) error   : %s", idx, reply->str);
		break;

	case REDIS_REPLY_STATUS:
		RDEBUGX(lvl, "(%i) status  : %s", idx, reply->str);
		break;

	case REDIS_REPLY_STRING:
		RDEBUGX(lvl, "(%i) string  : %s", idx, reply->str);
		break;

	case REDIS_REPLY_INTEGER:
		RDEBUGX(lvl, "(%i) integer : %lld", idx, reply->integer);
		break;

	case REDIS_REPLY_NIL:
		RDEBUGX(lvl, "(%i) nil", idx);
		break;

	case REDIS_REPLY_ARRAY:
		RDEBUGX(lvl, "(%i) array[%zu]", idx, reply->elements);
		for (i = 0; i < reply->elements; i++) {
			RINDENT();
			fr_redis_reply_print(lvl, reply->element[i], request, i);
			REXDENT();
		}
		break;
	}
}
Ejemplo n.º 10
0
/*
 *	Run a virtual server auth and postauth
 *
 */
int rad_virtual_server(REQUEST *request)
{
	VALUE_PAIR *vp;
	int result;

	RDEBUG("Virtual server %s received request", request->server);
	rdebug_pair_list(L_DBG_LVL_1, request, request->packet->vps, NULL);

	RDEBUG("server %s {", request->server);
	RINDENT();

	/*
	 *	We currently only handle AUTH packets here.
	 *	This could be expanded to handle other packets as well if required.
	 */
	rad_assert(request->packet->code == PW_CODE_ACCESS_REQUEST);

	result = rad_authenticate(request);

	if (request->reply->code == PW_CODE_ACCESS_REJECT) {
		pairdelete(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
		vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
		if (vp) rad_postauth(request);
	}

	if (request->reply->code == PW_CODE_ACCESS_ACCEPT) {
		rad_postauth(request);
	}

	REXDENT();
	RDEBUG("} # server %s", request->server);

	RDEBUG("Virtual server sending reply");
	rdebug_pair_list(L_DBG_LVL_1, request, request->reply->vps, NULL);

	return result;
}
Ejemplo n.º 11
0
/*
 *  	get the vps and put them in perl hash
 *  	If one VP have multiple values it is added as array_ref
 *  	Example for this is Cisco-AVPair that holds multiple values.
 *  	Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'}
 */
static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, HV *rad_hv,
			   const char *hash_name, const char *list_name)
{
	VALUE_PAIR *vp;
	char *tbuff;
	size_t tbufflen = 1024;

	hv_undef(rad_hv);

	vp_cursor_t cursor;

	/*
	 *	Find out how much room to allocate.
	 */
	for (vp = fr_cursor_init(&cursor, vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		if (((vp->length * 2) + 3) > tbufflen) {
			tbufflen = (vp->vp_length * 2) + 3;
		}
	}
	tbuff = talloc_array(request, char, tbufflen);

	RINDENT();
	fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
	for (vp = fr_cursor_init(&cursor, vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
	     	VALUE_PAIR *next;
	     	char const *name;
		size_t len;
		char namebuf[256];

		/*
		 *	Tagged attributes are added to the hash with name
		 *	<attribute>:<tag>, others just use the normal attribute
		 *	name as the key.
		 */
		if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
			snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
			name = namebuf;
		} else {
			name = vp->da->name;
		}

		/*
		 *	We've sorted by type, then tag, so attributes of the
		 *	same type/tag should follow on from each other.
		 */
		if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) {
			int i = 0;
			AV *av;

			av = newAV();

			perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name);
			do {
				perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name);
				fr_cursor_next(&cursor);
			} while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next));
			(void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0);

			continue;
		}

		/*
		 *	It's a normal single valued attribute
		 */
		switch (vp->da->type) {
		case PW_TYPE_STRING:
			RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name, list_name,
			       vp->da->name, vp->vp_strvalue);
			(void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0);
			break;

		default:
			len = vp_prints_value(tbuff, tbufflen, vp, 0);
			RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name,
			       list_name, vp->da->name, tbuff);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn(tbuff, truncate_len(len, tbufflen)), 0);
			break;
		}
	}
	REXDENT();

	talloc_free(tbuff);
}
Ejemplo n.º 12
0
/** Retrieve UMTS quintuplets from sets of attributes.
 *
 * Hunt for a source of UMTS quintuplets
 *
 * @param eap_session		The current eap_session.
 * @param vps			List to hunt for triplets in.
 * @param keys			UMTS keys.
 * @param src			Forces quintuplets to be retrieved from a particular src.
 *
 * @return
 *	- 1	Vector could not be retrieved from the specified src.
 *	- 0	Vector was retrieved OK and written to the specified index.
 *	- -1	Error retrieving vector from the specified src.
 */
int fr_sim_vector_umts_from_attrs(eap_session_t *eap_session, VALUE_PAIR *vps,
				  fr_sim_keys_t *keys, fr_sim_vector_src_t *src)
{
	REQUEST		*request = eap_session->request;
	int		ret;

	rad_assert((keys->vector_type == SIM_VECTOR_NONE) || (keys->vector_type == SIM_VECTOR_UMTS));

	switch (*src) {
	default:
	case SIM_VECTOR_SRC_KI:
		ret = vector_umts_from_ki(eap_session, vps, keys);
		if (ret == 0) {
			*src = SIM_VECTOR_SRC_KI;
			break;
		}
		if (ret < 0) return -1;
		if (*src != SIM_VECTOR_SRC_AUTO) return 1;
		/* FALL-THROUGH */

	case SIM_VECTOR_SRC_QUINTUPLETS:
		ret = vector_umts_from_quintuplets(eap_session, vps, keys);
		if (ret == 0) {
			*src = SIM_VECTOR_SRC_QUINTUPLETS;
			break;;
		}
		if (ret < 0) return -1;
		break;
	}

	if (ret == 1) {
		RWDEBUG("Could not find or derive data for UMTS vector");
		return 1;
	}

	if (RDEBUG_ENABLED2) {
		RDEBUG2("UMTS vector");

		RINDENT();
		/*
		 *	Don't change colon indent, matches other messages later...
		 */
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->umts.vector.autn, SIM_VECTOR_UMTS_AUTN_SIZE,
				"AUTN         :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->umts.vector.ck, SIM_VECTOR_UMTS_CK_SIZE,
				"CK           :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->umts.vector.ik, SIM_VECTOR_UMTS_IK_SIZE,
				"IK           :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->umts.vector.rand, SIM_VECTOR_UMTS_RAND_SIZE,
				"RAND         :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->umts.vector.xres, keys->umts.vector.xres_len,
				"XRES         :");
		REXDENT();
	}

	keys->vector_type = SIM_VECTOR_UMTS;

	return 0;
}
Ejemplo n.º 13
0
static int vector_umts_from_ki(eap_session_t *eap_session, VALUE_PAIR *vps, fr_sim_keys_t *keys)
{
	REQUEST		*request = eap_session->request;
	VALUE_PAIR	*ki_vp, *amf_vp, *sqn_vp, *version_vp;

	uint64_t	sqn;
	uint8_t		amf_buff[MILENAGE_AMF_SIZE] = { 0x00, 0x00 };
	uint8_t 	opc_buff[MILENAGE_OPC_SIZE];
	uint8_t	const	*opc_p;
	uint32_t	version = 4;
	int		i;

	ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY);
	if (!ki_vp) {
		RDEBUG3("No &control:%s found, not generating quintuplets locally", attr_sim_ki->name);
		return 1;
	} else if (ki_vp->vp_length != MILENAGE_KI_SIZE) {
		REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
			attr_sim_ki->name, MILENAGE_KI_SIZE, ki_vp->vp_length);
		return -1;
	}

	if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;

	amf_vp = fr_pair_find_by_da(vps, attr_sim_amf, TAG_ANY);
	if (amf_vp) {
		if (amf_vp->vp_length != sizeof(amf_buff)) {
			REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
				attr_sim_amf->name, MILENAGE_AMF_SIZE, amf_vp->vp_length);
			return -1;
		}
		memcpy(amf_buff, amf_vp->vp_octets, sizeof(amf_buff));
	}

	sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY);
	sqn = sqn_vp ? sqn_vp->vp_uint64 : 2;

	/*
	 *	We default to milenage
	 */
	version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
	if (version_vp) version = version_vp->vp_uint32;

	for (i = 0; i < SIM_VECTOR_UMTS_RAND_SIZE; i += sizeof(uint32_t)) {
		uint32_t rand = fr_rand();
		memcpy(&keys->umts.vector.rand[i], &rand, sizeof(rand));
	}

	switch (version) {
	case 4:
	{
		uint8_t sqn_buff[MILENAGE_SQN_SIZE];

		keys->sqn = sqn_vp ? sqn_vp->vp_uint64 : 0;
		uint48_to_buff(sqn_buff, keys->sqn);

		RDEBUG3("Milenage inputs");
		RINDENT();
		/*
		 *	Don't change colon indent, matches other messages later...
		 */
		RHEXDUMP_INLINE(L_DBG_LVL_3,
				ki_vp->vp_octets, MILENAGE_KI_SIZE,
				"Ki           :");
		RHEXDUMP_INLINE(L_DBG_LVL_3,
				opc_p, MILENAGE_OPC_SIZE,
				"OPc          :");
		RHEXDUMP_INLINE(L_DBG_LVL_3,
				sqn_buff, MILENAGE_SQN_SIZE,
				"SQN          :");
		RHEXDUMP_INLINE(L_DBG_LVL_3,
				amf_buff, MILENAGE_AMF_SIZE,
				"AMF          :");
		REXDENT();

		if (milenage_umts_generate(keys->umts.vector.autn,
					   keys->umts.vector.ik,
					   keys->umts.vector.ck,
					   keys->umts.vector.ak,
					   keys->umts.vector.xres,
					   opc_p,
					   amf_buff,
					   ki_vp->vp_octets,
					   sqn,
					   keys->umts.vector.rand) < 0) {
			RPEDEBUG2("Failed deriving UMTS Quintuplet");
			return -1;
		}

		keys->umts.vector.xres_len = 8;
	}
		return 0;

	case 1:
	case 2:
	case 3:
		REDEBUG("Only Milenage can be used to generate quintuplets");
		return -1;

	default:
		REDEBUG("Unknown/unsupported algorithm %i", version);
		return -1;
	}
}
Ejemplo n.º 14
0
/** Retrieve GSM triplets from sets of attributes.
 *
 * Hunt for a source of SIM triplets
 *
 * @param eap_session		The current eap_session.
 * @param vps			List to hunt for triplets in.
 * @param idx			To write EAP-SIM triplets to.
 * @param keys			EAP session keys.
 * @param src			Forces triplets to be retrieved from a particular src
 *				and ensures if multiple triplets are being retrieved
 *				that they all come from the same src.
 * @return
 *	- 1	Vector could not be retrieved from the specified src.
 *	- 0	Vector was retrieved OK and written to the specified index.
 *	- -1	Error retrieving vector from the specified src.
 */
int fr_sim_vector_gsm_from_attrs(eap_session_t *eap_session, VALUE_PAIR *vps,
				 int idx, fr_sim_keys_t *keys, fr_sim_vector_src_t *src)
{
	REQUEST		*request = eap_session->request;
	int		ret;

	rad_assert(idx >= 0 && idx < 3);
	rad_assert((keys->vector_type == SIM_VECTOR_NONE) || (keys->vector_type == SIM_VECTOR_GSM));

	switch (*src) {
	default:
	case SIM_VECTOR_SRC_KI:
		ret = vector_gsm_from_ki(eap_session, vps, idx, keys);
		if (ret == 0) {
			*src = SIM_VECTOR_SRC_KI;
			break;
		}
		if (ret < 0) return -1;
		if (*src != SIM_VECTOR_SRC_AUTO) return 1;
		/* FALL-THROUGH */

	case SIM_VECTOR_SRC_TRIPLETS:
		ret = vector_gsm_from_triplets(eap_session, vps, idx, keys);
		if (ret == 0) {
			*src = SIM_VECTOR_SRC_TRIPLETS;
			break;
		}
		if (ret < 0) return -1;
		if (*src != SIM_VECTOR_SRC_AUTO) return 1;
		/* FALL-THROUGH */

	case SIM_VECTOR_SRC_QUINTUPLETS:
		ret = vector_gsm_from_quintuplets(eap_session, vps, idx, keys);
		if (ret == 0) {
			*src = SIM_VECTOR_SRC_QUINTUPLETS;
			break;
		}
		if (ret < 0) return -1;
		break;
	}

	if (ret == 1) {
		RWDEBUG("Could not find or derive data for GSM vector[%i]", idx);
		return 1;
	}

	if (RDEBUG_ENABLED2) {
		RDEBUG2("GSM vector[%i]", idx);

		RINDENT();
		/*
		 *	Don't change colon indent, matches other messages later...
		 */
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->gsm.vector[idx].kc, SIM_VECTOR_GSM_KC_SIZE,
				"KC           :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->gsm.vector[idx].rand, SIM_VECTOR_GSM_RAND_SIZE,
				"RAND         :");
		RHEXDUMP_INLINE(L_DBG_LVL_2,
				keys->gsm.vector[idx].sres, SIM_VECTOR_GSM_SRES_SIZE,
				"SRES         :");
		REXDENT();
	}

	keys->vector_type = SIM_VECTOR_GSM;

	return 0;
}
Ejemplo n.º 15
0
static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
{
	REQUEST *request = handler->request;
	VALUE_PAIR *vp, *ki, *algo_version;

	rad_assert(idx >= 0 && idx < 3);

	/*
	 *	Generate a new RAND value, and derive Kc and SRES from Ki
	 */
	ki = pairfind(vps, PW_EAP_SIM_KI, 0, TAG_ANY);
	if (ki) {
		int i;

		/*
		 *	Check to see if have a Ki for the IMSI, this allows us to generate the rest
		 *	of the triplets.
		 */
		algo_version = pairfind(vps, PW_EAP_SIM_ALGO_VERSION, 0, TAG_ANY);
		if (!algo_version) {
			REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version");
			return 0;
		}

		for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
			ess->keys.rand[idx][i] = fr_rand();
		}

		switch (algo_version->vp_integer) {
		case 1:
			comp128v1(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx]);
			break;

		case 2:
			comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
				   true);
			break;

		case 3:
			comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
				   false);
			break;

		case 4:
			REDEBUG("Comp128-4 algorithm is not supported as details have not yet been published. "
				"If you have details of this algorithm please contact the FreeRADIUS "
				"maintainers");
			return 0;

		default:
			REDEBUG("Unknown/unsupported algorithm Comp128-%i", algo_version->vp_integer);
		}

		if (RDEBUG_ENABLED2) {
			char buffer[33];	/* 32 hexits (16 bytes) + 1 */
			char *p;

			RDEBUG2("Generated following triplets for round %i:", idx);

			RINDENT();
			p = buffer;
			for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
				p += sprintf(p, "%02x", ess->keys.rand[idx][i]);
			}
			RDEBUG2("RAND : 0x%s", buffer);

			p = buffer;
			for (i = 0; i < EAPSIM_SRES_SIZE; i++) {
				p += sprintf(p, "%02x", ess->keys.sres[idx][i]);
			}
			RDEBUG2("SRES : 0x%s", buffer);

			p = buffer;
			for (i = 0; i < EAPSIM_KC_SIZE; i++) {
				p += sprintf(p, "%02x", ess->keys.Kc[idx][i]);
			}
			RDEBUG2("Kc   : 0x%s", buffer);
			REXDENT();
		}
		return 1;
	}

	/*
	 *	Use known RAND, SRES, and Kc values, these may of been pulled in from an AuC,
	 *	or created by sending challenges to the SIM directly.
	 */
	vp = pairfind(vps, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
	if (!vp) {
		/* bad, we can't find stuff! */
		REDEBUG("control:EAP-SIM-RAND%i not found", idx + 1);
		return 0;
	}
	if (vp->length != EAPSIM_RAND_SIZE) {
		REDEBUG("control:EAP-SIM-RAND%i is not " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, got %zu bytes",
			idx + 1, vp->length);
		return 0;
	}
	memcpy(ess->keys.rand[idx], vp->vp_octets, EAPSIM_RAND_SIZE);

	vp = pairfind(vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
	if (!vp) {
		/* bad, we can't find stuff! */
		REDEBUG("control:EAP-SIM-SRES%i not found", idx + 1);
		return 0;
	}
	if (vp->length != EAPSIM_SRES_SIZE) {
		REDEBUG("control:EAP-SIM-SRES%i is not " STRINGIFY(EAPSIM_SRES_SIZE) " bytes, got %zu bytes",
			idx + 1, vp->length);
		return 0;
	}
	memcpy(ess->keys.sres[idx], vp->vp_octets, EAPSIM_SRES_SIZE);

	vp = pairfind(vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY);
	if (!vp) {
		/* bad, we can't find stuff! */
		REDEBUG("control:EAP-SIM-Kc%i not found", idx + 1);
		return 0;
	}
	if (vp->length != EAPSIM_KC_SIZE) {
		REDEBUG("control:EAP-SIM-Kc%i is not 8 bytes, got %zu bytes", idx + 1, vp->length);
		return 0;
	}
	memcpy(ess->keys.Kc[idx], vp->vp_octets, EAPSIM_KC_SIZE);
	if (vp->length != EAPSIM_KC_SIZE) {
		REDEBUG("control:EAP-SIM-Kc%i is not " STRINGIFY(EAPSIM_KC_SIZE) " bytes, got %zu bytes",
			idx + 1, vp->length);
		return 0;
	}
	memcpy(ess->keys.Kc[idx], vp->vp_strvalue, EAPSIM_KC_SIZE);

	return 1;
}
Ejemplo n.º 16
0
/*
 *	Find the named user in this modules database.  Create the set
 *	of attribute-value pairs to check and reply with for this user
 *	from the database. The authentication code only needs to check
 *	the password, the rest is done here.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
{
	VALUE_PAIR *password, *chap;
	uint8_t pass_str[MAX_STRING_LEN];

	if (!request->username) {
		REDEBUG("&request:User-Name attribute is required for authentication");
		return RLM_MODULE_INVALID;
	}

	chap = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
	if (!chap) {
		REDEBUG("You set '&control:Auth-Type = CHAP' for a request that "
			"does not contain a CHAP-Password attribute!");
		return RLM_MODULE_INVALID;
	}

	if (chap->vp_length == 0) {
		REDEBUG("&request:CHAP-Password is empty");
		return RLM_MODULE_INVALID;
	}

	if (chap->vp_length != CHAP_VALUE_LENGTH + 1) {
		REDEBUG("&request:CHAP-Password has invalid length");
		return RLM_MODULE_INVALID;
	}

	password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
	if (password == NULL) {
		if (fr_pair_find_by_num(request->config, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
			REDEBUG("!!! Please update your configuration so that the \"known !!!");
			REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
			REDEBUG("!!! and NOT in User-Password.                            !!!");
			REDEBUG("!!!						          !!!");
			REDEBUG("!!! Authentication will fail because of this.	          !!!");
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		}

		REDEBUG("&control:Cleartext-Password is required for authentication");
		return RLM_MODULE_FAIL;
	}

	rad_chap_encode(request->packet, pass_str, chap->vp_octets[0], password);

	if (RDEBUG_ENABLED3) {
		uint8_t const *p;
		size_t length;
		VALUE_PAIR *vp;
		char buffer[MAX_STRING_LEN * 2 + 1];

		RDEBUG3("Comparing with \"known good\" &control:Cleartext-Password value \"%s\"",
			password->vp_strvalue);

		vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
		if (vp) {
			RDEBUG2("Using challenge from &request:CHAP-Challenge");
			p = vp->vp_octets;
			length = vp->vp_length;
		} else {
			RDEBUG2("Using challenge from authenticator field");
			p = request->packet->vector;
			length = sizeof(request->packet->vector);
		}

		fr_bin2hex(buffer, p, length);
		RINDENT();
		RDEBUG3("CHAP challenge : %s", buffer);

		fr_bin2hex(buffer, chap->vp_octets + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("Client sent    : %s", buffer);

		fr_bin2hex(buffer, pass_str + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("We calculated  : %s", buffer);
		REXDENT();
	} else {
		RDEBUG2("Comparing with \"known good\" Cleartext-Password");
	}

	if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1, CHAP_VALUE_LENGTH) != 0) {
		REDEBUG("Password comparison failed: password is incorrect");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("CHAP user \"%s\" authenticated successfully", request->username->vp_strvalue);

	return RLM_MODULE_OK;
}
Ejemplo n.º 17
0
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_rcode_t rcode = RLM_MODULE_NOOP;

	rlm_sql_t *inst = instance;
	rlm_sql_handle_t  *handle;

	VALUE_PAIR *check_tmp = NULL;
	VALUE_PAIR *reply_tmp = NULL;
	VALUE_PAIR *user_profile = NULL;

	bool	user_found = false;

	sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;

	int	rows;

	char	*expanded = NULL;

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

	if (!inst->config->authorize_check_query && !inst->config->authorize_reply_query &&
	    !inst->config->read_groups && !inst->config->read_profiles) {
	 	RWDEBUG("No authorization checks configured, returning noop");

	 	return RLM_MODULE_NOOP;
	}

	/*
	 *	Set, escape, and check the user attr here
	 */
	if (sql_set_user(inst, request, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Reserve a socket
	 *
	 *	After this point use goto error or goto release to cleanup socket temporary pairlists and
	 *	temporary attributes.
	 */
	handle = sql_get_socket(inst);
	if (!handle) {
		rcode = RLM_MODULE_FAIL;
		goto error;
	}

	/*
	 *	Query the check table to find any conditions associated with this user/realm/whatever...
	 */
	if (inst->config->authorize_check_query) {
		vp_cursor_t cursor;
		VALUE_PAIR *vp;

		if (radius_axlat(&expanded, request, inst->config->authorize_check_query,
				 sql_escape_func, inst) < 0) {
			REDEBUG("Error generating query");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		rows = sql_getvpdata(request, inst, &handle, &check_tmp, expanded);
		TALLOC_FREE(expanded);
		if (rows < 0) {
			REDEBUG("SQL query error");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		if (rows == 0) goto skipreply;	/* Don't need to free VPs we don't have */

		/*
		 *	Only do this if *some* check pairs were returned
		 */
		RDEBUG2("User found in radcheck table");
		user_found = true;
		if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
			pairfree(&check_tmp);
			check_tmp = NULL;
			goto skipreply;
		}

		RDEBUG2("Conditional check items matched, merging assignment check items");
		RINDENT();
		for (vp = fr_cursor_init(&cursor, &check_tmp);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			if (!fr_assignment_op[vp->op]) continue;

			rdebug_pair(2, request, vp);
		}
		REXDENT();
		radius_pairmove(request, &request->config_items, check_tmp, true);

		rcode = RLM_MODULE_OK;
		check_tmp = NULL;
	}

	if (inst->config->authorize_reply_query) {
		/*
		 *	Now get the reply pairs since the paircompare matched
		 */
		if (radius_axlat(&expanded, request, inst->config->authorize_reply_query,
				 sql_escape_func, inst) < 0) {
			REDEBUG("Error generating query");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		rows = sql_getvpdata(request->reply, inst, &handle, &reply_tmp, expanded);
		TALLOC_FREE(expanded);
		if (rows < 0) {
			REDEBUG("SQL query error");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		if (rows == 0) goto skipreply;

		do_fall_through = fall_through(reply_tmp);

		RDEBUG2("User found in radreply table, merging reply items");
		user_found = true;

		rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp);

		radius_pairmove(request, &request->reply->vps, reply_tmp, true);

		rcode = RLM_MODULE_OK;
		reply_tmp = NULL;
	}

skipreply:
	if ((do_fall_through == FALL_THROUGH_YES) ||
	    (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
		rlm_rcode_t ret;

		RDEBUG3("... falling-through to group processing");
		ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
		switch (ret) {
		/*
		 *	Nothing bad happened, continue...
		 */
		case RLM_MODULE_UPDATED:
			rcode = RLM_MODULE_UPDATED;
			/* FALL-THROUGH */
		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) {
				rcode = RLM_MODULE_OK;
			}
			/* FALL-THROUGH */
		case RLM_MODULE_NOOP:
			user_found = true;
			break;

		case RLM_MODULE_NOTFOUND:
			break;

		default:
			rcode = ret;
			goto release;
		}
	}

	/*
	 *	Repeat the above process with the default profile or User-Profile
	 */
	if ((do_fall_through == FALL_THROUGH_YES) ||
	    (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
		rlm_rcode_t ret;

		/*
		 *  Check for a default_profile or for a User-Profile.
		 */
		RDEBUG3("... falling-through to profile processing");
		user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);

		char const *profile = user_profile ?
				      user_profile->vp_strvalue :
				      inst->config->default_profile;

		if (!profile || !*profile) {
			goto release;
		}

		RDEBUG2("Checking profile %s", profile);

		if (sql_set_user(inst, request, profile) < 0) {
			REDEBUG("Error setting profile");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
		switch (ret) {
		/*
		 *	Nothing bad happened, continue...
		 */
		case RLM_MODULE_UPDATED:
			rcode = RLM_MODULE_UPDATED;
			/* FALL-THROUGH */
		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) {
				rcode = RLM_MODULE_OK;
			}
			/* FALL-THROUGH */
		case RLM_MODULE_NOOP:
			user_found = true;
			break;

		case RLM_MODULE_NOTFOUND:
			break;

		default:
			rcode = ret;
			goto release;
		}
	}

	/*
	 *	At this point the key (user) hasn't be found in the check table, the reply table
	 *	or the group mapping table, and there was no matching profile.
	 */
release:
	if (!user_found) {
		rcode = RLM_MODULE_NOTFOUND;
	}

	sql_release_socket(inst, handle);
	sql_unset_user(inst, request);

	return rcode;

error:
	pairfree(&check_tmp);
	pairfree(&reply_tmp);
	sql_unset_user(inst, request);

	sql_release_socket(inst, handle);

	return rcode;
}
Ejemplo n.º 18
0
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_rcode_t	rcode = RLM_MODULE_OK;
	ldap_rcode_t	status;
	int		ldap_errno;
	int		i;
	ldap_instance_t	*inst = instance;
	struct berval	**values;
	VALUE_PAIR	*vp;
	ldap_handle_t	*conn;
	LDAPMessage	*result, *entry;
	char const 	*dn = NULL;
	rlm_ldap_map_xlat_t	expanded; /* faster than mallocing every time */

	if (!request->username) {
		RDEBUG2("Attribute \"User-Name\" is required for authorization");

		return RLM_MODULE_NOOP;
	}

	/*
	 *	Check for valid input, zero length names not permitted
	 */
	if (request->username->vp_length == 0) {
		RDEBUG2("Zero length username not permitted");

		return RLM_MODULE_INVALID;
	}

	if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
		return RLM_MODULE_FAIL;
	}

	conn = mod_conn_get(inst, request);
	if (!conn) return RLM_MODULE_FAIL;

	/*
	 *	Add any additional attributes we need for checking access, memberships, and profiles
	 */
	if (inst->userobj_access_attr) {
		expanded.attrs[expanded.count++] = inst->userobj_access_attr;
	}

	if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) {
		expanded.attrs[expanded.count++] = inst->userobj_membership_attr;
	}

	if (inst->profile_attr) {
		expanded.attrs[expanded.count++] = inst->profile_attr;
	}

	if (inst->valuepair_attr) {
		expanded.attrs[expanded.count++] = inst->valuepair_attr;
	}

	expanded.attrs[expanded.count] = NULL;

	dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode);
	if (!dn) {
		goto finish;
	}

	entry = ldap_first_entry(conn->handle, result);
	if (!entry) {
		ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
		REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));

		goto finish;
	}

	/*
	 *	Check for access.
	 */
	if (inst->userobj_access_attr) {
		rcode = rlm_ldap_check_access(inst, request, conn, entry);
		if (rcode != RLM_MODULE_OK) {
			goto finish;
		}
	}

	/*
	 *	Check if we need to cache group memberships
	 */
	if (inst->cacheable_group_dn || inst->cacheable_group_name) {
		if (inst->userobj_membership_attr) {
			rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr);
			if (rcode != RLM_MODULE_OK) {
				goto finish;
			}
		}

		rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn);
		if (rcode != RLM_MODULE_OK) {
			goto finish;
		}
	}

#ifdef WITH_EDIR
	/*
	 *	We already have a Cleartext-Password.  Skip edir.
	 */
	if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
		goto skip_edir;
	}

	/*
	 *      Retrieve Universal Password if we use eDirectory
	 */
	if (inst->edir) {
		int res = 0;
		char password[256];
		size_t pass_size = sizeof(password);

		/*
		 *	Retrive universal password
		 */
		res = nmasldap_get_password(conn->handle, dn, password, &pass_size);
		if (res != 0) {
			REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res));
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		/*
		 *	Add Cleartext-Password attribute to the request
		 */
		vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
		pairstrcpy(vp, password);
		vp->vp_length = pass_size;

		if (RDEBUG_ENABLED3) {
			RDEBUG3("Added eDirectory password.  control:%s += '%s'", vp->da->name, vp->vp_strvalue);
		} else {
			RDEBUG2("Added eDirectory password");
		}

		if (inst->edir_autz) {
			RDEBUG2("Binding as user for eDirectory authorization checks");
			/*
			 *	Bind as the user
			 */
			conn->rebound = true;
			status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true);
			switch (status) {
			case LDAP_PROC_SUCCESS:
				rcode = RLM_MODULE_OK;
				RDEBUG("Bind as user '%s' was successful", dn);
				break;

			case LDAP_PROC_NOT_PERMITTED:
				rcode = RLM_MODULE_USERLOCK;
				goto finish;

			case LDAP_PROC_REJECT:
				rcode = RLM_MODULE_REJECT;
				goto finish;

			case LDAP_PROC_BAD_DN:
				rcode = RLM_MODULE_INVALID;
				goto finish;

			case LDAP_PROC_NO_RESULT:
				rcode = RLM_MODULE_NOTFOUND;
				goto finish;

			default:
				rcode = RLM_MODULE_FAIL;
				goto finish;
			};
		}
	}

skip_edir:
#endif

	/*
	 *	Apply ONE user profile, or a default user profile.
	 */
	if (inst->default_profile) {
		char profile[1024];

		if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) {
			REDEBUG("Failed creating default profile string");

			rcode = RLM_MODULE_INVALID;
			goto finish;
		}

		rlm_ldap_map_profile(inst, request, &conn, profile, &expanded);
	}

	/*
	 *	Apply a SET of user profiles.
	 */
	if (inst->profile_attr) {
		values = ldap_get_values_len(conn->handle, entry, inst->profile_attr);
		if (values != NULL) {
			for (i = 0; values[i] != NULL; i++) {
				char *value;

				value = rlm_ldap_berval_to_string(request, values[i]);
				rlm_ldap_map_profile(inst, request, &conn, value, &expanded);
				talloc_free(value);
			}
			ldap_value_free_len(values);
		}
	}

	if (inst->user_map || inst->valuepair_attr) {
		RDEBUG("Processing user attributes");
		RINDENT();
		rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry);
		REXDENT();
		rlm_ldap_check_reply(inst, request);
	}

finish:
	rlm_ldap_map_xlat_free(&expanded);
	if (result) ldap_msgfree(result);
	mod_conn_release(inst, conn);

	return rcode;
}
Ejemplo n.º 19
0
static ssize_t redis_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen,
			  void const *mod_inst, UNUSED void const *xlat_inst,
			  REQUEST *request, char const *fmt)
{
	rlm_redis_t const	*inst = mod_inst;
	fr_redis_conn_t		*conn;

	bool			read_only = false;
	uint8_t	const		*key = NULL;
	size_t			key_len = 0;

	fr_redis_cluster_state_t	state;
	fr_redis_rcode_t		status;
	redisReply		*reply = NULL;
	int			s_ret;

	size_t			len;
	int			ret;

	char const		*p = fmt, *q;

	int			argc;
	char const		*argv[MAX_REDIS_ARGS];
	char			argv_buf[MAX_REDIS_COMMAND_LEN];

	if (p[0] == '-') {
		p++;
		read_only = true;
	}

	/*
	 *	Hack to allow querying against a specific node for testing
	 */
	if (p[0] == '@') {
		fr_socket_addr_t	node_addr;
		fr_pool_t	*pool;

		RDEBUG3("Overriding node selection");

		p++;
		q = strchr(p, ' ');
		if (!q) {
			REDEBUG("Found node specifier but no command, format is [-][@<host>[:port]] <redis command>");
			return -1;
		}

		if (fr_inet_pton_port(&node_addr.ipaddr, &node_addr.port, p, q - p, AF_UNSPEC, true, true) < 0) {
			RPEDEBUG("Failed parsing node address");
			return -1;
		}

		p = q + 1;

		if (fr_redis_cluster_pool_by_node_addr(&pool, inst->cluster, &node_addr, true) < 0) {
			RPEDEBUG("Failed locating cluster node");
			return -1;
		}

		conn = fr_pool_connection_get(pool, request);
		if (!conn) {
			REDEBUG("No connections available for cluster node");
			return -1;
		}

		argc = rad_expand_xlat(request, p, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
		if (argc <= 0) {
			RPEDEBUG("Invalid command: %s", p);
		arg_error:
			fr_pool_connection_release(pool, request, conn);
			return -1;
		}
		if (argc >= (MAX_REDIS_ARGS - 1)) {
			RPEDEBUG("Too many parameters; increase MAX_REDIS_ARGS and recompile: %s", p);
			goto arg_error;
		}

		RDEBUG2("Executing command: %s", argv[0]);
		if (argc > 1) {
			RDEBUG2("With argments");
			RINDENT();
			for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
			REXDENT();
		}

		if (!read_only) {
			reply = redisCommandArgv(conn->handle, argc, argv, NULL);
			status = fr_redis_command_status(conn, reply);
		} else if (redis_command_read_only(&status, &reply, request, conn, argc, argv) == -2) {
			goto close_conn;
		}

		if (!reply) goto fail;

		switch (status) {
		case REDIS_RCODE_SUCCESS:
			goto reply_parse;

		case REDIS_RCODE_RECONNECT:
		close_conn:
			fr_pool_connection_close(pool, request, conn);
			ret = -1;
			goto finish;

		default:
		fail:
			fr_pool_connection_release(pool, request, conn);
			ret = -1;
			goto finish;
		}
	}

	/*
	 *	Normal node selection and execution based on key
	 */
	argc = rad_expand_xlat(request, p, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
	if (argc <= 0) {
		RPEDEBUG("Invalid command: %s", p);
		ret = -1;
		goto finish;
	}

	if (argc >= (MAX_REDIS_ARGS - 1)) {
		RPEDEBUG("Too many parameters; increase MAX_REDIS_ARGS and recompile: %s", p);
		ret = -1;
		goto finish;
	}

	/*
	 *	If we've got multiple arguments, the second one is usually the key.
	 *	The Redis docs say commands should be analysed first to get key
	 *	positions, but this involves sending them to the server, which is
	 *	just as expensive as sending them to the wrong server and receiving
	 *	a redirect.
	 */
	if (argc > 1) {
		key = (uint8_t const *)argv[1];
	 	key_len = strlen((char const *)key);
	}
	for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, read_only);
	     s_ret == REDIS_RCODE_TRY_AGAIN;	/* Continue */
	     s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
		RDEBUG2("Executing command: %s", argv[0]);
		if (argc > 1) {
			RDEBUG2("With arguments");
			RINDENT();
			for (int i = 1; i < argc; i++) RDEBUG2("[%i] %s", i, argv[i]);
			REXDENT();
		}
		if (!read_only) {
			reply = redisCommandArgv(conn->handle, argc, argv, NULL);
			status = fr_redis_command_status(conn, reply);
		} else if (redis_command_read_only(&status, &reply, request, conn, argc, argv) == -2) {
			state.close_conn = true;
		}
	}
	if (s_ret != REDIS_RCODE_SUCCESS) {
		ret = -1;
		goto finish;
	}

	if (!fr_cond_assert(reply)) {
		ret = -1;
		goto finish;
	}

reply_parse:
	switch (reply->type) {
	case REDIS_REPLY_INTEGER:
		ret = snprintf(*out, outlen, "%lld", reply->integer);
		break;

	case REDIS_REPLY_STATUS:
	case REDIS_REPLY_STRING:
		len = (((size_t)reply->len) >= outlen) ? outlen - 1: (size_t) reply->len;
		memcpy(*out, reply->str, len);
		(*out)[len] = '\0';
		ret = reply->len;
		break;

	default:
		REDEBUG("Server returned non-value type \"%s\"",
			fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
		ret = -1;
		break;
	}

finish:
	fr_redis_reply_free(reply);
	return ret;
}
Ejemplo n.º 20
0
/*
 *	Find the named user in this modules database.  Create the set
 *	of attribute-value pairs to check and reply with for this user
 *	from the database. The authentication code only needs to check
 *	the password, the rest is done here.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance,
				     REQUEST *request)
{
	VALUE_PAIR *passwd_item, *chap;
	uint8_t pass_str[MAX_STRING_LEN];

	if (!request->username) {
		RWDEBUG("Attribute 'User-Name' is required for authentication.\n");
		return RLM_MODULE_INVALID;
	}

	chap = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
	if (!chap) {
		REDEBUG("You set 'Auth-Type = CHAP' for a request that does not contain a CHAP-Password attribute!");
		return RLM_MODULE_INVALID;
	}

	if (chap->length == 0) {
		REDEBUG("CHAP-Password is empty");
		return RLM_MODULE_INVALID;
	}

	if (chap->length != CHAP_VALUE_LENGTH + 1) {
		REDEBUG("CHAP-Password has invalid length");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	Don't print out the CHAP password here.  It's binary crap.
	 */
	RDEBUG("Login attempt by \"%s\" with CHAP password",
		request->username->vp_strvalue);

	if ((passwd_item = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL){
		if (pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
			REDEBUG("!!! Please update your configuration so that the \"known !!!");
			REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
			REDEBUG("!!! and NOT in User-Password.                            !!!");
			REDEBUG("!!!						          !!!");
			REDEBUG("!!! Authentication will fail because of this.	          !!!");
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		}

		REDEBUG("Cleartext password is required for authentication");
		return RLM_MODULE_INVALID;
	}

	rad_chap_encode(request->packet, pass_str,
			chap->vp_octets[0], passwd_item);

	if (RDEBUG_ENABLED3) {
		uint8_t const *p;
		size_t length;
		VALUE_PAIR *vp;
		char buffer[CHAP_VALUE_LENGTH * 2 + 1];

		RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\"", passwd_item->vp_strvalue);

		vp = pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
		if (vp) {
			p = vp->vp_octets;
			length = vp->length;
		} else {
			p = request->packet->vector;
			length = sizeof(request->packet->vector);
		}

		fr_bin2hex(buffer, p, length);
		RINDENT();
		RDEBUG3("CHAP challenge :  %s", buffer);

		fr_bin2hex(buffer, chap->vp_octets + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("Client sent    : %s", buffer);

		fr_bin2hex(buffer, pass_str + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("We calculated  : %s", buffer);
		REXDENT();
	} else {
		RDEBUG2("Comparing with \"known good\" Cleartext-Password");
	}

	if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1,
			   CHAP_VALUE_LENGTH) != 0) {
		REDEBUG("Password is comparison failed: password is incorrect");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("CHAP user \"%s\" authenticated successfully",
	      request->username->vp_strvalue);

	return RLM_MODULE_OK;
}
Ejemplo n.º 21
0
static int mod_process(void *arg, eap_handler_t *handler)
{
	pwd_session_t *session;
	pwd_hdr *hdr;
	pwd_id_packet_t *packet;
	eap_packet_t *response;
	REQUEST *request, *fake;
	VALUE_PAIR *pw, *vp;
	EAP_DS *eap_ds;
	size_t in_len;
	int ret = 0;
	eap_pwd_t *inst = (eap_pwd_t *)arg;
	uint16_t offset;
	uint8_t exch, *in, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
	uint8_t peer_confirm[SHA256_DIGEST_LENGTH];

	if (((eap_ds = handler->eap_ds) == NULL) || !inst) return 0;

	session = (pwd_session_t *)handler->opaque;
	request = handler->request;
	response = handler->eap_ds->response;
	hdr = (pwd_hdr *)response->type.data;

	/*
	 *	The header must be at least one byte.
	 */
	if (!hdr || (response->type.length < sizeof(pwd_hdr))) {
		RDEBUG("Packet with insufficient data");
		return 0;
	}

	in = hdr->data;
	in_len = response->type.length - sizeof(pwd_hdr);

	/*
	* see if we're fragmenting, if so continue until we're done
	*/
	if (session->out_pos) {
		if (in_len) RDEBUG2("pwd got something more than an ACK for a fragment");

		return send_pwd_request(session, eap_ds);
	}

	/*
	* the first fragment will have a total length, make a
	* buffer to hold all the fragments
	*/
	if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
		if (session->in) {
			RDEBUG2("pwd already alloced buffer for fragments");
			return 0;
		}

		if (in_len < 2) {
			RDEBUG("Invalid packet: length bit set, but no length field");
			return 0;
		}

		session->in_len = ntohs(in[0] * 256 | in[1]);
		if ((session->in = talloc_zero_array(session, uint8_t, session->in_len)) == NULL) {
			RDEBUG2("pwd cannot allocate %zd buffer to hold fragments",
				session->in_len);
			return 0;
		}
		memset(session->in, 0, session->in_len);
		session->in_pos = 0;
		in += sizeof(uint16_t);
		in_len -= sizeof(uint16_t);
	}

	/*
	 * all fragments, including the 1st will have the M(ore) bit set,
	 * buffer those fragments!
	 */
	if (EAP_PWD_GET_MORE_BIT(hdr)) {
		rad_assert(session->in != NULL);

		if ((session->in_pos + in_len) > session->in_len) {
			RDEBUG2("Fragment overflows packet.");
			return 0;
		}

		memcpy(session->in + session->in_pos, in, in_len);
		session->in_pos += in_len;

		/*
		 * send back an ACK for this fragment
		 */
		exch = EAP_PWD_GET_EXCHANGE(hdr);
		eap_ds->request->code = PW_EAP_REQUEST;
		eap_ds->request->type.num = PW_EAP_PWD;
		eap_ds->request->type.length = sizeof(pwd_hdr);
		if ((eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, sizeof(pwd_hdr))) == NULL) {
			return 0;
		}
		hdr = (pwd_hdr *)eap_ds->request->type.data;
		EAP_PWD_SET_EXCHANGE(hdr, exch);
		return 1;
	}


	if (session->in) {
		/*
		 * the last fragment...
		 */
		if ((session->in_pos + in_len) > session->in_len) {
			RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent");
			return 0;
		}
		memcpy(session->in + session->in_pos, in, in_len);
		in = session->in;
		in_len = session->in_len;
	}

	switch (session->state) {
	case PWD_STATE_ID_REQ:
	{
		BIGNUM	*x = NULL, *y = NULL;

		if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
			RDEBUG2("pwd exchange is incorrect: not ID");
			return 0;
		}

		packet = (pwd_id_packet_t *) in;
		if (in_len < sizeof(*packet)) {
			RDEBUG("Packet is too small (%zd < %zd).", in_len, sizeof(*packet));
			return 0;
		}

		if ((packet->prf != EAP_PWD_DEF_PRF) ||
		    (packet->random_function != EAP_PWD_DEF_RAND_FUN) ||
		    (packet->prep != EAP_PWD_PREP_NONE) ||
		    (CRYPTO_memcmp(packet->token, &session->token, 4)) ||
		    (packet->group_num != ntohs(session->group_num))) {
			RDEBUG2("pwd id response is invalid");
			return 0;
		}
		/*
		 * we've agreed on the ciphersuite, record it...
		 */
		ptr = (uint8_t *)&session->ciphersuite;
		memcpy(ptr, (char *)&packet->group_num, sizeof(uint16_t));
		ptr += sizeof(uint16_t);
		*ptr = EAP_PWD_DEF_RAND_FUN;
		ptr += sizeof(uint8_t);
		*ptr = EAP_PWD_DEF_PRF;

		session->peer_id_len = in_len - sizeof(pwd_id_packet_t);
		if (session->peer_id_len >= sizeof(session->peer_id)) {
			RDEBUG2("pwd id response is malformed");
			return 0;
		}

		memcpy(session->peer_id, packet->identity, session->peer_id_len);
		session->peer_id[session->peer_id_len] = '\0';

		/*
		 * make fake request to get the password for the usable ID
		 */
		if ((fake = request_alloc_fake(handler->request)) == NULL) {
			RDEBUG("pwd unable to create fake request!");
			return 0;
		}
		fake->username = fr_pair_afrom_num(fake->packet, PW_USER_NAME, 0);
		if (!fake->username) {
			RDEBUG("Failed creating pair for peer id");
			talloc_free(fake);
			return 0;
		}
		fr_pair_value_bstrncpy(fake->username, session->peer_id, session->peer_id_len);
		fr_pair_add(&fake->packet->vps, fake->username);

		if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
			fake->server = vp->vp_strvalue;
		} else if (inst->virtual_server) {
			fake->server = inst->virtual_server;
		} /* else fake->server == request->server */

		RDEBUG("Sending tunneled request");
		rdebug_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL);

		if (fake->server) {
			RDEBUG("server %s {", fake->server);
		} else {
			RDEBUG("server {");
		}

		/*
		 *	Call authorization recursively, which will
		 *	get the password.
		 */
		RINDENT();
		process_authorize(0, fake);
		REXDENT();

		/*
		 *	Note that we don't do *anything* with the reply
		 *	attributes.
		 */
		if (fake->server) {
			RDEBUG("} # server %s", fake->server);
		} else {
			RDEBUG("}");
		}

		RDEBUG("Got tunneled reply code %d", fake->reply->code);
		rdebug_pair_list(L_DBG_LVL_1, request, fake->reply->vps, NULL);

		if ((pw = fr_pair_find_by_num(fake->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
			DEBUG2("failed to find password for %s to do pwd authentication",
			session->peer_id);
			talloc_free(fake);
			return 0;
		}

		if (compute_password_element(session, session->group_num,
			     		     pw->data.strvalue, strlen(pw->data.strvalue),
					     inst->server_id, strlen(inst->server_id),
					     session->peer_id, strlen(session->peer_id),
					     &session->token)) {
			DEBUG2("failed to obtain password element");
			talloc_free(fake);
			return 0;
		}
		TALLOC_FREE(fake);

		/*
		 * compute our scalar and element
		 */
		if (compute_scalar_element(session, inst->bnctx)) {
			DEBUG2("failed to compute server's scalar and element");
			return 0;
		}

		MEM(x = BN_new());
		MEM(y = BN_new());

		/*
		 * element is a point, get both coordinates: x and y
		 */
		if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y,
							 inst->bnctx)) {
			DEBUG2("server point assignment failed");
			BN_clear_free(x);
			BN_clear_free(y);
			return 0;
		}

		/*
		 * construct request
		 */
		session->out_len = BN_num_bytes(session->order) + (2 * BN_num_bytes(session->prime));
		if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
			return 0;
		}
		memset(session->out, 0, session->out_len);

		ptr = session->out;
		offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
		BN_bn2bin(x, ptr + offset);
		BN_clear_free(x);

		ptr += BN_num_bytes(session->prime);
		offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
		BN_bn2bin(y, ptr + offset);
		BN_clear_free(y);

		ptr += BN_num_bytes(session->prime);
		offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
		BN_bn2bin(session->my_scalar, ptr + offset);

		session->state = PWD_STATE_COMMIT;
		ret = send_pwd_request(session, eap_ds);
	}
		break;

		case PWD_STATE_COMMIT:
		if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) {
			RDEBUG2("pwd exchange is incorrect: not commit!");
			return 0;
		}

		/*
		 * process the peer's commit and generate the shared key, k
		 */
		if (process_peer_commit(session, in, in_len, inst->bnctx)) {
			RDEBUG2("failed to process peer's commit");
			return 0;
		}

		/*
		 * compute our confirm blob
		 */
		if (compute_server_confirm(session, session->my_confirm, inst->bnctx)) {
			ERROR("rlm_eap_pwd: failed to compute confirm!");
			return 0;
		}

		/*
		 * construct a response...which is just our confirm blob
		 */
		session->out_len = SHA256_DIGEST_LENGTH;
		if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
			return 0;
		}

		memset(session->out, 0, session->out_len);
		memcpy(session->out, session->my_confirm, SHA256_DIGEST_LENGTH);

		session->state = PWD_STATE_CONFIRM;
		ret = send_pwd_request(session, eap_ds);
		break;

	case PWD_STATE_CONFIRM:
		if (in_len < SHA256_DIGEST_LENGTH) {
			RDEBUG("Peer confirm is too short (%zd < %d)",
			       in_len, SHA256_DIGEST_LENGTH);
			return 0;
		}

		if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
			RDEBUG2("pwd exchange is incorrect: not commit!");
			return 0;
		}
		if (compute_peer_confirm(session, peer_confirm, inst->bnctx)) {
			RDEBUG2("pwd exchange cannot compute peer's confirm");
			return 0;
		}
		if (CRYPTO_memcmp(peer_confirm, in, SHA256_DIGEST_LENGTH)) {
			RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
			return 0;
		}
		if (compute_keys(session, peer_confirm, msk, emsk)) {
			RDEBUG2("pwd exchange cannot generate (E)MSK!");
			return 0;
		}
		eap_ds->request->code = PW_EAP_SUCCESS;
		/*
		 * return the MSK (in halves)
		 */
		eap_add_reply(handler->request, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
		eap_add_reply(handler->request, "MS-MPPE-Send-Key", msk + MPPE_KEY_LEN, MPPE_KEY_LEN);

		ret = 1;
		break;

	default:
		RDEBUG2("unknown PWD state");
		return 0;
	}

	/*
	 * we processed the buffered fragments, get rid of them
	 */
	if (session->in) {
		talloc_free(session->in);
		session->in = NULL;
	}

	return ret;
}
Ejemplo n.º 22
0
static rlm_rcode_t mod_cache_it(void *instance, UNUSED void *thread, REQUEST *request)
{
	rlm_cache_entry_t	*c = NULL;
	rlm_cache_t const	*inst = instance;

	rlm_cache_handle_t	*handle;

	fr_cursor_t		cursor;
	VALUE_PAIR		*vp;

	bool			merge = true, insert = true, expire = false, set_ttl = false;
	int			exists = -1;

	uint8_t			buffer[1024];
	uint8_t const		*key;
	ssize_t			key_len;
	rlm_rcode_t		rcode = RLM_MODULE_NOOP;

	int			ttl = inst->config.ttl;

	key_len = tmpl_expand((char const **)&key, (char *)buffer, sizeof(buffer),
			      request, inst->config.key, NULL, NULL);
	if (key_len < 0) return RLM_MODULE_FAIL;

	if (key_len == 0) {
		REDEBUG("Zero length key string is invalid");
		return RLM_MODULE_INVALID;
	}

	/*
	 *	If Cache-Status-Only == yes, only return whether we found a
	 *	valid cache entry
	 */
	vp = fr_pair_find_by_da(request->control, attr_cache_status_only, TAG_ANY);
	if (vp && vp->vp_bool) {
		RINDENT();
		RDEBUG3("status-only: yes");
		REXDENT();

		if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;

		rcode = cache_find(&c, inst, request, &handle, key, key_len);
		if (rcode == RLM_MODULE_FAIL) goto finish;
		rad_assert(!inst->driver->acquire || handle);

		rcode = c ? RLM_MODULE_OK:
			    RLM_MODULE_NOTFOUND;
		goto finish;
	}

	/*
	 *	Figure out what operation we're doing
	 */
	vp = fr_pair_find_by_da(request->control, attr_cache_allow_merge, TAG_ANY);
	if (vp) merge = vp->vp_bool;

	vp = fr_pair_find_by_da(request->control, attr_cache_allow_insert, TAG_ANY);
	if (vp) insert = vp->vp_bool;

	vp = fr_pair_find_by_da(request->control, attr_cache_ttl, TAG_ANY);
	if (vp) {
		if (vp->vp_int32 == 0) {
			expire = true;
		} else if (vp->vp_int32 < 0) {
			expire = true;
			ttl = -(vp->vp_int32);
		/* Updating the TTL */
		} else {
			set_ttl = true;
			ttl = vp->vp_int32;
		}
	}

	RINDENT();
	RDEBUG3("merge  : %s", merge ? "yes" : "no");
	RDEBUG3("insert : %s", insert ? "yes" : "no");
	RDEBUG3("expire : %s", expire ? "yes" : "no");
	RDEBUG3("ttl    : %i", ttl);
	REXDENT();
	if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;

	/*
	 *	Retrieve the cache entry and merge it with the current request
	 *	recording whether the entry existed.
	 */
	if (merge) {
		rcode = cache_find(&c, inst, request, &handle, key, key_len);
		switch (rcode) {
		case RLM_MODULE_FAIL:
			goto finish;

		case RLM_MODULE_OK:
			rcode = cache_merge(inst, request, c);
			exists = 1;
			break;

		case RLM_MODULE_NOTFOUND:
			rcode = RLM_MODULE_NOTFOUND;
			exists = 0;
			break;

		default:
			rad_assert(0);
		}
		rad_assert(!inst->driver->acquire || handle);
	}

	/*
	 *	Expire the entry if told to, and we either don't know whether
	 *	it exists, or we know it does.
	 *
	 *	We only expire if we're not inserting, as driver insert methods
	 *	should perform upserts.
	 */
	if (expire && ((exists == -1) || (exists == 1))) {
		if (!insert) {
			rad_assert(!set_ttl);
			switch (cache_expire(inst, request, &handle, key, key_len)) {
			case RLM_MODULE_FAIL:
				rcode = RLM_MODULE_FAIL;
				goto finish;

			case RLM_MODULE_OK:
				if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_OK;
				break;

			case RLM_MODULE_NOTFOUND:
				if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_NOTFOUND;
				break;

			default:
				rad_assert(0);
				break;
			}
			/* If it previously existed, it doesn't now */
		}
		/* Otherwise use insert to overwrite */
		exists = 0;
	}

	/*
	 *	If we still don't know whether it exists or not
	 *	and we need to do an insert or set_ttl operation
	 *	determine that now.
	 */
	if ((exists < 0) && (insert || set_ttl)) {
		switch (cache_find(&c, inst, request, &handle, key, key_len)) {
		case RLM_MODULE_FAIL:
			rcode = RLM_MODULE_FAIL;
			goto finish;

		case RLM_MODULE_OK:
			exists = 1;
			if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
			break;

		case RLM_MODULE_NOTFOUND:
			exists = 0;
			break;

		default:
			rad_assert(0);
		}
		rad_assert(!inst->driver->acquire || handle);
	}

	/*
	 *	We can only alter the TTL on an entry if it exists.
	 */
	if (set_ttl && (exists == 1)) {
		rad_assert(c);

		c->expires = request->packet->timestamp.tv_sec + ttl;

		switch (cache_set_ttl(inst, request, &handle, c)) {
		case RLM_MODULE_FAIL:
			rcode = RLM_MODULE_FAIL;
			goto finish;

		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
			goto finish;

		default:
			rad_assert(0);
		}
	}

	/*
	 *	Inserts are upserts, so we don't care about the
	 *	entry state, just that we're not meant to be
	 *	setting the TTL, which precludes performing an
	 *	insert.
	 */
	if (insert && (exists == 0)) {
		switch (cache_insert(inst, request, &handle, key, key_len, ttl)) {
		case RLM_MODULE_FAIL:
			rcode = RLM_MODULE_FAIL;
			goto finish;

		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
			break;

		case RLM_MODULE_UPDATED:
			rcode = RLM_MODULE_UPDATED;
			break;

		default:
			rad_assert(0);
		}
		rad_assert(!inst->driver->acquire || handle);
		goto finish;
	}


finish:
	cache_free(inst, &c);
	cache_release(inst, request, &handle);

	/*
	 *	Clear control attributes
	 */
	for (vp = fr_cursor_init(&cursor, &request->control);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
	     again:
		if (!fr_dict_attr_is_top_level(vp->da)) continue;

		switch (vp->da->attr) {
		case FR_CACHE_TTL:
		case FR_CACHE_STATUS_ONLY:
		case FR_CACHE_ALLOW_MERGE:
		case FR_CACHE_ALLOW_INSERT:
		case FR_CACHE_MERGE_NEW:
			RDEBUG2("Removing &control:%s", vp->da->name);
			vp = fr_cursor_remove(&cursor);
			talloc_free(vp);
			vp = fr_cursor_current(&cursor);
			if (!vp) break;
			goto again;
		}
	}

	return rcode;
}
Ejemplo n.º 23
0
/** Create and insert a cache entry
 *
 * @return
 *	- #RLM_MODULE_OK on success.
 *	- #RLM_MODULE_UPDATED if we merged the cache entry.
 *	- #RLM_MODULE_FAIL on failure.
 */
static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle,
				uint8_t const *key, size_t key_len, int ttl)
{
	vp_map_t		const *map;
	vp_map_t		**last, *c_map;

	VALUE_PAIR		*vp;
	bool			merge = false;
	rlm_cache_entry_t	*c;
	size_t			len;

	TALLOC_CTX		*pool;

	if ((inst->config.max_entries > 0) && inst->driver->count &&
	    (inst->driver->count(&inst->config, inst->driver_inst->data, request, handle) > inst->config.max_entries)) {
		RWDEBUG("Cache is full: %d entries", inst->config.max_entries);
		return RLM_MODULE_FAIL;
	}

	c = cache_alloc(inst, request);
	if (!c) return RLM_MODULE_FAIL;

	c->key = talloc_memdup(c, key, key_len);
	c->key_len = key_len;
	c->created = c->expires = request->packet->timestamp.tv_sec;
	c->expires += ttl;

	last = &c->maps;

	RDEBUG2("Creating new cache entry");

	/*
	 *	Alloc a pool so we don't have excessive allocs when
	 *	gathering VALUE_PAIRs to cache.
	 */
	pool = talloc_pool(NULL, 2048);
	for (map = inst->maps; map != NULL; map = map->next) {
		VALUE_PAIR	*to_cache = NULL;
		fr_cursor_t	cursor;

		rad_assert(map->lhs && map->rhs);

		/*
		 *	Calling map_to_vp gives us exactly the same result,
		 *	as if this were an update section.
		 */
		if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) {
			RDEBUG2("Skipping %s", map->rhs->name);
			continue;
		}

		for (vp = fr_cursor_init(&cursor, &to_cache);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Prevent people from accidentally caching
			 *	cache control attributes.
			 */
			if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
			case FR_CACHE_TTL:
			case FR_CACHE_STATUS_ONLY:
			case FR_CACHE_MERGE_NEW:
			case FR_CACHE_ENTRY_HITS:
				RDEBUG2("Skipping %s", vp->da->name);
				continue;

			default:
				break;
			}

			RINDENT();
			if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
			REXDENT();

			MEM(c_map = talloc_zero(c, vp_map_t));
			c_map->op = map->op;

			/*
			 *	Now we turn the VALUE_PAIRs into maps.
			 */
			switch (map->lhs->type) {
			/*
			 *	Attributes are easy, reuse the LHS, and create a new
			 *	RHS with the fr_value_box_t from the VALUE_PAIR.
			 */
			case TMPL_TYPE_ATTR:
				c_map->lhs = map->lhs;	/* lhs shouldn't be touched, so this is ok */
			do_rhs:
				MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t),
							   TMPL_TYPE_DATA, map->rhs->name, map->rhs->len, T_BARE_WORD));
				if (fr_value_box_copy(c_map->rhs, &c_map->rhs->tmpl_value, &vp->data) < 0) {
					REDEBUG("Failed copying attribute value");
				error:
					talloc_free(pool);
					talloc_free(c);
					return RLM_MODULE_FAIL;
				}
				c_map->rhs->tmpl_value_type = vp->vp_type;
				if (vp->vp_type == FR_TYPE_STRING) {
					c_map->rhs->quote = is_printable(vp->vp_strvalue, vp->vp_length) ?
						T_SINGLE_QUOTED_STRING : T_DOUBLE_QUOTED_STRING;
				}
				break;

			/*
			 *	Lists are weird... We need to fudge a new LHS template,
			 *	which is a combination of the LHS list and the attribute.
			 */
			case TMPL_TYPE_LIST:
			{
				char attr[256];

				MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t),
							   TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len, T_BARE_WORD));
				c_map->lhs->tmpl_da = vp->da;
				if (vp->da->flags.is_unknown) { /* for tmpl_verify() */
					c_map->lhs->tmpl_unknown = fr_dict_unknown_acopy(c_map->lhs, vp->da);
					c_map->lhs->tmpl_da = c_map->lhs->tmpl_unknown;
				}

				c_map->lhs->tmpl_tag = vp->tag;
				c_map->lhs->tmpl_list = map->lhs->tmpl_list;
				c_map->lhs->tmpl_num = map->lhs->tmpl_num;
				c_map->lhs->tmpl_request = map->lhs->tmpl_request;

				/*
				 *	We need to rebuild the attribute name, to be the
				 *	one we copied from the source list.
				 */
				len = tmpl_snprint(attr, sizeof(attr), c_map->lhs);
				if (is_truncated(len, sizeof(attr))) {
					REDEBUG("Serialized attribute too long.  Must be < "
						STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", len);
					goto error;
				}
				c_map->lhs->len = len;
				c_map->lhs->name = talloc_typed_strdup(c_map->lhs, attr);
			}
				goto do_rhs;

			default:
				rad_assert(0);
			}
			*last = c_map;
			last = &(*last)->next;
		}
		talloc_free_children(pool); /* reset pool state */
	}
	talloc_free(pool);

	/*
	 *	Check to see if we need to merge the entry into the request
	 */
	vp = fr_pair_find_by_da(request->control, attr_cache_merge_new, TAG_ANY);
	if (vp && vp->vp_bool) merge = true;

	if (merge) cache_merge(inst, request, c);

	for (;;) {
		cache_status_t ret;

		ret = inst->driver->insert(&inst->config, inst->driver_inst->data, request, *handle, c);
		switch (ret) {
		case CACHE_RECONNECT:
			if (cache_reconnect(handle, inst, request) == 0) continue;
			return RLM_MODULE_FAIL;

		case CACHE_OK:
			RDEBUG2("Committed entry, TTL %d seconds", ttl);
			cache_free(inst, &c);
			return merge ? RLM_MODULE_UPDATED :
				       RLM_MODULE_OK;

		default:
			talloc_free(c);	/* Failed insertion - use talloc_free not the driver free */
			return RLM_MODULE_FAIL;
		}
	}
}
Ejemplo n.º 24
0
/** Convert attribute map into valuepairs
 *
 * Use the attribute map built earlier to convert LDAP values into valuepairs and insert them into whichever
 * list they need to go into.
 *
 * This is *NOT* atomic, but there's no condition for which we should error out...
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in] handle associated with entry.
 * @param[in] expanded attributes (rhs of map).
 * @param[in] entry to retrieve attributes from.
 * @return
 *	- Number of maps successfully applied.
 *	- -1 on failure.
 */
int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle,
		    rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry)
{
	vp_map_t const 	*map;
	unsigned int		total = 0;
	int			applied = 0;	/* How many maps have been applied to the current request */

	rlm_ldap_result_t	result;
	char const		*name;

	RINDENT();
	for (map = expanded->maps; map != NULL; map = map->next) {
		int ret;

		name = expanded->attrs[total++];

		/*
		 *	Binary safe
		 */
		result.values = ldap_get_values_len(handle, entry, name);
		if (!result.values) {
			RDEBUG3("Attribute \"%s\" not found in LDAP object", name);

			goto next;
		}

		/*
		 *	Find out how many values there are for the
		 *	attribute and extract all of them.
		 */
		result.count = ldap_count_values_len(result.values);

		/*
		 *	If something bad happened, just skip, this is probably
		 *	a case of the dst being incorrect for the current
		 *	request context
		 */
		ret = map_to_request(request, map, rlm_ldap_map_getvalue, &result);
		if (ret == -1) return -1;	/* Fail */

		/*
		 *	How many maps we've processed
		 */
		applied++;

	next:
		ldap_value_free_len(result.values);
	}
	REXDENT();

	/*
	 *	Retrieve any valuepair attributes from the result, these are generic values specifying
	 *	a radius list, operator and value.
	 */
	if (inst->valuepair_attr) {
		struct berval	**values;
		int		count, i;

		values = ldap_get_values_len(handle, entry, inst->valuepair_attr);
		count = ldap_count_values_len(values);

		RINDENT();
		for (i = 0; i < count; i++) {
			vp_map_t *attr;
			char *value;

			value = rlm_ldap_berval_to_string(request, values[i]);
			RDEBUG3("Parsing attribute string '%s'", value);
			if (map_afrom_attr_str(request, &attr, value,
					       REQUEST_CURRENT, PAIR_LIST_REPLY,
					       REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
				RWDEBUG("Failed parsing '%s' value \"%s\" as valuepair (%s), skipping...",
					fr_strerror(), inst->valuepair_attr, value);
				talloc_free(value);
				continue;
			}
			if (map_to_request(request, attr, map_to_vp, NULL) < 0) {
				RWDEBUG("Failed adding \"%s\" to request, skipping...", value);
			} else {
				applied++;
			}
			talloc_free(attr);
			talloc_free(value);
		}
		REXDENT();
		ldap_value_free_len(values);
	}

	return applied;
}
Ejemplo n.º 25
0
/*
 *	Process an EAP request
 */
fr_tls_status_t eaptls_process(eap_handler_t *handler)
{
	tls_session_t *tls_session = (tls_session_t *) handler->opaque;
	EAPTLS_PACKET	*tlspacket;
	fr_tls_status_t	status;
	REQUEST *request = handler->request;

	if (!request) return FR_TLS_FAIL;

	RDEBUG2("Continuing EAP-TLS");

	SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request);

	if (handler->certs) fr_pair_add(&request->packet->vps,
				    fr_pair_list_copy(request->packet, handler->certs));

	/*
	 *	This case is when SSL generates Alert then we
	 *	send that alert to the client and then send the EAP-Failure
	 */
	status = eaptls_verify(handler);
	if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
		REDEBUG("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
	} else {
		RDEBUG2("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
	}

	switch (status) {
	default:
	case FR_TLS_INVALID:
	case FR_TLS_FAIL:

	/*
	 *	Success means that we're done the initial
	 *	handshake.  For TTLS, this means send stuff
	 *	back to the client, and the client sends us
	 *	more tunneled data.
	 */
	case FR_TLS_SUCCESS:
		goto done;

	/*
	 *	Normal TLS request, continue with the "get rest
	 *	of fragments" phase.
	 */
	case FR_TLS_REQUEST:
		eaptls_request(handler->eap_ds, tls_session);
		status = FR_TLS_HANDLED;
		goto done;

	/*
	 *	The handshake is done, and we're in the "tunnel
	 *	data" phase.
	 */
	case FR_TLS_OK:
		RDEBUG2("Done initial handshake");

	/*
	 *	Get the rest of the fragments.
	 */
	case FR_TLS_FIRST_FRAGMENT:
	case FR_TLS_MORE_FRAGMENTS:
	case FR_TLS_LENGTH_INCLUDED:
		break;
	}

	/*
	 *	Extract the TLS packet from the buffer.
	 */
	if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) {
		status = FR_TLS_FAIL;
		goto done;
	}

	/*
	 *	Get the session struct from the handler
	 *
	 *	update the dirty_in buffer
	 *
	 *	NOTE: This buffer will contain partial data when M bit is set.
	 *
	 * 	CAUTION while reinitializing this buffer, it should be
	 * 	reinitialized only when this M bit is NOT set.
	 */
	if (tlspacket->dlen !=
	    (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
		talloc_free(tlspacket);
		REDEBUG("Exceeded maximum record size");
		status = FR_TLS_FAIL;
		goto done;
	}

	/*
	 *	No longer needed.
	 */
	talloc_free(tlspacket);

	/*
	 *	SSL initalization is done.  Return.
	 *
	 *	The TLS data will be in the tls_session structure.
	 */
	if (SSL_is_init_finished(tls_session->ssl)) {
		/*
		 *	The initialization may be finished, but if
		 *	there more fragments coming, then send ACK,
		 *	and get the caller to continue the
		 *	conversation.
		 */
		if ((status == FR_TLS_MORE_FRAGMENTS) ||
		    (status == FR_TLS_FIRST_FRAGMENT)) {
			/*
			 *	Send the ACK.
			 */
			eaptls_send_ack(handler, tls_session->peap_flag);
			RDEBUG2("Init is done, but tunneled data is fragmented");
			status = FR_TLS_HANDLED;
			goto done;
		}

		status = tls_application_data(tls_session, request);
		goto done;
	}

	/*
	 *	Continue the handshake.
	 */
	status = eaptls_operation(status, handler);
	if (status == FR_TLS_SUCCESS) {
#define MAX_SESSION_SIZE (256)
		size_t size;
		VALUE_PAIR *vps;
		char buffer[2 * MAX_SESSION_SIZE + 1];
		/*
		 *	Restore the cached VPs before processing the
		 *	application data.
		 */
		size = tls_session->ssl->session->session_id_length;
		if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;

		fr_bin2hex(buffer, tls_session->ssl->session->session_id, size);

		vps = SSL_SESSION_get_ex_data(tls_session->ssl->session, fr_tls_ex_index_vps);
		if (!vps) {
			RWDEBUG("No information in cached session %s", buffer);
		} else {
			vp_cursor_t cursor;
			VALUE_PAIR *vp;

			RDEBUG("Adding cached attributes from session %s", buffer);

			/*
			 *	The cbtls_get_session() function doesn't have
			 *	access to sock->certs or handler->certs, which
			 *	is where the certificates normally live.  So
			 *	the certs are all in the VPS list here, and
			 *	have to be manually extracted.
			 */
			RINDENT();
			for (vp = fr_cursor_init(&cursor, &vps);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				/*
				 *	TLS-* attrs get added back to
				 *	the request list.
				 */
				if ((vp->da->vendor == 0) &&
				    (vp->da->attr >= PW_TLS_CERT_SERIAL) &&
				    (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) {
					/*
					 *	Certs already exist.  Don't re-add them.
					 */
					if (!handler->certs) {
						rdebug_pair(L_DBG_LVL_2, request, vp, "request:");
						fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp));
					}
				} else {
					rdebug_pair(L_DBG_LVL_2, request, vp, "reply:");
					fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
				}
			}
			REXDENT();
		}
	}

 done:
	SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL);

	return status;
}
Ejemplo n.º 26
0
/** Insert a new entry into the data store
 *
 * @copydetails cache_entry_insert_t
 */
static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, void *driver_inst,
					 REQUEST *request, UNUSED void *handle, const rlm_cache_entry_t *c)
{
	rlm_cache_redis_t	*driver = driver_inst;
	TALLOC_CTX		*pool;

	vp_map_t		*map;

	fr_redis_conn_t		*conn;
	fr_redis_cluster_state_t	state;
	fr_redis_rcode_t	status;
	redisReply		*reply = NULL;
	int			s_ret;

	static char const	command[] = "RPUSH";
	char const		**argv;
	size_t			*argv_len;
	char const		**argv_p;
	size_t			*argv_len_p;

	int			pipelined = 0;	/* How many commands pending in the pipeline */
	redisReply		*replies[5];	/* Should have the same number of elements as pipelined commands */
	size_t			reply_num = 0, i;

	char			*p;
	int			cnt;

	vp_tmpl_t		expires_value;
	vp_map_t		expires = {
					.op	= T_OP_SET,
					.lhs	= &driver->expires_attr,
					.rhs	= &expires_value,
				};

	vp_tmpl_t		created_value;
	vp_map_t		created = {
					.op	= T_OP_SET,
					.lhs	= &driver->created_attr,
					.rhs	= &created_value,
					.next	= &expires
				};

	/*
	 *	Encode the entry created date
	 */
	tmpl_init(&created_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
	created_value.tmpl_data_type = PW_TYPE_DATE;
	created_value.tmpl_data_length = sizeof(created_value.tmpl_data_value.date);
	created_value.tmpl_data_value.date = c->created;

	/*
	 *	Encode the entry expiry time
	 *
	 *	Although Redis objects expire on their own, we still need this
	 *	to ignore entries that were created before the last epoch.
	 */
	tmpl_init(&expires_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD);
	expires_value.tmpl_data_type = PW_TYPE_DATE;
	expires_value.tmpl_data_length = sizeof(expires_value.tmpl_data_value.date);
	expires_value.tmpl_data_value.date = c->expires;
	expires.next = c->maps;	/* Head of the list */

	for (cnt = 0, map = &created; map; cnt++, map = map->next);

	/*
	 *	The majority of serialized entries should be under 1k.
	 *
	 * @todo We should really calculate this using some sort of moving average.
	 */
	pool = talloc_pool(request, 1024);
	if (!pool) return CACHE_ERROR;

	argv_p = argv = talloc_array(pool, char const *, (cnt * 3) + 2);	/* pair = 3 + cmd + key */
	argv_len_p = argv_len = talloc_array(pool, size_t, (cnt * 3) + 2);	/* pair = 3 + cmd + key */

	*argv_p++ = command;
	*argv_len_p++ = sizeof(command) - 1;

	*argv_p++ = (char const *)c->key;
	*argv_len_p++ = c->key_len;

	/*
	 *	Add the maps to the command string in reverse order
	 */
	for (map = &created; map; map = map->next) {
		if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, map) < 0) {
			REDEBUG("Failed encoding map as Redis K/V pair");
			talloc_free(pool);
			return CACHE_ERROR;
		}
		argv_p += 3;
		argv_len_p += 3;
	}

	RDEBUG3("Pipelining commands");
	RINDENT();

	for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, c->key, c->key_len, false);
	     s_ret == REDIS_RCODE_TRY_AGAIN;	/* Continue */
	     s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) {
		/*
		 *	Start the transaction, as we need to set an expiry time too.
		 */
		if (c->expires > 0) {
			RDEBUG3("MULTI");
			if (redisAppendCommand(conn->handle, "MULTI") != REDIS_OK) {
			append_error:
				REXDENT();
				RERROR("Failed appending Redis command to output buffer: %s", conn->handle->errstr);
				talloc_free(pool);
				return CACHE_ERROR;
			}
			pipelined++;
		}

		if (RDEBUG_ENABLED3) {
			p = fr_asprint(request, (char const *)c->key, c->key_len, '\0');
			RDEBUG3("DEL \"%s\"", p);
			talloc_free(p);

		}

		if (redisAppendCommand(conn->handle, "DEL %b", c->key, c->key_len) != REDIS_OK) goto append_error;
		pipelined++;

		if (RDEBUG_ENABLED3) {
			RDEBUG3("argv command");
			RINDENT();
			for (i = 0; i < talloc_array_length(argv); i++) {
				p = fr_asprint(request, argv[i], argv_len[i], '\0');
				RDEBUG3("%s", p);
				talloc_free(p);
			}
			REXDENT();
		}
		redisAppendCommandArgv(conn->handle, talloc_array_length(argv), argv, argv_len);
		pipelined++;

		/*
		 *	Set the expiry time and close out the transaction.
		 */
		if (c->expires > 0) {
			if (RDEBUG_ENABLED3) {
				p = fr_asprint(request, (char const *)c->key, c->key_len, '\"');
				RDEBUG3("EXPIREAT \"%s\" %li", p, (long)c->expires);
				talloc_free(p);
			}
			if (redisAppendCommand(conn->handle, "EXPIREAT %b %i", c->key,
					       c->key_len, c->expires) != REDIS_OK) goto append_error;
			pipelined++;
			RDEBUG3("EXEC");
			if (redisAppendCommand(conn->handle, "EXEC") != REDIS_OK) goto append_error;
			pipelined++;
		}
		REXDENT();

		reply_num = fr_redis_pipeline_result(&status, replies, sizeof(replies) / sizeof(*replies),
						     conn, pipelined);
		reply = replies[0];
	}
	talloc_free(pool);

	if (s_ret != REDIS_RCODE_SUCCESS) {
		RERROR("Failed inserting entry");
		return CACHE_ERROR;
	}

	RDEBUG3("Command results");
	RINDENT();
	for (i = 0; i < reply_num; i++) {
		fr_redis_reply_print(L_DBG_LVL_3, replies[i], request, i);
		fr_redis_reply_free(replies[i]);
	}
	REXDENT();

	return CACHE_OK;
}

/** Call delete the cache entry from redis
 *
 * @copydetails cache_entry_expire_t
 */
static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, void *driver_inst,
					 REQUEST *request, UNUSED void *handle,  uint8_t const *key, size_t key_len)
{
	rlm_cache_redis_t		*driver = driver_inst;
	fr_redis_cluster_state_t	state;
	fr_redis_conn_t			*conn;
	fr_redis_rcode_t			status;
	redisReply			*reply = NULL;
	int				s_ret;

	for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, key, key_len, false);
	     s_ret == REDIS_RCODE_TRY_AGAIN;	/* Continue */
	     s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) {
	     	reply = redisCommand(conn->handle, "DEL %b", key, key_len);
	     	status = fr_redis_command_status(conn, reply);
	}
	if (s_ret != REDIS_RCODE_SUCCESS) {
		RERROR("Failed expiring entry");
		fr_redis_reply_free(reply);
		return CACHE_ERROR;
	}

	rad_assert(reply);	/* clang scan */
	if (reply->type == REDIS_REPLY_INTEGER) {
		fr_redis_reply_free(reply);
		if (reply->integer) return CACHE_OK;	/* Affected */
		return CACHE_MISS;
	}

	REDEBUG("Bad result type, expected integer, got %s",
		fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
	fr_redis_reply_free(reply);

	return CACHE_ERROR;
}

extern cache_driver_t rlm_cache_redis;
cache_driver_t rlm_cache_redis = {
	.name		= "rlm_cache_redis",
	.instantiate	= mod_instantiate,
	.inst_size	= sizeof(rlm_cache_redis_t),
	.free		= cache_entry_free,

	.find		= cache_entry_find,
	.insert		= cache_entry_insert,
	.expire		= cache_entry_expire,
};
Ejemplo n.º 27
0
/** Create and insert a cache entry.
 *
 * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure.
 */
static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle,
				char const *key, int ttl)
{
	VALUE_PAIR *vp, *to_cache;
	vp_cursor_t src_list, cached_request, cached_reply, cached_control;

	value_pair_map_t const *map;

	bool merge = false;
	rlm_cache_entry_t *c;

	if ((inst->max_entries > 0) && inst->module->count &&
	    (inst->module->count(inst, request, handle) > inst->max_entries)) {
		RWDEBUG("Cache is full: %d entries", inst->max_entries);
		return RLM_MODULE_FAIL;
	}

	c = cache_alloc(inst, request);
	if (!c) return RLM_MODULE_FAIL;

	c->key = talloc_typed_strdup(c, key);
	c->created = c->expires = request->timestamp;
	c->expires += ttl;

	RDEBUG("Creating new cache entry");

	fr_cursor_init(&cached_request, &c->packet);
	fr_cursor_init(&cached_reply, &c->reply);
	fr_cursor_init(&cached_control, &c->control);

	for (map = inst->maps; map != NULL; map = map->next) {
		rad_assert(map->lhs && map->rhs);

		if (map_to_vp(&to_cache, request, map, NULL) < 0) {
			RDEBUG("Skipping %s", map->rhs->name);
			continue;
		}

		/*
		 *	Reparent the VPs map_to_vp may return multiple.
		 */
		for (vp = fr_cursor_init(&src_list, &to_cache);
		     vp;
		     vp = fr_cursor_next(&src_list)) {
			VERIFY_VP(vp);

			/*
			 *	Prevent people from accidentally caching
			 *	cache control attributes.
			 */
			if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
			case PW_CACHE_TTL:
			case PW_CACHE_STATUS_ONLY:
			case PW_CACHE_READ_ONLY:
			case PW_CACHE_MERGE:
			case PW_CACHE_ENTRY_HITS:
				RDEBUG2("Skipping %s", vp->da->name);
				continue;

			default:
				break;
			}

			RINDENT();
			if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
			REXDENT();
			(void) talloc_steal(c, vp);

			vp->op = map->op;

			switch (map->lhs->tmpl_list) {
			case PAIR_LIST_REQUEST:
				fr_cursor_insert(&cached_request, vp);
				break;

			case PAIR_LIST_REPLY:
				fr_cursor_insert(&cached_reply, vp);
				break;

			case PAIR_LIST_CONTROL:
				fr_cursor_insert(&cached_control, vp);
				break;

			default:
				rad_assert(0);	/* should have been caught by validation */
			}
		}
	}

	/*
	 *	Check to see if we need to merge the entry into the request
	 */
	vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
	if (vp && (vp->vp_integer > 0)) merge = true;

	if (merge) cache_merge(inst, request, c);

	for (;;) {
		cache_status_t ret;

		ret = inst->module->insert(inst, request, handle, c);
		switch (ret) {
		case CACHE_RECONNECT:
			if (cache_reconnect(inst, request, handle) == 0) continue;
			return RLM_MODULE_FAIL;

		case CACHE_OK:
			RDEBUG("Commited entry, TTL %d seconds", ttl);
			cache_free(inst, &c);
			return merge ? RLM_MODULE_UPDATED :
				       RLM_MODULE_OK;

		default:
			talloc_free(c);	/* Failed insertion - use talloc_free not the driver free */
			return RLM_MODULE_FAIL;
		}
	}
}
Ejemplo n.º 28
0
static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle,
					  sql_fall_through_t *do_fall_through)
{
	rlm_rcode_t		rcode = RLM_MODULE_NOOP;
	VALUE_PAIR		*check_tmp = NULL, *reply_tmp = NULL, *sql_group = NULL;
	rlm_sql_grouplist_t	*head = NULL, *entry = NULL;

	char			*expanded = NULL;
	int			rows;

	rad_assert(request->packet != NULL);

	/*
	 *	Get the list of groups this user is a member of
	 */
	rows = sql_get_grouplist(inst, handle, request, &head);
	if (rows < 0) {
		REDEBUG("Error retrieving group list");

		return RLM_MODULE_FAIL;
	}
	if (rows == 0) {
		RDEBUG2("User not found in any groups");
		rcode = RLM_MODULE_NOTFOUND;
		goto finish;
	}
	rad_assert(head);

	RDEBUG2("User found in the group table");

	entry = head;
	do {
		/*
		 *	Add the Sql-Group attribute to the request list so we know
		 *	which group we're retrieving attributes for
		 */
		sql_group = pairmake_packet("Sql-Group", entry->name, T_OP_EQ);
		if (!sql_group) {
			REDEBUG("Error creating Sql-Group attribute");
			rcode = RLM_MODULE_FAIL;
			goto finish;
		}

		if (inst->config->authorize_group_check_query && (*inst->config->authorize_group_check_query != '\0')) {
			vp_cursor_t cursor;
			VALUE_PAIR *vp;

			/*
			 *	Expand the group query
			 */
			if (radius_axlat(&expanded, request, inst->config->authorize_group_check_query,
					 sql_escape_func, inst) < 0) {
				REDEBUG("Error generating query");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			rows = sql_getvpdata(request, inst, handle, &check_tmp, expanded);
			TALLOC_FREE(expanded);
			if (rows < 0) {
				REDEBUG("Error retrieving check pairs for group %s", entry->name);
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			/*
			 *	If we got check rows we need to process them before we decide to process the reply rows
			 */
			if ((rows > 0) &&
			    (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) {
				pairfree(&check_tmp);
				pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);

				continue;
			}

			RDEBUG2("Group \"%s\": Conditional check items matched", entry->name);
			rcode = RLM_MODULE_OK;

			RDEBUG2("Group \"%s\": Merging assignment check items", entry->name);
			RINDENT();
			for (vp = fr_cursor_init(&cursor, &check_tmp);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
			 	if (!fr_assignment_op[vp->op]) continue;

			 	rdebug_pair(2, request, vp);
			}
			REXDENT();
			radius_pairmove(request, &request->config_items, check_tmp, true);
			check_tmp = NULL;
		}

		if (inst->config->authorize_group_reply_query && (*inst->config->authorize_group_reply_query != '\0')) {
			/*
			 *	Now get the reply pairs since the paircompare matched
			 */
			if (radius_axlat(&expanded, request, inst->config->authorize_group_reply_query,
					 sql_escape_func, inst) < 0) {
				REDEBUG("Error generating query");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			rows = sql_getvpdata(request->reply, inst, handle, &reply_tmp, expanded);
			TALLOC_FREE(expanded);
			if (rows < 0) {
				REDEBUG("Error retrieving reply pairs for group %s", entry->name);
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}
			*do_fall_through = fall_through(reply_tmp);

			RDEBUG2("Group \"%s\": Merging reply items", entry->name);
			rcode = RLM_MODULE_OK;

			rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp);

			radius_pairmove(request, &request->reply->vps, reply_tmp, true);
			reply_tmp = NULL;
		}

		pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
		entry = entry->next;
	} while (entry != NULL && (*do_fall_through == FALL_THROUGH_YES));

finish:
	talloc_free(head);
	pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);

	return rcode;
}
Ejemplo n.º 29
0
/*
 *  	get the vps and put them in perl hash
 *  	If one VP have multiple values it is added as array_ref
 *  	Example for this is Cisco-AVPair that holds multiple values.
 *  	Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'}
 */
static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, HV *rad_hv,
			   const char *hash_name, const char *list_name)
{
	VALUE_PAIR *vp;

	hv_undef(rad_hv);

	fr_cursor_t cursor;

	RINDENT();
	fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
	for (vp = fr_cursor_init(&cursor, vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		VALUE_PAIR *next;

		char const *name;
		char namebuf[256];

		/*
		 *	Tagged attributes are added to the hash with name
		 *	<attribute>:<tag>, others just use the normal attribute
		 *	name as the key.
		 */
		if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
			snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
			name = namebuf;
		} else {
			name = vp->da->name;
		}

		/*
		 *	We've sorted by type, then tag, so attributes of the
		 *	same type/tag should follow on from each other.
		 */
		if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) {
			int i = 0;
			AV *av;

			av = newAV();
			perl_vp_to_svpvn_element(request, av, vp, &i, hash_name, list_name);
			do {
				perl_vp_to_svpvn_element(request, av, next, &i, hash_name, list_name);
				fr_cursor_next(&cursor);
			} while ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next));
			(void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0);

			continue;
		}

		/*
		 *	It's a normal single valued attribute
		 */
		switch (vp->vp_type) {
		case FR_TYPE_STRING:
			RDEBUG2("$%s{'%s'} = &%s:%s -> '%pV'", hash_name, vp->da->name, list_name,
			       vp->da->name, &vp->data);
			(void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->vp_length), 0);
			break;

		case FR_TYPE_OCTETS:
			RDEBUG2("$%s{'%s'} = &%s:%s -> %pV", hash_name, vp->da->name, list_name,
			       vp->da->name, &vp->data);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn((char const *)vp->vp_octets, vp->vp_length), 0);
			break;

		default:
		{
			char buffer[1024];
			size_t len;

			len = fr_pair_value_snprint(buffer, sizeof(buffer), vp, '\0');
			RDEBUG2("$%s{'%s'} = &%s:%s -> '%s'", hash_name, vp->da->name,
			       list_name, vp->da->name, buffer);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0);
		}
			break;
		}
	}
	REXDENT();
}
Ejemplo n.º 30
0
/*
 *  	get the vps and put them in perl hash
 *  	If one VP have multiple values it is added as array_ref
 *  	Example for this is Cisco-AVPair that holds multiple values.
 *  	Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'}
 */
static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *vps, HV *rad_hv,
			   const char *hashname, const char *list_name)
{
	VALUE_PAIR *vp;

	hv_undef(rad_hv);

	vp_cursor_t cursor;

	RINDENT();
	pairsort(&vps, attrtagcmp);
	for (vp = fr_cursor_init(&cursor, &vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		VALUE_PAIR *next;

		char const *name;
		char namebuf[256];
		char buffer[1024];

		size_t len;

		/*
		 *	Tagged attributes are added to the hash with name
		 *	<attribute>:<tag>, others just use the normal attribute
		 *	name as the key.
		 */
		if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
			snprintf(namebuf, sizeof(namebuf), "%s:%d", vp->da->name, vp->tag);
			name = namebuf;
		} else {
			name = vp->da->name;
		}

		/*
		 *	We've sorted by type, then tag, so attributes of the
		 *	same type/tag should follow on from each other.
		 */
		if ((next = fr_cursor_next_peek(&cursor)) && ATTRIBUTE_EQ(vp, next)) {
			int i;
			AV *av;

			av = newAV();
			for (next = fr_cursor_next_by_da(&cursor, vp->da, vp->tag), i = 0;
			     next;
			     next = fr_cursor_next_by_da(&cursor, vp->da, vp->tag), i++) {
				switch (vp->da->type) {
				case PW_TYPE_STRING:
					RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hashname, next->da->name, i,
					       list_name, next->da->name, next->vp_strvalue);
					av_push(av, newSVpvn(next->vp_strvalue, next->length));
					break;

				case PW_TYPE_OCTETS:
					if (RDEBUG_ENABLED) {
						char *hex;

						hex = fr_abin2hex(request, next->vp_octets, next->length);
						RDEBUG("$%s{'%s'}[%i] = &%s:%s -> 0x%s", hashname, next->da->name, i,
						       list_name, next->da->name, hex);
						talloc_free(hex);
					}
					av_push(av, newSVpvn((char const *)next->vp_octets, next->length));
					break;

				default:
					len = vp_prints_value(buffer, sizeof(buffer), next, 0);
					RDEBUG("$%s{'%s'}[%i] = &%s:%s -> '%s'", hashname, next->da->name, i,
					       list_name, next->da->name, buffer);
					av_push(av, newSVpvn(buffer, truncate_len(len, sizeof(buffer))));
					break;
				}
			}
			(void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0);

			continue;
		}

		/*
		 *	It's a normal single valued attribute
		 */
		switch (vp->da->type) {
		case PW_TYPE_STRING:
			RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hashname, vp->da->name, list_name,
			       vp->da->name, vp->vp_strvalue);
			(void)hv_store(rad_hv, name, strlen(name), newSVpvn(vp->vp_strvalue, vp->length), 0);
			break;

		case PW_TYPE_OCTETS:
			if (RDEBUG_ENABLED) {
				char *hex;

				hex = fr_abin2hex(request, vp->vp_octets, vp->length);
				RDEBUG("$%s{'%s'} = &%s:%s -> 0x%s", hashname, vp->da->name,
				       list_name, vp->da->name, hex);
				talloc_free(hex);
			}
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn((char const *)vp->vp_octets, vp->length), 0);
			break;

		default:
			len = vp_prints_value(buffer, sizeof(buffer), vp, 0);
			RDEBUG("$%s{'%s'} = &%s:%s -> '%s'", hashname, vp->da->name,
			       list_name, vp->da->name, buffer);
			(void)hv_store(rad_hv, name, strlen(name),
				       newSVpvn(buffer, truncate_len(len, sizeof(buffer))), 0);
			break;
		}
	}
	REXDENT();
}