/* Copyright (c) 1995-1998 by Cisco systems, Inc. Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems, Inc. Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "tac_plus.h" #include #include #include #include #include #ifdef HAVE_FCNTL_H #include #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #include "do_acct.h" #include "report.h" #include "utils.h" #include "main.h" #include "do_author.h" /* for "struct identity" */ char *wtmpfile = NULL; /* for wtmp file logging */ static int wtmpfd = 0; static int acctfd = 0; /* Make a acct entry into the accounting file for accounting. Return 1 on error */ static int acct_write TAC_ARGS((char *string)); static int acct_write(string) char *string; { if ((unsigned long)write(acctfd, string, strlen(string)) != strlen(string)) { report(LOG_ERR, "%s: couldn't write acct file %s %s", session.peer, session.acctfile, sys_errlist[errno]); return(1); } if (debug & DEBUG_ACCT_FLAG) report(LOG_DEBUG, "'%s'", string); return(0); } static int acct_write_field TAC_ARGS((char *string)); /* Write a string or "unknown" into the accounting file. Return 1 on error */ static int acct_write_field(string) char *string; { if (string && string[0]) { if (acct_write(string)) return(1); } else { if (acct_write("unknown")) return(1); } return(0); } int do_acct TAC_ARGS((struct acct_rec *rec)); int do_acct(rec) struct acct_rec *rec; { int i, errors; time_t t = time(NULL); char *ct = ctime(&t); ct[24] = '\0'; if (!acctfd) { acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND,0640); if (acctfd < 0) { report(LOG_ERR, "Can't open acct file %s -- %s", session.acctfile, sys_errlist[errno]); return(1); } } if (!tac_lockfd(session.acctfile, acctfd)) { rec->admin_msg = tac_strdup("Cannot lock log file"); report(LOG_ERR, "%s: Cannot lock %s", session.peer, session.acctfile); return(1); } errors = 0; errors += acct_write(ct); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAS_name); errors += acct_write("\t"); errors += acct_write_field(rec->identity->username); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAS_port); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAC_address); errors += acct_write("\t"); switch(rec->acct_type) { case ACCT_TYPE_UPDATE: errors += acct_write("update\t"); break; case ACCT_TYPE_START: errors += acct_write("start\t"); break; case ACCT_TYPE_STOP: errors += acct_write("stop\t"); break; default: errors += acct_write("unknown\t"); break; } for (i=0; i < rec->num_args; i++) { errors += acct_write(rec->args[i]); if (i < (rec->num_args-1)) errors += acct_write("\t"); } errors += acct_write("\n"); close(acctfd); acctfd = 0; if (errors) { return(1); } return (0); } static int wtmp_entry TAC_ARGS((char *line, char *name, char *host, time_t utime)); static int wtmp_entry (line, name, host, utime) char *line, *name, *host; time_t utime; { struct utmp entry; if (!wtmpfile) { return(1); } bzero(&entry, sizeof entry); if (strlen(line) < sizeof entry.ut_line) strcpy(entry.ut_line, line); else bcopy(line, entry.ut_line, sizeof entry.ut_line); if (strlen(name) < sizeof entry.ut_name) strcpy(entry.ut_name, name); else bcopy(name, entry.ut_name, sizeof entry.ut_name); #ifdef HAVE_UTMP_UT_HOST if (strlen(host) < sizeof entry.ut_host) strcpy(entry.ut_host, host); else bcopy(host, entry.ut_host, sizeof entry.ut_host); #endif entry.ut_time = utime; wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0644); if (wtmpfd < 0) { report(LOG_ERR, "Can't open wtmp file %s -- %s", wtmpfile, sys_errlist[errno]); return(1); } if (!tac_lockfd(wtmpfile, wtmpfd)) { report(LOG_ERR, "%s: Cannot lock %s", session.peer, wtmpfile); return(1); } if (write(wtmpfd, &entry, sizeof entry) != (sizeof entry)) { report(LOG_ERR, "%s: couldn't write wtmp file %s %s", session.peer, wtmpfile, sys_errlist[errno]); return(1); } close(wtmpfd); if (debug & DEBUG_ACCT_FLAG) { report(LOG_DEBUG, "wtmp: %s, %s %s %ld", line, name, host, (long)utime); } return(0); } char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt)); char * find_attr_value (attr, args, cnt) char *attr, **args; int cnt; { int i; for (i=0; i < cnt; i++) { if (!strncmp(attr, args[i], strlen(attr))) { char *ptr; for (ptr = args[i]; ptr && *ptr; ptr++) { if ((*ptr == '*') || (*ptr == '=')) { return(ptr+1); } } return(NULL); } } return(NULL); } int do_wtmp TAC_ARGS((struct acct_rec *rec)); int do_wtmp(rec) struct acct_rec *rec; { time_t now = time(NULL); char *service; char *elapsed_time, *start_time; time_t start_utime = 0, stop_utime = 0, elapsed_utime = 0; switch(rec->acct_type) { case ACCT_TYPE_START: case ACCT_TYPE_STOP: break; case ACCT_TYPE_UPDATE: default: return(0); } service = find_attr_value("service", rec->args, rec->num_args); if (!service) { /* An error */ return(1); } if (STREQ(service, "system")) { if (rec->acct_type == ACCT_TYPE_START) { /* A reload */ wtmp_entry("~", "", session.peer, now); } return(0); } if (rec->acct_type != ACCT_TYPE_STOP) { return(0); } /* * Since xtacacs logged start records containing the peer address * for a connection, we have to generate them from T+ stop records. * Might as well do this for exec records too. */ elapsed_time = find_attr_value("elapsed_time", rec->args, rec->num_args); if (elapsed_time) { elapsed_utime = strtol(elapsed_time, NULL, 10); } start_time = find_attr_value("start_time", rec->args, rec->num_args); /* * Use the start_time if there is one. If not (e.g. the NAS may * not know the time), assume the stop time is now, and calculate * the rest */ if (start_time) { start_utime = strtol(start_time, NULL, 10); stop_utime = start_utime + elapsed_utime; } else { start_utime = now - elapsed_utime; stop_utime = now; } if (STREQ(service, "slip") || STREQ(service, "ppp")) { char *dest_addr = find_attr_value("addr", rec->args, rec->num_args); /* The start record */ wtmp_entry(rec->identity->NAS_port, rec->identity->username, dest_addr, start_utime); /* The stop record */ wtmp_entry(rec->identity->NAS_port, "", dest_addr, stop_utime); return(0); } if (STREQ(service, "shell")) { /* Start */ wtmp_entry(rec->identity->NAS_port, rec->identity->username, session.peer, start_utime); /* Stop */ wtmp_entry(rec->identity->NAS_port, "", session.peer, stop_utime); return(0); } return(0); }