Release bumped to "gts4".
[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
21 #include "tac_plus.h"
22
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #ifdef HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #include <string.h>
32 #include <utmp.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include "do_acct.h"
38 #include "report.h"
39 #include "utils.h"
40 #include "main.h"
41 #include "do_author.h"                  /* for "struct identity" */
42
43
44 char *wtmpfile = NULL; /* for wtmp file logging */
45
46
47 static int wtmpfd = 0;
48 static int acctfd = 0;
49
50 /* Make a acct entry into the accounting file for accounting.
51    Return 1 on error  */
52
53 static int acct_write TAC_ARGS((char *string));
54
55 static int
56 acct_write(string)
57 char *string;
58 {
59     if ((unsigned long)write(acctfd, string, strlen(string)) != strlen(string)) {
60         report(LOG_ERR, "%s: couldn't write acct file %s %s",
61                session.peer,
62                session.acctfile, sys_errlist[errno]);
63         return(1);
64     }
65
66     if (debug & DEBUG_ACCT_FLAG)
67         report(LOG_DEBUG, "'%s'", string);
68
69     return(0);
70 }
71
72 static int acct_write_field TAC_ARGS((char *string));
73
74 /* Write a string or "unknown" into the accounting file.
75    Return 1 on error  */
76 static int
77 acct_write_field(string)
78 char *string;
79 {
80     if (string && string[0]) {
81         if (acct_write(string))
82             return(1);
83     } else {
84         if (acct_write("unknown"))
85             return(1);
86     }
87     return(0);
88 }
89
90 int do_acct TAC_ARGS((struct acct_rec *rec));
91
92 int
93 do_acct(rec)
94 struct acct_rec *rec;
95 {
96     int i, errors;
97     time_t t = time(NULL);
98     char *ct = ctime(&t);
99
100     ct[24] = '\0';
101
102     if (!acctfd) {
103         acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND,0640);
104         if (acctfd < 0) {
105             report(LOG_ERR, "Can't open acct file %s -- %s",
106                    session.acctfile, sys_errlist[errno]);
107             return(1);
108         }
109     }
110
111    if (!tac_lockfd(session.acctfile, acctfd)) {
112         rec->admin_msg = tac_strdup("Cannot lock log file");
113         report(LOG_ERR, "%s: Cannot lock %s",
114                session.peer, session.acctfile);
115         return(1);
116     }
117
118     errors = 0;
119
120     errors += acct_write(ct);
121     errors += acct_write("\t");
122
123     errors += acct_write_field(rec->identity->NAS_name);
124     errors += acct_write("\t");
125
126     errors += acct_write_field(rec->identity->username);
127     errors += acct_write("\t");
128
129     errors += acct_write_field(rec->identity->NAS_port);
130     errors += acct_write("\t");
131
132     errors += acct_write_field(rec->identity->NAC_address);
133     errors += acct_write("\t");
134
135     switch(rec->acct_type) {
136     case ACCT_TYPE_UPDATE:
137         errors += acct_write("update\t");
138         break;
139     case ACCT_TYPE_START:
140         errors += acct_write("start\t");
141         break;
142     case ACCT_TYPE_STOP:
143         errors += acct_write("stop\t");
144         break;
145     default:
146         errors += acct_write("unknown\t");
147         break;
148     }
149
150     for (i=0; i < rec->num_args; i++) {
151         errors += acct_write(rec->args[i]);
152         if (i < (rec->num_args-1))
153             errors += acct_write("\t");
154     }
155     errors += acct_write("\n");
156
157     close(acctfd);
158     acctfd = 0;
159
160     if (errors) {
161         return(1);
162     }
163     return (0);
164 }
165
166 static int wtmp_entry TAC_ARGS((char *line, char *name, char *host, time_t utime));
167
168 static int
169 wtmp_entry (line, name, host, utime)
170 char *line, *name, *host;
171 time_t utime;
172 {
173     struct utmp entry;
174
175     if (!wtmpfile) {
176         return(1);
177     }
178
179     bzero(&entry, sizeof entry);
180
181     if (strlen(line) < sizeof entry.ut_line)
182         strcpy(entry.ut_line, line);
183     else bcopy(line, entry.ut_line, sizeof entry.ut_line);
184
185     if (strlen(name) < sizeof entry.ut_name)
186         strcpy(entry.ut_name, name);
187     else bcopy(name, entry.ut_name, sizeof entry.ut_name);
188
189 #ifdef HAVE_UTMP_UT_HOST
190     if (strlen(host) < sizeof entry.ut_host)
191         strcpy(entry.ut_host, host);
192     else bcopy(host, entry.ut_host, sizeof entry.ut_host);
193 #endif
194     entry.ut_time = utime;
195
196     wtmpfd = open(wtmpfile, O_CREAT | O_WRONLY | O_APPEND | O_SYNC, 0644);
197     if (wtmpfd < 0) {
198         report(LOG_ERR, "Can't open wtmp file %s -- %s",
199                wtmpfile, sys_errlist[errno]);
200         return(1);
201     }
202
203     if (!tac_lockfd(wtmpfile, wtmpfd)) {
204         report(LOG_ERR, "%s: Cannot lock %s", session.peer, wtmpfile);
205         return(1);
206     }
207
208     if (write(wtmpfd, &entry, sizeof entry) != (sizeof entry)) {
209         report(LOG_ERR, "%s: couldn't write wtmp file %s %s",
210                session.peer, wtmpfile, sys_errlist[errno]);
211         return(1);
212     }
213
214     close(wtmpfd);
215
216     if (debug & DEBUG_ACCT_FLAG) {
217         report(LOG_DEBUG, "wtmp: %s, %s %s %ld", line, name, host, (long)utime);
218     }
219
220     return(0);
221 }
222
223 char *find_attr_value TAC_ARGS((char *attr, char **args, int cnt));
224
225 char *
226 find_attr_value (attr, args, cnt)
227 char *attr, **args;
228 int cnt;
229 {
230     int i;
231
232     for (i=0; i < cnt; i++) {
233         if (!strncmp(attr, args[i], strlen(attr))) {
234             char *ptr;
235
236             for (ptr = args[i]; ptr && *ptr; ptr++) {
237                 if ((*ptr == '*') || (*ptr == '=')) {
238                     return(ptr+1);
239                 }
240             }
241             return(NULL);
242         }
243     }
244     return(NULL);
245 }
246
247 int do_wtmp TAC_ARGS((struct acct_rec *rec));
248
249 int
250 do_wtmp(rec)
251 struct acct_rec *rec;
252 {
253     time_t now = time(NULL);
254     char *service;
255     char *elapsed_time, *start_time;
256     time_t start_utime = 0, stop_utime = 0, elapsed_utime = 0;
257
258
259     switch(rec->acct_type) {
260     case ACCT_TYPE_START:
261     case ACCT_TYPE_STOP:
262         break;
263
264     case ACCT_TYPE_UPDATE:
265     default:
266         return(0);
267     }
268
269     service = find_attr_value("service", rec->args, rec->num_args);
270
271     if (!service) {
272         /* An error */
273         return(1);
274     }
275
276     if (STREQ(service, "system")) {
277         if (rec->acct_type == ACCT_TYPE_START) {
278             /* A reload */
279             wtmp_entry("~", "", session.peer, now);
280         }
281         return(0);
282     }
283
284     if (rec->acct_type != ACCT_TYPE_STOP) {
285         return(0);
286     }
287
288     /*
289      * Since xtacacs logged start records containing the peer address
290      * for a connection, we have to generate them from T+ stop records.
291      * Might as well do this for exec records too.
292      */
293
294     elapsed_time = find_attr_value("elapsed_time", rec->args, rec->num_args);
295
296     if (elapsed_time) {
297         elapsed_utime = strtol(elapsed_time, NULL, 10);
298     }
299
300     start_time = find_attr_value("start_time", rec->args, rec->num_args);
301
302     /*
303      * Use the start_time if there is one. If not (e.g. the NAS may
304      * not know the time), assume the stop time is now, and calculate
305      * the rest
306      */
307
308     if (start_time) {
309         start_utime = strtol(start_time, NULL, 10);
310         stop_utime  = start_utime + elapsed_utime;
311     } else {
312         start_utime = now - elapsed_utime;
313         stop_utime  = now;
314     }
315
316     if (STREQ(service, "slip") || STREQ(service, "ppp")) {
317         char *dest_addr = find_attr_value("addr", rec->args, rec->num_args);
318
319         /* The start record */
320         wtmp_entry(rec->identity->NAS_port,
321                    rec->identity->username,
322                    dest_addr,
323                    start_utime);
324
325         /* The stop record */
326         wtmp_entry(rec->identity->NAS_port,
327                    "",
328                    dest_addr,
329                    stop_utime);
330         return(0);
331     }
332
333     if (STREQ(service, "shell")) {
334         /* Start */
335         wtmp_entry(rec->identity->NAS_port,
336                    rec->identity->username,
337                    session.peer,
338                    start_utime);
339
340         /* Stop */
341         wtmp_entry(rec->identity->NAS_port,
342                    "",
343                    session.peer,
344                    stop_utime);
345         return(0);
346     }
347     return(0);
348 }