2 * radutmp.c Radius session management.
8 static const char rcsid[] =
27 # include <sys/wait.h>
30 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
33 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
39 static const char porttypes[] = "ASITX";
41 #define LOCK_LEN sizeof(struct radutmp)
44 * used for caching radutmp lookups.
46 typedef struct nas_port {
50 struct nas_port *next;
52 static NAS_PORT *nas_port_list = NULL;
56 * Lookup a NAS_PORT in the nas_port_list
58 static NAS_PORT *nas_port_find(uint32_t nasaddr, int port)
62 for(cl = nas_port_list; cl; cl = cl->next)
63 if (nasaddr == cl->nasaddr &&
71 * Zap a user, or all users on a NAS, from the radutmp file.
73 int radutmp_zap(uint32_t nasaddr, int port, char *user, time_t t)
80 fp = fopen(RADWTMP, "a");
82 if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
86 * Lock the utmp file, prefer lockf() over flock().
88 #if defined(F_LOCK) && !defined(BSD)
89 (void)lockf(fd, F_LOCK, LOCK_LEN);
91 (void)flock(fd, LOCK_EX);
94 * Find the entry for this NAS / portno combination.
97 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
98 if (((nasaddr != 0 && nasaddr != u.nas_address) ||
99 (port >= 0 && port != u.nas_port) ||
101 strncmp(u.login, user, sizeof(u.login)) != 0) ||
107 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
108 radlog(L_ERR, "Accounting: radutmp_zap: "
109 "negative lseek!\n");
110 lseek(fd, (off_t)0, SEEK_SET);
114 write(fd, &u, sizeof(u));
116 #if 0 /* FIXME: should we fixup radwtmp as well or not ? */
118 * Add a logout entry to the wtmp file.
121 make_wtmp(&u, &wt, PW_STATUS_STOP);
122 fwrite(&wt, sizeof(wt), 1, fp);
135 * Store logins in the RADIUS utmp file.
137 int radutmp_add(REQUEST *request)
139 struct radutmp ut, u;
144 int framed_address = 0;
149 int just_an_update = 0;
151 int nas_port_type = 0;
155 * Which type is this.
157 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
158 radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
162 if (status == PW_STATUS_ACCOUNTING_ON ||
163 status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
166 (vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL) do {
171 * ComOS (up to and including 3.5.1b20) does not send
172 * standard PW_STATUS_ACCOUNTING_XXX messages.
174 * Check for: o no Acct-Session-Time, or time of 0
175 * o Acct-Session-Id of "00000000".
177 * We could also check for NAS-Port, that attribute
178 * should NOT be present (but we don't right now).
180 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
181 == NULL || vp->lvalue == 0)
183 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
184 != NULL && vp->length == 8 &&
185 memcmp(vp->strvalue, "00000000", 8) == 0)
187 if (check1 == 0 || check2 == 0) {
188 #if 0 /* Cisco sometimes sends START records without username. */
189 radlog(L_ERR, "Accounting: no username in record");
195 radlog(L_INFO, "Accounting: converting reboot records.");
196 if (status == PW_STATUS_STOP)
197 status = PW_STATUS_ACCOUNTING_OFF;
198 if (status == PW_STATUS_START)
199 status = PW_STATUS_ACCOUNTING_ON;
204 memset(&ut, 0, sizeof(ut));
208 * First, find the interesting attributes.
210 for (vp = request->packet->vps; vp; vp = vp->next) {
211 switch (vp->attribute) {
213 strncpy(ut.login, (char *)vp->strvalue,
216 case PW_LOGIN_IP_HOST:
217 case PW_FRAMED_IP_ADDRESS:
218 framed_address = vp->lvalue;
219 ut.framed_address = vp->lvalue;
221 case PW_FRAMED_PROTOCOL:
222 protocol = vp->lvalue;
224 case PW_NAS_IP_ADDRESS:
225 nas_address = vp->lvalue;
226 ut.nas_address = vp->lvalue;
229 ut.nas_port = vp->lvalue;
232 case PW_ACCT_DELAY_TIME:
233 ut.delay = vp->lvalue;
235 case PW_ACCT_SESSION_ID:
237 * If length > 8, only store the
240 off = vp->length - sizeof(ut.session_id);
241 if (off < 0) off = 0;
242 memcpy(ut.session_id, vp->strvalue + off,
243 sizeof(ut.session_id));
245 case PW_NAS_PORT_TYPE:
246 if (vp->lvalue >= 0 && vp->lvalue <= 4)
247 ut.porttype = porttypes[vp->lvalue];
248 nas_port_type = vp->lvalue;
250 case PW_CALLING_STATION_ID:
251 strncpy(ut.caller_id, (char *)vp->strvalue,
252 sizeof(ut.caller_id));
253 ut.caller_id[sizeof(ut.caller_id) - 1] = 0;
259 * If we didn't find out the NAS address, use the
260 * originator's IP address.
262 if (nas_address == 0) {
263 nas_address = request->packet->src_ipaddr;
264 ut.nas_address = nas_address;
267 if (protocol == PW_PPP)
269 else if (protocol == PW_SLIP)
273 ut.time = t - ut.delay;
276 * See if this was a portmaster reboot.
278 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
279 radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
280 nas_name(nas_address));
281 radutmp_zap(nas_address, -1, NULL, ut.time);
284 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
285 radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
286 nas_name(nas_address));
287 radutmp_zap(nas_address, -1, NULL, ut.time);
292 * If we don't know this type of entry pretend we succeeded.
294 if (status != PW_STATUS_START &&
295 status != PW_STATUS_STOP &&
296 status != PW_STATUS_ALIVE) {
297 radlog(L_ERR, "NAS %s port %d unknown packet type %d)",
298 nas_name(nas_address), ut.nas_port, status);
303 * Perhaps we don't want to store this record into
304 * radutmp. We skip records:
306 * - without a NAS-Port-Id (telnet / tcp access)
307 * - with the username "!root" (console admin login)
309 if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
313 * Enter into the radutmp file.
315 if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
320 * Lock the utmp file, prefer lockf() over flock().
322 #if defined(F_LOCK) && !defined(BSD)
323 (void)lockf(fd, F_LOCK, LOCK_LEN);
325 (void)flock(fd, LOCK_EX);
328 * Find the entry for this NAS / portno combination.
330 if ((cache = nas_port_find(ut.nas_address, ut.nas_port)) != NULL)
331 lseek(fd, (off_t)cache->offset, SEEK_SET);
335 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
337 if (u.nas_address != ut.nas_address ||
338 u.nas_port != ut.nas_port)
341 if (status == PW_STATUS_STOP &&
342 strncmp(ut.session_id, u.session_id,
343 sizeof(u.session_id)) != 0) {
345 * Don't complain if this is not a
346 * login record (some clients can
347 * send _only_ logout records).
349 if (u.type == P_LOGIN)
351 "Accounting: logout: entry for NAS %s port %d has wrong ID",
352 nas_name(nas_address), u.nas_port);
357 if (status == PW_STATUS_START &&
358 strncmp(ut.session_id, u.session_id,
359 sizeof(u.session_id)) == 0 &&
361 if (u.type == P_LOGIN) {
363 "Accounting: login: entry for NAS %s port %d duplicate",
364 nas_name(nas_address), u.nas_port);
369 "Accounting: login: entry for NAS %s port %d wrong order",
370 nas_name(nas_address), u.nas_port);
376 * FIXME: the ALIVE record could need
377 * some more checking, but anyway I'd
378 * rather rewrite this mess -- miquels.
380 if (status == PW_STATUS_ALIVE &&
381 strncmp(ut.session_id, u.session_id,
382 sizeof(u.session_id)) == 0 &&
385 * Keep the original login time.
392 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
393 radlog(L_ERR, "Accounting: negative lseek!\n");
394 lseek(fd, (off_t)0, SEEK_SET);
402 if (r >= 0 && (status == PW_STATUS_START ||
403 status == PW_STATUS_ALIVE)) {
405 if ((cache = malloc(sizeof(NAS_PORT))) != NULL) {
406 cache->nasaddr = ut.nas_address;
407 cache->port = ut.nas_port;
409 cache->next = nas_port_list;
410 nas_port_list = cache;
414 write(fd, &ut, sizeof(u));
416 if (status == PW_STATUS_STOP) {
421 write(fd, &u, sizeof(u));
424 "Accounting: logout: login entry for NAS %s port %d not found",
425 nas_name(nas_address), ut.nas_port);
431 radlog(L_ERR, "Accounting: %s: %s", RADUTMP, strerror(errno));
440 * Timeout handler (10 secs)
443 static void alrm_handler(int sig)
445 sig = sig; /* -Wunused */
450 * Check one terminal server to see if a user is logged in.
452 static int rad_check_ts(uint32_t nasaddr, int portnum, const char *user,
453 const char *session_id)
460 void (*handler)(int);
465 if ((nas = nas_find(nasaddr)) == NULL) {
466 radlog(L_ERR, "Accounting: unknown NAS");
473 handler = signal(SIGCHLD, SIG_DFL);
474 if ((pid = fork()) < 0) {
475 radlog(L_ERR, "Accounting: fork: %s", strerror(errno));
476 signal(SIGCHLD, handler);
482 * Parent - Wait for checkrad to terminate.
483 * We timeout in 10 seconds.
486 signal(SIGALRM, alrm_handler);
488 while((e = waitpid(pid, &st, 0)) != pid)
489 if (e < 0 && (errno != EINTR || got_alrm))
492 signal(SIGCHLD, handler);
497 radlog(L_ERR, "Check-TS: timeout waiting for checkrad");
501 radlog(L_ERR, "Check-TS: unknown error in waitpid()");
504 return WEXITSTATUS(st);
508 * Child - exec checklogin with the right parameters.
510 for (n = 32; n >= 3; n--)
513 ip_ntoa(address, nasaddr);
514 sprintf(port, "%d", portnum);
517 /* OS/2 can't directly execute scripts then we call the command
518 processor to execute checkrad
520 execl(getenv("COMSPEC"), "", "/C","checkrad",nas->nastype, address, port,
521 user, session_id, NULL);
523 execl(CHECKRAD, "checkrad",nas->nastype, address, port,
524 user, session_id, NULL);
526 radlog(L_ERR, "Check-TS: exec %s: %s", CHECKRAD, strerror(errno));
529 * Exit - 2 means "some error occured".
535 * See if a user is already logged in.
537 * Check twice. If on the first pass the user exceeds his
538 * max. number of logins, do a second pass and validate all
539 * logins by querying the terminal server (using eg. SNMP).
541 * Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
543 int radutmp_checksimul(char *name, VALUE_PAIR *request, int maxsimul)
552 if ((fd = open(RADUTMP, O_CREAT|O_RDWR, 0644)) < 0)
556 * We don't lock in the first pass.
559 while(read(fd, &u, sizeof(u)) == sizeof(u))
560 if (strncmp(name, u.login, RUT_NAMESIZE) == 0
561 && u.type == P_LOGIN)
564 if (count < maxsimul) {
568 lseek(fd, (off_t)0, SEEK_SET);
571 * Setup some stuff, like for MPP detection.
573 if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
577 * lockf() the file while reading/writing.
579 #if defined(F_LOCK) && !defined(BSD)
580 (void)lockf(fd, F_LOCK, LOCK_LEN);
582 (void)flock(fd, LOCK_EX);
586 * Allright, there are too many concurrent logins.
587 * Check all registered logins by querying the
588 * terminal server directly.
589 * FIXME: rad_check_ts() runs with locked radutmp file!
592 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
593 if (strncmp(name, u.login, RUT_NAMESIZE) == 0
594 && u.type == P_LOGIN) {
595 char session_id[sizeof u.session_id+1];
596 strNcpy(session_id, u.session_id, sizeof session_id);
597 if (rad_check_ts(u.nas_address, u.nas_port,
598 u.login, session_id) == 1) {
601 * Does it look like a MPP attempt?
603 if (strchr("SCPA", u.proto) &&
604 ipno && u.framed_address == ipno)
609 * False record - zap it.
612 lseek(fd, -(off_t)sizeof(u), SEEK_CUR);
614 write(fd, &u, sizeof(u));
616 #if 0 /* FIXME: should we fixup radwtmp as well or not ? */
617 if ((wfp = fopen(RADWTMP, "a")) != NULL) {
618 make_wtmp(&u, &wt, PW_STATUS_STOP);
619 fwrite(&wt, sizeof(wt), 1, wfp);
628 return (count < maxsimul) ? 0 : mpp;