Пример #1
0
/*
 *	Run a virtual server auth and postauth
 *
 */
int rad_virtual_server(REQUEST *request)
{
	VALUE_PAIR *vp;
	int result;

	/*
	 *	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_AUTHENTICATION_REQUEST);

	result = rad_authenticate(request);

	if (request->reply->code == PW_AUTHENTICATION_REJECT) {
		pairdelete(&request->config_items, 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_AUTHENTICATION_ACK) {
		rad_postauth(request);
	}

	return result;
}
Пример #2
0
/*
 * This does the exact same thing as the mod_authorize, it's just called
 * differently.
 */
static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
{
	int rcode;
	char const *name = request->username->vp_strvalue;
	REALM *realm;

	if (!name)
	  return RLM_MODULE_OK;


	/*
	 *	Check if we've got to proxy the request.
	 *	If not, return without adding a Proxy-To-Realm
	 *	attribute.
	 */
	rcode = check_for_realm(instance, request, &realm);
	if (rcode != RLM_MODULE_UPDATED) return rcode;
	if (!realm) return RLM_MODULE_NOOP;

	/*
	 *	Maybe add a Proxy-To-Realm attribute to the request.
	 */
	RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
	       realm->name);
	pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);

	return RLM_MODULE_UPDATED; /* try the next module */
}
Пример #3
0
/*
 *	Run a virtual server auth and postauth
 *
 */
int rad_virtual_server(REQUEST *request)
{
	VALUE_PAIR *vp;
	int result;

	RDEBUG("server %s {", request->server);
	RDEBUG("  Request:");
	debug_pair_list(request->packet->vps);

	/*
	 *	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_items, 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);
	}

	RDEBUG("  Reply:");
	debug_pair_list(request->reply->vps);
	RDEBUG("} # server %s", request->server);

	return result;
}
Пример #4
0
/*
 *	If we have something to log, then we log it.
 *	Otherwise we return the retcode as soon as possible
 */
static int do_logging(REQUEST *request, char const *str, int rcode)
{
	char *expanded = NULL;

	if (!str || !*str) return rcode;

	if (radius_axlat(&expanded, request, str, NULL, NULL) < 0) {
		return rcode;
	}

	pairmake_config("Module-Success-Message", expanded, T_OP_SET);

	talloc_free(expanded);

	return rcode;
}
Пример #5
0
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
{
	if (!pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
		return RLM_MODULE_NOOP;
	}

	if (pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY) != NULL) {
		RWDEBUG2("Auth-Type already set.  Not setting to CHAP");
		return RLM_MODULE_NOOP;
	}

	RDEBUG("Setting 'Auth-Type := CHAP'");
	pairmake_config("Auth-Type", "CHAP", T_OP_EQ);

	return RLM_MODULE_OK;
}
Пример #6
0
/*
 *	CoA realms via Operator-Name.  Because the realm isn't in a
 *	User-Name, concepts like "prefix" and "suffix' don't matter.
 */
static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
{
	VALUE_PAIR *vp;
	REALM *realm;

	if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
		RDEBUG2("Request already proxied.  Ignoring.");
		return RLM_MODULE_OK;
	}

	vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
	if (!vp) return RLM_MODULE_NOOP;
	
	/*
	 *	Catch the case of broken dictionaries.
	 */
	if (vp->da->type != PW_TYPE_STRING) return RLM_MODULE_NOOP;

	/*
	 *	The string is too short.
	 */
	if (vp->length == 1) return RLM_MODULE_NOOP;

	/*
	 *	'1' means "the rest of the string is a realm"
	 */
	if (vp->vp_strvalue[0] != '1') return RLM_MODULE_NOOP;

	realm = realm_find(vp->vp_strvalue + 1);
	if (!realm) return RLM_MODULE_NOTFOUND;

	if (!realm->coa_pool) {
		RDEBUG2("CoA realm is LOCAL.");
		return RLM_MODULE_OK;
	}

	/*
	 *	Maybe add a Proxy-To-Realm attribute to the request.
	 */
	RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
	       realm->name);
	pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);

	return RLM_MODULE_UPDATED; /* try the next module */
}
Пример #7
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_authorize(void *instance, REQUEST *request)
{
	VALUE_PAIR *state;
	rlm_smsotp_t *inst = instance;

	/*
	 *  Look for the 'state' attribute.
	 */
	state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
	if (state != NULL) {
		DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",inst->authtype);

		pairdelete(&request->config, PW_AUTH_TYPE, 0, TAG_ANY); /* delete old auth-type */
		pairmake_config("Auth-Type", inst->authtype, T_OP_SET);
	}

	return RLM_MODULE_OK;
}
Пример #8
0
/*
 *  Examine a request for a username with an realm, and if it
 *  corresponds to something in the realms file, set that realm as
 *  Proxy-To.
 *
 *  This should very nearly duplicate the old proxy_send() code
 */
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
{
	rlm_rcode_t rcode;
	REALM *realm;

	/*
	 *	Check if we've got to proxy the request.
	 *	If not, return without adding a Proxy-To-Realm
	 *	attribute.
	 */
	rcode = check_for_realm(instance, request, &realm);
	if (rcode != RLM_MODULE_UPDATED) return rcode;
	if (!realm) return RLM_MODULE_NOOP;

	/*
	 *	Maybe add a Proxy-To-Realm attribute to the request.
	 */
	RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
	       realm->name);
	pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);

	return RLM_MODULE_UPDATED; /* try the next module */
}
Пример #9
0
/**
 * This function is called when a EAP_TNC_RESPONSE was received.
 * It basically forwards the EAP_TNC data to NAA-TNCS and forms
 * and appropriate EAP_RESPONSE. Furthermore, it sets the VlanID
 * based on the TNC_ConnectionState determined by NAA-TNCS.
 *
 * @param instance The configuration data.
 * @param handler The eap_handler_t.
 * @return True, if successfully, else false.
 */
static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
{
	TNC_ConnectionID conn_id;
	TNC_Result result;

	TNC_BufferReference data = NULL;
	TNC_UInt32 datalen = 0;

	TNC_ConnectionState connection_state;
	uint8_t code = 0;
	REQUEST *request = handler->request;

	if (handler->eap_ds->response->type.num != PW_EAP_TNC) {
		ERROR("rlm_eap_tnc: Incorrect response type");

		return 0;
	}

	/*
	 *	Retrieve connection ID
	 */
	conn_id = *((TNC_ConnectionID *) (handler->opaque));

	RDEBUG2("Starting authentication for connection ID %lX",
	       conn_id);

	/*
	 * 	Pass EAP_TNC data to NAA-EAP and get answer data
	 */
	connection_state = TNC_CONNECTION_STATE_CREATE;

	/*
	 * 	Forwards the eap_tnc data to NAA-EAP and gets the response
	 */
	result = processEAPTNCData(conn_id, handler->eap_ds->response->type.data,
				   handler->eap_ds->response->type.length,
				   &data, &datalen, &connection_state);
	if (result != TNC_RESULT_SUCCESS) {
		RDEBUG("NAA-EAP processEAPTNCData returned "
		       "an error code");

		return 0;
	}
	/*
	 *	Determine eap code for the response
	 */
	switch (connection_state) {
	case TNC_CONNECTION_STATE_HANDSHAKE:
		code = PW_EAP_REQUEST;

		break;
	case TNC_CONNECTION_STATE_ACCESS_NONE:
		code = PW_EAP_FAILURE;
		pairmake_config("TNC-Status", "None", T_OP_SET);

		break;

	case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
		code = PW_EAP_SUCCESS;
		pairmake_config("TNC-Status", "Access", T_OP_SET);
		break;

	case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
		code = PW_EAP_SUCCESS;
		pairmake_config("TNC-Status", "Isolate", T_OP_SET);

		break;
	default:
		ERROR("rlm_eap_tnc: Invalid connection state");

		return 0;
	}

	/*
	 *	Build the TNC EAP request
	 */
	handler->eap_ds->request->code = code;
	handler->eap_ds->request->type.num = PW_EAP_TNC;

	handler->eap_ds->request->type.length = datalen;

	talloc_free(handler->eap_ds->request->type.data);

	/*
	 *	"data" is not talloc'd memory.
	 */
	handler->eap_ds->request->type.data = talloc_array(handler->eap_ds->request,
							   uint8_t, datalen);
	memcpy(handler->eap_ds->request->type.data, data, datalen);
	free(data);

	return 1;
}
Пример #10
0
/*
 *	Generate a challenge to be presented to the user.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = (rlm_otp_t *) instance;

	char challenge[OTP_MAX_CHALLENGE_LEN + 1];	/* +1 for '\0' terminator */
	int auth_type_found;

	/* Early exit if Auth-Type != inst->name */
	{
		VALUE_PAIR *vp;

		auth_type_found = 0;
		vp = pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY);
		if (vp) {
			auth_type_found = 1;
			if (strcmp(vp->vp_strvalue, inst->name)) {
				return RLM_MODULE_NOOP;
			}
		}
	}

	/* The State attribute will be present if this is a response. */
	if (pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
		DEBUG("rlm_otp: autz: Found response to Access-Challenge");

		return RLM_MODULE_OK;
	}

	/* User-Name attribute required. */
	if (!request->username) {
		RWDEBUG("Attribute \"User-Name\" "
		       "required for authentication");

		return RLM_MODULE_INVALID;
	}

	if (otp_pwe_present(request) == 0) {
		RWDEBUG("Attribute "
			"\"User-Password\" or equivalent required "
			"for authentication");

		return RLM_MODULE_INVALID;
	}

	/*
	 * 	We used to check for special "challenge" and "resync" passcodes
	 * 	here, but these are complicated to explain and application is
	 * 	limited.  More importantly, since we've removed all actual OTP
	 * 	code (now we ask otpd), it's awkward for us to support them.
	 * 	Should the need arise to reinstate these options, the most
	 *	likely choice is to duplicate some otpd code here.
	 */
	if (inst->allow_sync && !inst->allow_async) {
		/* This is the token sync response. */
		if (!auth_type_found) {
			pairmake_config("Auth-Type", inst->name, T_OP_EQ);
		}

		return RLM_MODULE_OK;
	}

	/*
	 *	Generate a random challenge.
	 */
	otp_async_challenge(challenge, inst->challenge_len);

	/*
	 *	Create the State attribute, which will be returned to
	 *	us along with the response.
	 *
	 *	We will need this to verify the response.
	 *
	 *	It must be hmac protected to prevent insertion of arbitrary
	 *	State by an inside attacker.
	 *
	 *	If we won't actually use the State (server config doesn't
	 *	allow async), we just use a trivial State.
	 *
	 *	We always create at least a trivial State, so mod_authorize()
	 *	can quickly pass on to mod_authenticate().
	 */
	{
		int32_t now = htonl(time(NULL)); //!< Low-order 32 bits on LP64.

		char gen_state[OTP_MAX_RADSTATE_LEN];
		size_t len;
		VALUE_PAIR *vp;

		len = otp_gen_state(gen_state, challenge, inst->challenge_len,
				    0, now, inst->hmac_key);

		vp = paircreate(request->reply, PW_STATE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		pairmemcpy(vp, (uint8_t const *) gen_state, len);
		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Add the challenge to the reply.
	 */
	{
		VALUE_PAIR *vp;

		char *expanded = NULL;
		ssize_t len;

		/*
		 *	First add the internal OTP challenge attribute to
		 *	the reply list.
		 */
		vp = paircreate(request->reply, PW_OTP_CHALLENGE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		pairstrcpy(vp, challenge);
		vp->op = T_OP_SET;

		pairadd(&request->reply->vps, vp);

		/*
		 *	Then add the message to the user to they known
		 *	what the challenge value is.
		 */

		len = radius_axlat(&expanded, request, inst->chal_prompt, NULL, NULL);
		if (len < 0) {
			return RLM_MODULE_FAIL;
		}

		vp = paircreate(request->reply, PW_REPLY_MESSAGE, 0);
		if (!vp) {
			talloc_free(expanded);
			return RLM_MODULE_FAIL;
		}

		(void) talloc_steal(vp, expanded);
		vp->vp_strvalue = expanded;
		vp->length = len;
		vp->op = T_OP_SET;
		vp->type = VT_DATA;

		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Mark the packet as an Access-Challenge packet.
	 * 	The server will take care of sending it to the user.
	 */
	request->reply->code = PW_CODE_ACCESS_CHALLENGE;
	DEBUG("rlm_otp: Sending Access-Challenge");

	if (!auth_type_found) {
		pairmake_config("Auth-Type", inst->name, T_OP_EQ);
	}

	return RLM_MODULE_HANDLED;
}
Пример #11
0
/*
 *	member of the radius group?
 */
static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
{
	struct passwd *userdata = NULL;
	struct group *groupdata = NULL;
	int ismember = 0;
	RADCLIENT *rad_client = NULL;
	uuid_t uuid;
	uuid_t guid_sacl;
	uuid_t guid_nasgroup;
	int err;
	char host_ipaddr[128] = {0};
	
	if (!request || !request->username) {
		RDEBUG("OpenDirectory requires a User-Name attribute.");
		return RLM_MODULE_NOOP;
	}
	
	/* resolve SACL */
	uuid_clear(guid_sacl);
	groupdata = getgrnam(kRadiusSACLName);
	if (groupdata != NULL) {
		err = mbr_gid_to_uuid(groupdata->gr_gid, guid_sacl);
		if (err != 0) {
			ERROR("rlm_opendirectory: The group \"%s\" does not have a GUID.", kRadiusSACLName);
			return RLM_MODULE_FAIL;
		}		
	}
	else {
		RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName);
	}
	
	/* resolve client access list */
	uuid_clear(guid_nasgroup);

	rad_client = request->client;
#if 0
	if (rad_client->community[0] != '\0' )
	{
		/*
		 *	The "community" can be a GUID (Globally Unique ID) or
		 *	a group name
		 */
		if (uuid_parse(rad_client->community, guid_nasgroup) != 0) {
			/* attempt to resolve the name */
			groupdata = getgrnam(rad_client->community);
			if (!groupdata) {
				AUTH("rlm_opendirectory: The group \"%s\" does not exist on this system.", rad_client->community);
				return RLM_MODULE_FAIL;
			}
			err = mbr_gid_to_uuid(groupdata->gr_gid, guid_nasgroup);
			if (err != 0) {
				AUTH("rlm_opendirectory: The group \"%s\" does not have a GUID.", rad_client->community);
				return RLM_MODULE_FAIL;
			}
		}
	}
	else
#endif
	{
		if (!rad_client) {
			RDEBUG("The client record could not be found for host %s.",
					ip_ntoh(&request->packet->src_ipaddr,
						host_ipaddr, sizeof(host_ipaddr)));
		}
		else {
			RDEBUG("The host %s does not have an access group.",
					ip_ntoh(&request->packet->src_ipaddr,
						host_ipaddr, sizeof(host_ipaddr)));
		}
	}
	
	if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
		RDEBUG("no access control groups, all users allowed.");
		if (pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
			pairmake_config("Auth-Type", kAuthType, T_OP_EQ);
			RDEBUG("Setting Auth-Type = %s", kAuthType);
		}
		return RLM_MODULE_OK;
	}

	/* resolve user */
	uuid_clear(uuid);

	userdata = getpwnam(request->username->vp_strvalue);
	if (userdata != NULL) {
		err = mbr_uid_to_uuid(userdata->pw_uid, uuid);
		if (err != 0)
			uuid_clear(uuid);
	}
	
	if (uuid_is_null(uuid)) {
		REDEBUG("Could not get the user's uuid",
				   T_OP_EQ);
		return RLM_MODULE_NOTFOUND;
	}
	
	if (!uuid_is_null(guid_sacl)) {
		err = mbr_check_service_membership(uuid, kRadiusServiceName, &ismember);
		if (err != 0) {
			REDEBUG("Failed to check group membership", T_OP_EQ);
			return RLM_MODULE_FAIL;
		}
		
		if (ismember == 0) {
			REDEBUG("User is not authorized", T_OP_EQ);
			return RLM_MODULE_USERLOCK;
		}
	}
	
	if (!uuid_is_null(guid_nasgroup)) {
		err = mbr_check_membership_refresh(uuid, guid_nasgroup, &ismember);
		if (err != 0) {
			REDEBUG("Failed to check group membership", T_OP_EQ);
			return RLM_MODULE_FAIL;
		}
		
		if (ismember == 0) {
			REDEBUG("User is not authorized", T_OP_EQ);
			return RLM_MODULE_USERLOCK;
		}
	}
	
	if (pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
		pairmake_config("Auth-Type", kAuthType, T_OP_EQ);
		RDEBUG("Setting Auth-Type = %s", kAuthType);
	}

	return RLM_MODULE_OK;
}
Пример #12
0
/*
 * EAP authorization DEPENDS on other rlm authorizations,
 * to check for user existance & get their configured values.
 * It Handles EAP-START Messages, User-Name initilization.
 */
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
{
	rlm_eap_t	*inst;
	int		status;
	VALUE_PAIR	*vp;

	inst = (rlm_eap_t *)instance;

#ifdef WITH_PROXY
	/*
	 *	We don't do authorization again, once we've seen the
	 *	proxy reply (or the proxied packet)
	 */
	if (request->proxy != NULL)
		return RLM_MODULE_NOOP;
#endif

	/*
	 *	For EAP_START, send Access-Challenge with EAP Identity
	 *	request.  even when we have to proxy this request
	 *
	 *	RFC 2869, Section 2.3.1 notes that the "domain" of the
	 *	user, (i.e. where to proxy him) comes from the EAP-Identity,
	 *	so we CANNOT proxy the user, until we know his identity.
	 *
	 *	We therefore send an EAP Identity request.
	 */
	status = eap_start(inst, request);
	switch(status) {
	case EAP_NOOP:
		return RLM_MODULE_NOOP;
	case EAP_FAIL:
		return RLM_MODULE_FAIL;
	case EAP_FOUND:
		return RLM_MODULE_HANDLED;
	case EAP_OK:
	case EAP_NOTFOUND:
	default:
		break;
	}

	/*
	 *	RFC 2869, Section 2.3.1.  If a NAS sends an EAP-Identity,
	 *	it MUST copy the identity into the User-Name attribute.
	 *
	 *	But we don't worry about that too much.  We depend on
	 *	each EAP sub-module to look for handler->request->username,
	 *	and to get excited if it doesn't appear.
	 */
	vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
	if ((!vp) || (vp->vp_integer != PW_AUTHTYPE_REJECT)) {
		vp = pairmake_config("Auth-Type", inst->xlat_name, T_OP_EQ);
		if (!vp) {
			RDEBUG2("Failed to create Auth-Type %s: %s\n",
				inst->xlat_name, fr_strerror());
			return RLM_MODULE_FAIL;
		}
	} else {
		RDEBUG2W("Auth-Type already set.  Not setting to EAP");
	}

	if (status == EAP_OK) return RLM_MODULE_OK;

	return RLM_MODULE_UPDATED;
}
Пример #13
0
/** Convert group membership information into attributes
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;
	ldap_rcode_t status;
	int ldap_errno;

	LDAPMessage *result = NULL;
	LDAPMessage *entry;

	char base_dn[LDAP_MAX_DN_STR_LEN];

	char const *filters[] = { inst->groupobj_filter, inst->groupobj_membership_filter };
	char filter[LDAP_MAX_FILTER_STR_LEN + 1];

	char const *attrs[] = { inst->groupobj_name_attr, NULL };

	VALUE_PAIR *vp;
	char *dn;

	rad_assert(inst->groupobj_base_dn);

	if (!inst->groupobj_membership_filter) {
		RDEBUG2("Skipping caching group objects as directive 'group.membership_filter' is not set");

		return RLM_MODULE_OK;
	}

	if (rlm_ldap_xlat_filter(request,
				 filters, sizeof(filters) / sizeof(*filters),
				 filter, sizeof(filter)) < 0) {
		return RLM_MODULE_INVALID;
	}

	if (radius_xlat(base_dn, sizeof(base_dn), request, inst->groupobj_base_dn, rlm_ldap_escape_func, NULL) < 0) {
		REDEBUG("Failed creating base_dn");

		return RLM_MODULE_INVALID;
	}

	status = rlm_ldap_search(inst, request, pconn, base_dn, inst->groupobj_scope, filter, attrs, &result);
	switch (status) {
	case LDAP_PROC_SUCCESS:
		break;

	case LDAP_PROC_NO_RESULT:
		RDEBUG2("No cacheable group memberships found in group objects");

	default:
		goto finish;
	}

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

		goto finish;
	}

	do {
		if (inst->cacheable_group_dn) {
			dn = ldap_get_dn((*pconn)->handle, entry);
			if (!dn) {
				ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
				REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));

				goto finish;
			}
			rlm_ldap_normalise_dn(dn, dn);

			MEM(vp = pairmake_config(inst->cache_da->name, NULL, T_OP_ADD));
			pairstrcpy(vp, dn);

			RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, dn);
			ldap_memfree(dn);
		}

		if (inst->cacheable_group_name) {
			struct berval **values;

			values = ldap_get_values_len((*pconn)->handle, entry, inst->groupobj_name_attr);
			if (!values) continue;

			MEM(vp = pairmake_config(inst->cache_da->name, NULL, T_OP_ADD));
			pairstrncpy(vp, values[0]->bv_val, values[0]->bv_len);

			RDEBUG("Added control:%s with value \"%.*s\"", inst->cache_da->name,
			       (int)values[0]->bv_len, values[0]->bv_val);

			ldap_value_free_len(values);
		}
	} while ((entry = ldap_next_entry((*pconn)->handle, entry)));

finish:
	if (result) ldap_msgfree(result);

	return rcode;
}