/*
 *	See if the counter matches.
 */
static int counter_cmp(void *instance, UNUSED REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
		       UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
{
	rlm_counter_t *inst = instance;
	datum key_datum;
	datum count_datum;
	VALUE_PAIR *key_vp;
	rad_counter counter;

	/*
	 *	Find the key attribute.
	 */
	key_vp = pair_find_by_da(request, inst->key_attr, TAG_ANY);
	if (!key_vp) {
		return RLM_MODULE_NOOP;
	}

	ASSIGN(key_datum.dptr,key_vp->vp_strvalue);
	key_datum.dsize = key_vp->length;

	count_datum = gdbm_fetch(inst->gdbm, key_datum);

	if (!count_datum.dptr) {
		return -1;
	}
	memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
	free(count_datum.dptr);

	return counter.user_counter - check->vp_integer;
}
/*
 *	A lie!  It always returns!
 */
static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet, RADIUS_PACKET *reply)
{
	uint32_t hash;
	uint32_t value;
	rlm_sometimes_t *inst = instance;
	VALUE_PAIR *vp;

	/*
	 *	Set it to NOOP and the module will always do nothing
	 */
	if (inst->rcode == RLM_MODULE_NOOP) return inst->rcode;

	/*
	 *	Hash based on the given key.  Usually User-Name.
	 */
	vp = pair_find_by_da(packet->vps, inst->da, TAG_ANY);
	if (!vp) return RLM_MODULE_NOOP;

	hash = fr_hash(&vp->data, vp->vp_length);
	hash &= 0xff;		/* ensure it's 0..255 */
	value = hash;

	/*
	 *	Ranges are INCLUSIVE.
	 *	[start,end] returns "rcode"
	 *	Everything else returns "noop"
	 */
	if (value < inst->start) return RLM_MODULE_NOOP;
	if (value > inst->end) return RLM_MODULE_NOOP;

	/*
	 *	If we're returning "handled", then set the packet
	 *	code in the reply, so that the server responds.
	 */
	if ((inst->rcode == RLM_MODULE_HANDLED) && reply) {
		switch (packet->code) {
		case PW_CODE_ACCESS_REQUEST:
			reply->code = PW_CODE_ACCESS_ACCEPT;
			break;

		case PW_CODE_ACCOUNTING_REQUEST:
			reply->code = PW_CODE_ACCOUNTING_RESPONSE;
			break;

		case PW_CODE_COA_REQUEST:
			reply->code = PW_CODE_COA_ACK;
			break;

		case PW_CODE_DISCONNECT_REQUEST:
			reply->code = PW_CODE_DISCONNECT_ACK;
			break;

		default:
			break;
		}
	}

	return inst->rcode;
}
示例#3
0
/** Decrypt a Yubikey OTP AES block
 *
 * @param inst Module configuration.
 * @param passcode string to decrypt.
 * @return one of the RLM_RCODE_* constants.
 */
rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
{
	uint32_t counter;
	yubikey_token_st token;

	DICT_ATTR const *da;

	char private_id[(YUBIKEY_UID_SIZE * 2) + 1];
	VALUE_PAIR *key, *vp;

	da = dict_attrbyname("Yubikey-Key");
	if (!da) {
		REDEBUG("Dictionary missing entry for 'Yubikey-Key'");
		return RLM_MODULE_FAIL;
	}

	key = pair_find_by_da(request->config_items, da, TAG_ANY);
	if (!key) {
		REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
		return RLM_MODULE_INVALID;
	}

	if (key->length != YUBIKEY_KEY_SIZE) {
		REDEBUG("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->length);
		return RLM_MODULE_INVALID;
	}

	yubikey_parse((uint8_t const *) passcode + inst->id_len, key->vp_octets, &token);

	/*
	 *	Apparently this just uses byte offsets...
	 */
	if (!yubikey_crc_ok_p((uint8_t *) &token)) {
		REDEBUG("Decrypting OTP token data failed, rejecting");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("Token data decrypted successfully");

	if (request->log.lvl && request->log.func) {
		(void) fr_bin2hex((char *) &private_id, (uint8_t*) &token.uid, YUBIKEY_UID_SIZE);
		RDEBUG2("Private ID	: 0x%s", private_id);
		RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
		RDEBUG2("# used in session : %u", token.use);
		RDEBUG2("Token timestamp    : %u",
			(token.tstph << 16) | token.tstpl);
		RDEBUG2("Random data       : %u", token.rnd);
		RDEBUG2("CRC data          : 0x%x", token.crc);
	}

	/*
	 *	Private ID used for validation purposes
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Private-ID");

		return RLM_MODULE_FAIL;
	}
	pairmemcpy(vp, token.uid, YUBIKEY_UID_SIZE);

	/*
	 *	Token timestamp
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Timestamp");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = (token.tstph << 16) | token.tstpl;
	vp->length = 4;

	/*
	 *	Token random
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Random");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = token.rnd;
	vp->length = 4;

	/*
	 *	Combine the two counter fields together so we can do
	 *	replay attack checks.
	 */
	counter = (yubikey_counter(token.ctr) << 16) | token.use;

	vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
	if (!vp) {
		REDEBUG("Failed creating Yubikey-Counter");

		return RLM_MODULE_FAIL;
	}
	vp->vp_integer = counter;
	vp->length = 4;

	/*
	 *	Now we check for replay attacks
	 */
	vp = pair_find_by_da(request->config_items, da, TAG_ANY);
	if (!vp) {
		RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks");
		return RLM_MODULE_OK;
	}

	if (counter <= vp->vp_integer) {
		REDEBUG("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u",
			counter, vp->vp_integer);
		return RLM_MODULE_REJECT;
	}

	return RLM_MODULE_OK;
}
/*
 *	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(UNUSED void *instance, UNUSED REQUEST *request)
{
	rlm_counter_t *inst = instance;
	rlm_rcode_t rcode = RLM_MODULE_NOOP;
	datum key_datum;
	datum count_datum;
	rad_counter counter;
	VALUE_PAIR *key_vp, *check_vp;
	VALUE_PAIR *reply_item;
	char msg[128];

	/*
	 *	Before doing anything else, see if we have to reset
	 *	the counters.
	 */
	if (inst->reset_time && (inst->reset_time <= request->timestamp)) {
		rlm_rcode_t rcode2;

		inst->last_reset = inst->reset_time;
		find_next_reset(inst,request->timestamp);
		pthread_mutex_lock(&inst->mutex);
		rcode2 = reset_db(inst);
		pthread_mutex_unlock(&inst->mutex);
		if (rcode2 != RLM_MODULE_OK) {
			return rcode2;
		}
	}


	/*
	 *      Look for the key.  User-Name is special.  It means
	 *      The REAL username, after stripping.
	 */
	DEBUG2("rlm_counter: Entering module authorize code");
	key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
		 pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
	if (!key_vp) {
		DEBUG2("rlm_counter: Could not find Key value pair");
		return rcode;
	}

	/*
	 *      Look for the check item
	 */
	if ((check_vp = pair_find_by_da(request->config_items, inst->check_attr, TAG_ANY)) == NULL) {
		DEBUG2("rlm_counter: Could not find Check item value pair");
		return rcode;
	}

	ASSIGN(key_datum.dptr, key_vp->vp_strvalue);
	key_datum.dsize = key_vp->length;


	/*
	 * Init to be sure
	 */

	counter.user_counter = 0;

	DEBUG("rlm_counter: Searching the database for key '%s'",key_vp->vp_strvalue);
	pthread_mutex_lock(&inst->mutex);
	count_datum = gdbm_fetch(inst->gdbm, key_datum);
	pthread_mutex_unlock(&inst->mutex);
	if (count_datum.dptr != NULL) {
		DEBUG("rlm_counter: Key Found");
		memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
		free(count_datum.dptr);
	}
	else
		DEBUG("rlm_counter: Could not find the requested key in the database");

	/*
	 * Check if check item > counter
	 */
	DEBUG("rlm_counter: Check item = %d, Count = %d",check_vp->vp_integer,counter.user_counter);
	if (check_vp->vp_integer > counter.user_counter) {
		unsigned int res;

		res = check_vp->vp_integer - counter.user_counter;

		DEBUG("rlm_counter: res is greater than zero");
		if (inst->count_attr->attr == PW_ACCT_SESSION_TIME) {
			/*
			 * Do the following only if the count attribute is
			 * AcctSessionTime
			 */

			/*
			*	We are assuming that simultaneous-use=1. But
			*	even if that does not happen then our user
			*	could login at max for 2*max-usage-time Is
			*	that acceptable?
			*/

			/*
			*	User is allowed, but set Session-Timeout.
			*	Stolen from main/auth.c
			*/

			/*
			*	If we are near a reset then add the next
			*	limit, so that the user will not need to
			*	login again
			*	Before that set the return value to the time
			*	remaining to next reset
			*/
			if (inst->reset_time && (res >= (inst->reset_time - request->timestamp))) {
				res = inst->reset_time - request->timestamp;
				res += check_vp->vp_integer;
			}

			reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
			if (reply_item) {
				if (reply_item->vp_integer > res) {
					reply_item->vp_integer = res;
				}
			} else {
				reply_item = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
				reply_item->vp_integer = res;
			}
		} else if (inst->reply_attr) {
			reply_item = pair_find_by_da(request->reply->vps, inst->reply_attr, TAG_ANY);
			if (reply_item) {
				if (reply_item->vp_integer > res) {
					reply_item->vp_integer = res;
				}
			} else {
				reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr,
							       inst->reply_attr->vendor);
				reply_item->vp_integer = res;
			}
		}

		rcode = RLM_MODULE_OK;

		DEBUG2("rlm_counter: (Check item - counter) is greater than zero");
		DEBUG2("rlm_counter: Authorized user %s, check_item=%d, counter=%d",
				key_vp->vp_strvalue,check_vp->vp_integer,counter.user_counter);
		DEBUG2("rlm_counter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d", key_vp->vp_strvalue,res);
	} else {
		/*
		 * User is denied access, send back a reply message
		*/
		sprintf(msg, "Your maximum %s usage time has been reached", inst->reset);
		pairmake_reply("Reply-Message", msg, T_OP_EQ);

		REDEBUG("Maximum %s usage time reached", inst->reset);
		rcode = RLM_MODULE_REJECT;

		DEBUG2("rlm_counter: Rejected user %s, check_item=%d, counter=%d",
				key_vp->vp_strvalue,check_vp->vp_integer,counter.user_counter);
	}

	return rcode;
}
/*
 *	Write accounting information to this modules database.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
{
	rlm_counter_t *inst = instance;
	datum key_datum;
	datum count_datum;
	VALUE_PAIR *key_vp, *count_vp, *proto_vp, *uniqueid_vp;
	rad_counter counter;
	rlm_rcode_t rcode;
	int ret;
	int acctstatustype = 0;
	time_t diff;

	if ((key_vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL)
		acctstatustype = key_vp->vp_integer;
	else {
		DEBUG("rlm_counter: Could not find account status type in packet");
		return RLM_MODULE_NOOP;
	}
	if (acctstatustype != PW_STATUS_STOP) {
		DEBUG("rlm_counter: We only run on Accounting-Stop packets");
		return RLM_MODULE_NOOP;
	}
	uniqueid_vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0, TAG_ANY);
	if (uniqueid_vp != NULL)
		DEBUG("rlm_counter: Packet Unique ID = '%s'",uniqueid_vp->vp_strvalue);

	/*
	 *	Before doing anything else, see if we have to reset
	 *	the counters.
	 */
	if (inst->reset_time && (inst->reset_time <= request->timestamp)) {
		DEBUG("rlm_counter: Time to reset the database");
		inst->last_reset = inst->reset_time;
		find_next_reset(inst,request->timestamp);
		pthread_mutex_lock(&inst->mutex);
		rcode = reset_db(inst);
		pthread_mutex_unlock(&inst->mutex);
		if (rcode != RLM_MODULE_OK)
			return rcode;
	}
	/*
	 * Check if we need to watch out for a specific service-type. If yes then check it
	 */
	if (inst->service_type != NULL) {
		if ((proto_vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY)) == NULL) {
			DEBUG("rlm_counter: Could not find Service-Type attribute in the request. Returning NOOP");
			return RLM_MODULE_NOOP;
		}
		if ((unsigned)proto_vp->vp_integer != inst->service_val) {
			DEBUG("rlm_counter: This Service-Type is not allowed. Returning NOOP");
			return RLM_MODULE_NOOP;
		}
	}
	/*
	 * Check if request->timestamp - {Acct-Delay-Time} < last_reset
	 * If yes reject the packet since it is very old
	 */
	key_vp = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
	if (key_vp != NULL) {
		if ((key_vp->vp_integer != 0) && (request->timestamp - (time_t) key_vp->vp_integer) < inst->last_reset) {
			DEBUG("rlm_counter: This packet is too old. Returning NOOP");
			return RLM_MODULE_NOOP;
		}
	}



	/*
	 *	Look for the key.  User-Name is special.  It means
	 *	The REAL username, after stripping.
	 */
	key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
					pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
	if (!key_vp) {
		DEBUG("rlm_counter: Could not find the key-attribute in the request. Returning NOOP");
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Look for the attribute to use as a counter.
	 */
	count_vp = pair_find_by_da(request->packet->vps, inst->count_attr, TAG_ANY);
	if (!count_vp) {
		DEBUG("rlm_counter: Could not find the count_attribute in the request");
		return RLM_MODULE_NOOP;
	}

	ASSIGN(key_datum.dptr, key_vp->vp_strvalue);
	key_datum.dsize = key_vp->length;

	DEBUG("rlm_counter: Searching the database for key '%s'",key_vp->vp_strvalue);
	pthread_mutex_lock(&inst->mutex);
	count_datum = gdbm_fetch(inst->gdbm, key_datum);
	if (!count_datum.dptr) {
		DEBUG("rlm_counter: Could not find the requested key in the database");
		counter.user_counter = 0;
		if (uniqueid_vp != NULL)
			strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue, sizeof(counter.uniqueid));
		else
			memset((char *)counter.uniqueid,0,UNIQUEID_MAX_LEN);
	} else {
		DEBUG("rlm_counter: Key found");
		memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
		free(count_datum.dptr);
		DEBUG("rlm_counter: Counter Unique ID = '%s'",counter.uniqueid);
		if (uniqueid_vp != NULL) {
			if (strncmp(uniqueid_vp->vp_strvalue,counter.uniqueid, UNIQUEID_MAX_LEN - 1) == 0) {
				DEBUG("rlm_counter: Unique IDs for user match. Droping the request");
				pthread_mutex_unlock(&inst->mutex);
				return RLM_MODULE_NOOP;
			}
			strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue, sizeof(counter.uniqueid));
		}
		DEBUG("rlm_counter: User=%s, Counter=%d.",request->username->vp_strvalue,counter.user_counter);
	}

	if (inst->count_attr->attr == PW_ACCT_SESSION_TIME) {
		/*
		 *	If session time < diff then the user got in after the
		 *	last reset. So add his session time, otherwise add the
		 *	diff.
		 *
		 *	That way if he logged in at 23:00 and we reset the
		 *	daily counter at 24:00 and he logged out at 01:00
		 *	then we will only count one hour (the one in the new
		 *	day). That is the right thing
		 */
		diff = request->timestamp - inst->last_reset;
		counter.user_counter += ((time_t) count_vp->vp_integer < diff) ? count_vp->vp_integer : diff;

	} else if (count_vp->da->type == PW_TYPE_INTEGER) {
		/*
		 *	Integers get counted, without worrying about
		 *	reset dates.
		 */
		counter.user_counter += count_vp->vp_integer;

	} else {
		/*
		 *	The attribute is NOT an integer, just count once
		 *	more that we've seen it.
		 */
		counter.user_counter++;
	}

	DEBUG("rlm_counter: User=%s, New Counter=%d.",request->username->vp_strvalue,counter.user_counter);
	count_datum.dptr = (char *) &counter;
	count_datum.dsize = sizeof(rad_counter);

	DEBUG("rlm_counter: Storing new value in database");
	ret = gdbm_store(inst->gdbm, key_datum, count_datum, GDBM_REPLACE);
	pthread_mutex_unlock(&inst->mutex);
	if (ret < 0) {
		ERROR("rlm_counter: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
		return RLM_MODULE_FAIL;
	}
	DEBUG("rlm_counter: New value stored successfully");

	return RLM_MODULE_OK;
}
/*
 *	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)
{
	rlm_sqlcounter_t *inst = instance;
	int rcode = RLM_MODULE_NOOP;
	uint64_t counter, res;
	DICT_ATTR const *da;
	VALUE_PAIR *key_vp, *limit;
	VALUE_PAIR *reply_item;
	char msg[128];

	char query[MAX_QUERY_LEN], subst[MAX_QUERY_LEN];
	char *expanded = NULL;

	size_t len;

	/*
	 *	Before doing anything else, see if we have to reset
	 *	the counters.
	 */
	if (inst->reset_time && (inst->reset_time <= request->timestamp)) {
		/*
		 *	Re-set the next time and prev_time for this counters range
		 */
		inst->last_reset = inst->reset_time;
		find_next_reset(inst,request->timestamp);
	}

	/*
	 *      Look for the key.  User-Name is special.  It means
	 *      The REAL username, after stripping.
	 */
	if ((inst->key_attr->vendor == 0) && (inst->key_attr->attr == PW_USER_NAME)) {
		key_vp = request->username;
	} else {
		key_vp = pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
	}
	if (!key_vp) {
		RWDEBUG2("Couldn't find key attribute, request:%s, doing nothing...", inst->key_attr->name);
		return rcode;
	}

	/*
	 *      Look for the check item
	 */
	if ((da = dict_attrbyname(inst->limit_name)) == NULL) {
		return rcode;
	}

	limit = pair_find_by_da(request->config, da, TAG_ANY);
	if (limit == NULL) {
		/* Yes this really is 'check' as distinct from control */
		RWDEBUG2("Couldn't find check attribute, control:%s, doing nothing...", inst->limit_name);
		return rcode;
	}

	/* First, expand %k, %b and %e in query */
	if (sqlcounter_expand(subst, sizeof(subst), inst->query, inst) <= 0) {
		REDEBUG("Insufficient query buffer space");

		return RLM_MODULE_FAIL;
	}

	/* Then combine that with the name of the module were using to do the query */
	len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, subst);
	if (len >= (sizeof(query) - 1)) {
		REDEBUG("Insufficient query buffer space");

		return RLM_MODULE_FAIL;
	}

	/* Finally, xlat resulting SQL query */
	if (radius_axlat(&expanded, request, query, NULL, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}
	talloc_free(expanded);

	if (sscanf(expanded, "%" PRIu64, &counter) != 1) {
		RDEBUG2("No integer found in result string \"%s\".  May be first session, setting counter to 0",
			expanded);
		counter = 0;
	}

	/*
	 *	Check if check item > counter
	 */
	if (limit->vp_integer64 <= counter) {
		/* User is denied access, send back a reply message */
		snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", inst->reset);
		pairmake_reply("Reply-Message", msg, T_OP_EQ);

		REDEBUG2("Maximum %s usage time reached", inst->reset);
		REDEBUG2("Rejecting user, &control:%s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
			 inst->limit_name, limit->vp_integer64, counter);

		return RLM_MODULE_REJECT;
	}

	res = limit->vp_integer64 - counter;
	RDEBUG2("Allowing user, &control:%s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")",
		inst->limit_name, limit->vp_integer64, counter);
	/*
	 *	We are assuming that simultaneous-use=1. But
	 *	even if that does not happen then our user
	 *	could login at max for 2*max-usage-time Is
	 *	that acceptable?
	 */

	/*
	 *	If we are near a reset then add the next
	 *	limit, so that the user will not need to login
	 *	again.  Do this only for Session-Timeout.
	 */
	if (((inst->reply_attr->vendor == 0) && (inst->reply_attr->attr == PW_SESSION_TIMEOUT)) &&
	    inst->reset_time && (res >= (uint64_t)(inst->reset_time - request->timestamp))) {
	    	uint64_t to_reset = inst->reset_time - request->timestamp;

		RDEBUG2("Time remaining (%" PRIu64 "s) is greater than time to reset (%" PRIu64 "s).  "
			"Adding %" PRIu64 "s to reply value", to_reset, res, to_reset);
		res = to_reset + limit->vp_integer;
	}

	/*
	 *	Limit the reply attribute to the minimum of the existing value, or this new one.
	 */
	reply_item = pair_find_by_da(request->reply->vps, inst->reply_attr, TAG_ANY);
	if (reply_item) {
		if (reply_item->vp_integer64 <= res) {
			RDEBUG2("Leaving existing &reply:%s value of %" PRIu64, inst->reply_attr->name,
				reply_item->vp_integer64);

			return RLM_MODULE_OK;
		}
	} else {
		reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr,
					       inst->reply_attr->vendor);
	}
	reply_item->vp_integer64 = res;

	RDEBUG2("Setting &reply:%s value to %" PRIu64, inst->reply_name, reply_item->vp_integer64);

	return RLM_MODULE_OK;
}