📄 rlm_radutmp2.c
字号:
fd = open(cache->filename, O_RDWR | O_CREAT, inst->permission); } } else { /* exists, but may be empty */ struct stat buf; /* * If the file is empty, reset the cache. */ if ((stat(cache->filename, &buf) == 0) && (buf.st_size == 0) && (!cache_reset(inst, cache))) { return RLM_MODULE_FAIL; } DEBUG2(" rlm_radutmp: File %s was truncated. Resetting cache.", cache->filename); } /* * Error from creation, or error other than ENOENT: die. */ if (fd < 0) { radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s", cache->filename, strerror(errno)); return RLM_MODULE_FAIL; } /* * OK. Now that we've prepared everything we want to do, * let's see if we've cached the entry. */ myPort.nas_address = utmp.nas_address; myPort.nas_port = utmp.nas_port; pthread_mutex_lock(&cache->mutex); node = rbtree_find(cache->nas_ports, &myPort); pthread_mutex_unlock(&cache->mutex); if (node) { nas_port = rbtree_node2data(cache->nas_ports, node);#if 0 /* * stat the file, and get excited if it's been * truncated. * * i.e wipe out the cache, and re-read the file. */ /* * Now find the new entry. */ pthread_mutex_lock(&cache->mutex); node = rbtree_find(cache->nas_ports, &myPort); pthread_mutex_unlock(&cache->mutex);#endif } if (!node) { radutmp_simul_t *user; /* * Not found in the cache, and we're trying to * delete an existing record: ignore it. */ if (status == PW_STATUS_STOP) { DEBUG2(" rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.", nas, utmp.nas_port); return RLM_MODULE_NOOP; } pthread_mutex_lock(&cache->mutex); /* * It's a START or ALIVE. Try to find a free * offset where we can store the new entry, or * create one, if one doesn't already exist. */ if (!cache->free_offsets) { cache->free_offsets = rad_malloc(sizeof(NAS_PORT)); memset(cache->free_offsets, 0, sizeof(*(cache->free_offsets))); cache->free_offsets->offset = cache->max_offset; cache->max_offset += sizeof(u); } /* * Grab the offset, and put it into the various * caches. */ nas_port = cache->free_offsets; cache->free_offsets = nas_port->next; nas_port->nas_address = nas_address; nas_port->nas_port = utmp.nas_port; if (!rbtree_insert(cache->nas_ports, nas_port)) { rad_assert(0 == 1); } /* * Allocate new entry, and add it * to the tree. */ user = rad_malloc(sizeof(user)); strlcpy(user->login, utmp.login, sizeof(user->login)); user->simul_count = 1; if (!rbtree_insert(inst->user_tree, user)) { rad_assert(0 == 1); } pthread_mutex_unlock(&cache->mutex); } /* * Entry was found, or newly created in the cache. * Seek to the place in the file. */ lseek(fd, nas_port->offset, SEEK_SET); /* * Lock the utmp file, prefer lockf() over flock(). */ rad_lockfd(fd, LOCK_LEN); /* * If it WAS found in the cache, double-check it against * what is in the file. */ if (node) { /* * If we didn't read anything, then this entry * doesn't exist. * * Similarly, if the entry in the file doesn't * match what we recall, then nuke the cache * entry. */ read_size = read(fd, &u, sizeof(u)); if ((read_size < 0) || ((read_size > 0) && (read_size != sizeof(u)))) { /* * Bad read, or bad record. */ radlog(L_ERR, "rlm_radutmp: Badly formed file %s", cache->filename); close(fd); return RLM_MODULE_FAIL; } rad_assert(read_size != 0); /* * We've read a record, go poke at it. */ if (read_size > 0) { /* * If these aren't true, then * * a) we have cached a "logout" entry, * which we don't do. * * b) we have cached the wrong NAS address * * c) we have cached the wrong NAS port. */ rad_assert(u.type == P_LOGIN); rad_assert(u.nas_address == utmp.nas_address); rad_assert(u.nas_port == utmp.nas_port); /* * An update for the same session. */ if (strncmp(utmp.session_id, u.session_id, sizeof(u.session_id)) == 0) { /* * It's a duplicate start, so we * don't bother writing it. */ if (status == PW_STATUS_START) { DEBUG2(" rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.", nas, u.nas_port); close(fd); return RLM_MODULE_OK; /* * ALIVE for this session, keep the * original login time. */ } else if (status == PW_STATUS_ALIVE) { utmp.time = u.time; /* * Stop: delete it from our cache. */ } else if (status == PW_STATUS_STOP) { radutmp_simul_t *user, myUser; pthread_mutex_lock(&cache->mutex); rbtree_deletebydata(cache->nas_ports, nas_port); strlcpy(myUser.login, u.login, sizeof(myUser.login)); user = rbtree_finddata(inst->user_tree, &myUser); rad_assert(user != NULL); rad_assert(user->simul_count > 0); user->simul_count--; if (user->simul_count == 0) { rbtree_deletebydata(inst->user_tree, user); } pthread_mutex_unlock(&cache->mutex); } else { /* * We don't know how to * handle this. */ rad_assert(0 == 1); } } else { /* session ID doesn't match */ /* * STOP for the right NAS & port, * but the Acct-Session-Id is * different. This means that * we missed the original "stop", * and a new "start". */ if (status == PW_STATUS_STOP) { radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.", nas, u.nas_port); close(fd); return RLM_MODULE_OK; } } /* checked session ID's */ } /* else we haven't read anything from the file. */ } /* else the entry wasn't cached, but could have been inserted */ /* * Hmm... we may have received a start or alive packet * AFTER a stop or nas-down, in that case, we want to * discard the new packet. However, the original code * could over-write an idle record with a new login * record for another NAS && port, so we won't worry * about this case too much. */ /* * Seek to where the entry is, and write it blindly. */ lseek(fd, nas_port->offset, SEEK_SET); /* FIXME: err */ if (status != PW_STATUS_STOP) { utmp.type = P_LOGIN; rad_assert(nas_port != NULL); /* it WAS cached */ } else { /* FIXME: maybe assert that the entry was deleted... */ memcpy(&utmp, &u, sizeof(utmp)); utmp.type = P_IDLE; } write(fd, &utmp, sizeof(utmp)); /* FIXME: err */ close(fd); /* and implicitly release the locks */ 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 int 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 = '\0'; 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)) != NULL) ipno = vp->vp_ipaddr; if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != 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;}/* globally exported name */module_t rlm_radutmp = { "radutmp", 0, /* type: reserved */ NULL, /* initialization */ radutmp_instantiate, /* instantiation */ { NULL, /* authentication */ NULL, /* authorization */ NULL, /* preaccounting */ radutmp_accounting, /* accounting */ radutmp_checksimul, /* checksimul */ NULL, /* pre-proxy */ NULL, /* post-proxy */ NULL /* post-auth */ }, radutmp_detach, /* detach */ NULL, /* destroy */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -