/** Close the log file. Really just return it to the pool. * * When multithreaded, the FD is locked via a mutex. This way we're * sure that no other thread is writing to the file. This function * will unlock the mutex, so that other threads can write to the file. * * @param lf The logfile context returned from fr_logfile_init() * @param fd the FD to close (i.e. return to the pool) * @return 0 on success, or -1 on error */ int fr_logfile_close(fr_logfile_t *lf, int fd) { int i; for (i = 0; i < lf->max_entries; i++) { if (!lf->entries[i].filename) continue; /* * Unlock the bytes that we had previously locked. */ if (lf->entries[i].dup == fd) { (void) rad_unlockfd(lf->entries[i].dup, 0); close(lf->entries[i].dup); /* releases the fcntl lock */ lf->entries[i].dup = -1; PTHREAD_MUTEX_UNLOCK(&(lf->mutex)); return 0; } } PTHREAD_MUTEX_UNLOCK(&(lf->mutex)); fr_strerror_printf("Attempt to unlock file which does not exist"); return -1; }
/* * 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; }
/* * 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; }