tacacs-F4.0.4.28/do_acct.c (274 lines of code) (raw):

/* * $Id: do_acct.c,v 1.13 2009-03-17 18:38:12 heas Exp $ * * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved * 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 <limits.h> #include <time.h> #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_file(struct acct_rec *rec) { int i, errors; time_t t = time(NULL); char ct[LINE_MAX]; struct tm *tm; tm = localtime(&t); strftime(ct, LINE_MAX, "%h %e %T", tm); 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); } /* * Write accounting data to syslog */ int do_acct_syslog(struct acct_rec *rec) { char *acct_type; char *cmdbuf; int written, i; size_t bufsize; bufsize = 1024; cmdbuf = tac_malloc(bufsize); memset(cmdbuf, 0, bufsize); written = 0; switch(rec->acct_type) { case ACCT_TYPE_UPDATE: acct_type = "update"; break; case ACCT_TYPE_START: acct_type = "start"; break; case ACCT_TYPE_STOP: acct_type = "stop"; break; default: acct_type = "default"; } for (i = 0; i < rec->num_args; i++) { /* possible 4 spaces and and a null terminator == 5 */ if ((strlen(rec->args[i]) + written + 5) > bufsize) { cmdbuf = tac_realloc(cmdbuf, strlen(rec->args[i]) + written + 5); bufsize += strlen(rec->args[i]) + written + 5; } strncat(cmdbuf, rec->args[i], strlen(rec->args[i])); written += strlen(rec->args[i]); if (i < (rec->num_args - 1)) { strncat(cmdbuf, " ", 4); written += 4; } } syslog(LOG_INFO, "%s %s %s %s %s %s", ((rec->identity->NAS_name) && rec->identity->NAS_name[0]) ? rec->identity->NAS_name : "unknown", ((rec->identity->username) && rec->identity->username[0]) ? rec->identity->username : "unknown", ((rec->identity->NAS_port) && rec->identity->NAS_port[0]) ? rec->identity->NAS_port : "unknown", ((rec->identity->NAC_address) && rec->identity->NAC_address[0]) ? rec->identity->NAC_address : "unknown", acct_type, cmdbuf); free(cmdbuf); return 0; } int wtmp_entry(char *line, char *name, char *host, time_t utime) { #if HAVE_UTMP_H struct utmp entry; #elif HAVE_UTMPX_H struct utmpx entry; #endif if (!wtmpfile) { return(1); } #if HAVE_UTMPX_H && !HAVE_UTMP_H # define ut_name ut_user #endif memset(&entry, 0, sizeof entry); if (strlen(line) < sizeof entry.ut_line) strcpy(entry.ut_line, line); else memcpy(entry.ut_line, line, sizeof(entry.ut_line)); if (strlen(name) < sizeof entry.ut_name) strcpy(entry.ut_name, name); else memcpy(entry.ut_name, name, sizeof(entry.ut_name)); #ifndef SOLARIS if (strlen(host) < sizeof entry.ut_host) strcpy(entry.ut_host, host); else memcpy(entry.ut_host, host, sizeof(entry.ut_host)); #endif #if HAVE_UTMP_H entry.ut_time = utime; #elif HAVE_UTMPX_H entry.ut_tv.tv_sec = utime; #else # error "unknown utmp time field" #endif #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); }