2 * radutmp.c Radius session management.
4 * Version: @(#)radutmp.c 1.00 10--Aug-1999 miquels@cistron.nl
7 char radutmp_sccsid[] =
8 "@(#)radutmp.c 1.00 Copyright 1999 Cistron Internet Services B.V.";
12 #include <sys/types.h>
13 #include <sys/socket.h>
17 #include <netinet/in.h>
38 static char porttypes[] = "ASITX";
40 #define LOCK_LEN sizeof(struct radutmp)
43 * used for caching radutmp lookups.
45 typedef struct nas_port {
49 struct nas_port *next;
51 static NAS_PORT *nas_port_list = NULL;
55 * Lookup a NAS_PORT in the nas_port_list
57 static NAS_PORT *nas_port_find(UINT4 nasaddr, int port)
61 for(cl = nas_port_list; cl; cl = cl->next)
62 if (nasaddr == cl->nasaddr &&
70 * Zap a user, or all users on a NAS, from the radutmp file.
72 int radutmp_zap(UINT4 nasaddr, int port, char *user, time_t t)
79 fp = fopen(RADWTMP, "a");
81 if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
85 * Lock the utmp file, prefer lockf() over flock().
87 #if defined(F_LOCK) && !defined(BSD)
88 (void)lockf(fd, F_LOCK, LOCK_LEN);
90 (void)flock(fd, LOCK_EX);
93 * Find the entry for this NAS / portno combination.
96 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
97 if (((nasaddr != 0 && nasaddr != u.nas_address) ||
98 (port >= 0 && port != u.nas_port) ||
99 (user != NULL && strcmp(u.login, user) != 0) ||
105 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
106 log(L_ERR, "Accounting: radutmp_zap: "
107 "negative lseek!\n");
108 lseek(fd, (off_t)0, SEEK_SET);
112 write(fd, &u, sizeof(u));
114 #if 0 /* FIXME: should we fixup radwtmp as well or not ? */
116 * Add a logout entry to the wtmp file.
119 make_wtmp(&u, &wt, PW_STATUS_STOP);
120 fwrite(&wt, sizeof(wt), 1, fp);
133 * Store logins in the RADIUS utmp file.
135 int radutmp_add(REQUEST *request)
137 struct radutmp ut, u;
142 int framed_address = 0;
147 int just_an_update = 0;
149 int nas_port_type = 0;
153 * Which type is this.
155 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
156 log(L_ERR, "Accounting: no Accounting-Status-Type record.");
160 if (status == PW_STATUS_ACCOUNTING_ON ||
161 status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
164 (vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL) do {
169 * ComOS (up to and including 3.5.1b20) does not send
170 * standard PW_STATUS_ACCOUNTING_XXX messages.
172 * Check for: o no Acct-Session-Time, or time of 0
173 * o Acct-Session-Id of "00000000".
175 * We could also check for NAS-Port, that attribute
176 * should NOT be present (but we don't right now).
178 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
179 == NULL || vp->lvalue == 0)
181 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
182 != NULL && vp->length == 8 &&
183 memcmp(vp->strvalue, "00000000", 8) == 0)
185 if (check1 == 0 || check2 == 0) {
186 #if 0 /* Cisco sometimes sends START records without username. */
187 log(L_ERR, "Accounting: no username in record");
193 log(L_INFO, "Accounting: converting reboot records.");
194 if (status == PW_STATUS_STOP)
195 status = PW_STATUS_ACCOUNTING_OFF;
196 if (status == PW_STATUS_START)
197 status = PW_STATUS_ACCOUNTING_ON;
202 memset(&ut, 0, sizeof(ut));
206 * First, find the interesting attributes.
208 for (vp = request->packet->vps; vp; vp = vp->next) {
209 switch (vp->attribute) {
211 strncpy(ut.login, vp->strvalue, RUT_NAMESIZE);
213 case PW_LOGIN_IP_HOST:
214 case PW_FRAMED_IP_ADDRESS:
215 framed_address = vp->lvalue;
216 ut.framed_address = vp->lvalue;
218 case PW_FRAMED_PROTOCOL:
219 protocol = vp->lvalue;
221 case PW_NAS_IP_ADDRESS:
222 nas_address = vp->lvalue;
223 ut.nas_address = vp->lvalue;
226 ut.nas_port = vp->lvalue;
229 case PW_ACCT_DELAY_TIME:
230 ut.delay = vp->lvalue;
232 case PW_ACCT_SESSION_ID:
234 * If length > 8, only store the
237 off = vp->length - sizeof(ut.session_id);
238 if (off < 0) off = 0;
239 memcpy(ut.session_id, vp->strvalue + off,
240 sizeof(ut.session_id));
242 case PW_NAS_PORT_TYPE:
243 if (vp->lvalue >= 0 && vp->lvalue <= 4)
244 ut.porttype = porttypes[vp->lvalue];
245 nas_port_type = vp->lvalue;
247 case PW_CALLING_STATION_ID:
248 strncpy(ut.caller_id, vp->strvalue,
249 sizeof(ut.caller_id));
250 ut.caller_id[sizeof(ut.caller_id)] = 0;
256 * If we didn't find out the NAS address, use the
257 * originator's IP address.
259 if (nas_address == 0) {
260 nas_address = request->packet->src_ipaddr;
261 ut.nas_address = nas_address;
264 if (protocol == PW_PPP)
266 else if (protocol == PW_SLIP)
270 ut.time = t - ut.delay;
273 * See if this was a portmaster reboot.
275 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
276 log(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
277 nas_name(nas_address));
278 radutmp_zap(nas_address, -1, NULL, ut.time);
281 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
282 log(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
283 nas_name(nas_address));
284 radutmp_zap(nas_address, -1, NULL, ut.time);
289 * If we don't know this type of entry pretend we succeeded.
291 if (status != PW_STATUS_START &&
292 status != PW_STATUS_STOP &&
293 status != PW_STATUS_ALIVE) {
294 log(L_ERR, "NAS %s port %d unknown packet type %d)",
295 nas_name(nas_address), ut.nas_port, status);
300 * Perhaps we don't want to store this record into
301 * radutmp. We skip records:
303 * - without a NAS-Port-Id (telnet / tcp access)
304 * - with the username "!root" (console admin login)
306 if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
310 * Enter into the radutmp file.
312 if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
317 * Lock the utmp file, prefer lockf() over flock().
319 #if defined(F_LOCK) && !defined(BSD)
320 (void)lockf(fd, F_LOCK, LOCK_LEN);
322 (void)flock(fd, LOCK_EX);
325 * Find the entry for this NAS / portno combination.
327 if ((cache = nas_port_find(ut.nas_address, ut.nas_port)) != NULL)
328 lseek(fd, (off_t)cache->offset, SEEK_SET);
332 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
334 if (u.nas_address != ut.nas_address ||
335 u.nas_port != ut.nas_port)
338 if (status == PW_STATUS_STOP &&
339 strncmp(ut.session_id, u.session_id,
340 sizeof(u.session_id)) != 0) {
342 * Don't complain if this is not a
343 * login record (some clients can
344 * send _only_ logout records).
346 if (u.type == P_LOGIN)
348 "Accounting: logout: entry for NAS %s port %d has wrong ID",
349 nas_name(nas_address), u.nas_port);
354 if (status == PW_STATUS_START &&
355 strncmp(ut.session_id, u.session_id,
356 sizeof(u.session_id)) == 0 &&
358 if (u.type == P_LOGIN) {
360 "Accounting: login: entry for NAS %s port %d duplicate",
361 nas_name(nas_address), u.nas_port);
366 "Accounting: login: entry for NAS %s port %d wrong order",
367 nas_name(nas_address), u.nas_port);
373 * FIXME: the ALIVE record could need
374 * some more checking, but anyway I'd
375 * rather rewrite this mess -- miquels.
377 if (status == PW_STATUS_ALIVE &&
378 strncmp(ut.session_id, u.session_id,
379 sizeof(u.session_id)) == 0 &&
382 * Keep the original login time.
389 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
390 log(L_ERR, "Accounting: negative lseek!\n");
391 lseek(fd, (off_t)0, SEEK_SET);
399 if (r >= 0 && (status == PW_STATUS_START ||
400 status == PW_STATUS_ALIVE)) {
402 if ((cache = malloc(sizeof(NAS_PORT))) != NULL) {
403 cache->nasaddr = ut.nas_address;
404 cache->port = ut.nas_port;
406 cache->next = nas_port_list;
407 nas_port_list = cache;
411 write(fd, &ut, sizeof(u));
413 if (status == PW_STATUS_STOP) {
418 write(fd, &u, sizeof(u));
421 "Accounting: logout: login entry for NAS %s port %d not found",
422 nas_name(nas_address), ut.nas_port);
428 log(L_ERR, "Accounting: %s: %s", RADUTMP, strerror(errno));
437 * Timeout handler (10 secs)
440 static void alrm_handler()
446 * Check one terminal server to see if a user is logged in.
448 static int rad_check_ts(struct radutmp *ut)
456 void (*handler)(int);
461 if ((nas = nas_find(ut->nas_address)) == NULL) {
462 log(L_ERR, "Accounting: unknown NAS");
469 handler = signal(SIGCHLD, SIG_DFL);
470 if ((pid = fork()) < 0) {
471 log(L_ERR, "Accounting: fork: %s", strerror(errno));
472 signal(SIGCHLD, handler);
478 * Parent - Wait for checkrad to terminate.
479 * We timeout in 10 seconds.
482 signal(SIGALRM, alrm_handler);
484 while((e = waitpid(pid, &st, 0)) != pid)
485 if (e < 0 && (errno != EINTR || got_alrm))
488 signal(SIGCHLD, handler);
493 log(L_ERR, "Check-TS: timeout waiting for checkrad");
497 log(L_ERR, "Check-TS: unknown error in waitpid()");
500 return WEXITSTATUS(st);
504 * Child - exec checklogin with the right parameters.
506 for (n = 32; n >= 3; n--)
509 ip_ntoa(address, ut->nas_address);
510 sprintf(port, "%d", ut->nas_port);
511 sprintf(session_id, "%.8s", ut->session_id);
513 execl(CHECKRAD, "checkrad", nas->nastype, address, port,
514 ut->login, session_id, NULL);
515 log(L_ERR, "Check-TS: exec %s: %s", CHECKRAD, strerror(errno));
518 * Exit - 2 means "some error occured".
524 * See if a user is already logged in.
526 * Check twice. If on the first pass the user exceeds his
527 * max. number of logins, do a second pass and validate all
528 * logins by querying the terminal server (using eg. SNMP).
530 * Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
532 int radutmp_checksimul(char *name, VALUE_PAIR *request, int maxsimul)
541 if ((fd = open(RADUTMP, O_CREAT|O_RDWR, 0644)) < 0)
545 * We don't lock in the first pass.
548 while(read(fd, &u, sizeof(u)) == sizeof(u))
549 if (strncmp(name, u.login, RUT_NAMESIZE) == 0
550 && u.type == P_LOGIN)
553 if (count < maxsimul) {
557 lseek(fd, (off_t)0, SEEK_SET);
560 * Setup some stuff, like for MPP detection.
562 if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
566 * lockf() the file while reading/writing.
568 #if defined(F_LOCK) && !defined(BSD)
569 (void)lockf(fd, F_LOCK, LOCK_LEN);
571 (void)flock(fd, LOCK_EX);
575 * Allright, there are too many concurrent logins.
576 * Check all registered logins by querying the
577 * terminal server directly.
578 * FIXME: rad_check_ts() runs with locked radwtmp file!
581 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
582 if (strncmp(name, u.login, RUT_NAMESIZE) == 0
583 && u.type == P_LOGIN) {
584 if (rad_check_ts(&u) == 1) {
587 * Does it look like a MPP attempt?
589 if (strchr("SCPA", u.proto) &&
590 ipno && u.framed_address == ipno)
595 * False record - zap it.
598 lseek(fd, -(off_t)sizeof(u), SEEK_CUR);
600 write(fd, &u, sizeof(u));
602 #if 0 /* FIXME: should we fixup radwtmp as well or not ? */
603 if ((wfp = fopen(RADWTMP, "a")) != NULL) {
604 make_wtmp(&u, &wt, PW_STATUS_STOP);
605 fwrite(&wt, sizeof(wt), 1, wfp);
614 return (count < maxsimul) ? 0 : mpp;