📄 rlm_radutmp2.c
字号:
/* * Open the file, to re-write only a few of the entries. */ walk.fd = open(cache->filename, O_RDWR); if (walk.fd < 0) { pthread_mutex_unlock(&cache->mutex); rbtree_free(offset_tree); radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s", cache->filename, strerror(errno)); return 0; } /* * Lock the utmp file, prefer lockf() over flock(). * * FIXME: maybe we want to lock per-record? */ rad_lockfd(walk.fd, LOCK_LEN); /* * Walk through the offset tree, from start to finish, * deleting entries from the NAS tree, adding them to * the "free offset" cache, and lseek'ing to that offset * in the file, and clearing out the data. */ walk.cache = cache; walk.now = now; rcode = rbtree_walk(offset_tree, InOrder, offset_walk, &walk); rbtree_free(offset_tree); if (rcode != 0) { radlog(L_ERR, "rlm_radutmp: Failed walking the offsets."); return 0; } close(walk.fd); /* and implicitly release the locks */ /* * Just to clean up the file. If it's empty, * nuke everything. */ if (rbtree_num_elements(cache->nas_ports) == 0) { NAS_PORT *this, *next; /* too many copies of code */ for (this = inst->cache.free_offsets; this != NULL; this = next) { next = this->next; free(this); } truncate(cache->filename, 0); rad_assert(rbtree_num_elements(inst->user_tree) == 0); } pthread_mutex_unlock(&cache->mutex); return 1;}/* * Read a file, to cache all of its entries. */static int cache_file(rlm_radutmp_t *inst, radutmp_cache_t *cache){ int fd; int read_size; struct stat buf; struct radutmp utmp; NAS_PORT **tail; rad_assert(cache->max_offset == 0); rad_assert(cache->free_offsets == NULL); /* * Doesn't exist, we're fine. */ if (stat(cache->filename, &buf) < 0) { if (errno == ENOENT) { cache->cached_file = 1; return 0; } radlog(L_ERR, "rlm_radutmp: Cannot stat %s: %s", cache->filename, strerror(errno)); return 1; } /* * Nothing's there, we're OK. */ if (buf.st_size == 0) { cache->cached_file = 1; return 0; } /* * Don't let others much around with our data. */ pthread_mutex_lock(&cache->mutex); /* * Read the file and cache it's entries. */ fd = open(cache->filename, O_RDONLY, cache->permission); if (fd < 0) { pthread_mutex_unlock(&cache->mutex); radlog(L_ERR, "rlm_radutmp: Error opening %s: %s", cache->filename, strerror(errno)); return 1; } /* * Insert free entries into the tail, so that entries * get used from the start. */ tail = &(cache->free_offsets); /* * Don't lock the file, as we're only reading it. */ do { read_size = read(fd, &utmp, sizeof(utmp)); /* * Read one record. */ if (read_size == sizeof(utmp)) { radutmp_simul_t *user, myUser; NAS_PORT *nas_port = rad_malloc(sizeof(*nas_port)); memset(nas_port, 0, sizeof(nas_port)); nas_port->offset = cache->max_offset; cache->max_offset += sizeof(utmp); /* * Idle. Add it to the list of free * offsets. */ if (utmp.type == P_IDLE) { *tail = nas_port; tail = &(nas_port->next); continue; } /* * It's a login record, */ nas_port->nas_address = utmp.nas_address; nas_port->nas_port = utmp.nas_port; if (!rbtree_insert(cache->nas_ports, nas_port)) { rad_assert(0 == 1); } /* * Adds a trailing \0, so myUser.login has * an extra char allocated.. */ strlcpy(myUser.login, utmp.login, sizeof(myUser.login)); user = rbtree_finddata(inst->user_tree, &myUser); if (user) { user->simul_count++; } else { /* * 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); } } continue; } /* * We've read a partial record. WTF? */ if (read_size != 0) { pthread_mutex_unlock(&cache->mutex); close(fd); radlog(L_ERR, "rlm_radutmp: Badly formed file %s", cache->filename); return 1; } /* * Read nothing, stop. */ } while (read_size != 0); pthread_mutex_unlock(&cache->mutex); close(fd); /* and release the lock. */ cache->cached_file = 1; return 0;}/* * Store logins in the RADIUS utmp file. */static int radutmp_accounting(void *instance, REQUEST *request){ rlm_radutmp_t *inst = instance; struct radutmp utmp, u; VALUE_PAIR *vp; int status = -1; uint32_t nas_address = 0; uint32_t framed_address = 0; int protocol = -1; int fd; int port_seen = 0; char buffer[256]; char filename[1024]; char ip_name[32]; /* 255.255.255.255 */ const char *nas; NAS_PORT *nas_port, myPort; radutmp_cache_t *cache; int read_size; rbnode_t *node; /* * Which type is this. */ if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) { radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record."); return RLM_MODULE_NOOP; } status = vp->vp_integer; /* * Look for weird reboot packets. * * ComOS (up to and including 3.5.1b20) does not send * standard PW_STATUS_ACCOUNTING_* messages. * * Check for: o no Acct-Session-Time, or time of 0 * o Acct-Session-Id of "00000000". * * We could also check for NAS-Port, that attribute * should NOT be present (but we don't right now). */ if ((status != PW_STATUS_ACCOUNTING_ON) && (status != PW_STATUS_ACCOUNTING_OFF)) do { int check1 = 0; int check2 = 0; if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) == NULL || vp->vp_date == 0) check1 = 1; if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID)) != NULL && vp->length == 8 && memcmp(vp->vp_strvalue, "00000000", 8) == 0) check2 = 1; if (check1 == 0 || check2 == 0) {#if 0 /* Cisco sometimes sends START records without username. */ radlog(L_ERR, "rlm_radutmp: no username in record"); return RLM_MODULE_FAIL;#else break;#endif } radlog(L_INFO, "rlm_radutmp: converting reboot records."); if (status == PW_STATUS_STOP) status = PW_STATUS_ACCOUNTING_OFF; if (status == PW_STATUS_START) status = PW_STATUS_ACCOUNTING_ON; } while(0); memset(&utmp, 0, sizeof(utmp)); utmp.porttype = 'A'; /* * First, find the interesting attributes. */ for (vp = request->packet->vps; vp; vp = vp->next) { switch (vp->attribute) { case PW_LOGIN_IP_HOST: case PW_FRAMED_IP_ADDRESS: framed_address = vp->vp_ipaddr; utmp.framed_address = vp->vp_ipaddr; break; case PW_FRAMED_PROTOCOL: protocol = vp->vp_integer; break; case PW_NAS_IP_ADDRESS: nas_address = vp->vp_ipaddr; utmp.nas_address = vp->vp_ipaddr; break; case PW_NAS_PORT: utmp.nas_port = vp->vp_integer; port_seen = 1; break; case PW_ACCT_DELAY_TIME: utmp.delay = vp->vp_integer; break; case PW_ACCT_SESSION_ID: /* * If it's too big, only use the * last bit. */ if (vp->length > sizeof(utmp.session_id)) { int length = vp->length - sizeof(utmp.session_id); /* * Ascend is br0ken - it * adds a \0 to the end * of any string. * Compensate. */ if (vp->vp_strvalue[vp->length - 1] == 0) { length--; } memcpy(utmp.session_id, vp->vp_strvalue + length, sizeof(utmp.session_id)); } else { memset(utmp.session_id, 0, sizeof(utmp.session_id)); memcpy(utmp.session_id, vp->vp_strvalue, vp->length); } break; case PW_NAS_PORT_TYPE: if (vp->vp_integer <= 4) utmp.porttype = porttypes[vp->vp_integer]; break; case PW_CALLING_STATION_ID: if(inst->callerid_ok) strlcpy(utmp.caller_id, (char *)vp->vp_strvalue, sizeof(utmp.caller_id)); break; } } /* * If we didn't find out the NAS address, use the * originator's IP address. */ if (nas_address == 0) { nas_address = request->packet->src_ipaddr; utmp.nas_address = nas_address; nas = request->client->shortname; } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == nas_address) { /* might be a client, might not be. */ nas = request->client->shortname; } else { /* * The NAS isn't a client, it's behind * a proxy server. In that case, just * get the IP address. */ nas = ip_ntoa(ip_name, nas_address); } /* * Set the protocol field. */ if (protocol == PW_PPP) utmp.proto = 'P'; else if (protocol == PW_SLIP) utmp.proto = 'S'; else utmp.proto = 'T'; utmp.time = request->timestamp - utmp.delay; /* * Get the utmp 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; } /* * If the lookup failed, create a new one, and add it * to the filename tree, and cache the file, as below. */ /* * For aging, in the future. */ cache->last_used = request->timestamp; /* * If we haven't already read the file, then read the * entire file, in order to cache its entries. */ if (!cache->cached_file) { cache_file(inst, cache); } /* * See if this was a reboot. * * Hmm... we may not want to zap all of the users when * the NAS comes up, because of issues with receiving * UDP packets out of order. */ if (status == PW_STATUS_ACCOUNTING_ON && nas_address) { radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)", nas); if (!radutmp_zap(inst, cache, nas_address, utmp.time)) { rad_assert(0 == 1); } return RLM_MODULE_OK; } if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) { radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)", nas); if (!radutmp_zap(inst, cache, nas_address, utmp.time)) { rad_assert(0 == 1); } return RLM_MODULE_OK; } /* * If we don't know this type of entry, then pretend we * succeeded. */ if (status != PW_STATUS_START && status != PW_STATUS_STOP && status != PW_STATUS_ALIVE) { radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.", nas, utmp.nas_port, status); return RLM_MODULE_NOOP; } /* * Perhaps we don't want to store this record into * radutmp. We skip records: * * - without a NAS-Port (telnet / tcp access) * - with the username "!root" (console admin login) */ if (!port_seen) { DEBUG2(" rlm_radutmp: No NAS-Port in the packet. Cannot do anything."); DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!"); return RLM_MODULE_NOOP; } /* * Translate the User-Name attribute, or whatever else * they told us to use. */ *buffer = '\0'; radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL); /* * Don't log certain things... */ if (strcmp(buffer, "!root") == 0) { DEBUG2(" rlm_radutmp: Not recording administrative user"); return RLM_MODULE_NOOP; } strlcpy(utmp.login, buffer, RUT_NAMESIZE); /* * First, try to open the file. If it doesn't exist, * nuke the existing caches, and try to create it. * * FIXME: Create any intermediate directories, as * appropriate. See rlm_detail. */ fd = open(cache->filename, O_RDWR, inst->permission); if (fd < 0) { if (errno == ENOENT) { DEBUG2(" rlm_radutmp: File %s doesn't exist, creating it.", cache->filename); if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL; /* * Try to create the file. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -