Beispiel #1
0
static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
	rlm_rcode_t		rcode = RLM_MODULE_OK;
	rlm_sql_handle_t 	*handle = NULL;
	rlm_sql_t		*inst = instance;
	rlm_sql_row_t		row;
	int			check = 0;
	uint32_t		ipno = 0;
	char const     		*call_num = NULL;
	VALUE_PAIR		*vp;
	int			ret;
	uint32_t		nas_addr = 0;
	int			nas_port = 0;

	char 			*expanded = NULL;

	/* If simul_count_query is not defined, we don't do any checking */
	if (!inst->config->simul_count_query || (inst->config->simul_count_query[0] == '\0')) {
		return RLM_MODULE_NOOP;
	}

	if((!request->username) || (request->username->length == '\0')) {
		REDEBUG("Zero Length username not permitted");

		return RLM_MODULE_INVALID;
	}


	if(sql_set_user(inst, request, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	if (radius_axlat(&expanded, request, inst->config->simul_count_query, sql_escape_func, inst) < 0) {
		return RLM_MODULE_FAIL;
	}

	/* initialize the sql socket */
	handle = sql_get_socket(inst);
	if (!handle) {
		talloc_free(expanded);
		return RLM_MODULE_FAIL;
	}

	if (rlm_sql_select_query(&handle, inst, expanded)) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	ret = rlm_sql_fetch_row(&handle, inst);
	if (ret != 0) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	row = handle->row;
	if (!row) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	request->simul_count = atoi(row[0]);

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

	if(request->simul_count < request->simul_max) {
		rcode = RLM_MODULE_OK;
		goto finish;
	}

	/*
	 *	Looks like too many sessions, so let's start verifying
	 *	them, unless told to rely on count query only.
	 */
	if (!inst->config->simul_verify_query || (inst->config->simul_verify_query[0] == '\0')) {
		rcode = RLM_MODULE_OK;

		goto finish;
	}

	if (radius_axlat(&expanded, request, inst->config->simul_verify_query, sql_escape_func, inst) < 0) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	if(rlm_sql_select_query(&handle, inst, expanded)) {
		goto finish;
	}

	/*
	 *      Setup some stuff, like for MPP detection.
	 */
	request->simul_count = 0;

	if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
		ipno = vp->vp_ipaddr;
	}

	if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
		call_num = vp->vp_strvalue;
	}

	while (rlm_sql_fetch_row(&handle, inst) == 0) {
		row = handle->row;
		if (!row) {
			break;
		}

		if (!row[2]){
			RDEBUG("Cannot zap stale entry. No username present in entry");
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (!row[1]){
			RDEBUG("Cannot zap stale entry. No session id in entry");
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (row[3]) {
			nas_addr = inet_addr(row[3]);
		}

		if (row[4]) {
			nas_port = atoi(row[4]);
		}

		check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
		if (check == 0) {
			/*
			 *	Stale record - zap it.
			 */
			if (inst->config->deletestalesessions == true) {
				uint32_t framed_addr = 0;
				char proto = 0;
				int sess_time = 0;

				if (row[5])
					framed_addr = inet_addr(row[5]);
				if (row[7]){
					if (strcmp(row[7], "PPP") == 0)
						proto = 'P';
					else if (strcmp(row[7], "SLIP") == 0)
						proto = 'S';
				}
				if (row[8])
					sess_time = atoi(row[8]);
				session_zap(request, nas_addr, nas_port,
					    row[2], row[1], framed_addr,
					    proto, sess_time);
			}
		}
		else if (check == 1) {
			/*
			 *	User is still logged in.
			 */
			++request->simul_count;

			/*
			 *      Does it look like a MPP attempt?
			 */
			if (row[5] && ipno && inet_addr(row[5]) == ipno) {
				request->simul_mpp = 2;
			} else if (row[6] && call_num && !strncmp(row[6],call_num,16)) {
				request->simul_mpp = 2;
			}
		} else {
			/*
			 *      Failed to check the terminal server for
			 *      duplicate logins: return an error.
			 */
			REDEBUG("Failed to check the terminal server for user '%s'.", row[2]);

			rcode = RLM_MODULE_FAIL;
			goto finish;
		}
	}

	finish:

	(inst->module->sql_finish_select_query)(handle, inst->config);
	sql_release_socket(inst, handle);
	talloc_free(expanded);

	/*
	 *	The Auth module apparently looks at request->simul_count,
	 *	not the return value of this module when deciding to deny
	 *	a call for too many sessions.
	 */
	return rcode;
}
/*
 *	See if a user is already logged in. Sets request->simul_count to the
 *	current session count for this user and sets request->simul_mpp to 2
 *	if it looks like a multilink attempt based on the requested IP
 *	address, otherwise leaves request->simul_mpp alone.
 *
 *	Check twice. If on the first pass the user exceeds his
 *	max. number of logins, do a second pass and validate all
 *	logins by querying the terminal server (using eg. SNMP).
 */
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
{
	rlm_rcode_t	rcode = RLM_MODULE_OK;
	struct radutmp	u;
	int		fd = -1;
	VALUE_PAIR	*vp;
	uint32_t	ipno = 0;
	char const     	*call_num = NULL;
	rlm_radutmp_t	*inst = instance;

	char		*expanded = NULL;
	ssize_t		len;

	/*
	 *	Get the filename, via xlat.
	 */
	if (radius_axlat(&expanded, request, inst->filename, NULL, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	fd = open(expanded, O_RDWR);
	if (fd < 0) {
		/*
		 *	If the file doesn't exist, then no users
		 *	are logged in.
		 */
		if (errno == ENOENT) {
			request->simul_count=0;
			return RLM_MODULE_OK;
		}

		/*
		 *	Error accessing the file.
		 */
		ERROR("rlm_radumtp: Error accessing file %s: %s", expanded, strerror(errno));

		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	TALLOC_FREE(expanded);

	len = radius_axlat(&expanded, request, inst->username, NULL, NULL);
	if (len < 0) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	if (!len) {
		rcode = RLM_MODULE_NOOP;

		goto finish;
	}

	/*
	 *	WTF?  This is probably wrong... we probably want to
	 *	be able to check users across multiple session accounting
	 *	methods.
	 */
	request->simul_count = 0;

	/*
	 *	Loop over utmp, counting how many people MAY be logged in.
	 */
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		if (((strncmp(expanded, u.login, RUT_NAMESIZE) == 0) ||
		    (!inst->case_sensitive && (strncasecmp(expanded, u.login, RUT_NAMESIZE) == 0))) &&
		     (u.type == P_LOGIN)) {
			++request->simul_count;
		}
	}

	/*
	 *	The number of users logged in is OK,
	 *	OR, we've been told to not check the NAS.
	 */
	if ((request->simul_count < request->simul_max) || !inst->check_nas) {
		rcode = RLM_MODULE_OK;

		goto finish;
	}
	lseek(fd, (off_t)0, SEEK_SET);

	/*
	 *	Setup some stuff, like for MPP detection.
	 */
	if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
		ipno = vp->vp_ipaddr;
	}

	if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
		call_num = vp->vp_strvalue;
	}

	/*
	 *	lock the file while reading/writing.
	 */
	rad_lockfd(fd, LOCK_LEN);

	/*
	 *	FIXME: If we get a 'Start' for a user/nas/port which is
	 *	listed, but for which we did NOT get a 'Stop', then
	 *	it's not a duplicate session.  This happens with
	 *	static IP's like DSL.
	 */
	request->simul_count = 0;
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		if (((strncmp(expanded, u.login, RUT_NAMESIZE) == 0) || (!inst->case_sensitive &&
		    (strncasecmp(expanded, u.login, RUT_NAMESIZE) == 0))) && (u.type == P_LOGIN)) {
			char session_id[sizeof(u.session_id) + 1];
			char utmp_login[sizeof(u.login) + 1];

			strlcpy(session_id, u.session_id, sizeof(session_id));

			/*
			 *	The login name MAY fill the whole field,
			 *	and thus won't be zero-filled.
			 *
			 *	Note that we take the user name from
			 *	the utmp file, as that's the canonical
			 *	form.  The 'login' variable may contain
			 *	a string which is an upper/lowercase
			 *	version of u.login.  When we call the
			 *	routine to check the terminal server,
			 *	the NAS may be case sensitive.
			 *
			 *	e.g. We ask if "bob" is using a port,
			 *	and the NAS says "no", because "BOB"
			 *	is using the port.
			 */
			memset(utmp_login, 0, sizeof(utmp_login));
			memcpy(utmp_login, u.login, sizeof(u.login));

			/*
			 *	rad_check_ts may take seconds
			 *	to return, and we don't want
			 *	to block everyone else while
			 *	that's happening.  */
			rad_unlockfd(fd, LOCK_LEN);
			rcode = rad_check_ts(u.nas_address, u.nas_port, utmp_login, session_id);
			rad_lockfd(fd, LOCK_LEN);

			if (rcode == 0) {
				/*
				 *	Stale record - zap it.
				 */
				session_zap(request, u.nas_address, u.nas_port, expanded, session_id,
					    u.framed_address, u.proto, 0);
			}
			else if (rcode == 1) {
				/*
				 *	User is still logged in.
				 */
				++request->simul_count;

				/*
				 *	Does it look like a MPP attempt?
				 */
				if (strchr("SCPA", u.proto) && ipno && u.framed_address == ipno) {
					request->simul_mpp = 2;
				} else if (strchr("SCPA", u.proto) && call_num && !strncmp(u.caller_id, call_num,16)) {
					request->simul_mpp = 2;
				}
			} else {
				RWDEBUG("Failed to check the terminal server for user '%s'.", utmp_login);
				rcode = RLM_MODULE_FAIL;

				goto finish;
			}
		}
	}
	finish:

	talloc_free(expanded);

	if (fd > -1) {
		close(fd);		/* and implicitely release the locks */
	}

	return rcode;
}
Beispiel #3
0
static int rlm_sql_checksimul(void *instance, REQUEST * request) {
	SQLSOCK 	*sqlsocket;
	SQL_INST	*inst = instance;
	SQL_ROW		row;
	char		querystr[MAX_QUERY_LEN];
	char		sqlusername[MAX_STRING_LEN];
	int		check = 0;
        uint32_t        ipno = 0;
        char            *call_num = NULL;
	VALUE_PAIR      *vp;
	int		ret;
	uint32_t	nas_addr = 0;
	int		nas_port = 0;

	/* If simul_count_query is not defined, we don't do any checking */
	if (inst->config->simul_count_query[0] == 0) {
		return RLM_MODULE_NOOP;
	}

	if((request->username == NULL) || (request->username->length == 0)) {
		radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name);
		return RLM_MODULE_INVALID;
	}


	if(sql_set_user(inst, request, sqlusername, NULL) < 0)
		return RLM_MODULE_FAIL;

	radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);

	/* initialize the sql socket */
	sqlsocket = sql_get_socket(inst);
	if(sqlsocket == NULL)
		return RLM_MODULE_FAIL;

	if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
		radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_FAIL;
	}

	ret = rlm_sql_fetch_row(sqlsocket, inst);

	if (ret != 0) {
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_FAIL;
	}

	row = sqlsocket->row;
	if (row == NULL) {
		(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_FAIL;
	}

	request->simul_count = atoi(row[0]);
	(inst->module->sql_finish_select_query)(sqlsocket, inst->config);

	if(request->simul_count < request->simul_max) {
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_OK;
	}

	/* Looks like too many sessions, so lets start verifying them */

	if (inst->config->simul_verify_query[0] == 0) {
		/* No verify query defined, so skip verify step and rely on count query only */
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_OK;
	}

	radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
	if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
		radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name);
		sql_release_socket(inst, sqlsocket);
		return RLM_MODULE_FAIL;
	}

        /*
         *      Setup some stuff, like for MPP detection.
         */
	request->simul_count = 0;

        if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
                ipno = vp->lvalue;
        if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
                call_num = vp->strvalue;


	while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
		row = sqlsocket->row;
		if (row == NULL)
			break;
		if (!row[2]){
			(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
			sql_release_socket(inst, sqlsocket);
			DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
			return RLM_MODULE_FAIL;
		}
		if (!row[1]){
			(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
			sql_release_socket(inst, sqlsocket);
			DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
			return RLM_MODULE_FAIL;
		}
		if (row[3])
			nas_addr = inet_addr(row[3]);
		if (row[4])
			nas_port = atoi(row[4]);

		check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);

                /*
                 *      Failed to check the terminal server for
                 *      duplicate logins: Return an error.
                 */
		if (check < 0) {
			(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
			sql_release_socket(inst, sqlsocket);
			DEBUG("rlm_sql (%s) rad_check_ts() failed.",
			      inst->config->xlat_name);
			return RLM_MODULE_FAIL;
		}

		if(check == 1) {
			++request->simul_count;

                        /*
                         *      Does it look like a MPP attempt?
                         */
                        if (row[5] && ipno && inet_addr(row[5]) == ipno)
                                request->simul_mpp = 2;
                        else if (row[6] && call_num &&
                                !strncmp(row[6],call_num,16))
                                request->simul_mpp = 2;
		}
		else {
                        /*
                         *      Stale record - zap it.
                         */
			uint32_t framed_addr = 0;
			char proto = 'P';

			if (row[5])
				framed_addr = inet_addr(row[5]);
			if (row[7])
				if (strcmp(row[7],"SLIP") == 0)
					proto = 'S';

			session_zap(request,
				    nas_addr,nas_port,row[2],row[1],
				    framed_addr, proto);
		}
	}

	(inst->module->sql_finish_select_query)(sqlsocket, inst->config);
	sql_release_socket(inst, sqlsocket);

	/* The Auth module apparently looks at request->simul_count, not the return value
	   of this module when deciding to deny a call for too many sessions */
	return RLM_MODULE_OK;

}
/** Check if a given user is already logged in.
 *
 * Process accounting data to determine if a user is already logged in. Sets request->simul_count
 * to the current session count for this user.
 *
 * Check twice. If on the first pass the user exceeds his maximum number of logins, do a second
 * pass and validate all logins by querying the terminal server.
 *
 * @param instance The module instance.
 * @param request  The checksimul request object.
 * @return Returns operation status (@p rlm_rcode_t).
 */
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
	rlm_couchbase_t *inst = instance;      /* our module instance */
	rlm_rcode_t rcode = RLM_MODULE_OK;     /* return code */
	rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
	char vpath[256], vkey[MAX_KEY_SIZE];   /* view path and query key */
	char docid[MAX_KEY_SIZE];              /* document id returned from view */
	char error[512];                       /* view error return */
	int idx = 0;                           /* row array index counter */
	char element[MAX_KEY_SIZE];            /* mapped radius attribute to element name */
	lcb_error_t cb_error = LCB_SUCCESS;    /* couchbase error holder */
	json_object *json, *jval;              /* json object holders */
	json_object *jrows = NULL;             /* json object to hold view rows */
	VALUE_PAIR *vp;                        /* value pair */
	uint32_t client_ip_addr = 0;           /* current client ip address */
	char const *client_cs_id = NULL;       /* current client calling station id */
	char *user_name = NULL;                /* user name from accounting document */
	char *session_id = NULL;               /* session id from accounting document */
	char *cs_id = NULL;                    /* calling station id from accounting document */
	uint32_t nas_addr = 0;                 /* nas address from accounting document */
	uint32_t nas_port = 0;                 /* nas port from accounting document */
	uint32_t framed_ip_addr = 0;           /* framed ip address from accounting document */
	char framed_proto = 0;                 /* framed proto from accounting document */
	int session_time = 0;                  /* session time from accounting document */

	/* do nothing if this is not enabled */
	if (inst->check_simul != true) {
		RDEBUG3("mod_checksimul returning noop - not enabled");
		return RLM_MODULE_NOOP;
	}

	/* ensure valid username in request */
	if ((!request->username) || (request->username->vp_length == '\0')) {
		RDEBUG3("mod_checksimul - invalid username");
		return RLM_MODULE_INVALID;
	}

	/* attempt to build view key */
	if (radius_xlat(vkey, sizeof(vkey), request, inst->simul_vkey, NULL, NULL) < 0) {
		/* log error */
		RERROR("could not find simultaneous use view key attribute (%s) in packet", inst->simul_vkey);
		/* return */
		return RLM_MODULE_FAIL;
	}

	/* get handle */
	handle = fr_connection_get(inst->pool);

	/* check handle */
	if (!handle) return RLM_MODULE_FAIL;

	/* set couchbase instance */
	lcb_t cb_inst = handle->handle;

	/* set cookie */
	cookie_t *cookie = handle->cookie;

	/* build view path */
	snprintf(vpath, sizeof(vpath), "%s?key=\"%s\"&stale=update_after",
		 inst->simul_view, vkey);

	/* query view for document */
	cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);

	/* check error and object */
	if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
		/* log error */
		RERROR("failed to execute view request or parse return");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto free_and_return;
	}

	/* debugging */
	RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));

	/* check for error in json object */
	if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
		/* build initial error buffer */
		strlcpy(error, json_object_get_string(json), sizeof(error));
		/* get error reason */
		if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
			/* append divider */
			strlcat(error, " - ", sizeof(error));
			/* append reason */
			strlcat(error, json_object_get_string(json), sizeof(error));
		}
		/* log error */
		RERROR("view request failed with error: %s", error);
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto free_and_return;
	}

	/* check for document id in return */
	if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
		/* log error */
		RERROR("failed to fetch rows from view payload");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto free_and_return;
	}

	/* get and hold rows */
	jrows = json_object_get(json);

	/* free cookie object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	/* check for valid row value */
	if (!jrows || !json_object_is_type(jrows, json_type_array)) {
		/* log error */
		RERROR("no valid rows returned from view: %s", vpath);
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto free_and_return;
	}

	/* debugging */
	RDEBUG3("jrows == %s", json_object_to_json_string(jrows));

	/* set the count */
	request->simul_count = json_object_array_length(jrows);

	/* debugging */
	RDEBUG("found %d open sessions for %s", request->simul_count, request->username->vp_strvalue);

	/* check count */
	if (request->simul_count < request->simul_max) {
		rcode = RLM_MODULE_OK;
		goto free_and_return;
	}

	/*
	 * Current session count exceeds configured maximum.
	 * Continue on to verify the sessions if configured otherwise stop here.
	 */
	if (inst->verify_simul != true) {
		rcode = RLM_MODULE_OK;
		goto free_and_return;
	}

	/* debugging */
	RDEBUG("verifying session count");

	/* reset the count */
	request->simul_count = 0;

	/* get client ip address for MPP detection below */
	if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
		client_ip_addr = vp->vp_ipaddr;
	}

	/* get calling station id for MPP detection below */
	if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
		client_cs_id = vp->vp_strvalue;
	}

	/* loop across all row elements */
	for (idx = 0; idx < json_object_array_length(jrows); idx++) {
		/* clear docid */
		memset(docid, 0, sizeof(docid));

		/* fetch current index */
		json = json_object_array_get_idx(jrows, idx);

		/* get document id */
		if (json_object_object_get_ex(json, "id", &jval)) {
			/* copy and check length */
			if (strlcpy(docid, json_object_get_string(jval), sizeof(docid)) >= sizeof(docid)) {
				RERROR("document id from row longer than MAX_KEY_SIZE (%d)", MAX_KEY_SIZE);
				continue;
			}
		}

		/* check for valid doc id */
		if (docid[0] == 0) {
			RWARN("failed to fetch document id from row - skipping");
			continue;
		}

		/* fetch document */
		cb_error = couchbase_get_key(cb_inst, cookie, docid);

		/* check error and object */
		if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
			/* log error */
			RERROR("failed to execute get request or parse return");
			/* set return */
			rcode = RLM_MODULE_FAIL;
			/* return */
			goto free_and_return;
		}

		/* debugging */
		RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));

		/* get element name for User-Name attribute */
		if (mod_attribute_to_element("User-Name", inst->map, &element) == 0) {
			/* get and check username element */
			if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
				RDEBUG("cannot zap stale entry without username");
				rcode = RLM_MODULE_FAIL;
				goto free_and_return;
			}
			/* copy json string value to user_name */
			user_name = talloc_typed_strdup(request, json_object_get_string(jval));
		} else {
			RDEBUG("failed to find map entry for User-Name attribute");
			rcode = RLM_MODULE_FAIL;
			goto free_and_return;
		}

		/* get element name for Acct-Session-Id attribute */
		if (mod_attribute_to_element("Acct-Session-Id", inst->map, &element) == 0) {
			/* get and check session id element */
			if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
				RDEBUG("cannot zap stale entry without session id");
				rcode = RLM_MODULE_FAIL;
				goto free_and_return;
			}
			/* copy json string value to session_id */
			session_id = talloc_typed_strdup(request, json_object_get_string(jval));
		} else {
			RDEBUG("failed to find map entry for Acct-Session-Id attribute");
			rcode = RLM_MODULE_FAIL;
			goto free_and_return;
		}

		/* get element name for NAS-IP-Address attribute */
		if (mod_attribute_to_element("NAS-IP-Address", inst->map, &element) == 0) {
			/* attempt to get and nas address element */
			if (json_object_object_get_ex(cookie->jobj, element, &jval)){
				nas_addr = inet_addr(json_object_get_string(jval));
			}
		}

		/* get element name for NAS-Port attribute */
		if (mod_attribute_to_element("NAS-Port", inst->map, &element) == 0) {
			/* attempt to get nas port element */
			if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
				nas_port = (uint32_t) json_object_get_int(jval);
			}
		}

		/* check terminal server */
		int check = rad_check_ts(nas_addr, nas_port, user_name, session_id);

		/* take action based on check return */
		if (check == 0) {
			/* stale record - zap it if enabled */
			if (inst->delete_stale_sessions) {
				/* get element name for Framed-IP-Address attribute */
				if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) {
					/* attempt to get framed ip address element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						framed_ip_addr = inet_addr(json_object_get_string(jval));
					}
				}

				/* get element name for Framed-Port attribute */
				if (mod_attribute_to_element("Framed-Port", inst->map, &element) == 0) {
					/* attempt to get framed port element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						if (strcmp(json_object_get_string(jval), "PPP") == 0) {
							framed_proto = 'P';
						} else if (strcmp(json_object_get_string(jval), "SLIP") == 0) {
							framed_proto = 'S';
						}
					}
				}

				/* get element name for Acct-Session-Time attribute */
				if (mod_attribute_to_element("Acct-Session-Time", inst->map, &element) == 0) {
					/* attempt to get session time element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						session_time = json_object_get_int(jval);
					}
				}

				/* zap session */
				session_zap(request, nas_addr, nas_port, user_name, session_id,
					    framed_ip_addr, framed_proto, session_time);
			}
		} else if (check == 1) {
			/* user is still logged in - increase count */
			++request->simul_count;

			/* get element name for Framed-IP-Address attribute */
			if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) {
				/* attempt to get framed ip address element */
				if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
					framed_ip_addr = inet_addr(json_object_get_string(jval));
				} else {
					/* ensure 0 if not found */
					framed_ip_addr = 0;
				}
			}

			/* get element name for Calling-Station-Id attribute */
			if (mod_attribute_to_element("Calling-Station-Id", inst->map, &element) == 0) {
				/* attempt to get framed ip address element */
				if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
					/* copy json string value to cs_id */
					cs_id = talloc_typed_strdup(request, json_object_get_string(jval));
				} else {
					/* ensure null if not found */
					cs_id = NULL;
				}
			}

			/* Does it look like a MPP attempt? */
			if (client_ip_addr && framed_ip_addr && framed_ip_addr == client_ip_addr) {
				request->simul_mpp = 2;
			} else if (client_cs_id && cs_id && !strncmp(cs_id, client_cs_id, 16)) {
				request->simul_mpp = 2;
			}

		} else {
			/* check failed - return error */
			REDEBUG("failed to check the terminal server for user '%s'", user_name);
			rcode = RLM_MODULE_FAIL;
			goto free_and_return;
		}

		/* free and reset document user name talloc */
		if (user_name) {
			talloc_free(user_name);
			user_name = NULL;
		}

		/* free and reset document calling station id talloc */
		if (cs_id) {
			talloc_free(cs_id);
			cs_id = NULL;
		}

		/* free and reset document session id talloc */
		if (session_id) {
			talloc_free(session_id);
			session_id = NULL;
		}

		/* free and reset json object before fetching next row */
		if (cookie->jobj) {
			json_object_put(cookie->jobj);
			cookie->jobj = NULL;
		}
	}

	/* debugging */
	RDEBUG("retained %d open sessions for %s after verification",
	       request->simul_count, request->username->vp_strvalue);

	free_and_return:

	/* free document user name talloc */
	if (user_name) {
		talloc_free(user_name);
	}

	/* free document calling station id talloc */
	if (cs_id) {
		talloc_free(cs_id);
	}

	/* free document session id talloc */
	if (session_id) {
		talloc_free(session_id);
	}

	/* free rows */
	if (jrows) {
		json_object_put(jrows);
	}

	/* free and reset json object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	/* release handle */
	if (handle) {
		fr_connection_release(inst->pool, handle);
	}

	/*
	 * The Auth module apparently looks at request->simul_count,
	 * not the return value of this module when deciding to deny
	 * a call for too many sessions.
	 */
	return rcode;
}
static rlm_rcode_t rlm_redisn_checksimul(void *instance, REQUEST * request) {
	REDISSOCK 	*redis_socket;
	REDIS_INST	*inst = instance;
	REDIS_ROW	row;
	char		querystr[MAX_QUERY_LEN];
	char		redisnusername[MAX_STRING_LEN];
	int		check = 0;
        uint32_t        ipno = 0;
        char            *call_num = NULL;
	VALUE_PAIR      *vp;
	int		ret;
	uint32_t	nas_addr = 0;
	int		nas_port = 0;

	/* If simul_count_query is not defined, we don't do any checking */
	if (!inst->simul_count_query ||
	    (inst->simul_count_query[0] == 0)) {
		return RLM_MODULE_NOOP;
	}

	if((request->username == NULL) || (request->username->length == 0)) {
		radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
		return RLM_MODULE_INVALID;
	}


	if(redisn_set_user(inst, request, redisnusername, NULL) < 0)
		return RLM_MODULE_FAIL;

	radius_xlat(querystr, sizeof(querystr), inst->simul_count_query, request, redisn_escape_func, inst);

	/* initialize the redisn socket */
	redis_socket = redisn_get_socket(inst);
	if(redis_socket == NULL)
		return RLM_MODULE_FAIL;

	if(rlm_redisn_query(inst, redis_socket, querystr)) {
		radlog(L_ERR, "rlm_redisn (%s) redisn_checksimul: Database query failed", inst->xlat_name);
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_FAIL;
	}

	ret = rlm_redisn_fetch_row(inst, redis_socket);

	if (ret != 0) {
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_FAIL;
	}

	row = redis_socket->row;
	if (row == NULL) {
		(inst->redisn_finish_query)(inst, redis_socket);
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_FAIL;
	}

	request->simul_count = atoi(row[0]);
	(inst->redisn_finish_query)(inst, redis_socket);

	if(request->simul_count < request->simul_max) {
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_OK;
	}

	/*
	 *	Looks like too many sessions, so let's start verifying
	 *	them, unless told to rely on count query only.
	 */
	if (!inst->simul_verify_query ||
	    (inst->simul_verify_query[0] == '\0')) {
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_OK;
	}

	radius_xlat(querystr, sizeof(querystr), inst->simul_verify_query, request, redisn_escape_func, inst);
	if(rlm_redisn_query(inst, redis_socket, querystr)) {
		radlog_request(L_ERR, 0, request, "Database query error");
		redisn_release_socket(inst, redis_socket);
		return RLM_MODULE_FAIL;
	}

        /*
         *      Setup some stuff, like for MPP detection.
         */
	request->simul_count = 0;

        if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL)
                ipno = vp->vp_ipaddr;
        if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
                call_num = vp->vp_strvalue;


	while (rlm_redisn_fetch_row(inst, redis_socket) == 0) {
		row = redis_socket->row;
		if (row == NULL)
			break;
		if (!row[2]){
			(inst->redisn_finish_query)(inst, redis_socket);
			redisn_release_socket(inst, redis_socket);
			RDEBUG("Cannot zap stale entry. No username present in entry.", inst->xlat_name);
			return RLM_MODULE_FAIL;
		}
		if (!row[1]){
			(inst->redisn_finish_query)(inst, redis_socket);
			redisn_release_socket(inst, redis_socket);
			RDEBUG("Cannot zap stale entry. No session id in entry.", inst->xlat_name);
			return RLM_MODULE_FAIL;
		}
		if (row[3])
			nas_addr = inet_addr(row[3]);
		if (row[4])
			nas_port = atoi(row[4]);

		check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);

		if (check == 0) {
			/*
			 *	Stale record - zap it.
			 */
			if (inst->deletestalesessions == TRUE) {
				uint32_t framed_addr = 0;
				char proto = 0;
				int sess_time = 0;

				if (row[5])
					framed_addr = inet_addr(row[5]);
				if (row[7]){
					if (strcmp(row[7], "PPP") == 0)
						proto = 'P';
					else if (strcmp(row[7], "SLIP") == 0)
						proto = 'S';
				}
				if (row[8])
					sess_time = atoi(row[8]);
				session_zap(request, nas_addr, nas_port,
					    row[2], row[1], framed_addr,
					    proto, sess_time);
			}
		}
		else if (check == 1) {
			/*
			 *	User is still logged in.
			 */
			++request->simul_count;

                        /*
                         *      Does it look like a MPP attempt?
                         */
                        if (row[5] && ipno && inet_addr(row[5]) == ipno)
                                request->simul_mpp = 2;
                        else if (row[6] && call_num &&
                                !strncmp(row[6],call_num,16))
                                request->simul_mpp = 2;
		}
		else {
			/*
			 *      Failed to check the terminal server for
			 *      duplicate logins: return an error.
			 */
			(inst->redisn_finish_query)(inst, redis_socket);
			redisn_release_socket(inst, redis_socket);
			radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
			return RLM_MODULE_FAIL;
		}
	}

	(inst->redisn_finish_query)(inst, redis_socket);
	redisn_release_socket(inst, redis_socket);

	/*
	 *	The Auth module apparently looks at request->simul_count,
	 *	not the return value of this module when deciding to deny
	 *	a call for too many sessions.
	 */
	return RLM_MODULE_OK;
}
/*
 *	See if a user is already logged in. Sets request->simul_count
 *	to the current session count for this user and sets
 *	request->simul_mpp to 2 if it looks like a multilink attempt
 *	based on the requested IP address, otherwise leaves
 *	request->simul_mpp alone.
 *
 *	Check twice. If on the first pass the user exceeds his
 *	max. number of logins, do a second pass and validate all
 *	logins by querying the terminal server (using eg. SNMP).
 */
static rlm_rcode_t radutmp_checksimul(void *instance, REQUEST *request)
{
	struct radutmp	u;
	int		fd;
	VALUE_PAIR	*vp;
	uint32_t	ipno = 0;
	char		*call_num = NULL;
	int		rcode;
	rlm_radutmp_t	*inst = instance;
	char		login[256];
	char		filename[1024];
	radutmp_cache_t *cache;
	radutmp_simul_t *user, myUser;

	/*
	 *	Get the filename, via xlat.
	 */
	radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);

	/*
	 *	Future: look up filename in filename tree, to get
	 *	radutmp_cache_t pointer
	 */
	cache = &inst->cache;

	/*
	 *	For now, double-check the filename, to be sure it isn't
	 *	changing.
	 */
	if (!cache->filename) {
		cache->filename = strdup(filename);
		rad_assert(cache->filename != NULL);

	} else if (strcmp(cache->filename, filename) != 0) {
		radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
		return RLM_MODULE_FAIL;
	}

	*login = '******';
	radius_xlat(login, sizeof(login), inst->username, request, NULL);
	if (!*login) {
		return RLM_MODULE_NOOP;
	}

	/*
	 *	WTF?  This is probably wrong... we probably want to
	 *	be able to check users across multiple session accounting
	 *	methods.
	 */
	request->simul_count = 0;

	strlcpy(myUser.login, login, sizeof(myUser.login));
	pthread_mutex_lock(&inst->cache.mutex);
	user = rbtree_finddata(inst->user_tree, &myUser);
	if (user) request->simul_count = user->simul_count;
	user = NULL;		/* someone else may delete it */
	pthread_mutex_unlock(&inst->cache.mutex);

	/*
	 *	The number of users logged in is OK,
	 *	OR, we've been told to not check the NAS.
	 */
	if ((request->simul_count < request->simul_max) ||
	    !inst->check_nas) {
		return RLM_MODULE_OK;
	}

	/*
	 *	The user is logged in at least N times, and
	 *	we're told to check the NAS.  In that case,
	 *	we've got to read the file, and check each
	 *	NAS port by hand.
	 */
	if ((fd = open(cache->filename, O_RDWR)) < 0) {
		/*
		 *	If the file doesn't exist, then no users
		 *	are logged in.
		 */
		if (errno == ENOENT) {
			request->simul_count = 0;
			return RLM_MODULE_OK;
		}

		/*
		 *	Error accessing the file.
		 */
		radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
		       cache->filename, strerror(errno));
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Setup some stuff, like for MPP detection.
	 */
	if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL)
		ipno = vp->vp_ipaddr;
	if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
		call_num = vp->vp_strvalue;

	/*
	 *	lock the file while reading/writing.
	 */
	rad_lockfd(fd, LOCK_LEN);

	/*
	 *	FIXME: If we get a 'Start' for a user/nas/port which is
	 *	listed, but for which we did NOT get a 'Stop', then
	 *	it's not a duplicate session.  This happens with
	 *	static IP's like DSL.
	 */
	request->simul_count = 0;
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
		     (!inst->case_sensitive &&
		      (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
		    (u.type == P_LOGIN)) {
			char session_id[sizeof(u.session_id) + 1];
			char utmp_login[sizeof(u.login) + 1];

			strlcpy(session_id, u.session_id, sizeof(session_id));

			/*
			 *	The login name MAY fill the whole field,
			 *	and thus won't be zero-filled.
			 *
			 *	Note that we take the user name from
			 *	the utmp file, as that's the canonical
			 *	form.  The 'login' variable may contain
			 *	a string which is an upper/lowercase
			 *	version of u.login.  When we call the
			 *	routine to check the terminal server,
			 *	the NAS may be case sensitive.
			 *
			 *	e.g. We ask if "bob" is using a port,
			 *	and the NAS says "no", because "BOB"
			 *	is using the port.
			 */
			strlcpy(utmp_login, u.login, sizeof(u.login));

			/*
			 *	rad_check_ts may take seconds
			 *	to return, and we don't want
			 *	to block everyone else while
			 *	that's happening.  */
			rad_unlockfd(fd, LOCK_LEN);
			rcode = rad_check_ts(u.nas_address, u.nas_port,
					     utmp_login, session_id);
			rad_lockfd(fd, LOCK_LEN);

			if (rcode == 0) {
				/*
				 *	Stale record - zap it.
				 *
				 *	Hmm... this ends up calling
				 *	the accounting section
				 *	recursively...
				 */
				session_zap(request, u.nas_address,
					    u.nas_port, login, session_id,
					    u.framed_address, u.proto,0);
			}
			else if (rcode == 1) {
				/*
				 *	User is still logged in.
				 */
				++request->simul_count;

				/*
				 *	Does it look like a MPP attempt?
				 */
				if (strchr("SCPA", u.proto) &&
				    ipno && u.framed_address == ipno)
					request->simul_mpp = 2;
				else if (strchr("SCPA", u.proto) && call_num &&
					!strncmp(u.caller_id,call_num,16))
					request->simul_mpp = 2;
			}
			else {
				/*
				 *	Failed to check the terminal
				 *	server for duplicate logins:
				 *	Return an error.
				 */
				close(fd);
				radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
				return RLM_MODULE_FAIL;
			}
		}
	}
	close(fd);		/* and implicitly release the locks */

	return RLM_MODULE_OK;
}