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; }
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; }