/* * $Id: do_acct.c,v 1.11 2006/12/13 01:11:37 heas Exp $ * * 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 #if defined(__DragonFly__) && !defined(O_SYNC) #define O_SYNC O_FSYNC #endif static int acctfd = 0; /* * Make a acct entry into the accounting file for accounting. * Return 1 on error */ static int acct_write(char *string) { if (write(acctfd, string, strlen(string)) != strlen(string)) { report(LOG_ERR, "%s: couldn't write acct file %s %s", session.peer, session.acctfile, strerror(errno)); return(1); } if (debug & DEBUG_ACCT_FLAG) report(LOG_DEBUG, "'%s'", string); return(0); } /* * Write a string or "unknown" into the accounting file. * Return 1 on error */ static int acct_write_field(char *string) { if (string && string[0]) { if (acct_write(string)) return(1); } else { if (acct_write("unknown")) return(1); } return(0); } int do_acct(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, 0644); if (acctfd < 0) { report(LOG_ERR, "Can't open acct file %s -- %s", session.acctfile, strerror(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); } int wtmp_entry(char *line, char *name, char *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); #ifndef SOLARIS 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; #ifdef FREEBSD wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND, 0644); #else wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0644); #endif if (wtmpfd < 0) { report(LOG_ERR, "Can't open wtmp file %s -- %s", wtmpfile, strerror(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, strerror(errno)); return(1); } close(wtmpfd); if (debug & DEBUG_ACCT_FLAG) { report(LOG_DEBUG, "wtmp: %s, %s %s %d", line, name, host, utime); } return(0); } char * find_attr_value(char *attr, char **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(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); }