6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
21 * FIXME add copyrights
26 #include <sys/types.h>
42 #define LOCK_LEN sizeof(struct radutmp)
44 static const char porttypes[] = "ASITX";
47 * Internal wrapper for locking, to minimize the number of ifdef's
50 * Lock the utmp file, prefer lockf() over flock()
52 static void radutmp_lock(int fd)
54 #if defined(F_LOCK) && !defined(BSD)
55 (void)lockf(fd, F_LOCK, LOCK_LEN);
57 (void)flock(fd, LOCK_EX);
62 * Internal wrapper for unlocking, to minimize the number of ifdef's
65 * Unlock the utmp file, prefer lockf() over flock()
67 static void radutmp_unlock(int fd)
69 #if defined(F_LOCK) && !defined(BSD)
70 (void)lockf(fd, F_ULOCK, LOCK_LEN);
72 (void)flock(fd, LOCK_UN);
77 * used for caching radutmp lookups in the accounting component. The
78 * session (checksimul) component doesn't use it, but probably should.
80 typedef struct nas_port {
84 struct nas_port *next;
87 struct radutmp_instance {
88 NAS_PORT *nas_port_list;
95 static CONF_PARSER module_config[] = {
96 { "filename", PW_TYPE_STRING_PTR,
97 offsetof(struct radutmp_instance,radutmp_fn), NULL, RADUTMP },
98 { "username", PW_TYPE_STRING_PTR,
99 offsetof(struct radutmp_instance,username), NULL, "%{User-Name}"},
100 { "perm", PW_TYPE_INTEGER,
101 offsetof(struct radutmp_instance,permission), NULL, "0644" },
102 { "callerid", PW_TYPE_BOOLEAN,
103 offsetof(struct radutmp_instance,callerid_ok), NULL, "no" },
104 { NULL, -1, 0, NULL, NULL } /* end the list */
107 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
109 struct radutmp_instance *inst;
110 inst = rad_malloc(sizeof(*inst));
111 if (cf_section_parse(conf, inst, module_config)) {
115 inst->nas_port_list = NULL;
124 static int radutmp_detach(void *instance)
127 struct radutmp_instance *inst = instance;
129 for(p=inst->nas_port_list ; p ; p=next) {
133 free(inst->radutmp_fn);
139 * Zap all users on a NAS from the radutmp file.
141 static int radutmp_zap(struct radutmp_instance *inst, uint32_t nasaddr, time_t t)
146 if (t == 0) time(&t);
148 fd = open(inst->radutmp_fn, O_RDWR);
151 * Lock the utmp file, prefer lockf() over flock().
156 * Find the entry for this NAS / portno combination.
158 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
159 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
165 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
166 radlog(L_ERR, "Accounting: radutmp_zap: "
167 "negative lseek!\n");
168 lseek(fd, (off_t)0, SEEK_SET);
172 write(fd, &u, sizeof(u));
176 radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
183 * Lookup a NAS_PORT in the nas_port_list
185 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, int port)
189 for(cl = nas_port_list; cl; cl = cl->next)
190 if (nasaddr == cl->nasaddr &&
198 * Store logins in the RADIUS utmp file.
200 static int radutmp_accounting(void *instance, REQUEST *request)
202 struct radutmp ut, u;
206 uint32_t nas_address = 0;
207 uint32_t framed_address = 0;
211 int just_an_update = 0;
213 int nas_port_type = 0;
215 struct radutmp_instance *inst = instance;
219 * Which type is this.
221 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
222 radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
223 return RLM_MODULE_NOOP;
226 if (status == PW_STATUS_ACCOUNTING_ON ||
227 status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
230 * Translate the User-Name attribute, or whatever else
231 * they told us to use.
234 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
237 (*buffer != '\0')) do {
242 * ComOS (up to and including 3.5.1b20) does not send
243 * standard PW_STATUS_ACCOUNTING_XXX messages.
245 * Check for: o no Acct-Session-Time, or time of 0
246 * o Acct-Session-Id of "00000000".
248 * We could also check for NAS-Port, that attribute
249 * should NOT be present (but we don't right now).
251 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
252 == NULL || vp->lvalue == 0)
254 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
255 != NULL && vp->length == 8 &&
256 memcmp(vp->strvalue, "00000000", 8) == 0)
258 if (check1 == 0 || check2 == 0) {
259 #if 0 /* Cisco sometimes sends START records without username. */
260 radlog(L_ERR, "Accounting: no username in record");
261 return RLM_MODULE_FAIL;
266 radlog(L_INFO, "Accounting: converting reboot records.");
267 if (status == PW_STATUS_STOP)
268 status = PW_STATUS_ACCOUNTING_OFF;
269 if (status == PW_STATUS_START)
270 status = PW_STATUS_ACCOUNTING_ON;
275 memset(&ut, 0, sizeof(ut));
279 * Copy the previous translated user name.
281 strncpy(ut.login, buffer, RUT_NAMESIZE);
284 * First, find the interesting attributes.
286 for (vp = request->packet->vps; vp; vp = vp->next) {
287 switch (vp->attribute) {
288 case PW_LOGIN_IP_HOST:
289 case PW_FRAMED_IP_ADDRESS:
290 framed_address = vp->lvalue;
291 ut.framed_address = vp->lvalue;
293 case PW_FRAMED_PROTOCOL:
294 protocol = vp->lvalue;
296 case PW_NAS_IP_ADDRESS:
297 nas_address = vp->lvalue;
298 ut.nas_address = vp->lvalue;
301 ut.nas_port = vp->lvalue;
304 case PW_ACCT_DELAY_TIME:
305 ut.delay = vp->lvalue;
307 case PW_ACCT_SESSION_ID:
309 * If length > 8, only store the
312 off = vp->length - sizeof(ut.session_id);
314 * Ascend is br0ken - it adds a \0
315 * to the end of any string.
318 if (vp->length > 0 &&
319 vp->strvalue[vp->length - 1] == 0)
321 if (off < 0) off = 0;
322 memcpy(ut.session_id, vp->strvalue + off,
323 sizeof(ut.session_id));
325 case PW_NAS_PORT_TYPE:
326 if (vp->lvalue >= 0 && vp->lvalue <= 4)
327 ut.porttype = porttypes[vp->lvalue];
328 nas_port_type = vp->lvalue;
330 case PW_CALLING_STATION_ID:
331 if(inst->callerid_ok)
332 strNcpy(ut.caller_id,
333 (char *)vp->strvalue,
334 sizeof(ut.caller_id));
340 * If we didn't find out the NAS address, use the
341 * originator's IP address.
343 if (nas_address == 0) {
344 nas_address = request->packet->src_ipaddr;
345 ut.nas_address = nas_address;
348 if (protocol == PW_PPP)
350 else if (protocol == PW_SLIP)
354 ut.time = t - ut.delay;
357 * See if this was a portmaster reboot.
359 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
360 radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
361 nas_name(nas_address));
362 radutmp_zap(inst, nas_address, ut.time);
363 return RLM_MODULE_OK;
365 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
366 radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
367 nas_name(nas_address));
368 radutmp_zap(inst, nas_address, ut.time);
369 return RLM_MODULE_OK;
373 * If we don't know this type of entry pretend we succeeded.
375 if (status != PW_STATUS_START &&
376 status != PW_STATUS_STOP &&
377 status != PW_STATUS_ALIVE) {
378 radlog(L_ERR, "NAS %s port %d unknown packet type %d)",
379 nas_name(nas_address), ut.nas_port, status);
380 return RLM_MODULE_NOOP;
384 * Perhaps we don't want to store this record into
385 * radutmp. We skip records:
387 * - without a NAS-Port-Id (telnet / tcp access)
388 * - with the username "!root" (console admin login)
390 if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
391 return RLM_MODULE_NOOP;
394 * Enter into the radutmp file.
396 fd = open(inst->radutmp_fn, O_RDWR|O_CREAT, inst->permission);
402 * Lock the utmp file, prefer lockf() over flock().
407 * Find the entry for this NAS / portno combination.
409 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
410 ut.nas_port)) != NULL)
411 lseek(fd, (off_t)cache->offset, SEEK_SET);
415 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
417 if (u.nas_address != ut.nas_address ||
418 u.nas_port != ut.nas_port)
421 if (status == PW_STATUS_STOP &&
422 strncmp(ut.session_id, u.session_id,
423 sizeof(u.session_id)) != 0) {
425 * Don't complain if this is not a
426 * login record (some clients can
427 * send _only_ logout records).
429 if (u.type == P_LOGIN)
431 "Accounting: logout: entry for NAS %s port %d has wrong ID",
432 nas_name(nas_address), u.nas_port);
437 if (status == PW_STATUS_START &&
438 strncmp(ut.session_id, u.session_id,
439 sizeof(u.session_id)) == 0 &&
441 if (u.type == P_LOGIN) {
443 "Accounting: login: entry for NAS %s port %d duplicate",
444 nas_name(nas_address), u.nas_port);
449 "Accounting: login: entry for NAS %s port %d wrong order",
450 nas_name(nas_address), u.nas_port);
456 * FIXME: the ALIVE record could need
457 * some more checking, but anyway I'd
458 * rather rewrite this mess -- miquels.
460 if (status == PW_STATUS_ALIVE &&
461 strncmp(ut.session_id, u.session_id,
462 sizeof(u.session_id)) == 0 &&
465 * Keep the original login time.
472 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
473 radlog(L_ERR, "Accounting: negative lseek!\n");
474 lseek(fd, (off_t)0, SEEK_SET);
482 if (r >= 0 && (status == PW_STATUS_START ||
483 status == PW_STATUS_ALIVE)) {
485 cache = rad_malloc(sizeof(NAS_PORT));
486 cache->nasaddr = ut.nas_address;
487 cache->port = ut.nas_port;
489 cache->next = inst->nas_port_list;
490 inst->nas_port_list = cache;
493 write(fd, &ut, sizeof(u));
495 if (status == PW_STATUS_STOP) {
500 write(fd, &u, sizeof(u));
503 "Accounting: logout: login entry for NAS %s port %d not found",
504 nas_name(nas_address), ut.nas_port);
510 radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
511 return RLM_MODULE_FAIL;
514 return RLM_MODULE_OK;
518 * See if a user is already logged in. Sets request->simul_count to the
519 * current session count for this user and sets request->simul_mpp to 2
520 * if it looks like a multilink attempt based on the requested IP
521 * address, otherwise leaves request->simul_mpp alone.
523 * Check twice. If on the first pass the user exceeds his
524 * max. number of logins, do a second pass and validate all
525 * logins by querying the terminal server (using eg. SNMP).
527 static int radutmp_checksimul(void *instance, REQUEST *request)
533 char *call_num = NULL;
535 struct radutmp_instance *inst = instance;
538 if ((fd = open(inst->radutmp_fn, O_RDWR)) < 0) {
540 return RLM_MODULE_FAIL;
541 request->simul_count=0;
542 return RLM_MODULE_OK;
546 radius_xlat(login, sizeof(login), inst->username, request, NULL);
548 return RLM_MODULE_FAIL;
550 request->simul_count = 0;
551 while(read(fd, &u, sizeof(u)) == sizeof(u)) {
552 if (strncmp(login, u.login, RUT_NAMESIZE) == 0
553 && u.type == P_LOGIN)
554 ++request->simul_count;
557 if(request->simul_count < request->simul_max) {
559 return RLM_MODULE_OK;
561 lseek(fd, (off_t)0, SEEK_SET);
564 * Setup some stuff, like for MPP detection.
566 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
568 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
569 call_num = vp->strvalue;
572 * lockf() the file while reading/writing.
576 request->simul_count = 0;
577 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
578 if (strncmp(login, u.login, RUT_NAMESIZE) == 0
579 && u.type == P_LOGIN) {
580 char session_id[sizeof u.session_id+1];
581 strNcpy(session_id, u.session_id, sizeof session_id);
584 * rad_check_ts may take seconds to return,
585 * and we don't want to block everyone else
586 * while that's happening.
589 rcode = rad_check_ts(u.nas_address, u.nas_port, login,
594 ++request->simul_count;
596 * Does it look like a MPP attempt?
598 if (strchr("SCPA", u.proto) &&
599 ipno && u.framed_address == ipno)
600 request->simul_mpp = 2;
601 else if (strchr("SCPA", u.proto) && call_num &&
602 !strncmp(u.caller_id,call_num,16))
603 request->simul_mpp = 2;
607 * False record - zap it.
610 session_zap(u.nas_address, u.nas_port, login,
611 session_id, u.framed_address,
618 return RLM_MODULE_OK;
621 /* globally exported name */
622 module_t rlm_radutmp = {
624 0, /* type: reserved */
625 NULL, /* initialization */
626 radutmp_instantiate, /* instantiation */
628 NULL, /* authentication */
629 NULL, /* authorization */
630 NULL, /* preaccounting */
631 radutmp_accounting, /* accounting */
632 radutmp_checksimul /* checksimul */
634 radutmp_detach, /* detach */