{ FR_CONF_OFFSET("user_key", FR_TYPE_TMPL, rlm_couchbase_t, user_key), .dflt = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}", .quote = T_DOUBLE_QUOTED_STRING },
{ FR_CONF_OFFSET("read_clients", FR_TYPE_BOOL, rlm_couchbase_t, read_clients) }, /* NULL defaults to "no" */
{ FR_CONF_POINTER("client", FR_TYPE_SUBSECTION, NULL), .subcs = (void const *) client_config },
-#ifdef WITH_SESSION_MGMT
- { FR_CONF_OFFSET("check_simul", FR_TYPE_BOOL, rlm_couchbase_t, check_simul) }, /* NULL defaults to "no" */
- { FR_CONF_OFFSET("simul_view", FR_TYPE_STRING, rlm_couchbase_t, simul_view), .dflt = "_design/acct/_view/by_user" },
- { FR_CONF_OFFSET("simul_vkey", FR_TYPE_TMPL, rlm_couchbase_t, simul_vkey), .dflt = "%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}", .quote = T_DOUBLE_QUOTED_STRING },
- { FR_CONF_OFFSET("verify_simul", FR_TYPE_BOOL, rlm_couchbase_t, verify_simul) }, /* NULL defaults to "no" */
-#endif
CONF_PARSER_TERMINATOR
};
}
#endif
-#ifdef WITH_SESSION_MGMT
-/** 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 thread Thread specific data.
- * @param request The checksimul request object.
- * @return Operation status (#rlm_rcode_t).
- */
-static rlm_rcode_t mod_checksimul(void *instance, UNUSED void *thread, REQUEST *request) {
- rlm_couchbase_t const *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];
-
- char buffer[MAX_KEY_SIZE];
- char const *vkey; /* 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 */
- ssize_t slen;
-
- /* do nothing if this is not enabled */
- if (inst->check_simul != true) {
- RWDEBUG("Simultaneous-Use checking requires 'simul_count_query' to be configured");
- return RLM_MODULE_NOOP;
- }
-
- /* ensure valid username in request */
- if ((!request->username) || (request->username->vp_length == 0)) {
- REDEBUG("Zero Length username not permitted");
- return RLM_MODULE_INVALID;
- }
-
- slen = tmpl_expand(&vkey, buffer, sizeof(buffer), request, inst->simul_vkey, NULL, NULL);
- if (slen < 0) return RLM_MODULE_FAIL;
- if ((vkey == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
- REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
- return RLM_MODULE_FAIL;
- }
-
- /* get handle */
- handle = fr_pool_connection_get(inst->pool, request);
-
- /* 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 finish;
- }
-
- /* 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 finish;
- }
-
- /* 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 finish;
- }
-
- /* 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 || !fr_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 finish;
- }
-
- /* 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 finish;
- }
-
- /*
- * 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 finish;
- }
-
- /* debugging */
- RDEBUG("verifying session count");
-
- /* reset the count */
- request->simul_count = 0;
-
- /* get client ip address for MPP detection below */
- if ((vp = fr_pair_find_by_num(request->packet->vps, 0, FR_FRAMED_IP_ADDRESS, TAG_ANY)) != NULL) {
- client_ip_addr = vp->vp_ipv4addr;
- }
-
- /* get calling station id for MPP detection below */
- if ((vp = fr_pair_find_by_num(request->packet->vps, 0, FR_CALLING_STATION_ID, 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 finish;
- }
-
- /* 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 finish;
- }
- /* 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 finish;
- }
-
- /* 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 finish;
- }
- /* 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 finish;
- }
-
- /* 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 finish;
- }
-
- /* free and reset document user name talloc */
- if (user_name) TALLOC_FREE(user_name);
-
- /* free and reset document calling station id talloc */
- if (cs_id) TALLOC_FREE(cs_id);
-
- /* free and reset document session id talloc */
- if (session_id) TALLOC_FREE(session_id);
-
- /* 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);
-
-finish:
- if (user_name) talloc_free(user_name);
- if (cs_id) talloc_free(cs_id);
- 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;
- }
-
- if (handle) fr_pool_connection_release(inst->pool, request, 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;
-}
-#endif
/** Detach the module
*
[MOD_AUTHORIZE] = mod_authorize,
#ifdef WITH_ACCOUNTING
[MOD_ACCOUNTING] = mod_accounting,
-#endif
-#ifdef WITH_SESSION_MGMT
- [MOD_SESSION] = mod_checksimul
#endif
},
};