Import of tac_plus.v8.tar.gz: 173206 bytes, md5:
[tac_plus.git] / do_acct.c
1 /* 
2    Copyright (c) 1995-1998 by Cisco systems, Inc.
3
4    Permission to use, copy, modify, and distribute this software for
5    any purpose and without fee is hereby granted, provided that this
6    copyright and permission notice appear on all copies of the
7    software and supporting documentation, the name of Cisco Systems,
8    Inc. not be used in advertising or publicity pertaining to
9    distribution of the program without specific prior permission, and
10    notice be given in supporting documentation that modification,
11    copying and distribution is by permission of Cisco Systems, Inc.
12
13    Cisco Systems, Inc. makes no representations about the suitability
14    of this software for any purpose.  THIS SOFTWARE IS PROVIDED ``AS
15    IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
16    WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17    FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 #include "tac_plus.h"
21
22 static int acctfd = 0;
23
24 /* Make a acct entry into the accounting file for accounting. 
25    Return 1 on error  */
26
27 static int
28 acct_write(string)
29     char *string;
30 {
31     if (write(acctfd, string, strlen(string)) != strlen(string)) {
32         report(LOG_ERR, "%s: couldn't write acct file %s %s",
33                session.peer,
34                session.acctfile, sys_errlist[errno]);
35         return(1);
36     }
37     
38     if (debug & DEBUG_ACCT_FLAG)
39         report(LOG_DEBUG, "'%s'", string);
40
41     return(0);
42 }
43
44 /* Write a string or "unknown" into the accounting file.
45    Return 1 on error  */
46 static int
47 acct_write_field(string)
48     char *string;
49 {
50     if (string && string[0]) {
51         if (acct_write(string))
52             return(1);
53     } else {
54         if (acct_write("unknown"))
55             return(1);
56     }
57     return(0);
58 }
59
60 int
61 do_acct(rec)
62 struct acct_rec *rec;
63 {
64     int i, errors;
65     time_t t = time(NULL);
66     char *ct = ctime(&t);
67
68     ct[24] = '\0';
69
70     if (!acctfd) {
71         acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND,0640);
72         if (acctfd < 0) {
73             report(LOG_ERR, "Can't open acct file %s -- %s",
74                    session.acctfile, sys_errlist[errno]);
75             return(1);
76         }
77     }
78  
79    if (!tac_lockfd(session.acctfile, acctfd)) {
80         rec->admin_msg = tac_strdup("Cannot lock log file");
81         report(LOG_ERR, "%s: Cannot lock %s", 
82                session.peer, session.acctfile);
83         return(1);
84     }
85
86     errors = 0;
87
88     errors += acct_write(ct);
89     errors += acct_write("\t");
90
91     errors += acct_write_field(rec->identity->NAS_name);
92     errors += acct_write("\t");
93
94     errors += acct_write_field(rec->identity->username);
95     errors += acct_write("\t");
96
97     errors += acct_write_field(rec->identity->NAS_port);
98     errors += acct_write("\t");
99
100     errors += acct_write_field(rec->identity->NAC_address);
101     errors += acct_write("\t");
102
103     switch(rec->acct_type) {
104     case ACCT_TYPE_UPDATE:
105         errors += acct_write("update\t");
106         break;
107     case ACCT_TYPE_START:
108         errors += acct_write("start\t");
109         break;
110     case ACCT_TYPE_STOP:
111         errors += acct_write("stop\t");
112         break;
113     default:
114         errors += acct_write("unknown\t");
115         break;
116     }
117
118     for (i=0; i < rec->num_args; i++) {
119         errors += acct_write(rec->args[i]);
120         if (i < (rec->num_args-1)) 
121             errors += acct_write("\t");
122     }
123     errors += acct_write("\n");
124
125     close(acctfd);
126     acctfd = 0;
127
128     if (errors) {
129         return(1);
130     }
131     return (0);
132 }
133
134 int
135 wtmp_entry (line, name, host, utime)
136     char *line, *name, *host;
137     time_t utime;
138 {
139     struct utmp entry;
140
141     if (!wtmpfile) {
142         return(1);
143     }
144
145     bzero(&entry, sizeof entry);
146
147     if (strlen(line) < sizeof entry.ut_line)
148         strcpy(entry.ut_line, line);
149     else bcopy(line, entry.ut_line, sizeof entry.ut_line);
150
151     if (strlen(name) < sizeof entry.ut_name)
152         strcpy(entry.ut_name, name);
153     else bcopy(name, entry.ut_name, sizeof entry.ut_name);
154
155 #ifndef SOLARIS
156     if (strlen(host) < sizeof entry.ut_host)
157         strcpy(entry.ut_host, host);
158     else bcopy(host, entry.ut_host, sizeof entry.ut_host);
159 #endif
160     entry.ut_time = utime;
161
162     wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0644);      
163     if (wtmpfd < 0) {
164         report(LOG_ERR, "Can't open wtmp file %s -- %s",
165                wtmpfile, sys_errlist[errno]);
166         return(1);
167     }
168
169     if (!tac_lockfd(wtmpfile, wtmpfd)) {
170         report(LOG_ERR, "%s: Cannot lock %s", session.peer, wtmpfile);
171         return(1);
172     }
173
174     if (write(wtmpfd, &entry, sizeof entry) != (sizeof entry)) {
175         report(LOG_ERR, "%s: couldn't write wtmp file %s %s",
176                session.peer, wtmpfile, sys_errlist[errno]);
177         return(1);
178     } 
179
180     close(wtmpfd);
181
182     if (debug & DEBUG_ACCT_FLAG) {
183         report(LOG_DEBUG, "wtmp: %s, %s %s %d", line, name, host, utime);
184     }
185     
186     return(0);
187 }
188
189 char *
190 find_attr_value (attr, args, cnt)
191     char *attr, **args;
192     int cnt;
193 {
194     int i;
195
196     for (i=0; i < cnt; i++) {
197         if (!strncmp(attr, args[i], strlen(attr))) {
198             char *ptr;
199
200             for (ptr = args[i]; ptr && *ptr; ptr++) {
201                 if ((*ptr == '*') || (*ptr == '=')) {
202                     return(ptr+1);
203                 }
204             }
205             return(NULL);
206         }
207     }
208     return(NULL);
209 }
210
211 int
212 do_wtmp(rec)
213     struct acct_rec *rec;
214 {
215     time_t now = time(NULL);
216     char *service;
217     char *elapsed_time, *start_time;
218     time_t start_utime = 0, stop_utime = 0, elapsed_utime = 0;
219     
220
221     switch(rec->acct_type) {
222     case ACCT_TYPE_START:
223     case ACCT_TYPE_STOP:
224         break;
225
226     case ACCT_TYPE_UPDATE:
227     default:
228         return(0);
229     }
230
231     service = find_attr_value("service", rec->args, rec->num_args);
232
233     if (!service) {
234         /* An error */
235         return(1);
236     }
237
238     if (STREQ(service, "system")) {
239         if (rec->acct_type == ACCT_TYPE_START) {
240             /* A reload */
241             wtmp_entry("~", "", session.peer, now);
242         }
243         return(0);
244     }
245         
246     if (rec->acct_type != ACCT_TYPE_STOP) {
247         return(0);
248     }
249
250     /* 
251      * Since xtacacs logged start records containing the peer address
252      * for a connection, we have to generate them from T+ stop records.
253      * Might as well do this for exec records too.
254      */
255     
256     elapsed_time = find_attr_value("elapsed_time", rec->args, rec->num_args);
257
258     if (elapsed_time) {
259         elapsed_utime = strtol(elapsed_time, NULL, 10);
260     }
261
262     start_time = find_attr_value("start_time", rec->args, rec->num_args);
263
264     /* 
265      * Use the start_time if there is one. If not (e.g. the NAS may
266      * not know the time), assume the stop time is now, and calculate
267      * the rest
268      */
269
270     if (start_time) {
271         start_utime = strtol(start_time, NULL, 10);
272         stop_utime  = start_utime + elapsed_utime;
273     } else {
274         start_utime = now - elapsed_utime;
275         stop_utime  = now;
276     }   
277
278     if (STREQ(service, "slip") || STREQ(service, "ppp")) {
279         char *dest_addr = find_attr_value("addr", rec->args, rec->num_args);
280
281         /* The start record */
282         wtmp_entry(rec->identity->NAS_port,
283                    rec->identity->username,
284                    dest_addr,
285                    start_utime);
286
287         /* The stop record */
288         wtmp_entry(rec->identity->NAS_port,
289                    "",
290                    dest_addr,
291                    stop_utime);
292         return(0);
293     }
294
295     if (STREQ(service, "shell")) {
296         /* Start */
297         wtmp_entry(rec->identity->NAS_port,
298                    rec->identity->username,
299                    session.peer,
300                    start_utime);
301
302         /* Stop */
303         wtmp_entry(rec->identity->NAS_port,
304                    "",
305                    session.peer,
306                    stop_utime);
307         return(0);
308     }
309     return(0);
310 }